Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid usage of ipython_genutils #718

Merged
merged 2 commits into from Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions jupyter_server/_sysinfo.py
Expand Up @@ -9,8 +9,6 @@
import subprocess
import sys

from ipython_genutils import encoding

import jupyter_server


Expand Down Expand Up @@ -86,7 +84,6 @@ def pkg_info(pkg_path):
sys_platform=sys.platform,
platform=platform.platform(),
os_name=os.name,
default_encoding=encoding.DEFAULT_ENCODING,
)


Expand Down
11 changes: 4 additions & 7 deletions jupyter_server/auth/security.py
Expand Up @@ -11,9 +11,6 @@
import warnings
from contextlib import contextmanager

from ipython_genutils.py3compat import cast_bytes
from ipython_genutils.py3compat import cast_unicode
from ipython_genutils.py3compat import str_to_bytes
from jupyter_core.paths import jupyter_config_dir
from traitlets.config import Config
from traitlets.config import ConfigFileNotFound
Expand Down Expand Up @@ -72,11 +69,11 @@ def passwd(passphrase=None, algorithm="argon2"):
)
h = ph.hash(passphrase)

return ":".join((algorithm, cast_unicode(h, "ascii")))
return ":".join((algorithm, h))

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()))

Expand Down Expand Up @@ -133,7 +130,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

Expand All @@ -160,7 +157,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)
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/base/handlers.py
Expand Up @@ -16,7 +16,6 @@
from urllib.parse import urlparse

import prometheus_client
from ipython_genutils.path import filefind
from jinja2 import TemplateNotFound
from jupyter_core.paths import is_hidden
from tornado import escape
Expand All @@ -31,6 +30,7 @@
from jupyter_server.i18n import combine_translations
from jupyter_server.services.security import csp_report_uri
from jupyter_server.utils import ensure_async
from jupyter_server.utils import filefind
from jupyter_server.utils import url_escape
from jupyter_server.utils import url_is_absolute
from jupyter_server.utils import url_path_join
Expand Down
6 changes: 2 additions & 4 deletions jupyter_server/base/zmqhandlers.py
Expand Up @@ -9,7 +9,6 @@
from urllib.parse import urlparse

import tornado
from ipython_genutils.py3compat import cast_unicode

try:
from jupyter_client.jsonutil import json_default
Expand Down Expand Up @@ -268,8 +267,7 @@ def _reserialize_reply(self, msg_or_list, channel=None):
buf = serialize_binary_message(msg)
return buf
else:
smsg = json.dumps(msg, default=json_default)
return cast_unicode(smsg)
return json.dumps(msg, default=json_default)

def select_subprotocol(self, subprotocols):
preferred_protocol = self.settings.get("kernel_ws_protocol")
Expand Down Expand Up @@ -326,7 +324,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")

Expand Down
5 changes: 2 additions & 3 deletions jupyter_server/gateway/handlers.py
Expand Up @@ -6,7 +6,6 @@
import os
import random

from ipython_genutils.py3compat import cast_unicode
from jupyter_client.session import Session
from tornado import web
from tornado.concurrent import Future
Expand Down Expand Up @@ -59,7 +58,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")

Expand All @@ -70,7 +69,7 @@ def initialize(self):

async def get(self, kernel_id, *args, **kwargs):
self.authenticate()
self.kernel_id = cast_unicode(kernel_id, "ascii")
self.kernel_id = kernel_id
await super(WebSocketChannelsHandler, self).get(kernel_id=kernel_id, *args, **kwargs)

def send_ping(self):
Expand Down
14 changes: 10 additions & 4 deletions jupyter_server/nbconvert/handlers.py
Expand Up @@ -3,11 +3,10 @@
# Distributed under the terms of the Modified BSD License.
import io
import os
import sys
import zipfile

from anyio.to_thread import run_sync
from ipython_genutils import text
from ipython_genutils.py3compat import cast_bytes
from nbformat import from_dict
from tornado import web
from tornado.log import app_log
Expand All @@ -21,6 +20,13 @@

AUTH_RESOURCE = "nbconvert"

# datetime.strftime date format for jupyter
# inlined from ipython_genutils
if sys.platform == "win32":
date_format = "%B %d, %Y"
else:
date_format = "%B %-d, %Y"


def find_resource_files(output_files_dir):
files = []
Expand Down Expand Up @@ -50,7 +56,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.encode("utf-8"))
for filename, data in output_files.items():
zipf.writestr(os.path.basename(filename), data)
zipf.close()
Expand Down Expand Up @@ -111,7 +117,7 @@ async def get(self, format, path):
self.set_header("Last-Modified", model["last_modified"])

# create resources dictionary
mod_date = model["last_modified"].strftime(text.date_format)
mod_date = model["last_modified"].strftime(date_format)
nb_title = os.path.splitext(name)[0]

resource_dict = {
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/services/contents/filemanager.py
Expand Up @@ -11,7 +11,6 @@

import nbformat
from anyio.to_thread import run_sync
from ipython_genutils.importstring import import_item
from jupyter_core.paths import exists
from jupyter_core.paths import is_file_hidden
from jupyter_core.paths import is_hidden
Expand All @@ -33,6 +32,7 @@
from jupyter_server import _tz as tz
from jupyter_server.base.handlers import AuthenticatedFileHandler
from jupyter_server.transutils import _i18n
from jupyter_server.utils import import_item

try:
from os.path import samefile
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/services/contents/manager.py
Expand Up @@ -6,7 +6,6 @@
import re
from fnmatch import fnmatch

from ipython_genutils.importstring import import_item
from nbformat import sign
from nbformat import validate as validate_nb
from nbformat import ValidationError
Expand All @@ -30,6 +29,7 @@
from .checkpoints import Checkpoints
from jupyter_server.transutils import _i18n
from jupyter_server.utils import ensure_async
from jupyter_server.utils import import_item


copy_pat = re.compile(r"\-Copy\d*\.")
Expand Down
3 changes: 1 addition & 2 deletions jupyter_server/services/kernels/handlers.py
Expand Up @@ -8,7 +8,6 @@
from textwrap import dedent
from traceback import format_tb

from ipython_genutils.py3compat import cast_unicode
from jupyter_client import protocol_version as client_protocol_version

try:
Expand Down Expand Up @@ -406,7 +405,7 @@ def give_up():
await future

async def get(self, kernel_id):
self.kernel_id = cast_unicode(kernel_id, "ascii")
self.kernel_id = kernel_id
await super(ZMQChannelsHandler, self).get(kernel_id=kernel_id)

async def _register_session(self):
Expand Down
2 changes: 1 addition & 1 deletion jupyter_server/terminal/__init__.py
@@ -1,5 +1,6 @@
import os
import sys
from shutil import which

import terminado

Expand All @@ -8,7 +9,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 jupyter_server.utils import url_path_join as ujoin
from . import api_handlers
from .handlers import TermSocket
Expand Down
96 changes: 96 additions & 0 deletions jupyter_server/utils.py
Expand Up @@ -385,3 +385,99 @@ def is_namespace_package(namespace):
# e.g. module not installed
return None
return isinstance(spec.submodule_search_locations, _NamespacePath)


def filefind(filename, path_dirs=None):
"""Find a file by looking through a sequence of paths.
This iterates through a sequence of paths looking for a file and returns
the full, absolute path of the first occurence of the file. If no set of
path dirs is given, the filename is tested as is, after running through
:func:`expandvars` and :func:`expanduser`. Thus a simple call::
filefind('myfile.txt')
will find the file in the current working dir, but::
filefind('~/myfile.txt')
Will find the file in the users home directory. This function does not
automatically try any paths, such as the cwd or the user's home directory.
Parameters
----------
filename : str
The filename to look for.
path_dirs : str, None or sequence of str
The sequence of paths to look for the file in. If None, the filename
need to be absolute or be in the cwd. If a string, the string is
put into a sequence and the searched. If a sequence, walk through
each element and join with ``filename``, calling :func:`expandvars`
and :func:`expanduser` before testing for existence.
Returns
-------
Raises :exc:`IOError` or returns absolute path to file.
"""

# If paths are quoted, abspath gets confused, strip them...
filename = filename.strip('"').strip("'")
# If the input is an absolute path, just check it exists
if os.path.isabs(filename) and os.path.isfile(filename):
return filename

if path_dirs is None:
path_dirs = ("",)
elif isinstance(path_dirs, str):
path_dirs = (path_dirs,)

for path in path_dirs:
if path == ".":
path = os.getcwd()
testname = expand_path(os.path.join(path, filename))
if os.path.isfile(testname):
return os.path.abspath(testname)

raise IOError("File %r does not exist in any of the search paths: %r" % (filename, path_dirs))


def expand_path(s):
"""Expand $VARS and ~names in a string, like a shell
:Examples:
In [2]: os.environ['FOO']='test'
In [3]: expand_path('variable FOO is $FOO')
Out[3]: 'variable FOO is test'
"""
# This is a pretty subtle hack. When expand user is given a UNC path
# on Windows (\\server\share$\%username%), os.path.expandvars, removes
# the $ to get (\\server\share\%username%). I think it considered $
# alone an empty var. But, we need the $ to remains there (it indicates
# a hidden share).
if os.name == "nt":
s = s.replace("$\\", "IPYTHON_TEMP")
s = os.path.expandvars(os.path.expanduser(s))
if os.name == "nt":
s = s.replace("IPYTHON_TEMP", "$\\")
return s


def import_item(name):
"""Import and return ``bar`` given the string ``foo.bar``.
Calling ``bar = import_item("foo.bar")`` is the functional equivalent of
executing the code ``from foo import bar``.
Parameters
----------
name : string
The fully qualified name of the module/package being imported.
Returns
-------
mod : module object
The module that was imported.
"""

parts = name.rsplit(".", 1)
if len(parts) == 2:
# called with 'foo.bar....'
package, obj = parts
module = __import__(package, fromlist=[obj])
try:
pak = getattr(module, obj)
except AttributeError:
raise ImportError("No module named %s" % obj)
return pak
else:
# called with un-dotted string
return __import__(parts[0])
1 change: 0 additions & 1 deletion setup.cfg
Expand Up @@ -31,7 +31,6 @@ install_requires =
tornado>=6.1.0
pyzmq>=17
argon2-cffi
ipython_genutils
traitlets>=5
jupyter_core>=4.6.0
jupyter_client>=6.1.1
Expand Down
14 changes: 0 additions & 14 deletions tests/services/contents/test_fileio.py
@@ -1,27 +1,13 @@
import functools
import io
import os
import stat
import sys

import decorator
import pytest
from ipython_genutils.testing.decorators import skip_win32 as _skip_win32

from jupyter_server.services.contents.fileio import atomic_writing


@functools.wraps(_skip_win32)
def skip_win32(f):
# Patches the "skip_win32" method to allow pytest fixtures
# in methods wrapped by this decorator.
def inner(f, *args, **kwargs):
decorated_f = _skip_win32(f)
return decorated_f(*args, **kwargs)

return decorator.decorator(inner, f)


umask = 0


Expand Down