Skip to content

Commit

Permalink
Add backports for capturing output and a parameterization function. (i…
Browse files Browse the repository at this point in the history
…python#38)

* Simplify the way we handle exceptions
* Add tests for capture_output and partial
* Add Lazy to the top level API
* Include a notebook parameterization module (ipython#37)
  • Loading branch information
tonyfast authored May 18, 2018
1 parent be891d6 commit 2d1d14e
Show file tree
Hide file tree
Showing 19 changed files with 1,301 additions and 385 deletions.
126 changes: 114 additions & 12 deletions readme.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,64 @@
" import readme"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Parameterize Notebooks\n",
"\n",
"Literal ast statements are converted to notebooks parameters.\n",
"\n",
"In `readme`, `foo` is a parameter because it may be evaluated with ast.literal_val"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
" from importnb import Parameterize\n",
" f = Parameterize(readme)\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The parameterized module is a callable that evaluates with different literal statements."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Signature (*, foo=42)>"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
" assert callable(f)\n",
" f.__signature__"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" assert f().foo == 42\n",
" assert f(foo='importnb').foo == 'importnb'"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -178,15 +236,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"### Setuptools\n",
"\n",
"`importnb` provides a setuptool command that will place notebooks in a source distribution. In setuptools, update the command classs with\n",
"### Setup\n",
"\n",
" from importnb.utils.setup import build_ipynb\n",
" setup(\n",
" ...,\n",
" cmdclass=dict(build_py=build_ipynb)\n",
" ...,)"
"To package notebooks add `recursive-include package_name *.ipynb`"
]
},
{
Expand Down Expand Up @@ -240,9 +292,59 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 9,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"src/notebooks/capture.ipynb\n",
"src/notebooks/compiler_ipython.ipynb\n",
"src/notebooks/compiler_python.ipynb\n",
"src/notebooks/decoder.ipynb\n",
"src/notebooks/exporter.ipynb\n",
"src/notebooks/loader.ipynb\n",
"src/notebooks/parameterize.ipynb\n",
"src/notebooks/utils/__init__.ipynb\n",
"src/notebooks/utils/ipython.ipynb\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"test_import (src.importnb.tests.test_unittests.TestContext) ... "
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"src/notebooks/utils/pytest_plugin.ipynb\n",
"src/notebooks/utils/setup.ipynb\n",
"src/notebooks/utils/watch.ipynb\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"ok\n",
"test_reload_with_context (src.importnb.tests.test_unittests.TestContext) ... ok\n",
"test_failure (src.importnb.tests.test_unittests.TestExtension) ... expected failure\n",
"test_import (src.importnb.tests.test_unittests.TestExtension) ... ok\n",
"test_exception (src.importnb.tests.test_unittests.TestPartial) ... ok\n",
"test_traceback (src.importnb.tests.test_unittests.TestPartial) ... ok\n",
"test_imports (src.importnb.tests.test_unittests.TestRemote) ... skipped 'requires IP'\n",
"\n",
"----------------------------------------------------------------------\n",
"Ran 7 tests in 2.020s\n",
"\n",
"OK (skipped=1, expected failures=1)\n"
]
}
],
"source": [
" if __name__ == '__main__':\n",
" from pathlib import Path\n",
Expand Down Expand Up @@ -270,7 +372,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -291,7 +393,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
Expand Down
73 changes: 65 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,37 @@ The context manager is required to `reload` a module.
import readme
```

# Parameterize Notebooks

Literal ast statements are converted to notebooks parameters.

In `readme`, `foo` is a parameter because it may be evaluated with ast.literal_val


```python
from importnb import Parameterize
f = Parameterize(readme)

```

The parameterized module is a callable that evaluates with different literal statements.


```python
assert callable(f)
f.__signature__
```




<Signature (*, foo=42)>



assert f().foo == 42
assert f(foo='importnb').foo == 'importnb'

## Integrations


Expand Down Expand Up @@ -100,15 +131,9 @@ When the default extension is loaded any notebook can be run from the command li

`importnb` installs a pytest plugin when it is setup. Any notebook obeying the py.test discovery conventions can be used in to pytest. _This is great because notebooks are generally your first test._

### Setuptools

`importnb` provides a setuptool command that will place notebooks in a source distribution. In setuptools, update the command classs with
### Setup

from importnb.utils.setup import build_ipynb
setup(
...,
cmdclass=dict(build_py=build_ipynb)
...,)
To package notebooks add `recursive-include package_name *.ipynb`

### [Watchdog](https://github.com/gorakhargosh/watchdog/tree/master/src/watchdog/tricks)

Expand Down Expand Up @@ -159,6 +184,38 @@ For example, create a file called `tricks.yaml` containing

```

src/notebooks/capture.ipynb
src/notebooks/compiler_ipython.ipynb
src/notebooks/compiler_python.ipynb
src/notebooks/decoder.ipynb
src/notebooks/exporter.ipynb
src/notebooks/loader.ipynb
src/notebooks/parameterize.ipynb
src/notebooks/utils/__init__.ipynb
src/notebooks/utils/ipython.ipynb
src/notebooks/utils/pytest_plugin.ipynb

test_import (src.importnb.tests.test_unittests.TestContext) ...


src/notebooks/utils/setup.ipynb
src/notebooks/utils/watch.ipynb


ok
test_reload_with_context (src.importnb.tests.test_unittests.TestContext) ... ok
test_failure (src.importnb.tests.test_unittests.TestExtension) ... expected failure
test_import (src.importnb.tests.test_unittests.TestExtension) ... ok
test_exception (src.importnb.tests.test_unittests.TestPartial) ... ok
test_traceback (src.importnb.tests.test_unittests.TestPartial) ... ok
test_imports (src.importnb.tests.test_unittests.TestRemote) ... skipped 'requires IP'

----------------------------------------------------------------------
Ran 7 tests in 2.025s

OK (skipped=1, expected failures=1)


### Format the Github markdown files


Expand Down
7 changes: 4 additions & 3 deletions src/importnb/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
__all__ = 'Notebook', 'Partial', 'reload', 'load_ipython_extension', 'unload_ipython_extension'
__all__ = 'Notebook', 'Partial', 'reload', 'Parameterize', 'Lazy'

from .loader import Notebook, Partial, load_ipython_extension, unload_ipython_extension, reload
from . import utils
from .loader import Notebook, Partial, load_ipython_extension, unload_ipython_extension, reload, Lazy
from .parameterize import Parameterize
from . import utils
28 changes: 16 additions & 12 deletions src/importnb/capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@
from .utils import export, __IPYTHON__
except:
from utils import export, __IPYTHON__
__all__ = 'capture_output',
__all__ = "capture_output",

if __IPYTHON__:
from IPython.utils.capture import capture_output
else:
from contextlib import redirect_stdout, ExitStack
from io import StringIO

try:
from contextlib import redirect_stderr
except:
import sys

class redirect_stderr:

def __init__(self, new_target):
self._new_target = new_target
self._old_targets = []
Expand All @@ -26,8 +29,8 @@ def __enter__(self):
def __exit__(self, exctype, excinst, exctb):
sys.stderr = self._old_targets.pop()


class capture_output(ExitStack):

def __init__(self, stdout=True, stderr=True, display=True):
self.stdout = stdout
self.stderr = stderr
Expand All @@ -36,30 +39,31 @@ def __init__(self, stdout=True, stderr=True, display=True):

def __enter__(self):
super().__enter__()
stdout = stderr = outputs = None
if self.stdout:
stdout = stderr = outputs = None
if self.stdout:
stdout = StringIO()
self.enter_context(redirect_stdout(stdout))
if self.stderr:
if self.stderr:
stderr = StringIO()
self.enter_context(redirect_stderr(stderr))
return CapturedIO(stdout, stderr, outputs)

class CapturedIO:

def __init__(self, stdout=None, stderr=None, outputs=None):
self._stdout = stdout
self._stderr = stderr
self.outputs = outputs

@property
def stdout(self): return self._stdout and self._stdout.getvalue() or ''
def stdout(self):
return self._stdout and self._stdout.getvalue() or ""

@property
def stderr(self): return self._stderr and self._stderr.getvalue() or ''


def stderr(self):
return self._stderr and self._stderr.getvalue() or ""

if __name__ == '__main__':
export('capture.ipynb', '../importnb/capture.py')
__import__('doctest').testmod()

if __name__ == "__main__":
export("capture.ipynb", "../importnb/capture.py")
__import__("doctest").testmod()
Loading

0 comments on commit 2d1d14e

Please sign in to comment.