Skip to content

Commit 10cf9ca

Browse files
authored
Inline pytest_tornasync and add downstream tests (#30)
* inline pytest_tornasync and add downstream tests * reformat
1 parent f377218 commit 10cf9ca

File tree

8 files changed

+152
-47
lines changed

8 files changed

+152
-47
lines changed

.github/workflows/test.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,26 @@ jobs:
120120
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
121121
- uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1
122122

123+
jupyter_server_downstream:
124+
runs-on: ubuntu-latest
125+
timeout-minutes: 10
126+
steps:
127+
- uses: actions/checkout@v3
128+
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
129+
- uses: jupyterlab/maintainer-tools/.github/actions/downstream-test@v1
130+
with:
131+
package_name: jupyter_server
132+
133+
jupyter_client_downstream:
134+
runs-on: ubuntu-latest
135+
timeout-minutes: 10
136+
steps:
137+
- uses: actions/checkout@v3
138+
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
139+
- uses: jupyterlab/maintainer-tools/.github/actions/downstream-test@v1
140+
with:
141+
package_name: jupyter_client
142+
123143
tests_check: # This job does nothing and is only used for the branch protection
124144
if: always()
125145
needs:
@@ -130,6 +150,8 @@ jobs:
130150
- test_prereleases
131151
- check_links
132152
- check_release
153+
- jupyter_server_downstream
154+
- jupyter_client_downstream
133155
- test_sdist
134156
runs-on: ubuntu-latest
135157
steps:

.pre-commit-config.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ repos:
7070
["flake8-bugbear==22.6.22", "flake8-implicit-str-concat==0.2.0"]
7171
stages: [manual]
7272

73-
- repo: https://github.com/pre-commit/mirrors-eslint
74-
rev: v8.28.0
75-
hooks:
76-
- id: eslint
77-
stages: [manual]
78-
7973
- repo: https://github.com/python-jsonschema/check-jsonschema
8074
rev: 0.19.2
8175
hooks:

LICENSE

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,31 @@ to indicate the copyright and license terms:
5757

5858
# Copyright (c) Jupyter Development Team.
5959
# Distributed under the terms of the Modified BSD License.
60+
61+
62+
Pytest Tornasync File License
63+
=============================
64+
65+
The pytest_jupyter/pytest_tornasync.py was originally licensed as:
66+
67+
The MIT License (MIT)
68+
69+
Copyright (c) 2016 eukaryote
70+
71+
Permission is hereby granted, free of charge, to any person obtaining a copy
72+
of this software and associated documentation files (the "Software"), to deal
73+
in the Software without restriction, including without limitation the rights
74+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
75+
copies of the Software, and to permit persons to whom the Software is
76+
furnished to do so, subject to the following conditions:
77+
78+
The above copyright notice and this permission notice shall be included in all
79+
copies or substantial portions of the Software.
80+
81+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
82+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
83+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
84+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
85+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
86+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
87+
SOFTWARE.

pyproject.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ docs = [
3737
]
3838
client = [
3939
"jupyter_client>=7.4.0",
40-
"ipykernel>=6.14",
41-
"pytest-tornasync>=0.6"
40+
"ipykernel>=6.14"
4241
]
4342
server = [
4443
"jupyter_server>=1.21",
@@ -94,8 +93,6 @@ timeout_method = "thread"
9493
filterwarnings= [
9594
# Fail on warnings
9695
"error",
97-
# TODO: from jupyter_server
98-
"always:unclosed <socket.socket:ResourceWarning"
9996
]
10097

10198
[tool.coverage.report]

pytest_jupyter/jupyter_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
"you need. Try: `pip install 'pytest-jupyter[client]'`"
1717
)
1818

19-
# Bring in core plugins.
19+
# Bring in local plugins.
2020
from pytest_jupyter import * # noqa
21+
from pytest_jupyter.pytest_tornasync import * # noqa
2122

2223

2324
@pytest.fixture

pytest_jupyter/jupyter_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def jp_asyncio_loop():
4646

4747

4848
@pytest.fixture(autouse=True)
49-
def io_loop(jp_asyncio_loop):
49+
def jp_io_loop(jp_asyncio_loop):
5050
"""Override the io_loop for pytest_tornasync. This is a no-op
5151
if tornado is not installed."""
5252

pytest_jupyter/jupyter_server.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import shutil
1010
import urllib.parse
1111
from binascii import hexlify
12-
from contextlib import closing
1312

1413
import jupyter_core.paths
1514
import pytest
@@ -25,7 +24,6 @@
2524
from jupyter_server.extension import serverextension
2625
from jupyter_server.serverapp import JUPYTER_SERVICE_HANDLERS, ServerApp
2726
from jupyter_server.utils import url_path_join
28-
from pytest_tornasync.plugin import AsyncHTTPServerClient
2927
from tornado.escape import url_escape
3028
from tornado.httpclient import HTTPClientError
3129
from tornado.websocket import WebSocketHandler
@@ -44,50 +42,33 @@
4442
)
4543

4644

47-
# Bring in core plugins.
45+
# Bring in local plugins.
4846
from pytest_jupyter import * # noqa
47+
from pytest_jupyter.pytest_tornasync import * # noqa
4948
from pytest_jupyter.utils import mkdir
5049

51-
# List of dependencies needed for this plugin.
52-
pytest_plugins = ["pytest_tornasync"]
53-
54-
5550
# Override some of the fixtures from pytest_tornasync
5651
# The io_loop fixture is overidden in jupyter_core.py so it
5752
# can be shared by other plugins that need it (e.g. jupyter_client.py).
5853

5954

6055
@pytest.fixture
61-
def http_server_client(http_server, io_loop):
62-
"""
63-
Create an asynchronous HTTP client that can fetch from `http_server`.
64-
"""
65-
66-
async def get_client():
67-
return AsyncHTTPServerClient(http_server=http_server)
68-
69-
client = io_loop.run_sync(get_client)
70-
with closing(client) as context:
71-
yield context
72-
73-
74-
@pytest.fixture
75-
def http_server(io_loop, http_server_port, jp_web_app):
56+
def jp_http_server(jp_io_loop, jp_http_server_port, jp_web_app):
7657
"""Start a tornado HTTP server that listens on all available interfaces."""
7758

7859
async def get_server():
7960
server = tornado.httpserver.HTTPServer(jp_web_app)
80-
server.add_socket(http_server_port[0])
61+
server.add_socket(jp_http_server_port[0])
8162
return server
8263

83-
server = io_loop.run_sync(get_server)
64+
server = jp_io_loop.run_sync(get_server)
8465
yield server
8566
server.stop()
8667

8768
if hasattr(server, "close_all_connections"):
88-
io_loop.run_sync(server.close_all_connections)
69+
jp_io_loop.run_sync(server.close_all_connections)
8970

90-
http_server_port[0].close()
71+
jp_http_server_port[0].close()
9172

9273

9374
# End pytest_tornasync overrides
@@ -126,10 +107,10 @@ def jp_argv():
126107

127108

128109
@pytest.fixture()
129-
def jp_http_port(http_server_port):
110+
def jp_http_port(jp_http_server_port):
130111
"""Returns the port value from the http_server_port fixture."""
131-
yield http_server_port[-1]
132-
http_server_port[0].close()
112+
yield jp_http_server_port[-1]
113+
jp_http_server_port[0].close()
133114

134115

135116
@pytest.fixture
@@ -186,7 +167,7 @@ def jp_configurable_serverapp(
186167
jp_root_dir,
187168
jp_logging_stream,
188169
jp_asyncio_loop,
189-
io_loop,
170+
jp_io_loop,
190171
):
191172
"""Starts a Jupyter Server instance based on
192173
the provided configuration values.
@@ -216,7 +197,7 @@ def _configurable_serverapp(
216197
environ=jp_environ,
217198
http_port=jp_http_port,
218199
tmp_path=tmp_path,
219-
io_loop=io_loop,
200+
io_loop=jp_io_loop,
220201
root_dir=jp_root_dir,
221202
**kwargs,
222203
):
@@ -301,7 +282,7 @@ def jp_base_url():
301282

302283

303284
@pytest.fixture
304-
def jp_fetch(jp_serverapp, http_server_client, jp_auth_header, jp_base_url):
285+
def jp_fetch(jp_serverapp, jp_http_server_client, jp_auth_header, jp_base_url):
305286
"""Sends an (asynchronous) HTTP request to a test server.
306287
The fixture is a factory; it can be called like
307288
a function inside a unit test. Here's a basic
@@ -328,14 +309,13 @@ def client_fetch(*parts, headers=None, params=None, **kwargs):
328309
for key, value in jp_auth_header.items():
329310
headers.setdefault(key, value)
330311
# Make request.
331-
print(id(http_server_client.io_loop.asyncio_loop))
332-
return http_server_client.fetch(url, headers=headers, request_timeout=20, **kwargs)
312+
return jp_http_server_client.fetch(url, headers=headers, request_timeout=20, **kwargs)
333313

334314
return client_fetch
335315

336316

337317
@pytest.fixture
338-
def jp_ws_fetch(jp_serverapp, http_server_client, jp_auth_header, jp_http_port, jp_base_url):
318+
def jp_ws_fetch(jp_serverapp, jp_http_server_client, jp_auth_header, jp_http_port, jp_base_url):
339319
"""Sends a websocket request to a test server.
340320
The fixture is a factory; it can be called like
341321
a function inside a unit test. Here's a basic

pytest_jupyter/pytest_tornasync.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Vendored fork of pytest_tornasync from
2+
# https://github.com/eukaryote/pytest-tornasync/blob/9f1bdeec3eb5816e0183f975ca65b5f6f29fbfbb/src/pytest_tornasync/plugin.py
3+
4+
from contextlib import closing
5+
from inspect import iscoroutinefunction
6+
7+
try:
8+
import tornado.ioloop
9+
import tornado.testing
10+
from tornado.simple_httpclient import SimpleAsyncHTTPClient
11+
except ImportError:
12+
SimpleAsyncHTTPClient = object
13+
14+
import pytest
15+
16+
17+
@pytest.hookimpl(tryfirst=True)
18+
def pytest_pycollect_makeitem(collector, name, obj):
19+
if collector.funcnamefilter(name) and iscoroutinefunction(obj):
20+
return list(collector._genfunctions(name, obj))
21+
22+
23+
@pytest.hookimpl(tryfirst=True)
24+
def pytest_pyfunc_call(pyfuncitem):
25+
funcargs = pyfuncitem.funcargs
26+
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
27+
28+
if not iscoroutinefunction(pyfuncitem.obj):
29+
pyfuncitem.obj(**testargs)
30+
return True
31+
32+
try:
33+
loop = funcargs["jp_io_loop"]
34+
except KeyError:
35+
loop = tornado.ioloop.IOLoop.current()
36+
37+
loop.run_sync(lambda: pyfuncitem.obj(**testargs))
38+
return True
39+
40+
41+
@pytest.fixture
42+
def jp_http_server_port():
43+
"""
44+
Port used by `http_server`.
45+
"""
46+
return tornado.testing.bind_unused_port()
47+
48+
49+
@pytest.fixture
50+
def jp_http_server_client(jp_http_server, jp_io_loop):
51+
"""
52+
Create an asynchronous HTTP client that can fetch from `http_server`.
53+
"""
54+
55+
async def get_client():
56+
return AsyncHTTPServerClient(http_server=jp_http_server)
57+
58+
client = jp_io_loop.run_sync(get_client)
59+
with closing(client) as context:
60+
yield context
61+
62+
63+
class AsyncHTTPServerClient(SimpleAsyncHTTPClient):
64+
def initialize(self, *, http_server=None):
65+
super().initialize()
66+
self._http_server = http_server
67+
68+
def fetch(self, path, **kwargs):
69+
"""
70+
Fetch `path` from test server, passing `kwargs` to the `fetch`
71+
of the underlying `SimpleAsyncHTTPClient`.
72+
"""
73+
return super().fetch(self.get_url(path), **kwargs)
74+
75+
def get_protocol(self):
76+
return "http"
77+
78+
def get_http_port(self):
79+
for sock in self._http_server._sockets.values():
80+
return sock.getsockname()[1]
81+
82+
def get_url(self, path):
83+
return f"{self.get_protocol()}://127.0.0.1:{self.get_http_port()}{path}"

0 commit comments

Comments
 (0)