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

Support Python 3.8-3.11 #866

Merged
merged 3 commits into from
Nov 2, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.7", "3.10"]
python-version: ["3.8", "3.11"]
include:
- os: windows-latest
python-version: "3.9"
- os: ubuntu-latest
python-version: "pypy-3.8"
- os: ubuntu-latest
python-version: "3.11-dev"
python-version: "3.10"
- os: macos-latest
python-version: "3.8"
steps:
Expand Down Expand Up @@ -86,7 +86,7 @@ jobs:
- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
with:
python_version: "3.7"
python_version: "3.8"
- name: Install miniumum versions
uses: jupyterlab/maintainer-tools/.github/actions/install-minimums@v1
- name: Run the unit tests
Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ repos:
hooks:
- id: mdformat

- repo: https://github.com/asottile/pyupgrade
rev: v3.1.0
hooks:
- id: pyupgrade
args: [--py38-plus]

- repo: https://github.com/PyCQA/doc8
rev: v1.0.0
hooks:
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
sphinx:
configuration: docs/conf.py
python:
version: 3.7
version: 3.8
install:
# install jupyter-client itself
- method: pip
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def extract_oname_v4(code: str, cursor_pos: int) -> str:
return ""


class Adapter(object):
class Adapter:
"""Base class for adapting messages

Override message_type(msg) methods to create adapters.
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def call_handlers(self, since_last_heartbeat: float) -> None:
HBChannelABC.register(HBChannel)


class ZMQSocketChannel(object):
class ZMQSocketChannel:
"""A ZMQ socket wrapper"""

def __init__(self, socket: zmq.Socket, session: Session, loop: t.Any = None) -> None:
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/channelsabc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import abc


class ChannelABC(object, metaclass=abc.ABCMeta):
class ChannelABC(metaclass=abc.ABCMeta):
"""A base class for all channel ABCs."""

@abc.abstractmethod
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/clientabc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# -----------------------------------------------------------------------------


class KernelClientABC(object, metaclass=abc.ABCMeta):
class KernelClientABC(metaclass=abc.ABCMeta):
"""KernelManager ABC.

The docstrings for this class can be found in the base implementation:
Expand Down
8 changes: 4 additions & 4 deletions jupyter_client/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def write_connection_file(
else:
N = 1
for _ in range(ports_needed):
while os.path.exists("%s-%s" % (ip, str(N))):
while os.path.exists(f"{ip}-{str(N)}"):
N += 1
ports.append(N)
N += 1
Expand Down Expand Up @@ -229,7 +229,7 @@ def find_connection_file(
try:
# first, try explicit name
return _filefind(filename, path)
except IOError:
except OSError:
pass

# not found by full name
Expand All @@ -247,7 +247,7 @@ def find_connection_file(

matches = [os.path.abspath(m) for m in matches]
if not matches:
raise IOError("Could not find %r in %r" % (filename, path))
raise OSError(f"Could not find {filename!r} in {path!r}")
elif len(matches) == 1:
return matches[0]
else:
Expand Down Expand Up @@ -605,7 +605,7 @@ def _make_url(self, channel: str) -> str:
if transport == "tcp":
return "tcp://%s:%i" % (ip, port)
else:
return "%s://%s-%s" % (transport, ip, port)
return f"{transport}://{ip}-{port}"

def _create_connected_socket(
self, channel: str, identity: Optional[bytes] = None
Expand Down
4 changes: 2 additions & 2 deletions jupyter_client/consoleapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def init_connection_file(self) -> None:
self.connection_file = cf
try:
self.connection_file = _filefind(self.connection_file, [".", self.runtime_dir])
except IOError:
except OSError:
self.log.debug("Connection File not found: %s", self.connection_file)
return

Expand Down Expand Up @@ -247,7 +247,7 @@ def init_ssh(self) -> None:
control_port=self.control_port,
)

self.log.info("Forwarding connections to %s via %s" % (ip, self.sshserver))
self.log.info(f"Forwarding connections to {ip} via {self.sshserver}")

# tunnels return a new set of ports, which will be on localhost:
self.ip = localhost()
Expand Down
11 changes: 4 additions & 7 deletions jupyter_client/kernelspec.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Tools for managing kernel specs"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import io
import json
import os
import re
Expand Down Expand Up @@ -47,7 +46,7 @@ def from_resource_dir(cls, resource_dir):
Pass the path to the *directory* containing kernel.json.
"""
kernel_file = pjoin(resource_dir, "kernel.json")
with io.open(kernel_file, "r", encoding="utf-8") as f:
with open(kernel_file, encoding="utf-8") as f:
kernel_dict = json.load(f)
return cls(resource_dir=resource_dir, **kernel_dict)

Expand Down Expand Up @@ -106,7 +105,7 @@ def _list_kernels_in(dir):
key = f.lower()
if not _is_valid_kernel_name(key):
warnings.warn(
"Invalid kernelspec directory name (%s): %s" % (_kernel_name_description, path),
f"Invalid kernelspec directory name ({_kernel_name_description}): {path}",
stacklevel=3,
)
kernels[key] = path
Expand All @@ -118,7 +117,7 @@ def __init__(self, name):
self.name = name

def __str__(self):
return "No such kernel named {}".format(self.name)
return f"No such kernel named {self.name}"


class KernelSpecManager(LoggingConfigurable):
Expand Down Expand Up @@ -375,9 +374,7 @@ def install_kernel_spec(
kernel_name = os.path.basename(source_dir)
kernel_name = kernel_name.lower()
if not _is_valid_kernel_name(kernel_name):
raise ValueError(
"Invalid kernel name %r. %s" % (kernel_name, _kernel_name_description)
)
raise ValueError(f"Invalid kernel name {kernel_name!r}. {_kernel_name_description}")

if user and prefix:
raise ValueError("Can't specify both user and prefix. Please choose one or the other.")
Expand Down
4 changes: 2 additions & 2 deletions jupyter_client/kernelspecapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def path_key(item):

print("Available kernels:")
for kernelname, path in sorted(paths.items(), key=path_key):
print(" %s %s" % (kernelname.ljust(name_len), path))
print(f" {kernelname.ljust(name_len)} {path}")
else:
print(json.dumps({"kernelspecs": specs}, indent=2))

Expand Down Expand Up @@ -205,7 +205,7 @@ def start(self):
if not (self.force or self.answer_yes):
print("Kernel specs to remove:")
for name in self.spec_names:
print(" %s\t%s" % (name.ljust(20), spec_paths[name]))
print(f" {name.ljust(20)}\t{spec_paths[name]}")
answer = input("Remove %i kernel specs [y/N]: " % len(self.spec_names))
if not answer.lower().startswith("y"):
return
Expand Down
6 changes: 3 additions & 3 deletions jupyter_client/localinterfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _get_output(cmd):
p = Popen(cmd, stdout=PIPE, stderr=PIPE, startupinfo=startupinfo)
stdout, stderr = p.communicate()
if p.returncode:
raise IOError("Failed to run %s: %s" % (cmd, stderr.decode("utf8", "replace")))
raise OSError("Failed to run {}: {}".format(cmd, stderr.decode("utf8", "replace")))
return stdout.decode("utf8", "replace")


Expand Down Expand Up @@ -188,7 +188,7 @@ def _load_ips_gethostbyname():
global LOCALHOST
try:
LOCAL_IPS[:] = socket.gethostbyname_ex("localhost")[2]
except socket.error:
except OSError:
# assume common default
LOCAL_IPS[:] = ["127.0.0.1"]

Expand All @@ -198,7 +198,7 @@ def _load_ips_gethostbyname():
# try hostname.local, in case hostname has been short-circuited to loopback
if not hostname.endswith(".local") and all(ip.startswith("127") for ip in PUBLIC_IPS):
PUBLIC_IPS[:] = socket.gethostbyname_ex(socket.gethostname() + ".local")[2]
except socket.error:
except OSError:
pass
finally:
PUBLIC_IPS[:] = _uniq_stable(PUBLIC_IPS)
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/managerabc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import abc


class KernelManagerABC(object, metaclass=abc.ABCMeta):
class KernelManagerABC(metaclass=abc.ABCMeta):
"""KernelManager ABC.

The docstrings for this class can be found in the base implementation:
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/multikernelmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ def signal_kernel(self, kernel_id: str, signum: int) -> None:
signum : int
Signal number to send kernel.
"""
self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum))
self.log.info(f"Signaled Kernel {kernel_id} with {signum}")

async def _async_restart_kernel(self, kernel_id: str, now: bool = False) -> None:
"""Restart a kernel by its uuid, keeping the same ports.
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def __init__(self, **kwargs):
self.session = Session(**kwargs)


class Message(object):
class Message:
"""A simple message object that maps dict keys to attributes.

A Message can be created from a dict and a dict from a Message instance
Expand Down
4 changes: 2 additions & 2 deletions jupyter_client/ssh/tunnel.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def select_random_ports(n):
# -----------------------------------------------------------------------------
# Check for passwordless login
# -----------------------------------------------------------------------------
_password_pat = re.compile((r"pass(word|phrase):".encode("utf8")), re.IGNORECASE)
_password_pat = re.compile((br"pass(word|phrase):"), re.IGNORECASE)


def try_passwordless_ssh(server, keyfile, paramiko=None):
Expand Down Expand Up @@ -227,7 +227,7 @@ def openssh_tunnel(
server, port = server.split(":")
ssh += " -p %s" % port

cmd = "%s -O check %s" % (ssh, server)
cmd = f"{ssh} -O check {server}"
(output, exitstatus) = pexpect.run(cmd, withexitstatus=True)
if not exitstatus:
pid = int(output[output.find(b"(pid=") + 5 : output.find(b")")]) # noqa
Expand Down
2 changes: 1 addition & 1 deletion jupyter_client/threaded.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# during garbage collection of threads at exit


class ThreadedZMQSocketChannel(object):
class ThreadedZMQSocketChannel:
"""A ZMQ socket invoking a callback in the ioloop"""

session = None
Expand Down
4 changes: 1 addition & 3 deletions jupyter_client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,7 @@ def _filefind(filename, path_dirs=None):
if os.path.isfile(testname):
return os.path.abspath(testname)

raise IOError(
"File {!r} does not exist in any of the search paths: {!r}".format(filename, path_dirs)
)
raise OSError(f"File {filename!r} does not exist in any of the search paths: {path_dirs!r}")


def _expand_path(s):
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ classifiers = [
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
requires-python = ">=3.7"
requires-python = ">=3.8"
dependencies = [
"entrypoints",
"jupyter_core>=4.9.2",
Expand Down
4 changes: 2 additions & 2 deletions tests/test_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_write_connection_file():
cf = os.path.join(d, "kernel.json")
connect.write_connection_file(cf, **sample_info)
assert os.path.exists(cf)
with open(cf, "r") as f:
with open(cf) as f:
info = json.load(f)
info["key"] = info["key"].encode()
assert info == sample_info
Expand Down Expand Up @@ -129,7 +129,7 @@ def test_app_load_connection_file():
if attr in ("key", "signature_scheme"):
continue
value = getattr(app, attr)
assert value == expected, "app.%s = %s != %s" % (attr, value, expected)
assert value == expected, f"app.{attr} = {value} != {expected}"


def test_load_connection_info():
Expand Down
6 changes: 3 additions & 3 deletions tests/test_jsonutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
REFERENCE_DATETIME = datetime.datetime(2013, 7, 3, 16, 34, 52, 249482, tzlocal())


class MyInt(object):
class MyInt:
def __int__(self):
return 389


numbers.Integral.register(MyInt)


class MyFloat(object):
class MyFloat:
def __float__(self):
return 3.14

Expand Down Expand Up @@ -113,7 +113,7 @@ def test_json_default():
# Containers
([1, 2], None),
((1, 2), [1, 2]),
(set([1, 2]), [1, 2]),
({1, 2}, [1, 2]),
(dict(x=1), None),
({'x': 1, 'y': [1, 2, 3], '1': 'int'}, None),
# More exotic objects
Expand Down
4 changes: 2 additions & 2 deletions tests/test_kernelapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_kernelapp_lifecycle():
break
time.sleep(1 / POLL_FREQ)
else:
raise AssertionError("No started file created in {} seconds".format(WAIT_TIME))
raise AssertionError(f"No started file created in {WAIT_TIME} seconds")

# Connection file should be there by now
for _ in range(WAIT_TIME * POLL_FREQ):
Expand All @@ -48,7 +48,7 @@ def test_kernelapp_lifecycle():
break
time.sleep(1 / POLL_FREQ)
else:
raise AssertionError("No connection file created in {} seconds".format(WAIT_TIME))
raise AssertionError(f"No connection file created in {WAIT_TIME} seconds")
assert len(files) == 1
cf = files[0]
assert cf.startswith("kernel")
Expand Down
2 changes: 1 addition & 1 deletion tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def install_kernel(kernels_dir, argv=None, name="test", display_name=None):
return kernel_dir


class test_env(object):
class test_env:
"""Set Jupyter path variables to a temporary directory

Useful as a context manager or with explicit start/stop
Expand Down