Skip to content

Commit

Permalink
Merge pull request #45 from MSeal/kernelAware
Browse files Browse the repository at this point in the history
Added a warning for gluing an object outside of a context
  • Loading branch information
willingc authored May 8, 2019
2 parents 6115645 + e210d10 commit 0d8d886
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 2 deletions.
2 changes: 2 additions & 0 deletions scrapbook/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
from .scraps import Scrap, scrap_to_payload
from .schemas import GLUE_PAYLOAD_FMT
from .encoders import registry as encoder_registry
from .utils import kernel_required


@kernel_required
def glue(name, scrap, encoder=None, display=None):
"""
Records a scrap (data value) in the given notebook cell.
Expand Down
2 changes: 2 additions & 0 deletions scrapbook/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .schemas import GLUE_PAYLOAD_PREFIX, RECORD_PAYLOAD_PREFIX
from .encoders import registry as encoder_registry
from .exceptions import ScrapbookException
from .utils import kernel_required


def merge_dicts(dicts):
Expand Down Expand Up @@ -252,6 +253,7 @@ def _strip_scrapbook_metadata(self, metadata):
copied.pop("scrapbook", None)
return copied

@kernel_required
def reglue(self, name, new_name=None, raise_on_missing=True, unattached=False):
"""
Display output from a named source of the notebook.
Expand Down
16 changes: 16 additions & 0 deletions scrapbook/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@
from IPython.display import Image

from . import get_fixture_path
from .. import utils
from ..api import glue
from ..schemas import GLUE_PAYLOAD_FMT


@pytest.fixture(scope='session', autouse=True)
def kernel_mock():
"""Mocks the kernel to capture warnings during testing"""
with mock.patch.object(utils, 'is_kernel') as _fixture:
_fixture.return_value = True
yield _fixture


@pytest.mark.parametrize(
"name,scrap,encoder,data,metadata",
[
Expand Down Expand Up @@ -196,3 +205,10 @@ def test_glue_plus_display(
mock.call(display_output, metadata=display_metadata, raw=True),
]
)


@mock.patch("scrapbook.utils.is_kernel")
def test_glue_warning(kernel_mock):
kernel_mock.return_value = False
with pytest.warns(UserWarning):
glue('foo', 'bar', 'text')
23 changes: 22 additions & 1 deletion scrapbook/tests/test_notebooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from nbformat.v4 import new_notebook, new_code_cell, new_markdown_cell, new_output

from . import get_notebook_path, get_notebook_dir
from .. import read_notebook
from .. import read_notebook, utils
from ..models import Notebook
from ..exceptions import ScrapbookException

Expand All @@ -21,6 +21,13 @@
FileNotFoundError = IOError


@pytest.fixture(scope='session', autouse=True)
def kernel_mock():
"""Mocks the kernel to capture warnings during testing"""
with mock.patch.object(utils, 'is_kernel') as _fixture:
yield _fixture


class AnyDict(object):
def __eq__(self, other):
return isinstance(other, dict)
Expand Down Expand Up @@ -298,3 +305,17 @@ def test_metadata_but_empty_content():
def test_markdown():
nb = Notebook(new_notebook(cells=[new_markdown_cell("this is a test.")]))
assert nb.scraps == collections.OrderedDict()


@mock.patch("scrapbook.utils.is_kernel")
def test_reglue_warning(kernel_mock, notebook_result):
kernel_mock.return_value = False
with pytest.warns(UserWarning):
notebook_result.reglue('number')


@mock.patch("scrapbook.utils.is_kernel")
def test_reglue_kernel_no_warning(kernel_mock, notebook_result, recwarn):
kernel_mock.return_value = True
notebook_result.reglue('number')
assert len(recwarn) == 0
9 changes: 8 additions & 1 deletion scrapbook/tests/test_scrapbooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@
from pandas.util.testing import assert_frame_equal

from . import get_notebook_path
from .. import read_notebooks
from .. import read_notebooks, utils
from ..scraps import Scrap, Scraps


@pytest.fixture(scope='session', autouse=True)
def kernel_mock():
"""Mocks the kernel to capture warnings during testing"""
with mock.patch.object(utils, 'is_kernel') as _fixture:
yield _fixture


class AnyMarkdownWith(Markdown):
def __eq__(self, other):
try:
Expand Down
22 changes: 22 additions & 0 deletions scrapbook/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys

from mock import MagicMock
from ..utils import is_kernel


def test_is_kernel_true():
class FakeIPyKernel():
kernel = True
sys.modules['IPython'] = MagicMock()
sys.modules['IPython'].get_ipython.return_value = FakeIPyKernel
assert is_kernel()
del sys.modules['IPython']


def test_not_kernel_in_ipython():
sys.modules['IPython'] = MagicMock()
sys.modules['IPython'].get_ipython.return_value = {}
assert not is_kernel()
del sys.modules['IPython']
33 changes: 33 additions & 0 deletions scrapbook/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
"""
utils.py
Provides the utilities for scrapbook functions and operations.
"""
import sys
import warnings
from functools import wraps


def is_kernel():
"""
Returns True if execution context is inside a kernel
"""
# if IPython hasn't been imported, there's nothing to check
if 'IPython' in sys.modules:
from IPython import get_ipython
ipy = get_ipython()
if ipy is not None:
return getattr(ipy, 'kernel', None) is not None
return False


def kernel_required(f):
@wraps(f)
def wrapper(*args, **kwds):
if not is_kernel():
warnings.warn(
"No kernel detected for '{fname}'.".format(
fname=f.__name__))
return f(*args, **kwds)
return wrapper

0 comments on commit 0d8d886

Please sign in to comment.