[Python-modules-commits] [jupyter-client] 01/05: Import jupyter-client_5.1.0.orig.tar.gz
Gordon Ball
chronitis-guest at moszumanska.debian.org
Fri Jun 23 11:52:04 UTC 2017
This is an automated email from the git hooks/post-receive script.
chronitis-guest pushed a commit to branch master
in repository jupyter-client.
commit 79f89b801f8fe1112867307413a9c82103d30ae9
Author: Gordon Ball <gordon at chronitis.net>
Date: Fri Jun 23 13:32:50 2017 +0200
Import jupyter-client_5.1.0.orig.tar.gz
---
.travis.yml | 12 ++---
docs/changelog.rst | 20 ++++++++
docs/environment.yml | 1 -
docs/messaging.rst | 59 ++++++++++++++++++++--
jupyter_client/_version.py | 4 +-
jupyter_client/blocking/client.py | 5 ++
jupyter_client/channels.py | 7 +--
jupyter_client/connect.py | 6 ++-
jupyter_client/consoleapp.py | 2 +-
jupyter_client/manager.py | 2 +-
jupyter_client/session.py | 18 +++++++
jupyter_client/tests/test_adapter.py | 9 ++--
jupyter_client/tests/test_client.py | 6 +--
jupyter_client/tests/test_connect.py | 65 +++++++++++++++++++------
jupyter_client/tests/test_jsonutil.py | 32 ++++++------
jupyter_client/tests/test_kernelmanager.py | 10 ++--
jupyter_client/tests/test_kernelspec.py | 7 ++-
jupyter_client/tests/test_multikernelmanager.py | 7 ++-
jupyter_client/tests/test_public_api.py | 10 ++--
jupyter_client/tests/test_session.py | 18 +++++--
jupyter_client/tests/utils.py | 11 ++++-
jupyter_client/threaded.py | 9 ++--
setup.py | 2 +-
23 files changed, 234 insertions(+), 88 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index edb33b1..0a3a969 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,17 +7,13 @@ python:
- 3.3
- 2.7
sudo: false
-before_install:
- - git clone --quiet --depth 1 https://github.com/minrk/travis-wheels travis-wheels
-
install:
- - pip install -U setuptools
- - pip install -f travis-wheels/wheelhouse -e .[test] codecov
- - python -c 'import ipykernel.kernelspec; ipykernel.kernelspec.install(user=True)'
+ - pip install --upgrade setuptools pip
+ - pip install --upgrade --pre -e .[test] pytest-cov pytest-warnings codecov
script:
- - nosetests -v --with-coverage --cover-package jupyter_client jupyter_client
+ - py.test --cov jupyter_client jupyter_client
after_success:
- - codecov
+ - codecov
matrix:
allow_failures:
- python: nightly
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 1220f57..35e21b5 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -4,6 +4,26 @@
Changes in Jupyter Client
=========================
+5.1
+===
+
+`5.1 on GitHub <https://github.com/jupyter/jupyter_client/milestones/5.1>`__
+
+- Define Jupyter protocol version 5.2,
+ resolving ambiguity of ``cursor_pos`` field in the presence
+ of unicode surrogate pairs.
+
+ .. seealso::
+
+ :ref:`cursor_pos_unicode_note`
+
+- Add :meth:`Session.clone` for making a copy of a Session object
+ without sharing the digest history.
+ Reusing a single Session object to connect multiple sockets
+ to the same IOPub peer can cause digest collisions.
+- Avoid global references preventing garbage collection of background threads.
+
+
5.0
===
diff --git a/docs/environment.yml b/docs/environment.yml
index 9240fbc..3690c73 100644
--- a/docs/environment.yml
+++ b/docs/environment.yml
@@ -8,4 +8,3 @@ dependencies:
- jupyter_core
- sphinx>=1.3.6
- sphinx_rtd_theme
-- ipykernel
diff --git a/docs/messaging.rst b/docs/messaging.rst
index efe73f2..642b6b0 100644
--- a/docs/messaging.rst
+++ b/docs/messaging.rst
@@ -21,7 +21,7 @@ Versioning
The Jupyter message specification is versioned independently of the packages
that use it.
-The current version of the specification is 5.1.
+The current version of the specification is 5.2.
.. note::
*New in* and *Changed in* messages in this document refer to versions of the
@@ -547,6 +547,14 @@ Message type: ``inspect_request``::
``name`` key replaced with ``code`` and ``cursor_pos``,
moving the lexing responsibility to the kernel.
+.. versionchanged:: 5.2
+
+ Due to a widespread bug in many frontends, ``cursor_pos``
+ in versions prior to 5.2 is ambiguous in the presence of "astral-plane" characters.
+ In 5.2, cursor_pos **must be** the actual encoding-independent offset in unicode codepoints.
+ See :ref:`cursor_pos_unicode_note` for more.
+
+
The reply is a mime-bundle, like a `display_data`_ message,
which should be a formatted representation of information about the context.
In the notebook, this is used to show tooltips over function calls, etc.
@@ -595,6 +603,13 @@ Message type: ``complete_request``::
``line``, ``block``, and ``text`` keys are removed in favor of a single ``code`` for context.
Lexing is up to the kernel.
+.. versionchanged:: 5.2
+
+ Due to a widespread bug in many frontends, ``cursor_pos``
+ in versions prior to 5.2 is ambiguous in the presence of "astral-plane" characters.
+ In 5.2, cursor_pos **must be** the actual encoding-independent offset in unicode codepoints.
+ See :ref:`cursor_pos_unicode_note` for more.
+
Message type: ``complete_reply``::
@@ -1370,12 +1385,48 @@ handlers should set the parent header and publish status busy / idle,
just like an execute request.
-To Do
+Notes
=====
-Missing things include:
+.. _cursor_pos_unicode_note:
+
+``cursor_pos`` and unicode offsets
+----------------------------------
+
+Many frontends, especially those implemented in javascript,
+reported cursor_pos as the interpreter's string index,
+which is not the same as the unicode character offset if the interpreter uses UTF-16 (e.g. javascript or Python 2 on macOS),
+which stores "astral-plane" characters such as ``𝐚 (U+1D41A)`` as surrogate pairs,
+taking up two indices instead of one, causing a unicode offset
+drift of one per astral-plane character.
+Not all frontends have this behavior, however,
+and after JSON serialization information about which encoding was used
+when calculating the offset is lost,
+so assuming ``cursor_pos`` is calculated in UTF-16 could result in a similarly incorrect offset
+for frontends that did the right thing.
+
+For this reason, in protocol versions prior to 5.2, ``cursor_pos``
+is officially ambiguous in the presence of astral plane unicode characters.
+Frontends claiming to implement protocol 5.2 **MUST** identify cursor_pos as the encoding-independent unicode character offset.
+Kernels may choose to expect the UTF-16 offset from requests implementing protocol 5.1 and earlier, in order to behave correctly with the most popular frontends.
+But they should know that doing so *introduces* the inverse bug for the frontends that do not have this bug.
+
+Known affected frontends (as of 2017-06):
+
+- Jupyter Notebook < 5.1
+- JupyterLab < 0.24
+- nteract
+- CoCalc
+- Jupyter Console and QtConsole with Python 2 on macOS and Windows
+
+Known *not* affected frontends:
+
+- QtConsole, Jupyter Console with Python 3 or Python 2 on Linux
+
+.. see-also::
+
+ `Discussion on GitHub <https://github.com/jupyter/jupyter_client/issues/259>`_
-* Important: finish thinking through the payload concept and API.
.. _ZeroMQ: http://zeromq.org
.. _nteract: https://nteract.io
diff --git a/jupyter_client/_version.py b/jupyter_client/_version.py
index 81f3781..90dd2e9 100644
--- a/jupyter_client/_version.py
+++ b/jupyter_client/_version.py
@@ -1,5 +1,5 @@
-version_info = (5, 0, 1)
+version_info = (5, 1, 0)
__version__ = '.'.join(map(str, version_info))
-protocol_version_info = (5, 1)
+protocol_version_info = (5, 2)
protocol_version = "%i.%i" % protocol_version_info
diff --git a/jupyter_client/blocking/client.py b/jupyter_client/blocking/client.py
index e0c688a..c0196ba 100644
--- a/jupyter_client/blocking/client.py
+++ b/jupyter_client/blocking/client.py
@@ -46,6 +46,11 @@ def reqrep(meth):
return self._recv_reply(msg_id, timeout=timeout)
+ if not meth.__doc__:
+ # python -OO removes docstrings,
+ # so don't bother building the wrapped docstring
+ return wrapped
+
basedoc, _ = meth.__doc__.split('Returns\n', 1)
parts = [basedoc.strip()]
if 'Parameters' not in basedoc:
diff --git a/jupyter_client/channels.py b/jupyter_client/channels.py
index d7b0836..dd99067 100644
--- a/jupyter_client/channels.py
+++ b/jupyter_client/channels.py
@@ -70,7 +70,6 @@ class HBChannel(Thread):
raise InvalidPortNumber(message)
address = "tcp://%s:%i" % address
self.address = address
- atexit.register(self._notice_exit)
# running is False until `.start()` is called
self._running = False
@@ -78,8 +77,10 @@ class HBChannel(Thread):
self._pause = False
self.poller = zmq.Poller()
- def _notice_exit(self):
- self._exiting = True
+ @staticmethod
+ @atexit.register
+ def _notice_exit():
+ HBChannel._exiting = True
def _create_socket(self):
if self.socket is not None:
diff --git a/jupyter_client/connect.py b/jupyter_client/connect.py
index 0ee55b1..042904f 100644
--- a/jupyter_client/connect.py
+++ b/jupyter_client/connect.py
@@ -207,6 +207,7 @@ def find_connection_file(filename='kernel-*.json', path=None, profile=None):
for p in path:
matches.extend(glob.glob(os.path.join(p, pat)))
+ matches = [ os.path.abspath(m) for m in matches ]
if not matches:
raise IOError("Could not find %r in %r" % (filename, path))
elif len(matches) == 1:
@@ -371,8 +372,9 @@ class ConnectionFileMixin(LoggingConfigurable):
control_port=self.control_port,
)
if session:
- # add session
- info['session'] = self.session
+ # add *clone* of my session,
+ # so that state such as digest_history is not shared.
+ info['session'] = self.session.clone()
else:
# add session info
info.update(dict(
diff --git a/jupyter_client/consoleapp.py b/jupyter_client/consoleapp.py
index 2c57840..ce2ead4 100644
--- a/jupyter_client/consoleapp.py
+++ b/jupyter_client/consoleapp.py
@@ -161,7 +161,7 @@ class JupyterConsoleApp(ConnectionFileMixin):
"""
if self.existing:
try:
- cf = find_connection_file(self.existing, [self.runtime_dir])
+ cf = find_connection_file(self.existing, ['.', self.runtime_dir])
except Exception:
self.log.critical("Could not find existing kernel connection file %s", self.existing)
self.exit(1)
diff --git a/jupyter_client/manager.py b/jupyter_client/manager.py
index 54a8520..373105e 100644
--- a/jupyter_client/manager.py
+++ b/jupyter_client/manager.py
@@ -295,7 +295,7 @@ class KernelManager(ConnectionFileMixin):
self._close_control_socket()
def shutdown_kernel(self, now=False, restart=False):
- """Attempts to the stop the kernel process cleanly.
+ """Attempts to stop the kernel process cleanly.
This attempts to shutdown the kernels cleanly by:
diff --git a/jupyter_client/session.py b/jupyter_client/session.py
index eedaeeb..b172b63 100644
--- a/jupyter_client/session.py
+++ b/jupyter_client/session.py
@@ -488,6 +488,24 @@ class Session(Configurable):
if not self.key:
get_logger().warning("Message signing is disabled. This is insecure and not recommended!")
+ def clone(self):
+ """Create a copy of this Session
+
+ Useful when connecting multiple times to a given kernel.
+ This prevents a shared digest_history warning about duplicate digests
+ due to multiple connections to IOPub in the same process.
+
+ .. versionadded:: 5.1
+ """
+ # make a copy
+ new_session = type(self)()
+ for name in self.traits():
+ setattr(new_session, name, getattr(self, name))
+ # fork digest_history
+ new_session.digest_history = set()
+ new_session.digest_history.update(self.digest_history)
+ return new_session
+
@property
def msg_id(self):
"""always return new uuid"""
diff --git a/jupyter_client/tests/test_adapter.py b/jupyter_client/tests/test_adapter.py
index ae7791c..dae0207 100644
--- a/jupyter_client/tests/test_adapter.py
+++ b/jupyter_client/tests/test_adapter.py
@@ -6,7 +6,6 @@
import copy
import json
from unittest import TestCase
-import nose.tools as nt
from jupyter_client.adapter import adapt, V4toV5, V5toV4, code_to_line
from jupyter_client.session import Session
@@ -18,12 +17,12 @@ def test_default_version():
msg['header'].pop('version')
original = copy.deepcopy(msg)
adapted = adapt(original)
- nt.assert_equal(adapted['header']['version'], V4toV5.version)
+ assert adapted['header']['version'] == V4toV5.version
def test_code_to_line_no_code():
line, pos = code_to_line("", 0)
- nt.assert_equal(line, "")
- nt.assert_equal(pos, 0)
+ assert line == ""
+ assert pos == 0
class AdapterTest(TestCase):
@@ -263,7 +262,7 @@ class V5toV4TestCase(AdapterTest):
msg = self.msg(v5_type, {'key' : 'value'})
v5, v4 = self.adapt(msg)
self.assertEqual(v4['header']['msg_type'], v4_type)
- nt.assert_not_in('version', v4['header'])
+ assert 'version' not in v4['header']
self.assertEqual(v4['content'], v5['content'])
def test_execute_request(self):
diff --git a/jupyter_client/tests/test_client.py b/jupyter_client/tests/test_client.py
index 8375e6a..4181640 100644
--- a/jupyter_client/tests/test_client.py
+++ b/jupyter_client/tests/test_client.py
@@ -8,12 +8,12 @@ import os
pjoin = os.path.join
from unittest import TestCase
-from nose import SkipTest
-
from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel, NATIVE_KERNEL_NAME
from ..manager import start_new_kernel
from .utils import test_env
+import pytest
+
from ipython_genutils.py3compat import string_types
from IPython.utils.capture import capture_output
@@ -27,7 +27,7 @@ class TestKernelClient(TestCase):
try:
KernelSpecManager().get_kernel_spec(NATIVE_KERNEL_NAME)
except NoSuchKernel:
- raise SkipTest()
+ pytest.skip()
self.km, self.kc = start_new_kernel(kernel_name=NATIVE_KERNEL_NAME)
self.addCleanup(self.kc.stop_channels)
self.addCleanup(self.km.shutdown_kernel)
diff --git a/jupyter_client/tests/test_connect.py b/jupyter_client/tests/test_connect.py
index c84ee66..e1985e7 100644
--- a/jupyter_client/tests/test_connect.py
+++ b/jupyter_client/tests/test_connect.py
@@ -6,10 +6,9 @@
import json
import os
-import nose.tools as nt
-
from traitlets.config import Config
from jupyter_core.application import JupyterApp
+from jupyter_core.paths import jupyter_runtime_dir
from ipython_genutils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
from ipython_genutils.py3compat import str_to_bytes
from jupyter_client import connect, KernelClient
@@ -36,11 +35,11 @@ def test_write_connection_file():
with TemporaryDirectory() as d:
cf = os.path.join(d, 'kernel.json')
connect.write_connection_file(cf, **sample_info)
- nt.assert_true(os.path.exists(cf))
+ assert os.path.exists(cf)
with open(cf, 'r') as f:
info = json.load(f)
info['key'] = str_to_bytes(info['key'])
- nt.assert_equal(info, sample_info)
+ assert info == sample_info
def test_load_connection_file_session():
@@ -56,8 +55,8 @@ def test_load_connection_file_session():
app.connection_file = cf
app.load_connection_file()
- nt.assert_equal(session.key, sample_info['key'])
- nt.assert_equal(session.signature_scheme, sample_info['signature_scheme'])
+ assert session.key == sample_info['key']
+ assert session.signature_scheme == sample_info['signature_scheme']
def test_load_connection_file_session_with_kn():
@@ -73,8 +72,8 @@ def test_load_connection_file_session_with_kn():
app.connection_file = cf
app.load_connection_file()
- nt.assert_equal(session.key, sample_info_kn['key'])
- nt.assert_equal(session.signature_scheme, sample_info_kn['signature_scheme'])
+ assert session.key == sample_info_kn['key']
+ assert session.signature_scheme == sample_info_kn['signature_scheme']
def test_app_load_connection_file():
@@ -89,7 +88,7 @@ def test_app_load_connection_file():
if attr in ('key', 'signature_scheme'):
continue
value = getattr(app, attr)
- nt.assert_equal(value, expected, "app.%s = %s != %s" % (attr, value, expected))
+ assert value == expected, "app.%s = %s != %s" % (attr, value, expected)
def test_load_connection_info():
@@ -112,11 +111,9 @@ def test_load_connection_info():
def test_find_connection_file():
- cfg = Config()
with TemporaryDirectory() as d:
- cfg.ProfileDir.location = d
cf = 'kernel.json'
- app = DummyConsoleApp(config=cfg, connection_file=cf)
+ app = DummyConsoleApp(runtime_dir=d, connection_file=cf)
app.initialize()
security_dir = app.runtime_dir
@@ -131,7 +128,47 @@ def test_find_connection_file():
'*ernel*',
'k*',
):
- nt.assert_equal(connect.find_connection_file(query, path=security_dir), profile_cf)
+ assert connect.find_connection_file(query, path=security_dir) == profile_cf
+
- JupyterApp._instance = None
+def test_find_connection_file_local():
+ with TemporaryWorkingDirectory() as d:
+ cf = 'test.json'
+ abs_cf = os.path.abspath(cf)
+ with open(cf, 'w') as f:
+ f.write('{}')
+
+ for query in (
+ 'test.json',
+ 'test',
+ abs_cf,
+ os.path.join('.', 'test.json'),
+ ):
+ assert connect.find_connection_file(query, path=['.', jupyter_runtime_dir()]) == abs_cf
+
+
+def test_find_connection_file_relative():
+ with TemporaryWorkingDirectory() as d:
+ cf = 'test.json'
+ os.mkdir('subdir')
+ cf = os.path.join('subdir', 'test.json')
+ abs_cf = os.path.abspath(cf)
+ with open(cf, 'w') as f:
+ f.write('{}')
+
+ for query in (
+ os.path.join('.', 'subdir', 'test.json'),
+ os.path.join('subdir', 'test.json'),
+ abs_cf,
+ ):
+ assert connect.find_connection_file(query, path=['.', jupyter_runtime_dir()]) == abs_cf
+
+
+def test_find_connection_file_abspath():
+ with TemporaryDirectory() as d:
+ cf = 'absolute.json'
+ abs_cf = os.path.abspath(cf)
+ with open(cf, 'w') as f:
+ f.write('{}')
+ assert connect.find_connection_file(abs_cf, path=jupyter_runtime_dir()) == abs_cf
diff --git a/jupyter_client/tests/test_jsonutil.py b/jupyter_client/tests/test_jsonutil.py
index 834824e..9583a22 100644
--- a/jupyter_client/tests/test_jsonutil.py
+++ b/jupyter_client/tests/test_jsonutil.py
@@ -14,8 +14,6 @@ except ImportError:
# py2
import mock
-import nose.tools as nt
-
from dateutil.tz import tzlocal, tzoffset
from jupyter_client import jsonutil
from jupyter_client.session import utcnow
@@ -33,29 +31,29 @@ def test_extract_dates():
extracted = jsonutil.extract_dates(timestamps)
ref = extracted[0]
for dt in extracted:
- nt.assert_true(isinstance(dt, datetime.datetime))
- nt.assert_not_equal(dt.tzinfo, None)
+ assert isinstance(dt, datetime.datetime)
+ assert dt.tzinfo != None
- nt.assert_equal(extracted[0].tzinfo.utcoffset(ref), tzlocal().utcoffset(ref))
- nt.assert_equal(extracted[1].tzinfo.utcoffset(ref), timedelta(0))
- nt.assert_equal(extracted[2].tzinfo.utcoffset(ref), timedelta(hours=-8))
- nt.assert_equal(extracted[3].tzinfo.utcoffset(ref), timedelta(hours=8))
- nt.assert_equal(extracted[4].tzinfo.utcoffset(ref), timedelta(hours=-8))
- nt.assert_equal(extracted[5].tzinfo.utcoffset(ref), timedelta(hours=8))
+ assert extracted[0].tzinfo.utcoffset(ref) == tzlocal().utcoffset(ref)
+ assert extracted[1].tzinfo.utcoffset(ref) == timedelta(0)
+ assert extracted[2].tzinfo.utcoffset(ref) == timedelta(hours=-8)
+ assert extracted[3].tzinfo.utcoffset(ref) == timedelta(hours=8)
+ assert extracted[4].tzinfo.utcoffset(ref) == timedelta(hours=-8)
+ assert extracted[5].tzinfo.utcoffset(ref) == timedelta(hours=8)
def test_parse_ms_precision():
base = '2013-07-03T16:34:52'
digits = '1234567890'
parsed = jsonutil.parse_date(base)
- nt.assert_is_instance(parsed, datetime.datetime)
+ assert isinstance(parsed, datetime.datetime)
for i in range(len(digits)):
ts = base + '.' + digits[:i]
parsed = jsonutil.parse_date(ts)
if i >= 1 and i <= 6:
- nt.assert_is_instance(parsed, datetime.datetime)
+ assert isinstance(parsed, datetime.datetime)
else:
- nt.assert_is_instance(parsed, str)
+ assert isinstance(parsed, str)
@@ -66,10 +64,10 @@ def test_date_default():
data = dict(naive=naive, utc=utcnow(), withtz=naive.replace(tzinfo=other))
with mock.patch.object(jsonutil, 'tzlocal', lambda : local):
jsondata = json.dumps(data, default=jsonutil.date_default)
- nt.assert_in("Z", jsondata)
- nt.assert_equal(jsondata.count("Z"), 1)
+ assert "Z" in jsondata
+ assert jsondata.count("Z") == 1
extracted = jsonutil.extract_dates(json.loads(jsondata))
for dt in extracted.values():
- nt.assert_is_instance(dt, datetime.datetime)
- nt.assert_not_equal(dt.tzinfo, None)
+ assert isinstance(dt, datetime.datetime)
+ assert dt.tzinfo != None
diff --git a/jupyter_client/tests/test_kernelmanager.py b/jupyter_client/tests/test_kernelmanager.py
index 231ce00..a23b33f 100644
--- a/jupyter_client/tests/test_kernelmanager.py
+++ b/jupyter_client/tests/test_kernelmanager.py
@@ -13,13 +13,11 @@ import sys
import time
from unittest import TestCase
-from ipython_genutils.testing import decorators as dec
-
from traitlets.config.loader import Config
from jupyter_core import paths
from jupyter_client import KernelManager
from ..manager import start_new_kernel
-from .utils import test_env
+from .utils import test_env, skip_win32
TIMEOUT = 30
@@ -67,7 +65,7 @@ class TestKernelManager(TestCase):
km = self._get_tcp_km()
self._run_lifecycle(km)
- @dec.skip_win32
+ @skip_win32
def test_ipc_lifecycle(self):
km = self._get_ipc_km()
self._run_lifecycle(km)
@@ -82,8 +80,8 @@ class TestKernelManager(TestCase):
'key', 'signature_scheme',
])
self.assertEqual(keys, expected)
-
- @dec.skip_win32
+
+ @skip_win32
def test_signal_kernel_subprocesses(self):
self._install_test_kernel()
km, kc = start_new_kernel(kernel_name='signaltest')
diff --git a/jupyter_client/tests/test_kernelspec.py b/jupyter_client/tests/test_kernelspec.py
index 5d6d4d1..b02dd75 100644
--- a/jupyter_client/tests/test_kernelspec.py
+++ b/jupyter_client/tests/test_kernelspec.py
@@ -13,12 +13,13 @@ from subprocess import Popen, PIPE, STDOUT
import sys
import unittest
+import pytest
+
if str is bytes: # py2
StringIO = io.BytesIO
else:
StringIO = io.StringIO
-from ipython_genutils.testing.decorators import onlyif
from ipython_genutils.tempdir import TemporaryDirectory
from jupyter_client import kernelspec
from jupyter_core import paths
@@ -123,7 +124,9 @@ class KernelSpecTests(unittest.TestCase):
self.ksm.log.removeHandler(handler)
self.assertNotIn("may not be found", captured)
- @onlyif(os.name != 'nt' and not os.access('/usr/local/share', os.W_OK), "needs Unix system without root privileges")
+ @pytest.mark.skipif(
+ not (os.name != 'nt' and not os.access('/usr/local/share', os.W_OK)),
+ reason="needs Unix system without root privileges")
def test_cant_install_kernel_spec(self):
with self.assertRaises(OSError):
self.ksm.install_kernel_spec(self.installable_kernel,
diff --git a/jupyter_client/tests/test_multikernelmanager.py b/jupyter_client/tests/test_multikernelmanager.py
index ad9b5d4..2ca2ea4 100644
--- a/jupyter_client/tests/test_multikernelmanager.py
+++ b/jupyter_client/tests/test_multikernelmanager.py
@@ -4,12 +4,11 @@ from subprocess import PIPE
import time
from unittest import TestCase
-from ipython_genutils.testing import decorators as dec
-
from traitlets.config.loader import Config
from ..localinterfaces import localhost
from jupyter_client import KernelManager
from jupyter_client.multikernelmanager import MultiKernelManager
+from .utils import skip_win32
class TestKernelManager(TestCase):
@@ -75,12 +74,12 @@ class TestKernelManager(TestCase):
km = self._get_tcp_km()
self._run_cinfo(km, 'tcp', localhost())
- @dec.skip_win32
+ @skip_win32
def test_ipc_lifecycle(self):
km = self._get_ipc_km()
self._run_lifecycle(km)
- @dec.skip_win32
+ @skip_win32
def test_ipc_cinfo(self):
km = self._get_ipc_km()
self._run_cinfo(km, 'ipc', 'test')
diff --git a/jupyter_client/tests/test_public_api.py b/jupyter_client/tests/test_public_api.py
index 6b6512d..ab3883d 100644
--- a/jupyter_client/tests/test_public_api.py
+++ b/jupyter_client/tests/test_public_api.py
@@ -4,8 +4,6 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
-import nose.tools as nt
-
from jupyter_client import launcher, connect
import jupyter_client
@@ -13,17 +11,17 @@ import jupyter_client
def test_kms():
for base in ("", "Multi"):
KM = base + "KernelManager"
- nt.assert_in(KM, dir(jupyter_client))
+ assert KM in dir(jupyter_client)
def test_kcs():
for base in ("", "Blocking"):
KM = base + "KernelClient"
- nt.assert_in(KM, dir(jupyter_client))
+ assert KM in dir(jupyter_client)
def test_launcher():
for name in launcher.__all__:
- nt.assert_in(name, dir(jupyter_client))
+ assert name in dir(jupyter_client)
def test_connect():
for name in connect.__all__:
- nt.assert_in(name, dir(jupyter_client))
+ assert name in dir(jupyter_client)
diff --git a/jupyter_client/tests/test_session.py b/jupyter_client/tests/test_session.py
index 1719814..2d1ef98 100644
--- a/jupyter_client/tests/test_session.py
+++ b/jupyter_client/tests/test_session.py
@@ -8,6 +8,8 @@ import os
import uuid
from datetime import datetime
+import pytest
+
import zmq
from zmq.tests import BaseZMQTestCase
@@ -16,7 +18,6 @@ from zmq.eventloop.zmqstream import ZMQStream
from jupyter_client import session as ss
from jupyter_client import jsonutil
-from ipython_genutils.testing.decorators import skipif, module_not_available
from ipython_genutils.py3compat import string_types
def _bad_packer(obj):
@@ -286,9 +287,8 @@ class TestSession(SessionTestCase):
session = ss.Session(packer='pickle')
self._datetime_test(session)
- @skipif(module_not_available('msgpack'))
def test_datetimes_msgpack(self):
- import msgpack
+ msgpack = pytest.importorskip('msgpack')
session = ss.Session(
pack=msgpack.packb,
@@ -320,3 +320,15 @@ class TestSession(SessionTestCase):
A.close()
B.close()
ctx.term()
+
+ def test_clone(self):
+ s = self.session
+ s._add_digest('initial')
+ s2 = s.clone()
+ assert s2.session == s.session
+ assert s2.digest_history == s.digest_history
+ assert s2.digest_history is not s.digest_history
+ digest = 'abcdef'
+ s._add_digest(digest)
+ assert digest in s.digest_history
+ assert digest not in s2.digest_history
diff --git a/jupyter_client/tests/utils.py b/jupyter_client/tests/utils.py
index 0f68002..505084a 100644
--- a/jupyter_client/tests/utils.py
+++ b/jupyter_client/tests/utils.py
@@ -3,13 +3,20 @@
"""
import os
pjoin = os.path.join
+import sys
try:
from unittest.mock import patch
except ImportError:
from mock import patch
+import pytest
+
from ipython_genutils.tempdir import TemporaryDirectory
+
+skip_win32 = pytest.mark.skipif(sys.platform.startswith('win'), reason="Windows")
+
+
class test_env(object):
"""Set Jupyter path variables to a temporary directory
@@ -46,11 +53,11 @@ def execute(code='', kc=None, **kwargs):
validate_message(reply, 'execute_reply', msg_id)
busy = kc.get_iopub_msg(timeout=TIMEOUT)
validate_message(busy, 'status', msg_id)
- nt.assert_equal(busy['content']['execution_state'], 'busy')
+ assert busy['content']['execution_state'] == 'busy'
if not kwargs.get('silent'):
execute_input = kc.get_iopub_msg(timeout=TIMEOUT)
validate_message(execute_input, 'execute_input', msg_id)
- nt.assert_equal(execute_input['content']['code'], code)
+ assert execute_input['content']['code'] == code
return msg_id, reply['content']
diff --git a/jupyter_client/threaded.py b/jupyter_client/threaded.py
index 4f1f77a..ace1d1d 100644
--- a/jupyter_client/threaded.py
+++ b/jupyter_client/threaded.py
@@ -141,14 +141,17 @@ class ThreadedZMQSocketChannel(object):
class IOLoopThread(Thread):
"""Run a pyzmq ioloop in a thread to send and receive messages
"""
+ _exiting = False
+
def __init__(self, loop):
super(IOLoopThread, self).__init__()
self.daemon = True
- atexit.register(self._notice_exit)
self.ioloop = loop or ioloop.IOLoop()
- def _notice_exit(self):
- self._exiting = True
+ @staticmethod
+ @atexit.register
+ def _notice_exit():
+ IOLoopThread._exiting = True
def run(self):
"""Run my loop, ignoring EINTR events in the poller"""
diff --git a/setup.py b/setup.py
index ca7cb96..51bcf2f 100644
--- a/setup.py
+++ b/setup.py
@@ -82,7 +82,7 @@ install_requires = setuptools_args['install_requires'] = [
]
extras_require = setuptools_args['extras_require'] = {
- 'test': ['ipykernel', 'ipython', 'nose_warnings_filters'],
+ 'test': ['ipykernel', 'ipython', 'pytest'],
}
if 'setuptools' in sys.modules:
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/jupyter-client.git
More information about the Python-modules-commits
mailing list