From bc6a32744c58b711176f92f30fae22acfb51106b Mon Sep 17 00:00:00 2001
From: Aleksei Stepanov <penguinolog@gmail.com>
Date: Mon, 25 Oct 2021 11:06:10 +0200
Subject: [PATCH 1/3] Python compatibility: drop `u` string prefix as
 deprecated

`u` prefix was used in python 2, before all strings became unicode
---
 docs/source/public_server.rst                 |  14 +--
 notebook/_sysinfo.py                          |   6 +-
 notebook/auth/login.py                        |   6 +-
 notebook/auth/tests/test_security.py          |  10 +-
 notebook/base/handlers.py                     |   4 +-
 notebook/bundler/bundlerextensions.py         |   6 +-
 notebook/bundler/tests/test_bundler_api.py    |   6 +-
 notebook/edit/handlers.py                     |   2 +-
 notebook/jstest.py                            |   4 +-
 notebook/kernelspecs/handlers.py              |   2 +-
 notebook/nbconvert/handlers.py                |   2 +-
 .../tests/test_nbconvert_handlers.py          |  34 +++---
 notebook/nbextensions.py                      |  34 +++---
 notebook/notebookapp.py                       |  30 ++---
 notebook/serverextensions.py                  |  16 +--
 notebook/services/contents/checkpoints.py     |   4 +-
 notebook/services/contents/filecheckpoints.py |  10 +-
 notebook/services/contents/fileio.py          |   6 +-
 notebook/services/contents/filemanager.py     |  32 +++---
 notebook/services/contents/handlers.py        |  22 ++--
 .../services/contents/largefilemanager.py     |  12 +-
 notebook/services/contents/manager.py         |  14 +--
 .../contents/tests/test_contents_api.py       | 104 +++++++++---------
 .../services/contents/tests/test_fileio.py    |  26 ++---
 .../contents/tests/test_largefilemanager.py   |   4 +-
 .../services/contents/tests/test_manager.py   |  34 +++---
 notebook/services/kernels/kernelmanager.py    |   2 +-
 notebook/services/kernelspecs/handlers.py     |   2 +-
 .../kernelspecs/tests/test_kernelspecs_api.py |   2 +-
 notebook/services/sessions/sessionmanager.py  |   2 +-
 .../sessions/tests/test_sessionmanager.py     |  28 ++---
 notebook/terminal/terminalmanager.py          |   2 +-
 notebook/tests/selenium/conftest.py           |   4 +-
 notebook/tests/test_files.py                  |  12 +-
 notebook/tests/test_nbextensions.py           |  72 ++++++------
 notebook/tests/test_utils.py                  |   2 +-
 notebook/tree/tests/test_tree_handler.py      |   2 +-
 notebook/utils.py                             |   4 +-
 notebook/view/handlers.py                     |   2 +-
 39 files changed, 290 insertions(+), 290 deletions(-)

diff --git a/docs/source/public_server.rst b/docs/source/public_server.rst
index edadbe3ffc..badbc9c349 100644
--- a/docs/source/public_server.rst
+++ b/docs/source/public_server.rst
@@ -137,7 +137,7 @@ You can then add the hashed password to your
 :file:`jupyter_notebook_config.py` is in your Jupyter folder in your home
 directory, ``~/.jupyter``, e.g.::
 
-    c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
+    c.NotebookApp.password = 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
 
 Automatic password setup will store the hash in ``jupyter_notebook_config.json``
 while this method stores the hash in ``jupyter_notebook_config.py``. The ``.json``
@@ -210,11 +210,11 @@ following::
 
      # Set options for certfile, ip, password, and toggle off
      # browser auto-opening
-     c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
-     c.NotebookApp.keyfile = u'/absolute/path/to/your/certificate/mykey.key'
+     c.NotebookApp.certfile = '/absolute/path/to/your/certificate/mycert.pem'
+     c.NotebookApp.keyfile = '/absolute/path/to/your/certificate/mykey.key'
      # Set ip to '*' to bind on all interfaces (ips) for the public server
      c.NotebookApp.ip = '*'
-     c.NotebookApp.password = u'sha1:bcd259ccf...<your hashed password here>'
+     c.NotebookApp.password = 'sha1:bcd259ccf...<your hashed password here>'
      c.NotebookApp.open_browser = False
 
      # It is a good idea to set a known, fixed port for server access
@@ -249,11 +249,11 @@ following::
 
      # Set options for certfile, ip, password, and toggle off
      # browser auto-opening
-     c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/fullchain.pem'
-     c.NotebookApp.keyfile = u'/absolute/path/to/your/certificate/privkey.pem'
+     c.NotebookApp.certfile = '/absolute/path/to/your/certificate/fullchain.pem'
+     c.NotebookApp.keyfile = '/absolute/path/to/your/certificate/privkey.pem'
      # Set ip to '*' to bind on all interfaces (ips) for the public server
      c.NotebookApp.ip = '*'
-     c.NotebookApp.password = u'sha1:bcd259ccf...<your hashed password here>'
+     c.NotebookApp.password = 'sha1:bcd259ccf...<your hashed password here>'
      c.NotebookApp.open_browser = False
 
      # It is a good idea to set a known, fixed port for server access
diff --git a/notebook/_sysinfo.py b/notebook/_sysinfo.py
index 951ffd1014..4abeadcedb 100644
--- a/notebook/_sysinfo.py
+++ b/notebook/_sysinfo.py
@@ -55,10 +55,10 @@ def pkg_commit_hash(pkg_path):
             if repo_commit:
                 return 'repository', repo_commit.strip().decode('ascii')
             else:
-                return u'', u''
+                return '', ''
         par_path = p.dirname(par_path)
-                
-    return u'', u''
+
+    return '', ''
 
 
 def pkg_info(pkg_path):
diff --git a/notebook/auth/login.py b/notebook/auth/login.py
index 1ac434dc5e..c8ce992bf1 100644
--- a/notebook/auth/login.py
+++ b/notebook/auth/login.py
@@ -75,8 +75,8 @@ def passwd_check(self, a, b):
         return passwd_check(a, b)
     
     def post(self):
-        typed_password = self.get_argument('password', default=u'')
-        new_password = self.get_argument('new_password', default=u'')
+        typed_password = self.get_argument('password', default='')
+        new_password = self.get_argument('new_password', default='')
 
 
         
@@ -245,7 +245,7 @@ def password_from_settings(cls, settings):
 
         If there is no configured password, an empty string will be returned.
         """
-        return settings.get('password', u'')
+        return settings.get('password', '')
 
     @classmethod
     def get_login_available(cls, settings):
diff --git a/notebook/auth/tests/test_security.py b/notebook/auth/tests/test_security.py
index cf748cda82..b042fcbfa9 100644
--- a/notebook/auth/tests/test_security.py
+++ b/notebook/auth/tests/test_security.py
@@ -18,8 +18,8 @@ def test_bad():
 
 def test_passwd_check_unicode():
     # GH issue #4524
-    phash = u'sha1:23862bc21dd3:7a415a95ae4580582e314072143d9c382c491e4f'
-    assert passwd_check(phash, u"łe¶ŧ←↓→")
-    phash = (u'argon2:$argon2id$v=19$m=10240,t=10,p=8$'
-             u'qjjDiZUofUVVnrVYxacnbA$l5pQq1bJ8zglGT2uXP6iOg')
-    assert passwd_check(phash, u"łe¶ŧ←↓→")
+    phash = 'sha1:23862bc21dd3:7a415a95ae4580582e314072143d9c382c491e4f'
+    assert passwd_check(phash, "łe¶ŧ←↓→")
+    phash = ('argon2:$argon2id$v=19$m=10240,t=10,p=8$'
+             'qjjDiZUofUVVnrVYxacnbA$l5pQq1bJ8zglGT2uXP6iOg')
+    assert passwd_check(phash, "łe¶ŧ←↓→")
diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py
index 216480291d..ab69a5b1c6 100755
--- a/notebook/base/handlers.py
+++ b/notebook/base/handlers.py
@@ -543,13 +543,13 @@ def get_json_body(self):
         if not self.request.body:
             return None
         # Do we need to call body.decode('utf-8') here?
-        body = self.request.body.strip().decode(u'utf-8')
+        body = self.request.body.strip().decode('utf-8')
         try:
             model = json.loads(body)
         except Exception as e:
             self.log.debug("Bad JSON: %r", body)
             self.log.error("Couldn't parse JSON", exc_info=True)
-            raise web.HTTPError(400, u'Invalid JSON in body of request') from e
+            raise web.HTTPError(400, 'Invalid JSON in body of request') from e
         return model
 
     def write_error(self, status_code, **kwargs):
diff --git a/notebook/bundler/bundlerextensions.py b/notebook/bundler/bundlerextensions.py
index 2ac346f971..bb8ff802ff 100644
--- a/notebook/bundler/bundlerextensions.py
+++ b/notebook/bundler/bundlerextensions.py
@@ -248,7 +248,7 @@ def list_nbextensions(self):
         print("Known bundlerextensions:")
         
         for config_dir in config_dirs:
-            head = u'  config dir: {}'.format(config_dir)
+            head = '  config dir: {}'.format(config_dir)
             head_shown = False
 
             cm = BaseJSONConfigManager(parent=self, config_dir=config_dir)
@@ -263,9 +263,9 @@ def list_nbextensions(self):
                     label = info.get('label')
                     module = info.get('module_name')
                     if label is None or module is None:
-                        msg = u'    {} {}'.format(bundler_id, RED_DISABLED)
+                        msg = '    {} {}'.format(bundler_id, RED_DISABLED)
                     else:
-                        msg = u'    "{}" from {} {}'.format(
+                        msg = '    "{}" from {} {}'.format(
                             label, module, GREEN_ENABLED
                         )
                     print(msg)
diff --git a/notebook/bundler/tests/test_bundler_api.py b/notebook/bundler/tests/test_bundler_api.py
index 6c251e5e99..a273ff1922 100644
--- a/notebook/bundler/tests/test_bundler_api.py
+++ b/notebook/bundler/tests/test_bundler_api.py
@@ -30,9 +30,9 @@ def setup_class(cls):
 
         nb = new_notebook()
 
-        nb.cells.append(new_markdown_cell(u'Created by test'))
-        cc1 = new_code_cell(source=u'print(2*6)')
-        cc1.outputs.append(new_output(output_type="stream", text=u'12'))
+        nb.cells.append(new_markdown_cell('Created by test'))
+        cc1 = new_code_cell(source='print(2*6)')
+        cc1.outputs.append(new_output(output_type="stream", text='12'))
         nb.cells.append(cc1)
         
         with io.open(pjoin(nbdir, 'testnb.ipynb'), 'w',
diff --git a/notebook/edit/handlers.py b/notebook/edit/handlers.py
index 9ed9a9c380..ec9bdaa449 100644
--- a/notebook/edit/handlers.py
+++ b/notebook/edit/handlers.py
@@ -13,7 +13,7 @@ class EditorHandler(IPythonHandler):
     def get(self, path):
         path = path.strip('/')
         if not self.contents_manager.file_exists(path):
-            raise web.HTTPError(404, u'File does not exist: %s' % path)
+            raise web.HTTPError(404, 'File does not exist: %s' % path)
 
         basename = path.rsplit('/', 1)[-1]
         self.write(self.render_template('edit.html',
diff --git a/notebook/jstest.py b/notebook/jstest.py
index 2bb318af31..9ec4c1f6ba 100644
--- a/notebook/jstest.py
+++ b/notebook/jstest.py
@@ -226,8 +226,8 @@ def setup(self):
         self.dirs.append(self.home)
         self.dirs.append(self.config_dir)
         self.dirs.append(self.nbdir)
-        os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir1', u'sub ∂ir 1a')))
-        os.makedirs(os.path.join(self.nbdir.name, os.path.join(u'sub ∂ir2', u'sub ∂ir 1b')))
+        os.makedirs(os.path.join(self.nbdir.name, os.path.join('sub ∂ir1', 'sub ∂ir 1a')))
+        os.makedirs(os.path.join(self.nbdir.name, os.path.join('sub ∂ir2', 'sub ∂ir 1b')))
 
         if self.xunit:
             self.add_xunit()
diff --git a/notebook/kernelspecs/handlers.py b/notebook/kernelspecs/handlers.py
index be768b5ab7..438c369202 100644
--- a/notebook/kernelspecs/handlers.py
+++ b/notebook/kernelspecs/handlers.py
@@ -15,7 +15,7 @@ def get(self, kernel_name, path, include_body=True):
             self.root = ksm.get_kernel_spec(kernel_name).resource_dir
         except KeyError as e:
             raise web.HTTPError(404,
-                                u'Kernel spec %s not found' % kernel_name) from e
+                                'Kernel spec %s not found' % kernel_name) from e
         self.log.debug("Serving kernel resource from: %s", self.root)
         return web.StaticFileHandler.get(self, path, include_body=include_body)
 
diff --git a/notebook/nbconvert/handlers.py b/notebook/nbconvert/handlers.py
index 24a3f093fb..4583ee5a39 100644
--- a/notebook/nbconvert/handlers.py
+++ b/notebook/nbconvert/handlers.py
@@ -67,7 +67,7 @@ def get_exporter(format, **kwargs):
         Exporter = get_exporter(format)
     except KeyError as e:
         # should this be 400?
-        raise web.HTTPError(404, u"No exporter for format: %s" % format) from e
+        raise web.HTTPError(404, "No exporter for format: %s" % format) from e
 
     try:
         return Exporter(**kwargs)
diff --git a/notebook/nbconvert/tests/test_nbconvert_handlers.py b/notebook/nbconvert/tests/test_nbconvert_handlers.py
index e5af13c0c4..83b898eb3e 100644
--- a/notebook/nbconvert/tests/test_nbconvert_handlers.py
+++ b/notebook/nbconvert/tests/test_nbconvert_handlers.py
@@ -72,9 +72,9 @@ def cleanup_dir():
 
         nb = new_notebook()
 
-        nb.cells.append(new_markdown_cell(u'Created by test ³'))
-        cc1 = new_code_cell(source=u'print(2*6)')
-        cc1.outputs.append(new_output(output_type="stream", text=u'12'))
+        nb.cells.append(new_markdown_cell('Created by test ³'))
+        cc1 = new_code_cell(source='print(2*6)')
+        cc1.outputs.append(new_output(output_type="stream", text='12'))
         cc1.outputs.append(new_output(output_type="execute_result",
             data={'image/png' : png_green_pixel},
             execution_count=1,
@@ -94,13 +94,13 @@ def cleanup_dir():
     def test_from_file(self):
         r = self.nbconvert_api.from_file('html', 'foo', 'testnb.ipynb')
         self.assertEqual(r.status_code, 200)
-        self.assertIn(u'text/html', r.headers['Content-Type'])
-        self.assertIn(u'Created by test', r.text)
-        self.assertIn(u'print', r.text)
+        self.assertIn('text/html', r.headers['Content-Type'])
+        self.assertIn('Created by test', r.text)
+        self.assertIn('print', r.text)
 
         r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb')
-        self.assertIn(u'text/x-python', r.headers['Content-Type'])
-        self.assertIn(u'print(2*6)', r.text)
+        self.assertIn('text/x-python', r.headers['Content-Type'])
+        self.assertIn('print(2*6)', r.text)
 
     @pytest.mark.skipif(
         not cmd_exists('pandoc'),
@@ -126,8 +126,8 @@ def test_from_file_download(self):
     )
     def test_from_file_zip(self):
         r = self.nbconvert_api.from_file('latex', 'foo', 'testnb.ipynb', download=True)
-        self.assertIn(u'application/zip', r.headers['Content-Type'])
-        self.assertIn(u'.zip', r.headers['Content-Disposition'])
+        self.assertIn('application/zip', r.headers['Content-Type'])
+        self.assertIn('.zip', r.headers['Content-Disposition'])
 
     @pytest.mark.skipif(
         not cmd_exists('pandoc'),
@@ -138,13 +138,13 @@ def test_from_post(self):
 
         r = self.nbconvert_api.from_post(format='html', nbmodel=nbmodel)
         self.assertEqual(r.status_code, 200)
-        self.assertIn(u'text/html', r.headers['Content-Type'])
-        self.assertIn(u'Created by test', r.text)
-        self.assertIn(u'print', r.text)
+        self.assertIn('text/html', r.headers['Content-Type'])
+        self.assertIn('Created by test', r.text)
+        self.assertIn('print', r.text)
 
         r = self.nbconvert_api.from_post(format='python', nbmodel=nbmodel)
-        self.assertIn(u'text/x-python', r.headers['Content-Type'])
-        self.assertIn(u'print(2*6)', r.text)
+        self.assertIn('text/x-python', r.headers['Content-Type'])
+        self.assertIn('print(2*6)', r.text)
 
     @pytest.mark.skipif(
         not cmd_exists('pandoc'),
@@ -154,5 +154,5 @@ def test_from_post_zip(self):
         nbmodel = self.request('GET', 'api/contents/foo/testnb.ipynb').json()
 
         r = self.nbconvert_api.from_post(format='latex', nbmodel=nbmodel)
-        self.assertIn(u'application/zip', r.headers['Content-Type'])
-        self.assertIn(u'.zip', r.headers['Content-Disposition'])
+        self.assertIn('application/zip', r.headers['Content-Type'])
+        self.assertIn('.zip', r.headers['Content-Disposition'])
diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py
index aac752cce9..595d377b8e 100644
--- a/notebook/nbextensions.py
+++ b/notebook/nbextensions.py
@@ -516,12 +516,12 @@ def validate_nbextension(require, logger=None):
     js_exists = False
     for exts in jupyter_path('nbextensions'):
         # Does the Javascript entrypoint actually exist on disk?
-        js = u"{}.js".format(os.path.join(exts, *require.split("/")))
+        js = "{}.js".format(os.path.join(exts, *require.split("/")))
         js_exists = os.path.exists(js)
         if js_exists:
             break
 
-    require_tmpl = u"        - require? {} {}"
+    require_tmpl = "        - require? {} {}"
     if js_exists:
         infos.append(require_tmpl.format(GREEN_OK, require))
     else:
@@ -529,13 +529,13 @@ def validate_nbextension(require, logger=None):
     
     if logger:
         if warnings:
-            logger.warning(u"      - Validating: problems found:")
+            logger.warning("      - Validating: problems found:")
             for msg in warnings:
                 logger.warning(msg)
             for msg in infos:
                 logger.info(msg)
         else:
-            logger.info(u"      - Validating: {}".format(GREEN_OK))
+            logger.info("      - Validating: {}".format(GREEN_OK))
     
     return warnings
 
@@ -567,19 +567,19 @@ def validate_nbextension_python(spec, full_dest, logger=None):
 
     section = spec.get("section", None)
     if section in NBCONFIG_SECTIONS:
-        infos.append(u"  {} section: {}".format(GREEN_OK, section))
+        infos.append("  {} section: {}".format(GREEN_OK, section))
     else:
-        warnings.append(u"  {}  section: {}".format(RED_X, section))
+        warnings.append("  {}  section: {}".format(RED_X, section))
 
     require = spec.get("require", None)
     if require is not None:
         require_path = os.path.join(
             full_dest[0:-len(spec["dest"])],
-            u"{}.js".format(require))
+            "{}.js".format(require))
         if os.path.exists(require_path):
-            infos.append(u"  {} require: {}".format(GREEN_OK, require_path))
+            infos.append("  {} require: {}".format(GREEN_OK, require_path))
         else:
-            warnings.append(u"  {}  require: {}".format(RED_X, require_path))
+            warnings.append("  {}  require: {}".format(RED_X, require_path))
 
     if logger:
         if warnings:
@@ -588,9 +588,9 @@ def validate_nbextension_python(spec, full_dest, logger=None):
                 logger.warning(msg)
             for msg in infos:
                 logger.info(msg)
-            logger.warning(u"Full spec: {}".format(spec))
+            logger.warning("Full spec: {}".format(spec))
         else:
-            logger.info(u"- Validating: {}".format(GREEN_OK))
+            logger.info("- Validating: {}".format(GREEN_OK))
 
     return warnings
 
@@ -689,7 +689,7 @@ def install_extensions(self):
 
         if full_dests:
             self.log.info(
-                u"\nTo initialize this nbextension in the browser every time"
+                "\nTo initialize this nbextension in the browser every time"
                 " the notebook (or other app) loads:\n\n"
                 "      jupyter nbextension enable {}{}{}{}\n".format(
                     self.extra_args[0] if self.python else "<the entry point>",
@@ -927,7 +927,7 @@ def list_nbextensions(self):
         print("Known nbextensions:")
         
         for config_dir in config_dirs:
-            head = u'  config dir: {}'.format(config_dir)
+            head = '  config dir: {}'.format(config_dir)
             head_shown = False
 
             cm = BaseJSONConfigManager(parent=self, config_dir=config_dir)
@@ -938,10 +938,10 @@ def list_nbextensions(self):
                         # only show heading if there is an nbextension here
                         print(head)
                         head_shown = True
-                    print(u'    {} section'.format(section))
+                    print('    {} section'.format(section))
                     
                     for require, enabled in data['load_extensions'].items():
-                        print(u'      {} {}'.format(
+                        print('      {} {}'.format(
                             require,
                             GREEN_ENABLED if enabled else RED_DISABLED))
                         if enabled:
@@ -1082,9 +1082,9 @@ def _get_nbextension_dir(user=False, sys_prefix=False, prefix=None, nbextensions
             "cannot specify more than one of user, sys_prefix, prefix, or nbextensions_dir, but got: {}"
             .format(', '.join(conflicting_set)))
     if user:
-        nbext = pjoin(jupyter_data_dir(), u'nbextensions')
+        nbext = pjoin(jupyter_data_dir(), 'nbextensions')
     elif sys_prefix:
-        nbext = pjoin(ENV_JUPYTER_PATH[0], u'nbextensions')
+        nbext = pjoin(ENV_JUPYTER_PATH[0], 'nbextensions')
     elif prefix:
         nbext = pjoin(prefix, 'share', 'jupyter', 'nbextensions')
     elif nbextensions_dir:
diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py
index 510e51240c..c3097f076d 100755
--- a/notebook/notebookapp.py
+++ b/notebook/notebookapp.py
@@ -507,7 +507,7 @@ class NbserverStopApp(JupyterApp):
     port = Integer(DEFAULT_NOTEBOOK_PORT, config=True,
         help="Port of the server to be killed. Default %s" % DEFAULT_NOTEBOOK_PORT)
 
-    sock = Unicode(u'', config=True,
+    sock = Unicode('', config=True,
         help="UNIX socket of the server to be killed.")
 
     def parse_command_line(self, argv=None):
@@ -753,7 +753,7 @@ def _default_log_datefmt(self):
     @default('log_format')
     def _default_log_format(self):
         """override default log format to include time"""
-        return u"%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s]%(end_color)s %(message)s"
+        return "%(color)s[%(levelname)1.1s %(asctime)s.%(msecs).03d %(name)s]%(end_color)s %(message)s"
 
     ignore_minified_js = Bool(False,
             config=True,
@@ -844,11 +844,11 @@ def _default_ip(self):
     @validate('ip')
     def _validate_ip(self, proposal):
         value = proposal['value']
-        if value == u'*':
-            value = u''
+        if value == '*':
+            value = ''
         return value
 
-    custom_display_url = Unicode(u'', config=True,
+    custom_display_url = Unicode('', config=True,
         help=_("""Override URL shown to users.
 
         Replace actual URL, including protocol, address, port and base URL,
@@ -883,7 +883,7 @@ def port_retries_default(self):
         return int(os.getenv(self.port_retries_env, self.port_retries_default_value))
 
 
-    sock = Unicode(u'', config=True,
+    sock = Unicode('', config=True,
         help=_("The UNIX socket the notebook server will listen on.")
     )
 
@@ -914,15 +914,15 @@ def _validate_sock_mode(self, proposal):
         return value
 
 
-    certfile = Unicode(u'', config=True,
+    certfile = Unicode('', config=True,
         help=_("""The full path to an SSL/TLS certificate file.""")
     )
 
-    keyfile = Unicode(u'', config=True,
+    keyfile = Unicode('', config=True,
         help=_("""The full path to a private key file for usage with SSL/TLS.""")
     )
 
-    client_ca = Unicode(u'', config=True,
+    client_ca = Unicode('', config=True,
         help=_("""The full path to a certificate authority certificate for SSL/TLS client authentication.""")
     )
 
@@ -1000,7 +1000,7 @@ def _token_default(self):
         if self.password:
             # no token if password is enabled
             self._token_generated = False
-            return u''
+            return ''
         else:
             self._token_generated = True
             return binascii.hexlify(os.urandom(24)).decode('ascii')
@@ -1051,7 +1051,7 @@ def _default_min_open_files_limit(self):
     def _token_changed(self, change):
         self._token_generated = False
 
-    password = Unicode(u'', config=True,
+    password = Unicode('', config=True,
                       help="""Hashed password to use for web authentication.
 
                       To generate, type in a python/IPython shell:
@@ -1166,7 +1166,7 @@ def _default_allow_remote(self):
                         (NotebookApp.browser) configuration option.
                         """)
 
-    browser = Unicode(u'', config=True,
+    browser = Unicode('', config=True,
                       help="""Specify what command to use to invoke a web
                       browser when opening the notebook. If not specified, the
                       default browser will be determined by the `webbrowser`
@@ -1252,7 +1252,7 @@ def _update_webapp_settings(self, change):
     def _update_enable_mathjax(self, change):
         """set mathjax url to empty if mathjax is disabled"""
         if not change['new']:
-            self.mathjax_url = u''
+            self.mathjax_url = ''
 
     base_url = Unicode('/', config=True,
                                help='''The base URL for the notebook server.
@@ -1351,7 +1351,7 @@ def nbextensions_path(self):
     @default('mathjax_url')
     def _default_mathjax_url(self):
         if not self.enable_mathjax:
-            return u''
+            return ''
         static_url_prefix = self.tornado_settings.get("static_url_prefix", "static")
         return url_path_join(static_url_prefix, 'components', 'MathJax', 'MathJax.js')
 
@@ -1360,7 +1360,7 @@ def _update_mathjax_url(self, change):
         new = change['new']
         if new and not self.enable_mathjax:
             # enable_mathjax=False overrides mathjax_url
-            self.mathjax_url = u''
+            self.mathjax_url = ''
         else:
             self.log.info(_("Using MathJax: %s"), new)
 
diff --git a/notebook/serverextensions.py b/notebook/serverextensions.py
index 72020a24f5..54034cb699 100644
--- a/notebook/serverextensions.py
+++ b/notebook/serverextensions.py
@@ -59,14 +59,14 @@ def toggle_serverextension_python(import_name, enabled=None, parent=None,
 
     if logger:
         if new_enabled:
-            logger.info(u"Enabling: %s" % (import_name))
+            logger.info("Enabling: %s" % (import_name))
         else:
-            logger.info(u"Disabling: %s" % (import_name))
+            logger.info("Disabling: %s" % (import_name))
 
     server_extensions[import_name] = new_enabled
 
     if logger:
-        logger.info(u"- Writing config: {}".format(config_dir))
+        logger.info("- Writing config: {}".format(config_dir))
 
     cm.update("jupyter_notebook_config", cfg)
 
@@ -104,13 +104,13 @@ def validate_serverextension(import_name, logger=None):
     except Exception:
         logger.warning("Error loading server extension %s", import_name)
 
-    import_msg = u"     {} is {} importable?"
+    import_msg = "     {} is {} importable?"
     if func is not None:
         infos.append(import_msg.format(GREEN_OK, import_name))
     else:
         warnings.append(import_msg.format(RED_X, import_name))
 
-    post_mortem = u"      {} {} {}"
+    post_mortem = "      {} {} {}"
     if logger:
         if warnings:
             [logger.info(info) for info in infos]
@@ -254,9 +254,9 @@ def list_server_extensions(self):
                 .setdefault("nbserver_extensions", {})
             )
             if server_extensions:
-                print(u'config dir: {}'.format(config_dir))
+                print('config dir: {}'.format(config_dir))
             for import_name, enabled in server_extensions.items():
-                print(u'    {} {}'.format(
+                print('    {} {}'.format(
                               import_name,
                               GREEN_ENABLED if enabled else RED_DISABLED))
                 validate_serverextension(import_name, self.log)
@@ -324,7 +324,7 @@ def _get_server_extension_metadata(module):
     """
     m = import_item(module)
     if not hasattr(m, '_jupyter_server_extension_paths'):
-        raise KeyError(u'The Python module {} does not include any valid server extensions'.format(module))
+        raise KeyError('The Python module {} does not include any valid server extensions'.format(module))
     return m, m._jupyter_server_extension_paths()
 
 if __name__ == '__main__':
diff --git a/notebook/services/contents/checkpoints.py b/notebook/services/contents/checkpoints.py
index c29a669c22..839ae1d9b9 100644
--- a/notebook/services/contents/checkpoints.py
+++ b/notebook/services/contents/checkpoints.py
@@ -90,7 +90,7 @@ def create_checkpoint(self, contents_mgr, path):
                 path,
             )
         else:
-            raise HTTPError(500, u'Unexpected type %s' % type)
+            raise HTTPError(500, 'Unexpected type %s' % type)
 
     def restore_checkpoint(self, contents_mgr, checkpoint_id, path):
         """Restore a checkpoint."""
@@ -100,7 +100,7 @@ def restore_checkpoint(self, contents_mgr, checkpoint_id, path):
         elif type == 'file':
             model = self.get_file_checkpoint(checkpoint_id, path)
         else:
-            raise HTTPError(500, u'Unexpected type %s' % type)
+            raise HTTPError(500, 'Unexpected type %s' % type)
         contents_mgr.save(model, path)
 
     # Required Methods
diff --git a/notebook/services/contents/filecheckpoints.py b/notebook/services/contents/filecheckpoints.py
index 5a9c835749..91afe3bb39 100644
--- a/notebook/services/contents/filecheckpoints.py
+++ b/notebook/services/contents/filecheckpoints.py
@@ -50,7 +50,7 @@ def _root_dir_default(self):
     # ContentsManager-dependent checkpoint API
     def create_checkpoint(self, contents_mgr, path):
         """Create a checkpoint."""
-        checkpoint_id = u'checkpoint'
+        checkpoint_id = 'checkpoint'
         src_path = contents_mgr._get_os_path(path)
         dest_path = self.checkpoint_path(checkpoint_id, path)
         self._copy(src_path, dest_path)
@@ -107,7 +107,7 @@ def checkpoint_path(self, checkpoint_id, path):
         parent, name = ('/' + path).rsplit('/', 1)
         parent = parent.strip('/')
         basename, ext = os.path.splitext(name)
-        filename = u"{name}-{checkpoint_id}{ext}".format(
+        filename = "{name}-{checkpoint_id}{ext}".format(
             name=basename,
             checkpoint_id=checkpoint_id,
             ext=ext,
@@ -133,7 +133,7 @@ def checkpoint_model(self, checkpoint_id, os_path):
     def no_such_checkpoint(self, path, checkpoint_id):
         raise HTTPError(
             404,
-            u'Checkpoint does not exist: %s@%s' % (path, checkpoint_id)
+            'Checkpoint does not exist: %s@%s' % (path, checkpoint_id)
         )
 
 
@@ -146,7 +146,7 @@ def create_file_checkpoint(self, content, format, path):
         """Create a checkpoint from the current content of a file."""
         path = path.strip('/')
         # only the one checkpoint ID:
-        checkpoint_id = u"checkpoint"
+        checkpoint_id = "checkpoint"
         os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
         self.log.debug("creating checkpoint for %s", path)
         with self.perm_to_403():
@@ -159,7 +159,7 @@ def create_notebook_checkpoint(self, nb, path):
         """Create a checkpoint from the current content of a notebook."""
         path = path.strip('/')
         # only the one checkpoint ID:
-        checkpoint_id = u"checkpoint"
+        checkpoint_id = "checkpoint"
         os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
         self.log.debug("creating checkpoint for %s", path)
         with self.perm_to_403():
diff --git a/notebook/services/contents/fileio.py b/notebook/services/contents/fileio.py
index f99504e32b..f931dbbb97 100644
--- a/notebook/services/contents/fileio.py
+++ b/notebook/services/contents/fileio.py
@@ -226,7 +226,7 @@ def perm_to_403(self, os_path=''):
                 if not os_path:
                     os_path = str_to_unicode(e.filename or 'unknown file')
                 path = to_api_path(os_path, root=self.root_dir)
-                raise HTTPError(403, u'Permission denied: %s' % path) from e
+                raise HTTPError(403, 'Permission denied: %s' % path) from e
             else:
                 raise
 
@@ -276,7 +276,7 @@ def _read_notebook(self, os_path, as_version=4):
             if not self.use_atomic_writing or not os.path.exists(tmp_path):
                 raise HTTPError(
                     400,
-                    u"Unreadable Notebook: %s %r" % (os_path, e_orig),
+                    "Unreadable Notebook: %s %r" % (os_path, e_orig),
                 )
 
             # Move the bad file aside, restore the intermediate, and try again.
@@ -334,7 +334,7 @@ def _save_file(self, os_path, content, format):
                 bcontent = decodebytes(b64_bytes)
         except Exception as e:
             raise HTTPError(
-                400, u'Encoding error saving %s: %s' % (os_path, e)
+                400, 'Encoding error saving %s: %s' % (os_path, e)
             ) from e
 
         with self.atomic_writing(os_path, text=False) as f:
diff --git a/notebook/services/contents/filemanager.py b/notebook/services/contents/filemanager.py
index 0c9386b2fc..481bfc352e 100644
--- a/notebook/services/contents/filemanager.py
+++ b/notebook/services/contents/filemanager.py
@@ -132,7 +132,7 @@ def run_post_save_hook(self, model, os_path):
                 self.post_save_hook(os_path=os_path, model=model, contents_manager=self)
             except Exception as e:
                 self.log.error("Post-save hook failed o-n %s", os_path, exc_info=True)
-                raise web.HTTPError(500, u'Unexpected error while running post hook save: %s'
+                raise web.HTTPError(500, 'Unexpected error while running post hook save: %s'
                                     % e) from e
 
     @validate('root_dir')
@@ -292,7 +292,7 @@ def _dir_model(self, path, content=True):
         """
         os_path = self._get_os_path(path)
 
-        four_o_four = u'directory does not exist: %r' % path
+        four_o_four = 'directory does not exist: %r' % path
 
         if not os.path.isdir(os_path):
             raise web.HTTPError(404, four_o_four)
@@ -427,32 +427,32 @@ def get(self, path, content=True, type=None, format=None):
         path = path.strip('/')
 
         if not self.exists(path):
-            raise web.HTTPError(404, u'No such file or directory: %s' % path)
+            raise web.HTTPError(404, 'No such file or directory: %s' % path)
 
         os_path = self._get_os_path(path)
         if os.path.isdir(os_path):
             if type not in (None, 'directory'):
                 raise web.HTTPError(400,
-                                u'%s is a directory, not a %s' % (path, type), reason='bad type')
+                                '%s is a directory, not a %s' % (path, type), reason='bad type')
             model = self._dir_model(path, content=content)
         elif type == 'notebook' or (type is None and path.endswith('.ipynb')):
             model = self._notebook_model(path, content=content)
         else:
             if type == 'directory':
                 raise web.HTTPError(400,
-                                u'%s is not a directory' % path, reason='bad type')
+                                '%s is not a directory' % path, reason='bad type')
             model = self._file_model(path, content=content, format=format)
         return model
 
     def _save_directory(self, os_path, model, path=''):
         """create a directory"""
         if is_hidden(os_path, self.root_dir) and not self.allow_hidden:
-            raise web.HTTPError(400, u'Cannot create hidden directory %r' % os_path)
+            raise web.HTTPError(400, 'Cannot create hidden directory %r' % os_path)
         if not os.path.exists(os_path):
             with self.perm_to_403():
                 os.mkdir(os_path)
         elif not os.path.isdir(os_path):
-            raise web.HTTPError(400, u'Not a directory: %s' % (os_path))
+            raise web.HTTPError(400, 'Not a directory: %s' % (os_path))
         else:
             self.log.debug("Directory %r already exists", os_path)
 
@@ -461,9 +461,9 @@ def save(self, model, path=''):
         path = path.strip('/')
 
         if 'type' not in model:
-            raise web.HTTPError(400, u'No file type provided')
+            raise web.HTTPError(400, 'No file type provided')
         if 'content' not in model and model['type'] != 'directory':
-            raise web.HTTPError(400, u'No file content provided')
+            raise web.HTTPError(400, 'No file content provided')
 
         os_path = self._get_os_path(path)
         self.log.debug("Saving %s", os_path)
@@ -488,8 +488,8 @@ def save(self, model, path=''):
         except web.HTTPError:
             raise
         except Exception as e:
-            self.log.error(u'Error while saving file: %s %s', path, e, exc_info=True)
-            raise web.HTTPError(500, u'Unexpected error while saving file: %s %s' %
+            self.log.error('Error while saving file: %s %s', path, e, exc_info=True)
+            raise web.HTTPError(500, 'Unexpected error while saving file: %s %s' %
                                 (path, e)) from e
 
         validation_message = None
@@ -511,7 +511,7 @@ def delete_file(self, path):
         os_path = self._get_os_path(path)
         rm = os.unlink
         if not os.path.exists(os_path):
-            raise web.HTTPError(404, u'File or directory does not exist: %s' % os_path)
+            raise web.HTTPError(404, 'File or directory does not exist: %s' % os_path)
 
         def is_non_empty_dir(os_path):
             if os.path.isdir(os_path):
@@ -527,7 +527,7 @@ def is_non_empty_dir(os_path):
             if sys.platform == 'win32' and is_non_empty_dir(os_path):
                 # send2trash can really delete files on Windows, so disallow
                 # deleting non-empty files. See Github issue 3631.
-                raise web.HTTPError(400, u'Directory %s not empty' % os_path)
+                raise web.HTTPError(400, 'Directory %s not empty' % os_path)
             try:
                 self.log.debug("Sending %s to trash", os_path)
                 send2trash(os_path)
@@ -538,7 +538,7 @@ def is_non_empty_dir(os_path):
         if os.path.isdir(os_path):
             # Don't permanently delete non-empty directories.
             if is_non_empty_dir(os_path):
-                raise web.HTTPError(400, u'Directory %s not empty' % os_path)
+                raise web.HTTPError(400, 'Directory %s not empty' % os_path)
             self.log.debug("Removing directory %s", os_path)
             with self.perm_to_403():
                 shutil.rmtree(os_path)
@@ -563,7 +563,7 @@ def rename_file(self, old_path, new_path):
 
         # Should we proceed with the move?
         if os.path.exists(new_os_path) and not samefile(old_os_path, new_os_path):
-            raise web.HTTPError(409, u'File already exists: %s' % new_path)
+            raise web.HTTPError(409, 'File already exists: %s' % new_path)
 
         # Move the file
         try:
@@ -572,7 +572,7 @@ def rename_file(self, old_path, new_path):
         except web.HTTPError:
             raise
         except Exception as e:
-            raise web.HTTPError(500, u'Unknown error renaming file: %s %s' %
+            raise web.HTTPError(500, 'Unknown error renaming file: %s %s' %
                                 (old_path, e)) from e
 
     def info_string(self):
diff --git a/notebook/services/contents/handlers.py b/notebook/services/contents/handlers.py
index b3216335bb..4e751dd3fc 100644
--- a/notebook/services/contents/handlers.py
+++ b/notebook/services/contents/handlers.py
@@ -45,7 +45,7 @@ def validate_model(model, expect_content):
     if missing:
         raise web.HTTPError(
             500,
-            u"Missing Model Keys: {missing}".format(missing=missing),
+            "Missing Model Keys: {missing}".format(missing=missing),
         )
 
     maybe_none_keys = ['content', 'format']
@@ -54,7 +54,7 @@ def validate_model(model, expect_content):
         if errors:
             raise web.HTTPError(
                 500,
-                u"Keys unexpectedly None: {keys}".format(keys=errors),
+                "Keys unexpectedly None: {keys}".format(keys=errors),
             )
     else:
         errors = {
@@ -65,7 +65,7 @@ def validate_model(model, expect_content):
         if errors:
             raise web.HTTPError(
                 500,
-                u"Keys unexpectedly not None: {keys}".format(keys=errors),
+                "Keys unexpectedly not None: {keys}".format(keys=errors),
             )
 
 
@@ -103,14 +103,14 @@ def get(self, path=''):
         path = path or ''
         type = self.get_query_argument('type', default=None)
         if type not in {None, 'directory', 'file', 'notebook'}:
-            raise web.HTTPError(400, u'Type %r is invalid' % type)
+            raise web.HTTPError(400, 'Type %r is invalid' % type)
 
         format = self.get_query_argument('format', default=None)
         if format not in {None, 'text', 'base64'}:
-            raise web.HTTPError(400, u'Format %r is invalid' % format)
+            raise web.HTTPError(400, 'Format %r is invalid' % format)
         content = self.get_query_argument('content', default='1')
         if content not in {'0', '1'}:
-            raise web.HTTPError(400, u'Content %r is invalid' % content)
+            raise web.HTTPError(400, 'Content %r is invalid' % content)
         content = int(content)
 
         model = yield maybe_future(self.contents_manager.get(
@@ -126,7 +126,7 @@ def patch(self, path=''):
         cm = self.contents_manager
         model = self.get_json_body()
         if model is None:
-            raise web.HTTPError(400, u'JSON body missing')
+            raise web.HTTPError(400, 'JSON body missing')
         model = yield maybe_future(cm.update(model, path))
         validate_model(model, expect_content=False)
         self._finish_model(model)
@@ -134,7 +134,7 @@ def patch(self, path=''):
     @gen.coroutine
     def _copy(self, copy_from, copy_to=None):
         """Copy a file, optionally specifying a target directory."""
-        self.log.info(u"Copying {copy_from} to {copy_to}".format(
+        self.log.info("Copying {copy_from} to {copy_to}".format(
             copy_from=copy_from,
             copy_to=copy_to or '',
         ))
@@ -146,7 +146,7 @@ def _copy(self, copy_from, copy_to=None):
     @gen.coroutine
     def _upload(self, model, path):
         """Handle upload of a new file to path"""
-        self.log.info(u"Uploading file to %s", path)
+        self.log.info("Uploading file to %s", path)
         model = yield maybe_future(self.contents_manager.new(model, path))
         self.set_status(201)
         validate_model(model, expect_content=False)
@@ -155,7 +155,7 @@ def _upload(self, model, path):
     @gen.coroutine
     def _new_untitled(self, path, type='', ext=''):
         """Create a new, empty untitled entity"""
-        self.log.info(u"Creating new %s in %s", type or 'file', path)
+        self.log.info("Creating new %s in %s", type or 'file', path)
         model = yield maybe_future(self.contents_manager.new_untitled(path=path, type=type, ext=ext))
         self.set_status(201)
         validate_model(model, expect_content=False)
@@ -166,7 +166,7 @@ def _save(self, model, path):
         """Save an existing file."""
         chunk = model.get("chunk", None)
         if not chunk or chunk == -1:  # Avoid tedious log information
-            self.log.info(u"Saving file at %s", path)
+            self.log.info("Saving file at %s", path)
         model = yield maybe_future(self.contents_manager.save(model, path))
         validate_model(model, expect_content=False)
         self._finish_model(model)
diff --git a/notebook/services/contents/largefilemanager.py b/notebook/services/contents/largefilemanager.py
index 6779a0b5c2..9699a4e43a 100644
--- a/notebook/services/contents/largefilemanager.py
+++ b/notebook/services/contents/largefilemanager.py
@@ -15,11 +15,11 @@ def save(self, model, path=''):
             path = path.strip('/')
             
             if 'type' not in model:
-                raise web.HTTPError(400, u'No file type provided')
+                raise web.HTTPError(400, 'No file type provided')
             if model['type'] != 'file':
-                raise web.HTTPError(400, u'File type "{}" is not supported for large file transfer'.format(model['type']))
+                raise web.HTTPError(400, 'File type "{}" is not supported for large file transfer'.format(model['type']))
             if 'content' not in model and model['type'] != 'directory':
-                raise web.HTTPError(400, u'No file content provided')
+                raise web.HTTPError(400, 'No file content provided')
 
             os_path = self._get_os_path(path)
 
@@ -33,8 +33,8 @@ def save(self, model, path=''):
             except web.HTTPError:
                 raise
             except Exception as e:
-                self.log.error(u'Error while saving file: %s %s', path, e, exc_info=True)
-                raise web.HTTPError(500, u'Unexpected error while saving file: %s %s' % (path, e)) from e
+                self.log.error('Error while saving file: %s %s', path, e, exc_info=True)
+                raise web.HTTPError(500, 'Unexpected error while saving file: %s %s' % (path, e)) from e
 
             model = self.get(path, content=False)
 
@@ -60,7 +60,7 @@ def _save_large_file(self, os_path, content, format):
                 bcontent = base64.b64decode(b64_bytes)
         except Exception as e:
             raise web.HTTPError(
-                400, u'Encoding error saving %s: %s' % (os_path, e)
+                400, 'Encoding error saving %s: %s' % (os_path, e)
             ) from e
 
         with self.perm_to_403(os_path):
diff --git a/notebook/services/contents/manager.py b/notebook/services/contents/manager.py
index b556abc1d3..f570d89905 100644
--- a/notebook/services/contents/manager.py
+++ b/notebook/services/contents/manager.py
@@ -65,7 +65,7 @@ def _notary_default(self):
         return sign.NotebookNotary(parent=self)
 
     hide_globs = List(Unicode(), [
-            u'__pycache__', '*.pyc', '*.pyo',
+            '__pycache__', '*.pyc', '*.pyo',
             '.DS_Store', '*.so', '*.dylib', '*~',
         ], config=True, help="""
         Glob patterns to hide in file and directory listings.
@@ -342,9 +342,9 @@ def increment_filename(self, filename, path='', insert=''):
                 insert_i = '{}{}'.format(insert, i)
             else:
                 insert_i = ''
-            name = u'{basename}{insert}{suffix}'.format(basename=basename,
+            name = '{basename}{insert}{suffix}'.format(basename=basename,
                 insert=insert_i, suffix=suffix)
-            if not self.exists(u'{}/{}'.format(path, name)):
+            if not self.exists('{}/{}'.format(path, name)):
                 break
         return name
 
@@ -353,7 +353,7 @@ def validate_notebook_model(self, model):
         try:
             validate_nb(model['content'])
         except ValidationError as e:
-            model['message'] = u'Notebook validation failed: {}:\n{}'.format(
+            model['message'] = 'Notebook validation failed: {}:\n{}'.format(
                 e.message, json.dumps(e.instance, indent=1, default=lambda obj: '<UNKNOWN>'),
             )
         return model
@@ -393,7 +393,7 @@ def new_untitled(self, path='', type='', ext=''):
             raise HTTPError(400, "Unexpected model type: %r" % model['type'])
         
         name = self.increment_filename(untitled + ext, path, insert=insert)
-        path = u'{0}/{1}'.format(path, name)
+        path = '{0}/{1}'.format(path, name)
         return self.new(model, path)
     
     def new(self, model=None, path=''):
@@ -452,9 +452,9 @@ def copy(self, from_path, to_path=None):
         if to_path is None:
             to_path = from_dir
         if self.dir_exists(to_path):
-            name = copy_pat.sub(u'.', from_name)
+            name = copy_pat.sub('.', from_name)
             to_name = self.increment_filename(name, to_path, insert='-Copy')
-            to_path = u'{0}/{1}'.format(to_path, to_name)
+            to_path = '{0}/{1}'.format(to_path, to_name)
         
         model = self.save(model, to_path)
         return model
diff --git a/notebook/services/contents/tests/test_contents_api.py b/notebook/services/contents/tests/test_contents_api.py
index 6e4ad49dbc..b801845164 100644
--- a/notebook/services/contents/tests/test_contents_api.py
+++ b/notebook/services/contents/tests/test_contents_api.py
@@ -128,16 +128,16 @@ class APITest(NotebookTestBase):
     """Test the kernels web service API"""
     dirs_nbs = [('', 'inroot'),
                 ('Directory with spaces in', 'inspace'),
-                (u'unicodé', 'innonascii'),
+                ('unicodé', 'innonascii'),
                 ('foo', 'a'),
                 ('foo', 'b'),
                 ('foo', 'name with spaces'),
-                ('foo', u'unicodé'),
+                ('foo', 'unicodé'),
                 ('foo/bar', 'baz'),
                 ('ordering', 'A'),
                 ('ordering', 'b'),
                 ('ordering', 'C'),
-                (u'å b', u'ç d'),
+                ('å b', 'ç d'),
                ]
     hidden_dirs = ['.hidden', '__pycache__']
 
@@ -151,7 +151,7 @@ def _blob_for_name(name):
 
     @staticmethod
     def _txt_for_name(name):
-        return u'%s text file' % name
+        return '%s text file' % name
     
     def to_os_path(self, api_path):
         return to_os_path(api_path, root=self.notebook_dir)
@@ -215,18 +215,18 @@ def setUp(self):
         for d, name in self.dirs_nbs:
             # create a notebook
             nb = new_notebook()
-            nbname = u'{}/{}.ipynb'.format(d, name)
+            nbname = '{}/{}.ipynb'.format(d, name)
             self.make_nb(nbname, nb)
             self.addCleanup(partial(self.delete_file, nbname))
 
             # create a text file
             txt = self._txt_for_name(name)
-            txtname = u'{}/{}.txt'.format(d, name)
+            txtname = '{}/{}.txt'.format(d, name)
             self.make_txt(txtname, txt)
             self.addCleanup(partial(self.delete_file, txtname))
 
             blob = self._blob_for_name(name)
-            blobname = u'{}/{}.blob'.format(d, name)
+            blobname = '{}/{}.blob'.format(d, name)
             self.make_blob(blobname, blob)
             self.addCleanup(partial(self.delete_file, blobname))
 
@@ -241,10 +241,10 @@ def test_list_notebooks(self):
         self.assertEqual(len(nbs), 1)
         self.assertEqual(nbs[0]['name'], 'inspace.ipynb')
 
-        nbs = notebooks_only(self.api.list(u'/unicodé/').json())
+        nbs = notebooks_only(self.api.list('/unicodé/').json())
         self.assertEqual(len(nbs), 1)
         self.assertEqual(nbs[0]['name'], 'innonascii.ipynb')
-        self.assertEqual(nbs[0]['path'], u'unicodé/innonascii.ipynb')
+        self.assertEqual(nbs[0]['path'], 'unicodé/innonascii.ipynb')
 
         nbs = notebooks_only(self.api.list('/foo/bar/').json())
         self.assertEqual(len(nbs), 1)
@@ -254,7 +254,7 @@ def test_list_notebooks(self):
         nbs = notebooks_only(self.api.list('foo').json())
         self.assertEqual(len(nbs), 4)
         nbnames = { normalize('NFC', n['name']) for n in nbs }
-        expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodé.ipynb']
+        expected = [ 'a.ipynb', 'b.ipynb', 'name with spaces.ipynb', 'unicodé.ipynb']
         expected = { normalize('NFC', name) for name in expected }
         self.assertEqual(nbnames, expected)
 
@@ -284,7 +284,7 @@ def test_get_nb_contents(self):
         for d, name in self.dirs_nbs:
             path = url_path_join(d, name + '.ipynb')
             nb = self.api.read(path).json()
-            self.assertEqual(nb['name'], u'%s.ipynb' % name)
+            self.assertEqual(nb['name'], '%s.ipynb' % name)
             self.assertEqual(nb['path'], path)
             self.assertEqual(nb['type'], 'notebook')
             self.assertIn('content', nb)
@@ -296,7 +296,7 @@ def test_get_nb_no_content(self):
         for d, name in self.dirs_nbs:
             path = url_path_join(d, name + '.ipynb')
             nb = self.api.read(path, content=False).json()
-            self.assertEqual(nb['name'], u'%s.ipynb' % name)
+            self.assertEqual(nb['name'], '%s.ipynb' % name)
             self.assertEqual(nb['path'], path)
             self.assertEqual(nb['type'], 'notebook')
             self.assertIn('content', nb)
@@ -311,7 +311,7 @@ def test_get_nb_invalid(self):
                 'metadata': {},
             }],
         }
-        path = u'å b/Validate tést.ipynb'
+        path = 'å b/Validate tést.ipynb'
         self.make_txt(path, py3compat.cast_unicode(json.dumps(nb)))
         model = self.api.read(path).json()
         self.assertEqual(model['path'], path)
@@ -329,7 +329,7 @@ def test_get_text_file_contents(self):
         for d, name in self.dirs_nbs:
             path = url_path_join(d, name + '.txt')
             model = self.api.read(path).json()
-            self.assertEqual(model['name'], u'%s.txt' % name)
+            self.assertEqual(model['name'], '%s.txt' % name)
             self.assertEqual(model['path'], path)
             self.assertIn('content', model)
             self.assertEqual(model['format'], 'text')
@@ -348,7 +348,7 @@ def test_get_binary_file_contents(self):
         for d, name in self.dirs_nbs:
             path = url_path_join(d, name + '.blob')
             model = self.api.read(path).json()
-            self.assertEqual(model['name'], u'%s.blob' % name)
+            self.assertEqual(model['name'], '%s.blob' % name)
             self.assertEqual(model['path'], path)
             self.assertIn('content', model)
             self.assertEqual(model['format'], 'base64')
@@ -364,15 +364,15 @@ def test_get_binary_file_contents(self):
 
     def test_get_bad_type(self):
         with assert_http_error(400):
-            self.api.read(u'unicodé', type='file')  # this is a directory
+            self.api.read('unicodé', type='file')  # this is a directory
 
         with assert_http_error(400):
-            self.api.read(u'unicodé/innonascii.ipynb', type='directory')
+            self.api.read('unicodé/innonascii.ipynb', type='directory')
 
     def _check_created(self, resp, path, type='notebook'):
         self.assertEqual(resp.status_code, 201)
         location_header = py3compat.str_to_unicode(resp.headers['Location'])
-        self.assertEqual(location_header, url_path_join(self.url_prefix, u'api/contents', url_escape(path)))
+        self.assertEqual(location_header, url_path_join(self.url_prefix, 'api/contents', url_escape(path)))
         rjson = resp.json()
         self.assertEqual(rjson['name'], path.rsplit('/', 1)[-1])
         self.assertEqual(rjson['path'], path)
@@ -381,12 +381,12 @@ def _check_created(self, resp, path, type='notebook'):
         assert isright(path)
 
     def test_create_untitled(self):
-        resp = self.api.create_untitled(path=u'å b')
-        self._check_created(resp, u'å b/Untitled.ipynb')
+        resp = self.api.create_untitled(path='å b')
+        self._check_created(resp, 'å b/Untitled.ipynb')
 
         # Second time
-        resp = self.api.create_untitled(path=u'å b')
-        self._check_created(resp, u'å b/Untitled1.ipynb')
+        resp = self.api.create_untitled(path='å b')
+        self._check_created(resp, 'å b/Untitled1.ipynb')
 
         # And two directories down
         resp = self.api.create_untitled(path='foo/bar')
@@ -405,39 +405,39 @@ def test_create_untitled_txt(self):
     def test_upload(self):
         nb = new_notebook()
         nbmodel = {'content': nb, 'type': 'notebook'}
-        path = u'å b/Upload tést.ipynb'
+        path = 'å b/Upload tést.ipynb'
         resp = self.api.upload(path, body=json.dumps(nbmodel))
         self._check_created(resp, path)
 
     def test_mkdir_untitled(self):
-        resp = self.api.mkdir_untitled(path=u'å b')
-        self._check_created(resp, u'å b/Untitled Folder', type='directory')
+        resp = self.api.mkdir_untitled(path='å b')
+        self._check_created(resp, 'å b/Untitled Folder', type='directory')
 
         # Second time
-        resp = self.api.mkdir_untitled(path=u'å b')
-        self._check_created(resp, u'å b/Untitled Folder 1', type='directory')
+        resp = self.api.mkdir_untitled(path='å b')
+        self._check_created(resp, 'å b/Untitled Folder 1', type='directory')
 
         # And two directories down
         resp = self.api.mkdir_untitled(path='foo/bar')
         self._check_created(resp, 'foo/bar/Untitled Folder', type='directory')
 
     def test_mkdir(self):
-        path = u'å b/New ∂ir'
+        path = 'å b/New ∂ir'
         resp = self.api.mkdir(path)
         self._check_created(resp, path, type='directory')
 
     def test_mkdir_hidden_400(self):
         with assert_http_error(400):
-            resp = self.api.mkdir(u'å b/.hidden')
+            resp = self.api.mkdir('å b/.hidden')
 
     def test_upload_txt(self):
-        body = u'ünicode téxt'
+        body = 'ünicode téxt'
         model = {
             'content' : body,
             'format'  : 'text',
             'type'    : 'file',
         }
-        path = u'å b/Upload tést.txt'
+        path = 'å b/Upload tést.txt'
         resp = self.api.upload(path, body=json.dumps(model))
 
         # check roundtrip
@@ -455,7 +455,7 @@ def test_upload_b64(self):
             'format'  : 'base64',
             'type'    : 'file',
         }
-        path = u'å b/Upload tést.blob'
+        path = 'å b/Upload tést.blob'
         resp = self.api.upload(path, body=json.dumps(model))
 
         # check roundtrip
@@ -473,7 +473,7 @@ def test_upload_v2(self):
         nb.worksheets.append(ws)
         ws.cells.append(v2.new_code_cell(input='print("hi")'))
         nbmodel = {'content': nb, 'type': 'notebook'}
-        path = u'å b/Upload tést.ipynb'
+        path = 'å b/Upload tést.ipynb'
         resp = self.api.upload(path, body=json.dumps(nbmodel))
         self._check_created(resp, path)
         resp = self.api.read(path)
@@ -481,34 +481,34 @@ def test_upload_v2(self):
         self.assertEqual(data['content']['nbformat'], 4)
 
     def test_copy(self):
-        resp = self.api.copy(u'å b/ç d.ipynb', u'å b')
-        self._check_created(resp, u'å b/ç d-Copy1.ipynb')
+        resp = self.api.copy('å b/ç d.ipynb', 'å b')
+        self._check_created(resp, 'å b/ç d-Copy1.ipynb')
         
-        resp = self.api.copy(u'å b/ç d.ipynb', u'å b')
-        self._check_created(resp, u'å b/ç d-Copy2.ipynb')
+        resp = self.api.copy('å b/ç d.ipynb', 'å b')
+        self._check_created(resp, 'å b/ç d-Copy2.ipynb')
     
     def test_copy_copy(self):
-        resp = self.api.copy(u'å b/ç d.ipynb', u'å b')
-        self._check_created(resp, u'å b/ç d-Copy1.ipynb')
+        resp = self.api.copy('å b/ç d.ipynb', 'å b')
+        self._check_created(resp, 'å b/ç d-Copy1.ipynb')
         
-        resp = self.api.copy(u'å b/ç d-Copy1.ipynb', u'å b')
-        self._check_created(resp, u'å b/ç d-Copy2.ipynb')
+        resp = self.api.copy('å b/ç d-Copy1.ipynb', 'å b')
+        self._check_created(resp, 'å b/ç d-Copy2.ipynb')
     
     def test_copy_path(self):
-        resp = self.api.copy(u'foo/a.ipynb', u'å b')
-        self._check_created(resp, u'å b/a.ipynb')
+        resp = self.api.copy('foo/a.ipynb', 'å b')
+        self._check_created(resp, 'å b/a.ipynb')
         
-        resp = self.api.copy(u'foo/a.ipynb', u'å b')
-        self._check_created(resp, u'å b/a-Copy1.ipynb')
+        resp = self.api.copy('foo/a.ipynb', 'å b')
+        self._check_created(resp, 'å b/a-Copy1.ipynb')
 
     def test_copy_put_400(self):
         with assert_http_error(400):
-            resp = self.api.copy_put(u'å b/ç d.ipynb', u'å b/cøpy.ipynb')
+            resp = self.api.copy_put('å b/ç d.ipynb', 'å b/cøpy.ipynb')
 
     def test_copy_dir_400(self):
         # can't copy directories
         with assert_http_error(400):
-            resp = self.api.copy(u'å b', u'foo')
+            resp = self.api.copy('å b', 'foo')
 
     def test_delete(self):
         for d, name in self.dirs_nbs:
@@ -537,15 +537,15 @@ def test_delete_non_empty_dir(self):
             self.skipTest("Disabled deleting non-empty dirs on Windows")
         # Test that non empty directory can be deleted
         try:
-            self.api.delete(u'å b')
+            self.api.delete('å b')
         except requests.HTTPError as e:
             if e.response.status_code == 400:
-                if not self.can_send2trash(u'å b'):
+                if not self.can_send2trash('å b'):
                     self.skipTest("Dir can't be sent to trash")
             raise
         # Check if directory has actually been deleted
         with assert_http_error(404):
-            self.api.list(u'å b')
+            self.api.list('å b')
 
     def test_rename(self):
         resp = self.api.rename('foo/a.ipynb', 'foo/z.ipynb')
@@ -599,7 +599,7 @@ def test_save(self):
         resp = self.api.read('foo/a.ipynb')
         nbcontent = json.loads(resp.text)['content']
         nb = from_dict(nbcontent)
-        nb.cells.append(new_markdown_cell(u'Created by test ³'))
+        nb.cells.append(new_markdown_cell('Created by test ³'))
 
         nbmodel = {'content': nb, 'type': 'notebook'}
         resp = self.api.save('foo/a.ipynb', body=json.dumps(nbmodel))
@@ -607,7 +607,7 @@ def test_save(self):
         nbcontent = self.api.read('foo/a.ipynb').json()['content']
         newnb = from_dict(nbcontent)
         self.assertEqual(newnb.cells[0].source,
-                         u'Created by test ³')
+                         'Created by test ³')
 
     def test_checkpoints(self):
         resp = self.api.read('foo/a.ipynb')
diff --git a/notebook/services/contents/tests/test_fileio.py b/notebook/services/contents/tests/test_fileio.py
index adc06d97f1..206c53b3d5 100644
--- a/notebook/services/contents/tests/test_fileio.py
+++ b/notebook/services/contents/tests/test_fileio.py
@@ -22,7 +22,7 @@ class CustomExc(Exception): pass
     with TemporaryDirectory() as td:
         f1 = os.path.join(td, 'penguin')
         with stdlib_io.open(f1, 'w') as f:
-            f.write(u'Before')
+            f.write('Before')
         
         if os.name != 'nt':
             os.chmod(f1, 0o701)
@@ -40,18 +40,18 @@ class CustomExc(Exception): pass
 
         with pytest.raises(CustomExc):
             with atomic_writing(f1) as f:
-                f.write(u'Failing write')
+                f.write('Failing write')
                 raise CustomExc
 
         # Because of the exception, the file should not have been modified
         with stdlib_io.open(f1, 'r') as f:
-            assert f.read() == u'Before'
+            assert f.read() == 'Before'
 
         with atomic_writing(f1) as f:
-            f.write(u'Overwritten')
+            f.write('Overwritten')
 
         with stdlib_io.open(f1, 'r') as f:
-            assert f.read() == u'Overwritten'
+            assert f.read() == 'Overwritten'
 
         if os.name != 'nt':
             mode = stat.S_IMODE(os.stat(f1).st_mode)
@@ -60,10 +60,10 @@ class CustomExc(Exception): pass
         if have_symlink:
             # Check that writing over a file preserves a symlink
             with atomic_writing(f2) as f:
-                f.write(u'written from symlink')
+                f.write('written from symlink')
             
             with stdlib_io.open(f1, 'r') as f:
-                assert f.read() == u'written from symlink'
+                assert f.read() == 'written from symlink'
 
 class TestWithSetUmask(unittest.TestCase):
     def setUp(self):
@@ -82,14 +82,14 @@ def test_atomic_writing_umask(self):
             os.umask(0o022)
             f1 = os.path.join(td, '1')
             with atomic_writing(f1) as f:
-                f.write(u'1')
+                f.write('1')
             mode = stat.S_IMODE(os.stat(f1).st_mode)
             assert mode == 0o644
     
             os.umask(0o057)
             f2 = os.path.join(td, '2')
             with atomic_writing(f2) as f:
-                f.write(u'2')
+                f.write('2')
             mode = stat.S_IMODE(os.stat(f2).st_mode)
             assert mode == 0o620
 
@@ -98,9 +98,9 @@ def test_atomic_writing_newlines():
     with TemporaryDirectory() as td:
         path = os.path.join(td, 'testfile')
         
-        lf = u'a\nb\nc\n'
-        plat = lf.replace(u'\n', os.linesep)
-        crlf = lf.replace(u'\n', u'\r\n')
+        lf = 'a\nb\nc\n'
+        plat = lf.replace('\n', os.linesep)
+        crlf = lf.replace('\n', '\r\n')
         
         # test default
         with stdlib_io.open(path, 'w') as f:
@@ -124,7 +124,7 @@ def test_atomic_writing_newlines():
         assert read == crlf
         
         # test newline=no convert
-        text = u'crlf\r\ncr\rlf\n'
+        text = 'crlf\r\ncr\rlf\n'
         with atomic_writing(path, newline='') as f:
             f.write(text)
         with stdlib_io.open(path, 'r', newline='') as f:
diff --git a/notebook/services/contents/tests/test_largefilemanager.py b/notebook/services/contents/tests/test_largefilemanager.py
index 13d294b9b0..4f52b2453d 100644
--- a/notebook/services/contents/tests/test_largefilemanager.py
+++ b/notebook/services/contents/tests/test_largefilemanager.py
@@ -68,7 +68,7 @@ def test_save(self):
 
         try:
             model = {'name': 'test', 'path': 'test', 'chunk': 2, 'type': 'file',
-                     'content': u'test', 'format': 'json'}
+                     'content': 'test', 'format': 'json'}
             cm.save(model, model['path'])
         except web.HTTPError as e:
             self.assertEqual("HTTP 400: Bad Request (Must specify format of file contents as 'text' or 'base64')",
@@ -76,7 +76,7 @@ def test_save(self):
 
         # Save model for different chunks
         model = {'name': 'test', 'path': 'test', 'type': 'file',
-                 'content': u'test==', 'format': 'text'}
+                 'content': 'test==', 'format': 'text'}
         name = model['name']
         path = model['path']
         cm.save(model, path)
diff --git a/notebook/services/contents/tests/test_manager.py b/notebook/services/contents/tests/test_manager.py
index dfe5d272f5..69d58ffd2b 100644
--- a/notebook/services/contents/tests/test_manager.py
+++ b/notebook/services/contents/tests/test_manager.py
@@ -87,7 +87,7 @@ def test_get_os_path(self):
             self.assertEqual(path, fs_path)
 
     def test_checkpoint_subdir(self):
-        subd = u'sub ∂ir'
+        subd = 'sub ∂ir'
         cp_name = 'test-cp.ipynb'
         with TemporaryDirectory() as td:
             root = td
@@ -175,7 +175,7 @@ def test_403(self):
             os.chmod(os_path, 0o400)
             try:
                 with cm.open(os_path, 'w') as f:
-                    f.write(u"don't care")
+                    f.write("don't care")
             except HTTPError as e:
                 self.assertEqual(e.status_code, 403)
             else:
@@ -201,7 +201,7 @@ def test_escape_root(self):
             with self.assertRaisesHTTPError(404):
                 cm.save(model={
                     'type': 'file',
-                    'content': u'',
+                    'content': '',
                     'format': 'text',
                 }, path='../foo')
 
@@ -385,12 +385,12 @@ def test_get(self):
         file_model = cm.get(file_model_path)
         self.assertDictContainsSubset(
             {
-                'content': u'',
-                'format': u'text',
-                'mimetype': u'text/plain',
-                'name': u'untitled.txt',
-                'path': u'foo/untitled.txt',
-                'type': u'file',
+                'content': '',
+                'format': 'text',
+                'mimetype': 'text/plain',
+                'name': 'untitled.txt',
+                'path': 'foo/untitled.txt',
+                'type': 'file',
                 'writable': True,
             },
             file_model,
@@ -413,7 +413,7 @@ def test_get(self):
         # Directory contents should match the contents of each individual entry
         # when requested with content=False.
         model2_no_content = cm.get(sub_dir + name, content=False)
-        file_model_no_content = cm.get(u'foo/untitled.txt', content=False)
+        file_model_no_content = cm.get('foo/untitled.txt', content=False)
         sub_sub_dir_no_content = cm.get('foo/bar', content=False)
         self.assertEqual(sub_sub_dir_no_content['path'], 'foo/bar')
         self.assertEqual(sub_sub_dir_no_content['name'], 'bar')
@@ -582,9 +582,9 @@ def test_delete_root(self):
 
     def test_copy(self):
         cm = self.contents_manager
-        parent = u'å b'
-        name = u'nb √.ipynb'
-        path = u'{0}/{1}'.format(parent, name)
+        parent = 'å b'
+        name = 'nb √.ipynb'
+        path = '{0}/{1}'.format(parent, name)
         self.make_dir(parent)
 
         orig = cm.new(path=path)
@@ -593,11 +593,11 @@ def test_copy(self):
         self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy1.ipynb'))
 
         # copy with specified name
-        copy2 = cm.copy(path, u'å b/copy 2.ipynb')
-        self.assertEqual(copy2['name'], u'copy 2.ipynb')
-        self.assertEqual(copy2['path'], u'å b/copy 2.ipynb')
+        copy2 = cm.copy(path, 'å b/copy 2.ipynb')
+        self.assertEqual(copy2['name'], 'copy 2.ipynb')
+        self.assertEqual(copy2['path'], 'å b/copy 2.ipynb')
         # copy with specified path
-        copy2 = cm.copy(path, u'/')
+        copy2 = cm.copy(path, '/')
         self.assertEqual(copy2['name'], name)
         self.assertEqual(copy2['path'], name)
 
diff --git a/notebook/services/kernels/kernelmanager.py b/notebook/services/kernels/kernelmanager.py
index 7ed182dfc0..2e77385b53 100644
--- a/notebook/services/kernels/kernelmanager.py
+++ b/notebook/services/kernels/kernelmanager.py
@@ -391,7 +391,7 @@ def list_kernels(self):
     def _check_kernel_id(self, kernel_id):
         """Check a that a kernel_id exists and raise 404 if not."""
         if kernel_id not in self:
-            raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
+            raise web.HTTPError(404, 'Kernel does not exist: %s' % kernel_id)
 
     # monitoring activity:
 
diff --git a/notebook/services/kernelspecs/handlers.py b/notebook/services/kernelspecs/handlers.py
index a01d307fb2..505bd4d155 100644
--- a/notebook/services/kernelspecs/handlers.py
+++ b/notebook/services/kernelspecs/handlers.py
@@ -87,7 +87,7 @@ def get(self, kernel_name):
         try:
             spec = yield maybe_future(ksm.get_kernel_spec(kernel_name))
         except KeyError as e:
-            raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name) from e
+            raise web.HTTPError(404, 'Kernel spec %s not found' % kernel_name) from e
         if is_kernelspec_model(spec):
             model = spec
         else:
diff --git a/notebook/services/kernelspecs/tests/test_kernelspecs_api.py b/notebook/services/kernelspecs/tests/test_kernelspecs_api.py
index 215bfc861b..f5f725e455 100644
--- a/notebook/services/kernelspecs/tests/test_kernelspecs_api.py
+++ b/notebook/services/kernelspecs/tests/test_kernelspecs_api.py
@@ -20,7 +20,7 @@
                       'display_name':'Test kernel',
                      }
 
-some_resource = u"The very model of a modern major general"
+some_resource = "The very model of a modern major general"
 
 
 class KernelSpecAPI(object):
diff --git a/notebook/services/sessions/sessionmanager.py b/notebook/services/sessions/sessionmanager.py
index 92b2a73454..316e055c9a 100644
--- a/notebook/services/sessions/sessionmanager.py
+++ b/notebook/services/sessions/sessionmanager.py
@@ -187,7 +187,7 @@ def get_session(self, **kwargs):
             for key, value in kwargs.items():
                 q.append("%s=%r" % (key, value))
 
-            raise web.HTTPError(404, u'Session not found: %s' % (', '.join(q)))
+            raise web.HTTPError(404, 'Session not found: %s' % (', '.join(q)))
 
         model = yield maybe_future(self.row_to_model(row))
         raise gen.Return(model)
diff --git a/notebook/services/sessions/tests/test_sessionmanager.py b/notebook/services/sessions/tests/test_sessionmanager.py
index 719bbaef70..47f412308e 100644
--- a/notebook/services/sessions/tests/test_sessionmanager.py
+++ b/notebook/services/sessions/tests/test_sessionmanager.py
@@ -22,7 +22,7 @@ class DummyMKM(MappingKernelManager):
     """MappingKernelManager interface that doesn't start kernels, for testing"""
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        self.id_letters = iter(u'ABCDEFGHIJK')
+        self.id_letters = iter('ABCDEFGHIJK')
 
     def _new_id(self):
         return next(self.id_letters)
@@ -68,8 +68,8 @@ def test_get_session(self):
         session_id = self.create_session(path='/path/to/test.ipynb', kernel_name='bar')['id']
         model = self.loop.run_sync(lambda: sm.get_session(session_id=session_id))
         expected = {'id':session_id,
-                    'path': u'/path/to/test.ipynb',
-                    'notebook': {'path': u'/path/to/test.ipynb', 'name': None},
+                    'path': '/path/to/test.ipynb',
+                    'notebook': {'path': '/path/to/test.ipynb', 'name': None},
                     'type': 'notebook',
                     'name': None,
                     'kernel': {
@@ -112,9 +112,9 @@ def test_list_sessions(self):
         expected = [
             {
                 'id':sessions[0]['id'],
-                'path': u'/path/to/1/test1.ipynb',
+                'path': '/path/to/1/test1.ipynb',
                 'type': 'notebook',
-                'notebook': {'path': u'/path/to/1/test1.ipynb', 'name': None},
+                'notebook': {'path': '/path/to/1/test1.ipynb', 'name': None},
                 'name': None,
                 'kernel': {
                     'id': 'A',
@@ -125,7 +125,7 @@ def test_list_sessions(self):
                 }
             }, {
                 'id':sessions[1]['id'],
-                'path': u'/path/to/2/test2.py',
+                'path': '/path/to/2/test2.py',
                 'type': 'file',
                 'name': None,
                 'kernel': {
@@ -137,7 +137,7 @@ def test_list_sessions(self):
                 }
             }, {
                 'id':sessions[2]['id'],
-                'path': u'/path/to/3',
+                'path': '/path/to/3',
                 'type': 'console',
                 'name': 'foo',
                 'kernel': {
@@ -163,10 +163,10 @@ def test_list_sessions_dead_kernel(self):
         expected = [
             {
                 'id': sessions[1]['id'],
-                'path': u'/path/to/2/test2.ipynb',
+                'path': '/path/to/2/test2.ipynb',
                 'type': 'notebook',
                 'name': None,
-                'notebook': {'path': u'/path/to/2/test2.ipynb', 'name': None},
+                'notebook': {'path': '/path/to/2/test2.ipynb', 'name': None},
                 'kernel': {
                     'id': 'B',
                     'name':'python',
@@ -185,10 +185,10 @@ def test_update_session(self):
         self.loop.run_sync(lambda: sm.update_session(session_id, path='/path/to/new_name.ipynb'))
         model = self.loop.run_sync(lambda: sm.get_session(session_id=session_id))
         expected = {'id':session_id,
-                    'path': u'/path/to/new_name.ipynb',
+                    'path': '/path/to/new_name.ipynb',
                     'type': 'notebook',
                     'name': None,
-                    'notebook': {'path': u'/path/to/new_name.ipynb', 'name': None},
+                    'notebook': {'path': '/path/to/new_name.ipynb', 'name': None},
                     'kernel': {
                         'id': 'A',
                         'name':'julia',
@@ -218,10 +218,10 @@ def test_delete_session(self):
         new_sessions = self.loop.run_sync(lambda: sm.list_sessions())
         expected = [{
                 'id': sessions[0]['id'],
-                'path': u'/path/to/1/test1.ipynb',
+                'path': '/path/to/1/test1.ipynb',
                 'type': 'notebook',
                 'name': None,
-                'notebook': {'path': u'/path/to/1/test1.ipynb', 'name': None},
+                'notebook': {'path': '/path/to/1/test1.ipynb', 'name': None},
                 'kernel': {
                     'id': 'A',
                     'name':'python',
@@ -232,7 +232,7 @@ def test_delete_session(self):
             }, {
                 'id': sessions[2]['id'],
                 'type': 'console',
-                'path': u'/path/to/3',
+                'path': '/path/to/3',
                 'name': 'foo',
                 'kernel': {
                     'id': 'C',
diff --git a/notebook/terminal/terminalmanager.py b/notebook/terminal/terminalmanager.py
index ed901310ec..685acf01de 100644
--- a/notebook/terminal/terminalmanager.py
+++ b/notebook/terminal/terminalmanager.py
@@ -110,7 +110,7 @@ def get_terminal_model(self, name):
     def _check_terminal(self, name):
         """Check a that terminal 'name' exists and raise 404 if not."""
         if name not in self.terminals:
-            raise web.HTTPError(404, u'Terminal not found: %s' % name)
+            raise web.HTTPError(404, 'Terminal not found: %s' % name)
 
     def _initialize_culler(self):
         """Start culler if 'cull_inactive_timeout' is greater than zero.
diff --git a/notebook/tests/selenium/conftest.py b/notebook/tests/selenium/conftest.py
index 64cdfa23bd..f1e77cc8c3 100644
--- a/notebook/tests/selenium/conftest.py
+++ b/notebook/tests/selenium/conftest.py
@@ -39,8 +39,8 @@ def notebook_server():
     info = {}
     with TemporaryDirectory() as td:
         nbdir = info['nbdir'] = pjoin(td, 'notebooks')
-        os.makedirs(pjoin(nbdir, u'sub ∂ir1', u'sub ∂ir 1a'))
-        os.makedirs(pjoin(nbdir, u'sub ∂ir2', u'sub ∂ir 1b'))
+        os.makedirs(pjoin(nbdir, 'sub ∂ir1', 'sub ∂ir 1a'))
+        os.makedirs(pjoin(nbdir, 'sub ∂ir2', 'sub ∂ir 1b'))
 
         info['extra_env'] = {
             'JUPYTER_CONFIG_DIR': pjoin(td, 'jupyter_config'),
diff --git a/notebook/tests/test_files.py b/notebook/tests/test_files.py
index b711945756..7e42050d86 100644
--- a/notebook/tests/test_files.py
+++ b/notebook/tests/test_files.py
@@ -22,12 +22,12 @@
 class FilesTest(NotebookTestBase):
     def test_hidden_files(self):
         not_hidden = [
-            u'å b',
-            u'å b/ç. d',
+            'å b',
+            'å b/ç. d',
         ]
         hidden = [
-            u'.å b',
-            u'å b/.ç d',
+            '.å b',
+            'å b/.ç d',
         ]
         dirs = not_hidden + hidden
         
@@ -82,7 +82,7 @@ def test_contents_manager(self):
 
         nb = new_notebook(
             cells=[
-                new_markdown_cell(u'Created by test ³'),
+                new_markdown_cell('Created by test ³'),
                 new_code_cell("print(2*6)", outputs=[
                     new_output("stream", text="12"),
                 ])
@@ -98,7 +98,7 @@ def test_contents_manager(self):
             f.close()
 
         with io.open(pjoin(nbdir, 'test.txt'), 'w') as f:
-            f.write(u'foobar')
+            f.write('foobar')
             f.close()
 
         r = self.request('GET', 'files/testnb.ipynb')
diff --git a/notebook/tests/test_nbextensions.py b/notebook/tests/test_nbextensions.py
index 3d9549658a..11c3130f09 100644
--- a/notebook/tests/test_nbextensions.py
+++ b/notebook/tests/test_nbextensions.py
@@ -69,9 +69,9 @@ def cleanup_tempdirs():
 
         self.src = self.tempdir()
         self.files = files = [
-            pjoin(u'ƒile'),
-            pjoin(u'∂ir', u'ƒile1'),
-            pjoin(u'∂ir', u'∂ir2', u'ƒile2'),
+            pjoin('ƒile'),
+            pjoin('∂ir', 'ƒile1'),
+            pjoin('∂ir', '∂ir2', 'ƒile2'),
         ]
         for file_name in files:
             fullpath = os.path.join(self.src, file_name)
@@ -107,15 +107,15 @@ def cleanup_tempdirs():
     def assert_dir_exists(self, path):
         if not os.path.exists(path):
             do_exist = os.listdir(os.path.dirname(path))
-            self.fail(u"%s should exist (found %s)" % (path, do_exist))
+            self.fail("%s should exist (found %s)" % (path, do_exist))
 
     def assert_not_dir_exists(self, path):
         if os.path.exists(path):
-            self.fail(u"%s should not exist" % path)
+            self.fail("%s should not exist" % path)
 
     def assert_installed(self, relative_path, user=False):
         if user:
-            nbext = pjoin(self.data_dir, u'nbextensions')
+            nbext = pjoin(self.data_dir, 'nbextensions')
         else:
             nbext = self.system_nbext
         self.assert_dir_exists(
@@ -124,7 +124,7 @@ def assert_installed(self, relative_path, user=False):
 
     def assert_not_installed(self, relative_path, user=False):
         if user:
-            nbext = pjoin(self.data_dir, u'nbextensions')
+            nbext = pjoin(self.data_dir, 'nbextensions')
         else:
             nbext = self.system_nbext
         self.assert_not_dir_exists(
@@ -150,17 +150,17 @@ def test_create_nbextensions_user(self):
         with TemporaryDirectory() as td:
             install_nbextension(self.src, user=True)
             self.assert_installed(
-                pjoin(basename(self.src), u'ƒile'),
+                pjoin(basename(self.src), 'ƒile'),
                 user=True
             )
 
     def test_create_nbextensions_system(self):
         with TemporaryDirectory() as td:
-            self.system_nbext = pjoin(td, u'nbextensions')
+            self.system_nbext = pjoin(td, 'nbextensions')
             with patch.object(nbextensions, 'SYSTEM_JUPYTER_PATH', [td]):
                 install_nbextension(self.src, user=False)
                 self.assert_installed(
-                    pjoin(basename(self.src), u'ƒile'),
+                    pjoin(basename(self.src), 'ƒile'),
                     user=False
                 )
 
@@ -170,28 +170,28 @@ def test_single_file(self):
         self.assert_installed(file_name)
 
     def test_single_dir(self):
-        d = u'∂ir'
+        d = '∂ir'
         install_nbextension(pjoin(self.src, d))
         self.assert_installed(self.files[-1])
 
     def test_single_dir_trailing_slash(self):
-        d = u'∂ir/'
+        d = '∂ir/'
         install_nbextension(pjoin(self.src, d))
         self.assert_installed(self.files[-1])
         if os.name == 'nt':
-            d = u'∂ir\\'
+            d = '∂ir\\'
             install_nbextension(pjoin(self.src, d))
             self.assert_installed(self.files[-1])
 
     def test_destination_file(self):
         file_name = self.files[0]
-        install_nbextension(pjoin(self.src, file_name), destination = u'ƒiledest')
-        self.assert_installed(u'ƒiledest')
+        install_nbextension(pjoin(self.src, file_name), destination='ƒiledest')
+        self.assert_installed('ƒiledest')
 
     def test_destination_dir(self):
-        d = u'∂ir'
-        install_nbextension(pjoin(self.src, d), destination = u'ƒiledest2')
-        self.assert_installed(pjoin(u'ƒiledest2', u'∂ir2', u'ƒile2'))
+        d = '∂ir'
+        install_nbextension(pjoin(self.src, d), destination='ƒiledest2')
+        self.assert_installed(pjoin('ƒiledest2', '∂ir2', 'ƒile2'))
 
     def test_install_nbextension(self):
         with self.assertRaises(TypeError):
@@ -199,7 +199,7 @@ def test_install_nbextension(self):
 
     def test_overwrite_file(self):
         with TemporaryDirectory() as d:
-            fname = u'ƒ.js'
+            fname = 'ƒ.js'
             src = pjoin(d, fname)
             with open(src, 'w') as f:
                 f.write('first')
@@ -216,12 +216,12 @@ def test_overwrite_file(self):
     def test_overwrite_dir(self):
         with TemporaryDirectory() as src:
             base = basename(src)
-            fname = u'ƒ.js'
+            fname = 'ƒ.js'
             touch(pjoin(src, fname))
             install_nbextension(src)
             self.assert_installed(pjoin(base, fname))
             os.remove(pjoin(src, fname))
-            fname2 = u'∂.js'
+            fname2 = '∂.js'
             touch(pjoin(src, fname2))
             install_nbextension(src, overwrite=True)
             self.assert_installed(pjoin(base, fname2))
@@ -229,7 +229,7 @@ def test_overwrite_dir(self):
 
     def test_update_file(self):
         with TemporaryDirectory() as d:
-            fname = u'ƒ.js'
+            fname = 'ƒ.js'
             src = pjoin(d, fname)
             with open(src, 'w') as f:
                 f.write('first')
@@ -247,7 +247,7 @@ def test_update_file(self):
 
     def test_skip_old_file(self):
         with TemporaryDirectory() as d:
-            fname = u'ƒ.js'
+            fname = 'ƒ.js'
             src = pjoin(d, fname)
             mtime = touch(src)
             install_nbextension(src)
@@ -311,7 +311,7 @@ def fake_urlretrieve(url, dest):
 
     def test_check_nbextension(self):
         with TemporaryDirectory() as d:
-            f = u'ƒ.js'
+            f = 'ƒ.js'
             src = pjoin(d, f)
             touch(src)
             install_nbextension(src, user=True)
@@ -323,7 +323,7 @@ def test_check_nbextension(self):
     @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
     def test_install_symlink(self):
         with TemporaryDirectory() as d:
-            f = u'ƒ.js'
+            f = 'ƒ.js'
             src = pjoin(d, f)
             touch(src)
             install_nbextension(src, symlink=True)
@@ -335,8 +335,8 @@ def test_install_symlink(self):
     @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
     def test_overwrite_broken_symlink(self):
         with TemporaryDirectory() as d:
-            f = u'ƒ.js'
-            f2 = u'ƒ2.js'
+            f = 'ƒ.js'
+            f2 = 'ƒ2.js'
             src = pjoin(d, f)
             src2 = pjoin(d, f2)
             touch(src)
@@ -351,8 +351,8 @@ def test_overwrite_broken_symlink(self):
     @pytest.mark.skipif(sys.platform == "win32", reason="do not run on windows")
     def test_install_symlink_destination(self):
         with TemporaryDirectory() as d:
-            f = u'ƒ.js'
-            flink = u'ƒlink.js'
+            f = 'ƒ.js'
+            flink = 'ƒlink.js'
             src = pjoin(d, f)
             touch(src)
             install_nbextension(src, symlink=True, destination=flink)
@@ -367,7 +367,7 @@ def test_install_symlink_bad(self):
             install_nbextension("http://example.com/foo.js", symlink=True)
 
         with TemporaryDirectory() as d:
-            zf = u'ƒ.zip'
+            zf = 'ƒ.zip'
             zsrc = pjoin(d, zf)
             with zipfile.ZipFile(zsrc, 'w') as z:
                 z.writestr("a.js", b"b();")
@@ -377,7 +377,7 @@ def test_install_symlink_bad(self):
 
     def test_install_destination_bad(self):
         with TemporaryDirectory() as d:
-            zf = u'ƒ.zip'
+            zf = 'ƒ.zip'
             zsrc = pjoin(d, zf)
             with zipfile.ZipFile(zsrc, 'w') as z:
                 z.writestr("a.js", b"b();")
@@ -387,24 +387,24 @@ def test_install_destination_bad(self):
 
     def test_nbextension_enable(self):
         with TemporaryDirectory() as d:
-            f = u'ƒ.js'
+            f = 'ƒ.js'
             src = pjoin(d, f)
             touch(src)
             install_nbextension(src, user=True)
-            enable_nbextension(section='notebook', require=u'ƒ')
+            enable_nbextension(section='notebook', require='ƒ')
 
         config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
         cm = BaseJSONConfigManager(config_dir=config_dir)
-        enabled = cm.get('notebook').get('load_extensions', {}).get(u'ƒ', False)
+        enabled = cm.get('notebook').get('load_extensions', {}).get('ƒ', False)
         assert enabled
 
     def test_nbextension_disable(self):
         self.test_nbextension_enable()
-        disable_nbextension(section='notebook', require=u'ƒ')
+        disable_nbextension(section='notebook', require='ƒ')
 
         config_dir = os.path.join(_get_config_dir(user=True), 'nbconfig')
         cm = BaseJSONConfigManager(config_dir=config_dir)
-        enabled = cm.get('notebook').get('load_extensions', {}).get(u'ƒ', False)
+        enabled = cm.get('notebook').get('load_extensions', {}).get('ƒ', False)
         assert not enabled
 
 
diff --git a/notebook/tests/test_utils.py b/notebook/tests/test_utils.py
index 51f0e8accc..b6c386a2bc 100644
--- a/notebook/tests/test_utils.py
+++ b/notebook/tests/test_utils.py
@@ -84,7 +84,7 @@ def test_is_hidden():
 def test_is_hidden_win32():
     with TemporaryDirectory() as root:
         root = cast_unicode(root)
-        subdir1 = os.path.join(root, u'subdir')
+        subdir1 = os.path.join(root, 'subdir')
         os.makedirs(subdir1)
         assert not is_hidden(subdir1, root)
         r = ctypes.windll.kernel32.SetFileAttributesW(subdir1, 0x02)
diff --git a/notebook/tree/tests/test_tree_handler.py b/notebook/tree/tests/test_tree_handler.py
index 801185ae51..3c7034b84c 100644
--- a/notebook/tree/tests/test_tree_handler.py
+++ b/notebook/tree/tests/test_tree_handler.py
@@ -21,7 +21,7 @@ def setUp(self):
             write(nb, f, version=4)
 
         with io.open(os.path.join(d, 'baz.txt'), 'w', encoding='utf-8') as f:
-            f.write(u'flamingo')
+            f.write('flamingo')
 
         self.base_url()
 
diff --git a/notebook/utils.py b/notebook/utils.py
index ef6d9e437c..c8f7d5ca66 100644
--- a/notebook/utils.py
+++ b/notebook/utils.py
@@ -79,14 +79,14 @@ def url_escape(path):
     Turns '/foo bar/' into '/foo%20bar/'
     """
     parts = py3compat.unicode_to_str(path, encoding='utf8').split('/')
-    return u'/'.join([quote(p) for p in parts])
+    return '/'.join([quote(p) for p in parts])
 
 def url_unescape(path):
     """Unescape special characters in a URL path
 
     Turns '/foo%20bar/' into '/foo bar/'
     """
-    return u'/'.join([
+    return '/'.join([
         py3compat.str_to_unicode(unquote(p), encoding='utf8')
         for p in py3compat.unicode_to_str(path, encoding='utf8').split('/')
     ])
diff --git a/notebook/view/handlers.py b/notebook/view/handlers.py
index e788964662..c271670506 100644
--- a/notebook/view/handlers.py
+++ b/notebook/view/handlers.py
@@ -13,7 +13,7 @@ class ViewHandler(IPythonHandler):
     def get(self, path):
         path = path.strip('/')
         if not self.contents_manager.file_exists(path):
-            raise web.HTTPError(404, u'File does not exist: %s' % path)
+            raise web.HTTPError(404, 'File does not exist: %s' % path)
 
         basename = path.rsplit('/', 1)[-1]
         file_url = url_path_join(self.base_url, 'files', url_escape(path))

From 1cc25e12c4e07f2e79d4c201189f5e50411a79c7 Mon Sep 17 00:00:00 2001
From: Aleksei Stepanov <penguinolog@gmail.com>
Date: Mon, 25 Oct 2021 14:41:05 +0200
Subject: [PATCH 2/3] Partial #6153 #4463 : reduce ipython_genutils usage

* remove stdlib code can be used directly or helper redundant:
  * str is already unicode
  * type casting is done in external lib
  * import aliases
* remove unused imports
---
 notebook/_sysinfo.py                            |  2 +-
 notebook/auth/security.py                       |  9 ++++-----
 notebook/base/handlers.py                       |  3 +--
 notebook/base/zmqhandlers.py                    |  5 ++---
 notebook/bundler/tests/test_bundlerextension.py |  5 ++---
 notebook/gateway/handlers.py                    |  5 ++---
 notebook/jstest.py                              |  7 ++++---
 notebook/nbconvert/handlers.py                  |  3 +--
 .../nbconvert/tests/test_nbconvert_handlers.py  |  2 --
 notebook/nbextensions.py                        | 10 ++--------
 notebook/notebookapp.py                         |  9 +++------
 notebook/services/contents/filecheckpoints.py   |  3 +--
 notebook/services/contents/fileio.py            |  4 +---
 notebook/services/contents/filemanager.py       |  5 ++---
 notebook/services/contents/manager.py           |  3 +--
 .../contents/tests/test_contents_api.py         | 17 ++++++-----------
 notebook/services/contents/tests/test_fileio.py |  4 ++--
 .../contents/tests/test_largefilemanager.py     |  8 +++++---
 .../services/contents/tests/test_manager.py     |  2 +-
 notebook/services/kernels/handlers.py           |  3 +--
 notebook/services/kernels/kernelmanager.py      |  3 +--
 notebook/services/sessions/sessionmanager.py    |  3 +--
 notebook/terminal/__init__.py                   |  2 +-
 notebook/tests/launchnotebook.py                |  2 +-
 notebook/tests/test_files.py                    | 10 +++-------
 notebook/tests/test_nbextensions.py             |  7 +++----
 notebook/tests/test_notebookapp.py              |  2 +-
 notebook/tests/test_serverextensions.py         |  7 ++-----
 notebook/tests/test_utils.py                    |  4 +---
 notebook/utils.py                               | 14 ++++----------
 30 files changed, 60 insertions(+), 103 deletions(-)

diff --git a/notebook/_sysinfo.py b/notebook/_sysinfo.py
index 4abeadcedb..d411bf68d9 100644
--- a/notebook/_sysinfo.py
+++ b/notebook/_sysinfo.py
@@ -11,7 +11,7 @@
 import sys
 import subprocess
 
-from ipython_genutils import py3compat, encoding
+from ipython_genutils import encoding
 
 import notebook
 
diff --git a/notebook/auth/security.py b/notebook/auth/security.py
index 6664137137..482b2cc94e 100644
--- a/notebook/auth/security.py
+++ b/notebook/auth/security.py
@@ -12,7 +12,6 @@
 import traceback
 import warnings
 
-from ipython_genutils.py3compat import cast_bytes, str_to_bytes, cast_unicode
 from traitlets.config import Config, ConfigFileNotFound, JSONFileConfigLoader
 from jupyter_core.paths import jupyter_config_dir
 
@@ -68,11 +67,11 @@ def passwd(passphrase=None, algorithm='argon2'):
         )
         h = ph.hash(passphrase)
 
-        return ':'.join((algorithm, cast_unicode(h, 'ascii')))
+        return ':'.join((algorithm, h))
     else:
         h = hashlib.new(algorithm)
         salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len)
-        h.update(cast_bytes(passphrase, 'utf-8') + str_to_bytes(salt, 'ascii'))
+        h.update(passphrase.encode('utf-8') + salt.encode('ascii'))
 
         return ':'.join((algorithm, salt, h.hexdigest()))
 
@@ -127,7 +126,7 @@ def passwd_check(hashed_passphrase, passphrase):
         if len(pw_digest) == 0:
             return False
 
-        h.update(cast_bytes(passphrase, 'utf-8') + cast_bytes(salt, 'ascii'))
+        h.update(passphrase.encode('utf-8') + salt.encode('ascii'))
 
         return h.hexdigest() == pw_digest
 
@@ -153,7 +152,7 @@ def persist_config(config_file=None, mode=0o600):
     yield config
 
     with io.open(config_file, 'w', encoding='utf8') as f:
-        f.write(cast_unicode(json.dumps(config, indent=2)))
+        f.write(json.dumps(config, indent=2))
 
     try:
         os.chmod(config_file, mode)
diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py
index ab69a5b1c6..52335580a1 100755
--- a/notebook/base/handlers.py
+++ b/notebook/base/handlers.py
@@ -27,7 +27,6 @@
 
 from traitlets.config import Application
 from ipython_genutils.path import filefind
-from ipython_genutils.py3compat import string_types
 
 import notebook
 from notebook._tz import utcnow
@@ -807,7 +806,7 @@ def set_headers(self):
     def initialize(self, path, default_filename=None, no_cache_paths=None):
         self.no_cache_paths = no_cache_paths or []
         
-        if isinstance(path, string_types):
+        if isinstance(path, str):
             path = [path]
         
         self.root = tuple(
diff --git a/notebook/base/zmqhandlers.py b/notebook/base/zmqhandlers.py
index c4d4554a89..203bc5aa5a 100644
--- a/notebook/base/zmqhandlers.py
+++ b/notebook/base/zmqhandlers.py
@@ -20,7 +20,6 @@
     from jupyter_client.jsonutil import (
         date_default as json_default, extract_dates
     )
-from ipython_genutils.py3compat import cast_unicode
 
 from notebook.utils import maybe_future
 from .handlers import IPythonHandler
@@ -236,7 +235,7 @@ def _reserialize_reply(self, msg_or_list, channel=None):
             return buf
         else:
             smsg = json.dumps(msg, default=json_default)
-            return cast_unicode(smsg)
+            return smsg
 
     def _on_zmq_reply(self, stream, msg_list):
         # Sometimes this gets triggered when the on_close method is scheduled in the
@@ -281,7 +280,7 @@ def pre_get(self):
             raise web.HTTPError(403)
 
         if self.get_argument('session_id', False):
-            self.session.session = cast_unicode(self.get_argument('session_id'))
+            self.session.session = self.get_argument('session_id')
         else:
             self.log.warning("No session ID specified")
 
diff --git a/notebook/bundler/tests/test_bundlerextension.py b/notebook/bundler/tests/test_bundlerextension.py
index dcb32424cd..773d5bf530 100644
--- a/notebook/bundler/tests/test_bundlerextension.py
+++ b/notebook/bundler/tests/test_bundlerextension.py
@@ -6,10 +6,9 @@
 import os
 import shutil
 import unittest
+from tempfile import TemporaryDirectory
 
 from unittest.mock import patch
-from ipython_genutils.tempdir import TemporaryDirectory
-from ipython_genutils import py3compat
 
 from traitlets.tests.utils import check_help_all_output
 
@@ -29,7 +28,7 @@ def setUp(self):
         """Build an isolated config environment."""
         td = TemporaryDirectory()
         
-        self.test_dir = py3compat.cast_unicode(td.name)
+        self.test_dir = td.name
         self.data_dir = os.path.join(self.test_dir, 'data')
         self.config_dir = os.path.join(self.test_dir, 'config')
         self.system_data_dir = os.path.join(self.test_dir, 'system_data')
diff --git a/notebook/gateway/handlers.py b/notebook/gateway/handlers.py
index d774ba39e2..b31f35ef2d 100644
--- a/notebook/gateway/handlers.py
+++ b/notebook/gateway/handlers.py
@@ -16,7 +16,6 @@
 from tornado.httpclient import HTTPRequest
 from tornado.escape import url_escape, json_decode, utf8
 
-from ipython_genutils.py3compat import cast_unicode
 from jupyter_client.session import Session
 from traitlets.config.configurable import LoggingConfigurable
 
@@ -56,7 +55,7 @@ def authenticate(self):
             raise web.HTTPError(403)
 
         if self.get_argument('session_id', False):
-            self.session.session = cast_unicode(self.get_argument('session_id'))
+            self.session.session = self.get_argument('session_id')
         else:
             self.log.warning("No session ID specified")
 
@@ -68,7 +67,7 @@ def initialize(self):
     @gen.coroutine
     def get(self, kernel_id, *args, **kwargs):
         self.authenticate()
-        self.kernel_id = cast_unicode(kernel_id, 'ascii')
+        self.kernel_id = kernel_id
         yield super().get(kernel_id=kernel_id, *args, **kwargs)
 
     def send_ping(self):
diff --git a/notebook/jstest.py b/notebook/jstest.py
index 9ec4c1f6ba..3c37e13d03 100644
--- a/notebook/jstest.py
+++ b/notebook/jstest.py
@@ -18,14 +18,15 @@
 import subprocess
 import time
 from io import BytesIO
+from shutil import which
+from tempfile import TemporaryDirectory
 from threading import Thread, Lock, Event
 
 from unittest.mock import patch
 
 from jupyter_core.paths import jupyter_runtime_dir
-from ipython_genutils.py3compat import bytes_to_str, which
+from ipython_genutils.py3compat import bytes_to_str
 from notebook._sysinfo import get_sys_info
-from ipython_genutils.tempdir import TemporaryDirectory
 
 from subprocess import TimeoutExpired
 def popen_wait(p, timeout):
@@ -59,7 +60,7 @@ def run(self):
             with self.buffer_lock:
                 self.buffer.write(chunk)
             if self.echo:
-                sys.stdout.write(bytes_to_str(chunk))
+                sys.stdout.write(chunk)
     
         os.close(self.readfd)
         os.close(self.writefd)
diff --git a/notebook/nbconvert/handlers.py b/notebook/nbconvert/handlers.py
index 4583ee5a39..51c9b1c063 100644
--- a/notebook/nbconvert/handlers.py
+++ b/notebook/nbconvert/handlers.py
@@ -17,7 +17,6 @@
 from ..utils import maybe_future
 from nbformat import from_dict
 
-from ipython_genutils.py3compat import cast_bytes
 from ipython_genutils import text
 
 def find_resource_files(output_files_dir):
@@ -47,7 +46,7 @@ def respond_zip(handler, name, output, resources):
     buffer = io.BytesIO()
     zipf = zipfile.ZipFile(buffer, mode='w', compression=zipfile.ZIP_DEFLATED)
     output_filename = os.path.splitext(name)[0] + resources['output_extension']
-    zipf.writestr(output_filename, cast_bytes(output, 'utf-8'))
+    zipf.writestr(output_filename, output)
     for filename, data in output_files.items():
         zipf.writestr(os.path.basename(filename), data)
     zipf.close()
diff --git a/notebook/nbconvert/tests/test_nbconvert_handlers.py b/notebook/nbconvert/tests/test_nbconvert_handlers.py
index 83b898eb3e..d86cda3558 100644
--- a/notebook/nbconvert/tests/test_nbconvert_handlers.py
+++ b/notebook/nbconvert/tests/test_nbconvert_handlers.py
@@ -13,8 +13,6 @@
     new_notebook, new_markdown_cell, new_code_cell, new_output,
 )
 
-from ipython_genutils.testing.decorators import onlyif_cmds_exist
-
 from base64 import encodebytes
 
 
diff --git a/notebook/nbextensions.py b/notebook/nbextensions.py
index 595d377b8e..c9b3e5d84c 100644
--- a/notebook/nbextensions.py
+++ b/notebook/nbextensions.py
@@ -9,6 +9,7 @@
 import tarfile
 import zipfile
 from os.path import basename, join as pjoin, normpath
+from tempfile import TemporaryDirectory
 
 from urllib.parse import urlparse
 from urllib.request import urlretrieve
@@ -17,8 +18,6 @@
     SYSTEM_JUPYTER_PATH, ENV_JUPYTER_PATH,
 )
 from jupyter_core.utils import ensure_dir_exists
-from ipython_genutils.py3compat import string_types, cast_unicode_py2
-from ipython_genutils.tempdir import TemporaryDirectory
 from ._version import __version__
 from .config_manager import BaseJSONConfigManager
 
@@ -59,7 +58,7 @@ def check_nbextension(files, user=False, prefix=None, nbextensions_dir=None, sys
     if not os.path.exists(nbext):
         return False
     
-    if isinstance(files, string_types):
+    if isinstance(files, str):
         # one file given, turn it into a list
         files = [files]
     
@@ -123,8 +122,6 @@ def install_nbextension(path, overwrite=False, symlink=False,
     
     if isinstance(path, (list, tuple)):
         raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions")
-    
-    path = cast_unicode_py2(path)
 
     if path.startswith(('https://', 'http://')):
         if symlink:
@@ -158,7 +155,6 @@ def install_nbextension(path, overwrite=False, symlink=False,
     else:
         if not destination:
             destination = basename(normpath(path))
-        destination = cast_unicode_py2(destination)
         full_dest = normpath(pjoin(nbext, destination))
         if overwrite and os.path.lexists(full_dest):
             if logger:
@@ -252,7 +248,6 @@ def uninstall_nbextension(dest, require=None, user=False, sys_prefix=False, pref
         Logger instance to use
     """
     nbext = _get_nbextension_dir(user=user, sys_prefix=sys_prefix, prefix=prefix, nbextensions_dir=nbextensions_dir)
-    dest = cast_unicode_py2(dest)
     full_dest = pjoin(nbext, dest)
     if os.path.lexists(full_dest):
         if logger:
@@ -276,7 +271,6 @@ def _find_uninstall_nbextension(filename, logger=None):
 
     Returns True if files were removed, False otherwise.
     """
-    filename = cast_unicode_py2(filename)
     for nbext in jupyter_path('nbextensions'):
         path = pjoin(nbext, filename)
         if os.path.lexists(path):
diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py
index c3097f076d..7ef2808019 100755
--- a/notebook/notebookapp.py
+++ b/notebook/notebookapp.py
@@ -101,7 +101,6 @@
     Any, Dict, Unicode, Integer, List, Bool, Bytes, Instance,
     TraitError, Type, Float, observe, default, validate
 )
-from ipython_genutils import py3compat
 from jupyter_core.paths import jupyter_runtime_dir, jupyter_path
 from notebook._sysinfo import get_sys_info
 
@@ -198,7 +197,7 @@ def init_settings(self, jupyter_app, kernel_manager, contents_manager,
             "template_path",
             jupyter_app.template_file_path,
         )
-        if isinstance(_template_path, py3compat.string_types):
+        if isinstance(_template_path, str):
             _template_path = (_template_path,)
         template_path = [os.path.expanduser(path) for path in _template_path]
 
@@ -241,7 +240,7 @@ def init_settings(self, jupyter_app, kernel_manager, contents_manager,
         now = utcnow()
 
         root_dir = contents_manager.root_dir
-        home = py3compat.str_to_unicode(os.path.expanduser('~'), encoding=sys.getfilesystemencoding())
+        home = os.path.expanduser('~')
         if root_dir.startswith(home + os.path.sep):
             # collapse $HOME to ~
             root_dir = '~' + root_dir[len(home):]
@@ -1131,8 +1130,6 @@ def _default_allow_remote(self):
             # Address is a hostname
             for info in socket.getaddrinfo(self.ip, self.port, 0, socket.SOCK_STREAM):
                 addr = info[4][0]
-                if not py3compat.PY3:
-                    addr = addr.decode('ascii')
 
                 try:
                     parsed = ipaddress.ip_address(addr.split('%')[0])
@@ -1506,7 +1503,7 @@ def _default_notebook_dir(self):
         if self.file_to_run:
             return os.path.dirname(os.path.abspath(self.file_to_run))
         else:
-            return py3compat.getcwd()
+            return os.getcwd()
 
     @validate('notebook_dir')
     def _notebook_dir_validate(self, proposal):
diff --git a/notebook/services/contents/filecheckpoints.py b/notebook/services/contents/filecheckpoints.py
index 91afe3bb39..ac657481b3 100644
--- a/notebook/services/contents/filecheckpoints.py
+++ b/notebook/services/contents/filecheckpoints.py
@@ -13,7 +13,6 @@
 from .fileio import FileManagerMixin
 
 from jupyter_core.utils import ensure_dir_exists
-from ipython_genutils.py3compat import getcwd
 from traitlets import Unicode
 
 from notebook import _tz as tz
@@ -45,7 +44,7 @@ def _root_dir_default(self):
         try:
             return self.parent.root_dir
         except AttributeError:
-            return getcwd()
+            return os.getcwd()
 
     # ContentsManager-dependent checkpoint API
     def create_checkpoint(self, contents_mgr, path):
diff --git a/notebook/services/contents/fileio.py b/notebook/services/contents/fileio.py
index f931dbbb97..5565e515d9 100644
--- a/notebook/services/contents/fileio.py
+++ b/notebook/services/contents/fileio.py
@@ -19,8 +19,6 @@
 )
 import nbformat
 
-from ipython_genutils.py3compat import str_to_unicode
-
 from traitlets.config import Configurable
 from traitlets import Bool
 
@@ -224,7 +222,7 @@ def perm_to_403(self, os_path=''):
                 # this may not work perfectly on unicode paths on Python 2,
                 # but nobody should be doing that anyway.
                 if not os_path:
-                    os_path = str_to_unicode(e.filename or 'unknown file')
+                    os_path = e.filename or 'unknown file'
                 path = to_api_path(os_path, root=self.root_dir)
                 raise HTTPError(403, 'Permission denied: %s' % path) from e
             else:
diff --git a/notebook/services/contents/filemanager.py b/notebook/services/contents/filemanager.py
index 481bfc352e..f2e7f4a31b 100644
--- a/notebook/services/contents/filemanager.py
+++ b/notebook/services/contents/filemanager.py
@@ -25,7 +25,6 @@
 
 from ipython_genutils.importstring import import_item
 from traitlets import Any, Unicode, Bool, TraitError, observe, default, validate
-from ipython_genutils.py3compat import getcwd, string_types
 
 from notebook import _tz as tz
 from notebook.utils import (
@@ -73,7 +72,7 @@ def _default_root_dir(self):
         try:
             return self.parent.notebook_dir
         except AttributeError:
-            return getcwd()
+            return os.getcwd()
 
     save_script = Bool(False, config=True, help='DEPRECATED, use post_save_hook. Will be removed in Notebook 5.0')
     @observe('save_script')
@@ -118,7 +117,7 @@ def _update_save_script(self, change):
     @validate('post_save_hook')
     def _validate_post_save_hook(self, proposal):
         value = proposal['value']
-        if isinstance(value, string_types):
+        if isinstance(value, str):
             value = import_item(value)
         if not callable(value):
             raise TraitError("post_save_hook must be callable")
diff --git a/notebook/services/contents/manager.py b/notebook/services/contents/manager.py
index f570d89905..e741e8423c 100644
--- a/notebook/services/contents/manager.py
+++ b/notebook/services/contents/manager.py
@@ -29,7 +29,6 @@
     validate,
     default,
 )
-from ipython_genutils.py3compat import string_types
 from notebook.base.handlers import IPythonHandler
 from notebook.transutils import _
 
@@ -106,7 +105,7 @@ def _notary_default(self):
     @validate('pre_save_hook')
     def _validate_pre_save_hook(self, proposal):
         value = proposal['value']
-        if isinstance(value, string_types):
+        if isinstance(value, str):
             value = import_item(self.pre_save_hook)
         if not callable(value):
             raise TraitError("pre_save_hook must be callable")
diff --git a/notebook/services/contents/tests/test_contents_api.py b/notebook/services/contents/tests/test_contents_api.py
index b801845164..2b44fcdefd 100644
--- a/notebook/services/contents/tests/test_contents_api.py
+++ b/notebook/services/contents/tests/test_contents_api.py
@@ -7,6 +7,8 @@
 import os
 import shutil
 import sys
+from base64 import encodebytes, decodebytes
+from tempfile import TemporaryDirectory
 from unicodedata import normalize
 
 pjoin = os.path.join
@@ -25,13 +27,6 @@
     new_notebook, new_markdown_cell,
 )
 from nbformat import v2
-from ipython_genutils import py3compat
-from ipython_genutils.tempdir import TemporaryDirectory
-
-try: #PY3
-    from base64 import encodebytes, decodebytes
-except ImportError: #PY2
-    from base64 import encodestring as encodebytes, decodestring as decodebytes
 
 
 def uniq_stable(elems):
@@ -142,7 +137,7 @@ class APITest(NotebookTestBase):
     hidden_dirs = ['.hidden', '__pycache__']
 
     # Don't include root dir.
-    dirs = uniq_stable([py3compat.cast_unicode(d) for (d,n) in dirs_nbs[1:]])
+    dirs = uniq_stable([d for (d, _n) in dirs_nbs[1:]])
     top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs}
 
     @staticmethod
@@ -312,7 +307,7 @@ def test_get_nb_invalid(self):
             }],
         }
         path = 'å b/Validate tést.ipynb'
-        self.make_txt(path, py3compat.cast_unicode(json.dumps(nb)))
+        self.make_txt(path, json.dumps(nb))
         model = self.api.read(path).json()
         self.assertEqual(model['path'], path)
         self.assertEqual(model['type'], 'notebook')
@@ -369,9 +364,9 @@ def test_get_bad_type(self):
         with assert_http_error(400):
             self.api.read('unicodé/innonascii.ipynb', type='directory')
 
-    def _check_created(self, resp, path, type='notebook'):
+    def _check_created(self, resp: requests.Response, path, type='notebook'):
         self.assertEqual(resp.status_code, 201)
-        location_header = py3compat.str_to_unicode(resp.headers['Location'])
+        location_header = resp.headers['Location']
         self.assertEqual(location_header, url_path_join(self.url_prefix, 'api/contents', url_escape(path)))
         rjson = resp.json()
         self.assertEqual(rjson['name'], path.rsplit('/', 1)[-1])
diff --git a/notebook/services/contents/tests/test_fileio.py b/notebook/services/contents/tests/test_fileio.py
index 206c53b3d5..c6d089156b 100644
--- a/notebook/services/contents/tests/test_fileio.py
+++ b/notebook/services/contents/tests/test_fileio.py
@@ -9,13 +9,13 @@
 import pytest
 import stat
 import sys
+from tempfile import TemporaryDirectory
 
 from ..fileio import atomic_writing
 
-from ipython_genutils.tempdir import TemporaryDirectory
-
 umask = 0
 
+
 def test_atomic_writing():
     class CustomExc(Exception): pass
 
diff --git a/notebook/services/contents/tests/test_largefilemanager.py b/notebook/services/contents/tests/test_largefilemanager.py
index 4f52b2453d..53306ee9c8 100644
--- a/notebook/services/contents/tests/test_largefilemanager.py
+++ b/notebook/services/contents/tests/test_largefilemanager.py
@@ -1,9 +1,11 @@
-from unittest import TestCase
-from ipython_genutils.tempdir import TemporaryDirectory
-from ..largefilemanager import LargeFileManager
 import os
+from tempfile import TemporaryDirectory
+from unittest import TestCase
+
 from tornado import web
 
+from ..largefilemanager import LargeFileManager
+
 
 def _make_dir(contents_manager, api_path):
     """
diff --git a/notebook/services/contents/tests/test_manager.py b/notebook/services/contents/tests/test_manager.py
index 69d58ffd2b..47176cc9bf 100644
--- a/notebook/services/contents/tests/test_manager.py
+++ b/notebook/services/contents/tests/test_manager.py
@@ -9,10 +9,10 @@
 from tornado.web import HTTPError
 from unittest import TestCase, skipIf
 from tempfile import NamedTemporaryFile
+from tempfile import TemporaryDirectory
 
 from nbformat import v4 as nbformat
 
-from ipython_genutils.tempdir import TemporaryDirectory
 from traitlets import TraitError
 
 from ..filemanager import FileContentsManager
diff --git a/notebook/services/kernels/handlers.py b/notebook/services/kernels/handlers.py
index 2b7f89b6f8..7b6d316688 100644
--- a/notebook/services/kernels/handlers.py
+++ b/notebook/services/kernels/handlers.py
@@ -21,7 +21,6 @@
     from jupyter_client.jsonutil import (
         date_default as json_default
     )
-from ipython_genutils.py3compat import cast_unicode
 from notebook.utils import maybe_future, url_path_join, url_escape
 
 from ...base.handlers import APIHandler
@@ -355,7 +354,7 @@ def give_up():
 
     @gen.coroutine
     def get(self, kernel_id):
-        self.kernel_id = cast_unicode(kernel_id, 'ascii')
+        self.kernel_id = kernel_id
         yield super().get(kernel_id=kernel_id)
 
     @gen.coroutine
diff --git a/notebook/services/kernels/kernelmanager.py b/notebook/services/kernels/kernelmanager.py
index 2e77385b53..ef7458b651 100644
--- a/notebook/services/kernels/kernelmanager.py
+++ b/notebook/services/kernels/kernelmanager.py
@@ -23,7 +23,6 @@
 
 from notebook.utils import maybe_future, to_os_path, exists
 from notebook._tz import utcnow, isoformat
-from ipython_genutils.py3compat import getcwd
 
 from notebook.prometheus.metrics import KERNEL_CURRENTLY_RUNNING_TOTAL
 
@@ -61,7 +60,7 @@ def _default_root_dir(self):
         try:
             return self.parent.notebook_dir
         except AttributeError:
-            return getcwd()
+            return os.getcwd()
 
     @validate('root_dir')
     def _update_root_dir(self, proposal):
diff --git a/notebook/services/sessions/sessionmanager.py b/notebook/services/sessions/sessionmanager.py
index 316e055c9a..ec094825ee 100644
--- a/notebook/services/sessions/sessionmanager.py
+++ b/notebook/services/sessions/sessionmanager.py
@@ -14,7 +14,6 @@
 from tornado import gen, web
 
 from traitlets.config.configurable import LoggingConfigurable
-from ipython_genutils.py3compat import unicode_type
 from traitlets import Instance
 
 from notebook.utils import maybe_future
@@ -86,7 +85,7 @@ def session_exists(self, path):
 
     def new_session_id(self):
         "Create a uuid for a new session"
-        return unicode_type(uuid.uuid4())
+        return str(uuid.uuid4())
 
     @gen.coroutine
     def create_session(self, path=None, name=None, type=None, kernel_name=None, kernel_id=None):
diff --git a/notebook/terminal/__init__.py b/notebook/terminal/__init__.py
index 57e74c9bf4..3325368d53 100644
--- a/notebook/terminal/__init__.py
+++ b/notebook/terminal/__init__.py
@@ -1,5 +1,6 @@
 import os
 import sys
+from shutil import which
 
 import terminado
 from ..utils import check_version
@@ -7,7 +8,6 @@
 if not check_version(terminado.__version__, '0.8.3'):
     raise ImportError("terminado >= 0.8.3 required, found %s" % terminado.__version__)
 
-from ipython_genutils.py3compat import which
 from notebook.utils import url_path_join as ujoin
 from .terminalmanager import TerminalManager
 from .handlers import TerminalHandler, TermSocket, NewTerminalHandler, NamedTerminalHandler
diff --git a/notebook/tests/launchnotebook.py b/notebook/tests/launchnotebook.py
index bb5f8b7781..dc984ccb74 100644
--- a/notebook/tests/launchnotebook.py
+++ b/notebook/tests/launchnotebook.py
@@ -7,6 +7,7 @@
 import sys
 from threading import Thread, Event
 import time
+from tempfile import TemporaryDirectory
 from unittest import TestCase
 
 pjoin = os.path.join
@@ -21,7 +22,6 @@
 from traitlets.config import Config
 from ..notebookapp import NotebookApp, urlencode_unix_socket
 from ..utils import url_path_join
-from ipython_genutils.tempdir import TemporaryDirectory
 
 MAX_WAITTIME = 30   # seconds to wait for notebook server to start
 POLL_INTERVAL = 0.1 # time between attempts
diff --git a/notebook/tests/test_files.py b/notebook/tests/test_files.py
index 7e42050d86..ceb5b5641c 100644
--- a/notebook/tests/test_files.py
+++ b/notebook/tests/test_files.py
@@ -1,13 +1,8 @@
 """Test the /files/ handler."""
 
 import io
-import os
-from unicodedata import normalize
-
-pjoin = os.path.join
-
-import requests
 import json
+import os
 
 from nbformat import write
 from nbformat.v4 import (new_notebook,
@@ -16,7 +11,8 @@
 
 from notebook.utils import url_path_join
 from .launchnotebook import NotebookTestBase
-from ipython_genutils import py3compat
+
+pjoin = os.path.join
 
 
 class FilesTest(NotebookTestBase):
diff --git a/notebook/tests/test_nbextensions.py b/notebook/tests/test_nbextensions.py
index 11c3130f09..b85916f00f 100644
--- a/notebook/tests/test_nbextensions.py
+++ b/notebook/tests/test_nbextensions.py
@@ -11,13 +11,12 @@
 import zipfile
 from io import BytesIO, StringIO
 from os.path import basename, join as pjoin
-from traitlets.tests.utils import check_help_all_output
+from tempfile import TemporaryDirectory
 from unittest import TestCase
 
 from unittest.mock import patch
 
-from ipython_genutils import py3compat
-from ipython_genutils.tempdir import TemporaryDirectory
+from traitlets.tests.utils import check_help_all_output
 from notebook import nbextensions
 from notebook.nbextensions import (install_nbextension, check_nbextension,
     enable_nbextension, disable_nbextension,
@@ -55,7 +54,7 @@ class TestInstallNBExtension(TestCase):
     def tempdir(self):
         td = TemporaryDirectory()
         self.tempdirs.append(td)
-        return py3compat.cast_unicode(td.name)
+        return td.name
 
     def setUp(self):
         # Any TemporaryDirectory objects appended to this list will be cleaned
diff --git a/notebook/tests/test_notebookapp.py b/notebook/tests/test_notebookapp.py
index d48501b935..7759802e5a 100644
--- a/notebook/tests/test_notebookapp.py
+++ b/notebook/tests/test_notebookapp.py
@@ -8,6 +8,7 @@
 from subprocess import Popen, PIPE, STDOUT
 import sys
 from tempfile import NamedTemporaryFile
+from tempfile import TemporaryDirectory
 
 from unittest.mock import patch
 
@@ -16,7 +17,6 @@
 from traitlets.tests.utils import check_help_all_output
 
 from jupyter_core.application import NoStart
-from ipython_genutils.tempdir import TemporaryDirectory
 from traitlets import TraitError
 from notebook import notebookapp, __version__
 from notebook.auth.security import passwd_check
diff --git a/notebook/tests/test_serverextensions.py b/notebook/tests/test_serverextensions.py
index 35da80d4c5..a03b894058 100644
--- a/notebook/tests/test_serverextensions.py
+++ b/notebook/tests/test_serverextensions.py
@@ -1,12 +1,9 @@
-import imp
 import os
 import sys
+from tempfile import TemporaryDirectory
 from unittest import TestCase
 from unittest.mock import patch
 
-from ipython_genutils.tempdir import TemporaryDirectory
-from ipython_genutils import py3compat
-
 from notebook.config_manager import BaseJSONConfigManager
 from traitlets.tests.utils import check_help_all_output
 from jupyter_core import paths
@@ -50,7 +47,7 @@ class MockEnvTestCase(TestCase):
     def tempdir(self):
         td = TemporaryDirectory()
         self.tempdirs.append(td)
-        return py3compat.cast_unicode(td.name)
+        return td.name
 
     def setUp(self):
         self.tempdirs = []
diff --git a/notebook/tests/test_utils.py b/notebook/tests/test_utils.py
index b6c386a2bc..60e6a5b82a 100644
--- a/notebook/tests/test_utils.py
+++ b/notebook/tests/test_utils.py
@@ -6,13 +6,12 @@
 import ctypes
 import os
 import sys
+from tempfile import TemporaryDirectory
 
 import pytest
 
 from traitlets.tests.utils import check_help_all_output
 from notebook.utils import url_escape, url_unescape, is_hidden, is_file_hidden
-from ipython_genutils.py3compat import cast_unicode
-from ipython_genutils.tempdir import TemporaryDirectory
 
 
 def test_help_output():
@@ -83,7 +82,6 @@ def test_is_hidden():
 @pytest.mark.skipif(sys.platform != "win32", reason="run on windows only")
 def test_is_hidden_win32():
     with TemporaryDirectory() as root:
-        root = cast_unicode(root)
         subdir1 = os.path.join(root, 'subdir')
         os.makedirs(subdir1)
         assert not is_hidden(subdir1, root)
diff --git a/notebook/utils.py b/notebook/utils.py
index c8f7d5ca66..29f2739c34 100644
--- a/notebook/utils.py
+++ b/notebook/utils.py
@@ -21,7 +21,6 @@
 # in tornado >=5 with Python 3
 from tornado.concurrent import Future as TornadoFuture
 from tornado import gen
-from ipython_genutils import py3compat
 
 # UF_HIDDEN is a stat flag not defined in the stat module.
 # It is used by BSD to indicate hidden files.
@@ -78,18 +77,15 @@ def url_escape(path):
 
     Turns '/foo bar/' into '/foo%20bar/'
     """
-    parts = py3compat.unicode_to_str(path, encoding='utf8').split('/')
-    return '/'.join([quote(p) for p in parts])
+    parts = path.split('/')
+    return '/'.join(quote(p) for p in parts)
 
 def url_unescape(path):
     """Unescape special characters in a URL path
 
     Turns '/foo%20bar/' into '/foo bar/'
     """
-    return '/'.join([
-        py3compat.str_to_unicode(unquote(p), encoding='utf8')
-        for p in py3compat.unicode_to_str(path, encoding='utf8').split('/')
-    ])
+    return '/'.join(unquote(p) for p in path.split('/'))
 
 
 def is_file_hidden_win(abs_path, stat_res=None):
@@ -113,9 +109,7 @@ def is_file_hidden_win(abs_path, stat_res=None):
 
     win32_FILE_ATTRIBUTE_HIDDEN = 0x02
     try:
-        attrs = ctypes.windll.kernel32.GetFileAttributesW(
-            py3compat.cast_unicode(abs_path)
-        )
+        attrs = ctypes.windll.kernel32.GetFileAttributesW(abs_path)
     except AttributeError:
         pass
     else:

From 67420fcea31fed5201a0a2d490d955115d2915c4 Mon Sep 17 00:00:00 2001
From: Aleksei Stepanov <penguinolog@gmail.com>
Date: Mon, 25 Oct 2021 15:02:27 +0200
Subject: [PATCH 3/3] Logger.warn is deprecated for a while

---
 notebook/services/kernels/handlers.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/notebook/services/kernels/handlers.py b/notebook/services/kernels/handlers.py
index 7b6d316688..4571b3526d 100644
--- a/notebook/services/kernels/handlers.py
+++ b/notebook/services/kernels/handlers.py
@@ -602,7 +602,7 @@ def _send_status_message(self, status):
         self.write_message(json.dumps(msg, default=json_default))
 
     def on_kernel_restarted(self):
-        logging.warn("kernel %s restarted", self.kernel_id)
+        logging.warning("kernel %s restarted", self.kernel_id)
         self._send_status_message('restarting')
 
     def on_restart_failed(self):