Skip to content

Commit

Permalink
top-level await async support (+ docstrings) (#139)
Browse files Browse the repository at this point in the history
* docstrings and formatting
* add async support and test for it
  • Loading branch information
tonyfast committed Nov 25, 2022
1 parent c428d1a commit 489e541
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 103 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use either `pip` or `conda/mamba`
* `importnb.Notebook` offers parameters to customize how modules are imported
* imports Jupyter notebooks as python modules
* fuzzy finding conventions for finding files that are not valid python names
* works with top-level await statements
* integration with `pytest`
* extensible machinery
* translates Jupyter notebook files (ie `.ipynb` files) line-for-line to python source providing natural error messages
Expand Down
18 changes: 6 additions & 12 deletions docs/Untitled42.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
],
"source": [
"%%python\n",
"print(\"i'm only show when cell magics are active.\", sep=\"\\n\")"
"print(\"i'm only show when cell magics are active.\")"
]
},
{
Expand All @@ -149,6 +149,7 @@
"source": [
"if get_ipython():\n",
" magic_slug = \"i'm only show when cell magics are active.\"\n",
" if __import__('sys').platform == \"win32\": magic_slug += \"\\n\"\n",
"else:\n",
" magic_slug = f\"this was printed from the module named {__name__}\"\n",
" print(magic_slug)"
Expand Down Expand Up @@ -269,20 +270,13 @@
" data[\"source\"] = {}\n",
" display(JSON(data))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:root] *",
"display_name": "Python 3.9.7 ('base')",
"language": "python",
"name": "conda-root-py"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
Expand All @@ -294,11 +288,11 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.10"
"version": "3.9.7"
},
"vscode": {
"interpreter": {
"hash": "fe3da9b82b3647283ed4a64afb514c08b1aa39774194ba3fa7469a9333d7260c"
"hash": "6624ee388a1c346f3d0811b591fe9e170807496b8a5fea1a5f5986a819dc2334"
}
}
},
Expand Down
64 changes: 64 additions & 0 deletions docs/async-cells.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# top-level await\n",
"\n",
"this feature is tested separately because it requires a non-standard invocationin `exec_module`.\n",
"\n",
"## why\n",
"\n",
"[top-level auto-await](https://ipython.readthedocs.io/en/stable/interactive/autoawait.html) behavior is a feature of `IPython`, and [nodejs](https://maximorlov.com/tips/top-level-await-in-nodejs/). `importnb` supports top-level awaits by implicitly running coroutines in the current event loop.s"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"from contextlib import asynccontextmanager\n",
"async def async_function(): return 10\n",
"@asynccontextmanager\n",
"async def async_context_context_manager():\n",
" yield async_function\n",
"awaited_data = await async_function()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[⚠](https://gist.github.com/Rich-Harris/0b6f317657f5167663b493c722647221 \"top level async is a foot gun\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:root] *",
"language": "python",
"name": "conda-root-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
},
"vscode": {
"interpreter": {
"hash": "fe3da9b82b3647283ed4a64afb514c08b1aa39774194ba3fa7469a9333d7260c"
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}
5 changes: 4 additions & 1 deletion docs/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pathlib import Path
from subprocess import check_call
from sys import executable, path, version_info

from pytest import importorskip

from importnb import Notebook

GTE10 = version_info.major == 3 and version_info.minor >= 10
Expand Down Expand Up @@ -87,11 +89,12 @@ def test_module():
def test_empty_code():
""""""


@cli_test(rf"-m importnb -d {UNTITLED.parent.as_posix()} -t {UNTITLED.as_posix()} list")
def test_doit():
"""\
i was printed from {UNTITLED} and my name is __main__
{SLUG}
echo this the docstring for the `echo` task that echos hello.
"""
importorskip("doit")
importorskip("doit")
18 changes: 13 additions & 5 deletions docs/test_importnb.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
from shutil import copyfile, rmtree
from types import FunctionType

from pytest import fixture, mark, raises
from pytest import fixture, mark, raises, skip

import importnb
from importnb import Notebook, get_ipython
from importnb.loader import VERSION

CLOBBER = ("Untitled42", "my_package", "__42", "__ed42", "__d42")

Expand All @@ -22,6 +23,7 @@
sys.path.insert(0, str(HERE))

IPY = bool(get_ipython())
print(88, IPY)
ipy = mark.skipif(not IPY, reason="""Not IPython.""")


Expand Down Expand Up @@ -151,9 +153,9 @@ def test_no_magic(capsys, clean, magic, ref):
stdout = capsys.readouterr()[0]
if IPY:
if magic:
assert ref.magic_slug in stdout
assert ref.magic_slug.rstrip() in stdout
else:
assert ref.magic_slug not in stdout
assert ref.magic_slug.rstrip() not in stdout


@mark.parametrize("defs", [True, False])
Expand All @@ -162,12 +164,12 @@ def test_defs_only(defs, ref):
k for k, v in vars(ref).items() if not k[0] == "_" and isinstance(v, (type, FunctionType))
]
not_defs = [k for k, v in vars(ref).items() if not k[0] == "_" and isinstance(v, (str,))]
with Notebook(only_defs=defs):
with Notebook(include_non_defs=not defs):
import Untitled42

assert all(hasattr(Untitled42, k) for k in known_defs)

if not defs:
if defs:
assert not any(hasattr(Untitled42, k) for k in not_defs)


Expand Down Expand Up @@ -282,3 +284,9 @@ def test_cli(clean):
"ipython -m importnb -- {}".format(module.__file__).split(),
cwd=str(Path(module.__file__).parent),
)


@mark.skipif(VERSION < (3, 8), reason="async not supported in 3.7")
def test_top_level_async():
with Notebook():
import async_cells
6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,10 @@ version-file = "src/importnb/_version.py"
dependencies = ["pytest", "pytest-cov", "doit", "toml"]

[[tool.hatch.envs.test.matrix]]
version = ["interactive", "stdlib"]

[tool.hatch.envs.test.interactive.overrides]
matrix.version.features = ["interactive"]
version = ["stdlib", "interactive"]

[tool.hatch.envs.test.overrides]
matrix.version.features = [{value="interactive", if=["interactive"]}]
matrix.version.dev-mode = [{ value = false, env = ["CI=true"] }]

[tool.hatch.envs.test.scripts]
Expand Down

0 comments on commit 489e541

Please sign in to comment.