From ba3ca471f069f9420fd87b8fe41d8f7dc9f2f4e5 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Thu, 19 Oct 2017 16:19:23 -0400 Subject: [PATCH 01/20] DOC: Fix `RuntimeError` intersphinx --- doc/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/index.rst b/doc/source/index.rst index d2afda0..388d0da 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -29,7 +29,7 @@ in the usage instructions. **NOTE:** Due to the way Python handles non-global variable scopes, |TempVars| can only be used at the global scope. *Any attempt to use* |TempVars| *in non-global contexts will result in a* -:exc:`~exceptions.RuntimeError`. Viable use-cases include Jupyter notebooks, +:exc:`RuntimeError`. Viable use-cases include Jupyter notebooks, the IPython and basic Python REPLs, and the outermost scope of executed and imported modules. Preliminary testing indicates it also works with `cauldron-notebook `__, though From 809d901a7b36748dd882a9907b8ed8923ae3c2a4 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Wed, 13 Jun 2018 21:34:56 -0400 Subject: [PATCH 02/20] TEST: Update tox w/Python3.7 Works! --- tox.ini | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tox.ini b/tox.ini index 95cd45f..60c4337 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion=2.0 -envlist=py3{4,5,6}-at_{17_1,17_2,latest} +envlist=py3{4,5,6,7}-at_{17_1,17_2,latest} [testenv] commands= @@ -20,15 +20,12 @@ basepython= py36: C:\python36\python.exe py35: C:\python35\python.exe py34: C:\python34\python.exe - py33: C:\python33\python.exe - py32: C:\python32\python.exe [testenv:linux] platform=linux basepython= - py36: python36 - py35: python35 - py34: python34 - py33: python33 - py32: python32 + py37: python3.7 + py36: python3.6 + py35: python3.5 + py34: python3.4 From bd4f5e8caa4625cd2a3cef7033c54ff638dd0952 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Mon, 12 Nov 2018 13:56:08 -0500 Subject: [PATCH 03/20] ADMIN: Update req'ts Add reqts-dev, remove napoleon from -travis. --- requirements-dev.txt | 5 +++++ requirements-travis.txt | 9 ++++----- 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 requirements-dev.txt diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..dcfeb89 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,5 @@ +attrs>=17 +flake8 +flake8-docstrings +sphinx +sphinx-rtd-theme diff --git a/requirements-travis.txt b/requirements-travis.txt index fe52d6a..dcfeb89 100644 --- a/requirements-travis.txt +++ b/requirements-travis.txt @@ -1,6 +1,5 @@ attrs>=17 -flake8==3.4.1 -flake8-docstrings==1.1.0 -sphinx==1.6.3 -sphinx-rtd-theme==0.2.4 -sphinxcontrib-napoleon==0.6.1 +flake8 +flake8-docstrings +sphinx +sphinx-rtd-theme From d7263eb5b091b4b05ef6f4ee21fafbeaefcd5005 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Mon, 12 Nov 2018 14:05:45 -0500 Subject: [PATCH 04/20] TEST: Update .travis.yml and tox.ini Closes #26. --- .travis.yml | 5 ++--- tox.ini | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ed349d..17db2fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,9 @@ python: - 3.4 - 3.5 - 3.6 + - 3.7-dev script: - python tests.py -a - flake8 tempvars - - cd doc - - make doctest - + - sh -c 'cd doc; make doctest' diff --git a/tox.ini b/tox.ini index 60c4337..a92b85e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion=2.0 -envlist=py3{4,5,6,7}-at_{17_1,17_2,latest} +envlist=py3{4,5,6,7}-at_{17_1,17_4,latest} [testenv] commands= @@ -8,8 +8,9 @@ commands= python tests.py -a deps= at_17_1: attrs==17.1 - at_17_2: attrs==17.2 + at_17_4: attrs==17.4 latest: attrs + at18: attrs>=18,<19 at17: attrs>=17,<18 at16: attrs>=16,<17 at15: attrs>=15,<16 @@ -17,6 +18,7 @@ deps= [testenv:win] platform=win basepython= + py37: C:\python37\python.exe py36: C:\python36\python.exe py35: C:\python35\python.exe py34: C:\python34\python.exe From 1157695308bd9de8a11ad9b7749b302109f7cea9 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Mon, 12 Nov 2018 14:17:10 -0500 Subject: [PATCH 05/20] TEST: Set up tox All pass! --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index dcfeb89..9362a84 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,3 +3,4 @@ flake8 flake8-docstrings sphinx sphinx-rtd-theme +tox From 8655c8becc1b4149a0507b9c96ef551a34752499 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Mon, 12 Nov 2018 21:05:57 -0500 Subject: [PATCH 06/20] ADMIN: Blacken; add restview to req'ts-dev --- black | 4 + black.bat | 4 + requirements-dev.txt | 2 + setup.py | 52 ++- tempvars/__init__.py | 4 +- tempvars/tempvars.py | 45 +- tempvars/test/__init__.py | 2 +- tempvars/test/tempvars_base.py | 771 +++++++++++++++++---------------- tests.py | 53 ++- 9 files changed, 504 insertions(+), 433 deletions(-) create mode 100755 black create mode 100644 black.bat diff --git a/black b/black new file mode 100755 index 0000000..03d1cb4 --- /dev/null +++ b/black @@ -0,0 +1,4 @@ +#! /bin/bash + +black setup.py tests.py tempvars tempvars/test -l 79 + diff --git a/black.bat b/black.bat new file mode 100644 index 0000000..7e4c40f --- /dev/null +++ b/black.bat @@ -0,0 +1,4 @@ +@echo off + +black.exe setup.py tests.py tempvars tempvars\test -l 79 + diff --git a/requirements-dev.txt b/requirements-dev.txt index 9362a84..f846036 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,8 @@ attrs>=17 +black flake8 flake8-docstrings +restview sphinx sphinx-rtd-theme tox diff --git a/setup.py b/setup.py index 223dccb..36ec25a 100644 --- a/setup.py +++ b/setup.py @@ -2,32 +2,36 @@ def readme(): - with open('README.rst') as f: + with open("README.rst") as f: return f.read() setup( - name='tempvars', - version='1.0', - provides=['tempvars'], - install_requires=['attrs>=17'], - packages=['tempvars'], - url='https://www.github.com/bskinn/tempvars', - license='MIT License', - author='Brian Skinn', - author_email='bskinn@alum.mit.edu', - description=('Context manager for handling temporary variables ' - 'in Jupyter Notebook, IPython, etc.'), + name="tempvars", + version="1.0", + provides=["tempvars"], + install_requires=["attrs>=17"], + packages=["tempvars"], + url="https://www.github.com/bskinn/tempvars", + license="MIT License", + author="Brian Skinn", + author_email="bskinn@alum.mit.edu", + description=( + "Context manager for handling temporary variables " + "in Jupyter Notebook, IPython, etc." + ), long_description=readme(), - classifiers=['License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Intended Audience :: Science/Research', - 'Intended Audience :: Developers', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Topic :: Software Development', - 'Development Status :: 5 - Production/Stable'] - ) + classifiers=[ + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Topic :: Software Development", + "Development Status :: 5 - Production/Stable", + ], +) diff --git a/tempvars/__init__.py b/tempvars/__init__.py index bcfcbb5..c257d0e 100644 --- a/tempvars/__init__.py +++ b/tempvars/__init__.py @@ -22,8 +22,8 @@ from __future__ import absolute_import -__all__ = ['TempVars'] +__all__ = ["TempVars"] from .tempvars import TempVars -__version__ = '1.0' +__version__ = "1.0" diff --git a/tempvars/tempvars.py b/tempvars/tempvars.py index 27d97bb..fab6498 100644 --- a/tempvars/tempvars.py +++ b/tempvars/tempvars.py @@ -94,19 +94,22 @@ def _var_pattern_validator(self, at, val): for s in val: if type(s) != str: raise te - if at.name != 'names' and (s == '_' or s == '__'): - raise ValueError("'_' and '__' are not permitted " - "for '{0}'".format(at.name)) - if at.name == 'starts' and s.startswith('__'): + if at.name != "names" and (s == "_" or s == "__"): + raise ValueError( + "'_' and '__' are not permitted " + "for '{0}'".format(at.name) + ) + if at.name == "starts" and s.startswith("__"): raise ValueError("'starts' may not start with '__'") - if at.name == 'ends' and s.endswith('__'): + if at.name == "ends" and s.endswith("__"): raise ValueError("'ends' may not end with '__'") # ## Flag for whether to restore the prior namespace contents #: |bool| flag indicating whether to restore the prior namespace #: contents. **Can** be changed within the |with| suite. - restore = attr.ib(default=True, - validator=attr.validators.instance_of(bool)) + restore = attr.ib( + default=True, validator=attr.validators.instance_of(bool) + ) # ## Namespace for temp variable management. # Always the globals at the level of the invoker of the TempVars @@ -139,13 +142,13 @@ def _ns_default(self): # ## Internal vars, not set via the attrs __init__ #: |dict| container for preserving variables masked from #: the namespace, along with their associated values. - stored_nsvars = attr.ib(init=False, repr=False, - default=attr.Factory(dict)) + stored_nsvars = attr.ib(init=False, repr=False, default=attr.Factory(dict)) #: |dict| container for storing the temporary variables discarded from #: the namespace after exiting the |with| block. - retained_tempvars = attr.ib(init=False, repr=False, - default=attr.Factory(dict)) + retained_tempvars = attr.ib( + init=False, repr=False, default=attr.Factory(dict) + ) def __attrs_post_init__(self): """Process arguments post-init in various ways.""" @@ -157,10 +160,17 @@ def copy_if_not_none(v): return v if v is None else v[:] # Trigger a warning if no patterns were passed - if all(map(lambda a: a is None or len(a) == 0, - (self.names, self.starts, self.ends))): - warnings.warn("No masking patterns provided for TempVars", - RuntimeWarning, stacklevel=2) + if all( + map( + lambda a: a is None or len(a) == 0, + (self.names, self.starts, self.ends), + ) + ): + warnings.warn( + "No masking patterns provided for TempVars", + RuntimeWarning, + stacklevel=2, + ) # Copy any arguments that aren't None self.names = copy_if_not_none(self.names) @@ -179,8 +189,7 @@ def _pop_to(self, dest_dict, patterns, test_fxn): for _ in list(self._ns.keys()): # Pop the variable over to the destination dictionary if # any ``map`` result is truthy. - if any(map(lambda p, k=_, t=test_fxn: t(k, p), - patterns)): + if any(map(lambda p, k=_, t=test_fxn: t(k, p), patterns)): dest_dict.update({_: self._ns.pop(_)}) def __enter__(self): @@ -238,5 +247,5 @@ def __exit__(self, exc_type, exc_val, exc_tb): return False -if __name__ == '__main__': # pragma: no cover +if __name__ == "__main__": # pragma: no cover print("Module not executable.") diff --git a/tempvars/test/__init__.py b/tempvars/test/__init__.py index 0ba48b1..02a8f1a 100644 --- a/tempvars/test/__init__.py +++ b/tempvars/test/__init__.py @@ -17,6 +17,6 @@ from __future__ import absolute_import -__all__ = ['suite_expect_good', 'suite_expect_fail'] +__all__ = ["suite_expect_good", "suite_expect_fail"] from .tempvars_base import suite_expect_good, suite_expect_fail diff --git a/tempvars/test/tempvars_base.py b/tempvars/test/tempvars_base.py index ecbee9d..16de3cd 100644 --- a/tempvars/test/tempvars_base.py +++ b/tempvars/test/tempvars_base.py @@ -32,9 +32,9 @@ def locals_subTest(self, testid, locdict, val): """Wrap and test previously run True/False checks.""" # Strip leading test flag if present when constructing # subtest name - testname = testid[3:] if testid.startswith('_t_') else testid + testname = testid[3:] if testid.startswith("_t_") else testid - with self.subTest(testname): # pragma: no cover + with self.subTest(testname): # pragma: no cover if val: self.assertTrue(locdict[testid]) else: @@ -49,14 +49,16 @@ def test_Good_namesPassed(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "x = 5\n" - "with TempVars(names=['x']) as tv:\n" - " _t_inside_absent = 'x' not in dir()\n" - "_t_outside_present = 'x' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "x = 5\n" + "with TempVars(names=['x']) as tv:\n" + " _t_inside_absent = 'x' not in dir()\n" + "_t_outside_present = 'x' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_namesPassed_ToggleWorks(self): @@ -64,15 +66,17 @@ def test_Good_namesPassed_ToggleWorks(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "x = 5\n" - "with TempVars(names=['x']) as tv:\n" - " _t_inside_absent = 'x' not in dir()\n" - " tv.restore = False\n" - "_t_outside_absent = 'x' not in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "x = 5\n" + "with TempVars(names=['x']) as tv:\n" + " _t_inside_absent = 'x' not in dir()\n" + " tv.restore = False\n" + "_t_outside_absent = 'x' not in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_namesPassed_NoRestore(self): @@ -80,14 +84,16 @@ def test_Good_namesPassed_NoRestore(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "x = 5\n" - "with TempVars(names=['x'], restore=False) as tv:\n" - " _t_inside_absent = 'x' not in dir()\n" - "_t_outside_absent = 'x' not in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "x = 5\n" + "with TempVars(names=['x'], restore=False) as tv:\n" + " _t_inside_absent = 'x' not in dir()\n" + "_t_outside_absent = 'x' not in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_namesPassedButNotPresent(self): @@ -99,18 +105,20 @@ def test_Good_namesPassedButNotPresent(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "x = 5\n" - "with TempVars(names=['y']) as tv:\n" - " y = 12\n" - " _t_inside_x_present = 'x' in dir()\n" - " _t_inside_y_present = 'y' in dir()\n" - "_t_outside_x_present = 'x' in dir()\n" - "_t_outside_y_absent = 'y' not in dir()\n" - "_t_outside_y_retained = tv.retained_tempvars['y'] == 12\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "x = 5\n" + "with TempVars(names=['y']) as tv:\n" + " y = 12\n" + " _t_inside_x_present = 'x' in dir()\n" + " _t_inside_y_present = 'y' in dir()\n" + "_t_outside_x_present = 'x' in dir()\n" + "_t_outside_y_absent = 'y' not in dir()\n" + "_t_outside_y_retained = tv.retained_tempvars['y'] == 12\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_startsPassed(self): @@ -118,20 +126,22 @@ def test_Good_startsPassed(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "z_x = 14\n" - "with TempVars(starts=['t_']) as tv:\n" - " _t_inside_t_x_absent = 't_x' not in dir()\n" - " _t_inside_t_y_absent = 't_y' not in dir()\n" - " _t_inside_z_x_present = 'z_x' in dir()\n" - "_t_outside_t_x_present = 't_x' in dir()\n" - "_t_outside_t_y_present = 't_y' in dir()\n" - "_t_outside_z_x_present = 'z_x' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "z_x = 14\n" + "with TempVars(starts=['t_']) as tv:\n" + " _t_inside_t_x_absent = 't_x' not in dir()\n" + " _t_inside_t_y_absent = 't_y' not in dir()\n" + " _t_inside_z_x_present = 'z_x' in dir()\n" + "_t_outside_t_x_present = 't_x' in dir()\n" + "_t_outside_t_y_present = 't_y' in dir()\n" + "_t_outside_z_x_present = 'z_x' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_endsPassed(self): @@ -139,20 +149,22 @@ def test_Good_endsPassed(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "z_x = 14\n" - "with TempVars(ends=['_x']) as tv:\n" - " _t_inside_t_x_absent = 't_x' not in dir()\n" - " _t_inside_t_y_present = 't_y' in dir()\n" - " _t_inside_z_x_absent = 'z_x' not in dir()\n" - "_t_outside_t_x_present = 't_x' in dir()\n" - "_t_outside_t_y_present = 't_y' in dir()\n" - "_t_outside_z_x_present = 'z_x' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "z_x = 14\n" + "with TempVars(ends=['_x']) as tv:\n" + " _t_inside_t_x_absent = 't_x' not in dir()\n" + " _t_inside_t_y_present = 't_y' in dir()\n" + " _t_inside_z_x_absent = 'z_x' not in dir()\n" + "_t_outside_t_x_present = 't_x' in dir()\n" + "_t_outside_t_y_present = 't_y' in dir()\n" + "_t_outside_z_x_present = 'z_x' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_startsPassed_NoRestore(self): @@ -160,20 +172,22 @@ def test_Good_startsPassed_NoRestore(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "z_x = 14\n" - "with TempVars(starts=['t_'], restore=False) as tv:\n" - " _t_inside_t_x_absent = 't_x' not in dir()\n" - " _t_inside_t_y_absent = 't_y' not in dir()\n" - " _t_inside_z_x_present = 'z_x' in dir()\n" - "_t_outside_t_x_absent = 't_x' not in dir()\n" - "_t_outside_t_y_absent = 't_y' not in dir()\n" - "_t_outside_z_x_present = 'z_x' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "z_x = 14\n" + "with TempVars(starts=['t_'], restore=False) as tv:\n" + " _t_inside_t_x_absent = 't_x' not in dir()\n" + " _t_inside_t_y_absent = 't_y' not in dir()\n" + " _t_inside_z_x_present = 'z_x' in dir()\n" + "_t_outside_t_x_absent = 't_x' not in dir()\n" + "_t_outside_t_y_absent = 't_y' not in dir()\n" + "_t_outside_z_x_present = 'z_x' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_endsPassed_NoRestore(self): @@ -181,20 +195,22 @@ def test_Good_endsPassed_NoRestore(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "z_x = 14\n" - "with TempVars(ends=['_x'], restore=False) as tv:\n" - " _t_inside_t_x_absent = 't_x' not in dir()\n" - " _t_inside_t_y_present = 't_y' in dir()\n" - " _t_inside_z_x_absent = 'z_x' not in dir()\n" - "_t_outside_t_x_absent = 't_x' not in dir()\n" - "_t_outside_t_y_present = 't_y' in dir()\n" - "_t_outside_z_x_absent = 'z_x' not in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "z_x = 14\n" + "with TempVars(ends=['_x'], restore=False) as tv:\n" + " _t_inside_t_x_absent = 't_x' not in dir()\n" + " _t_inside_t_y_present = 't_y' in dir()\n" + " _t_inside_z_x_absent = 'z_x' not in dir()\n" + "_t_outside_t_x_absent = 't_x' not in dir()\n" + "_t_outside_t_y_present = 't_y' in dir()\n" + "_t_outside_z_x_absent = 'z_x' not in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_checkStorage_namesNoRestore(self): @@ -206,24 +222,26 @@ def test_Good_checkStorage_namesNoRestore(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "x = 5\n" - "before_val = x == 5\n" - "with TempVars(names=['x'], restore=False) as tv:\n" - " _t_inside_initial_absent = 'x' not in dir()\n" - " _t_inside_stored_nsvar = tv.stored_nsvars.get('x') == 5\n" - " x = 18\n" - " _t_inside_final_exist = 'x' in dir()\n" - " _t_inside_final_val = x == 18\n" - " _t_inside_retained_tempvars_empty =\\\n" - " len(tv.retained_tempvars) == 0\n" - "_t_outside_stored_nsvar = tv.stored_nsvars.get('x') == 5\n" - "_t_outside_retained_tempvar =\\\n" - " tv.retained_tempvars.get('x') == 18\n" - "_t_outside_final_absent = 'x' not in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "x = 5\n" + "before_val = x == 5\n" + "with TempVars(names=['x'], restore=False) as tv:\n" + " _t_inside_initial_absent = 'x' not in dir()\n" + " _t_inside_stored_nsvar = tv.stored_nsvars.get('x') == 5\n" + " x = 18\n" + " _t_inside_final_exist = 'x' in dir()\n" + " _t_inside_final_val = x == 18\n" + " _t_inside_retained_tempvars_empty =\\\n" + " len(tv.retained_tempvars) == 0\n" + "_t_outside_stored_nsvar = tv.stored_nsvars.get('x') == 5\n" + "_t_outside_retained_tempvar =\\\n" + " tv.retained_tempvars.get('x') == 18\n" + "_t_outside_final_absent = 'x' not in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_checkStorage_startsNoRestore(self): @@ -235,33 +253,35 @@ def test_Good_checkStorage_startsNoRestore(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "_t_before_var_present = t_x == 5\n" - "_t_before_newvar_absent = 't_y' not in dir()\n" - "with TempVars(starts=['t_'], restore=False) as tv:\n" - " _t_inside_initial_absent = 't_x' not in dir()\n" - " _t_inside_initial_newvar_absent = 't_y' not in dir()\n" - " _t_inside_stored_nsvar = tv.stored_nsvars.get('t_x') == 5\n" - " _t_inside_newvar_notin_nsvars =\\\n" - " 't_y' not in tv.stored_nsvars\n" - " t_x = 18\n" - " t_y = 43\n" - " _t_inside_final_exist = 't_x' in dir()\n" - " _t_inside_final_val = t_x == 18\n" - " _t_inside_retained_tempvars_empty =\\\n" - " len(tv.retained_tempvars) == 0\n" - "_t_outside_stored_nsvar = tv.stored_nsvars.get('t_x') == 5\n" - "_t_outside_newvar_absent = 't_y' not in dir()\n" - "_t_outside_newvar_notin_nsvars = 't_y' not in tv.stored_nsvars\n" - "_t_outside_retained_tempvar =\\\n" - " tv.retained_tempvars.get('t_x'\n) == 18\n" - "_t_outside_newvar_in_retained_tempvars =\\\n" - " 't_y' in tv.retained_tempvars\n" - "_t_outside_final_absent = 't_x' not in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "_t_before_var_present = t_x == 5\n" + "_t_before_newvar_absent = 't_y' not in dir()\n" + "with TempVars(starts=['t_'], restore=False) as tv:\n" + " _t_inside_initial_absent = 't_x' not in dir()\n" + " _t_inside_initial_newvar_absent = 't_y' not in dir()\n" + " _t_inside_stored_nsvar = tv.stored_nsvars.get('t_x') == 5\n" + " _t_inside_newvar_notin_nsvars =\\\n" + " 't_y' not in tv.stored_nsvars\n" + " t_x = 18\n" + " t_y = 43\n" + " _t_inside_final_exist = 't_x' in dir()\n" + " _t_inside_final_val = t_x == 18\n" + " _t_inside_retained_tempvars_empty =\\\n" + " len(tv.retained_tempvars) == 0\n" + "_t_outside_stored_nsvar = tv.stored_nsvars.get('t_x') == 5\n" + "_t_outside_newvar_absent = 't_y' not in dir()\n" + "_t_outside_newvar_notin_nsvars = 't_y' not in tv.stored_nsvars\n" + "_t_outside_retained_tempvar =\\\n" + " tv.retained_tempvars.get('t_x'\n) == 18\n" + "_t_outside_newvar_in_retained_tempvars =\\\n" + " 't_y' in tv.retained_tempvars\n" + "_t_outside_final_absent = 't_x' not in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_checkStorage_endsNoRestore(self): @@ -273,34 +293,36 @@ def test_Good_checkStorage_endsNoRestore(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "_t_before_val = t_x == 5\n" - "_t_before_newvar_absent = 'r_x' not in dir()\n" - "with TempVars(ends=['_x'], restore=False) as tv:\n" - " _t_inside_initial_absent = 't_x' not in dir()\n" - " _t_inside_initial_newvar_absent = 'r_x' not in dir()\n" - " _t_inside_stored_nsvar = tv.stored_nsvars.get('t_x') is 5\n" - " _t_inside_newvar_not_in_nsvars =\\\n" - " 'r_x' not in tv.stored_nsvars\n" - " t_x = 18\n" - " r_x = 43\n" - " _t_inside_final_exist = 't_x' in dir()\n" - " _t_inside_final_val = t_x == 18\n" - " _t_inside_retained_tempvars_empty =\\\n" - " len(tv.retained_tempvars) == 0\n" - "_t_outside_stored_nsvar = tv.stored_nsvars.get('t_x') == 5\n" - "_t_outside_newvar_absent = 'r_x' not in dir()\n" - "_t_outside_newvar_not_in_nsvars =\\\n" - " 'r_x' not in tv.stored_nsvars\n" - "_t_outside_retained_tempvar =\\\n" - " tv.retained_tempvars.get('t_x') == 18\n" - "_t_outside_newvar_in_retained_tempvars =\\\n" - " 'r_x' in tv.retained_tempvars\n" - "_t_outside_final_absent = 't_x' not in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "_t_before_val = t_x == 5\n" + "_t_before_newvar_absent = 'r_x' not in dir()\n" + "with TempVars(ends=['_x'], restore=False) as tv:\n" + " _t_inside_initial_absent = 't_x' not in dir()\n" + " _t_inside_initial_newvar_absent = 'r_x' not in dir()\n" + " _t_inside_stored_nsvar = tv.stored_nsvars.get('t_x') is 5\n" + " _t_inside_newvar_not_in_nsvars =\\\n" + " 'r_x' not in tv.stored_nsvars\n" + " t_x = 18\n" + " r_x = 43\n" + " _t_inside_final_exist = 't_x' in dir()\n" + " _t_inside_final_val = t_x == 18\n" + " _t_inside_retained_tempvars_empty =\\\n" + " len(tv.retained_tempvars) == 0\n" + "_t_outside_stored_nsvar = tv.stored_nsvars.get('t_x') == 5\n" + "_t_outside_newvar_absent = 'r_x' not in dir()\n" + "_t_outside_newvar_not_in_nsvars =\\\n" + " 'r_x' not in tv.stored_nsvars\n" + "_t_outside_retained_tempvar =\\\n" + " tv.retained_tempvars.get('t_x') == 18\n" + "_t_outside_newvar_in_retained_tempvars =\\\n" + " 'r_x' in tv.retained_tempvars\n" + "_t_outside_final_absent = 't_x' not in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_checkArgs(self): @@ -312,39 +334,41 @@ def test_Good_checkArgs(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "r_x = 12\n" - "f_z = 59\n" - "d_z = 12\n" - "g_r = 43\n" - "with TempVars(names=['g_r', 'm_m'], starts=['t_'],\n" - " ends=['_z']) as tv:\n" - " nsvk = tv.stored_nsvars.keys()\n" - " _t_g_r_absent = 'g_r' not in dir()\n" - " _t_t_x_absent = 't_x' not in dir()\n" - " _t_t_y_absent = 't_y' not in dir()\n" - " _t_r_x_present = 'r_x' in dir()\n" - " _t_f_z_absent = 'f_z' not in dir()\n" - " _t_d_z_absent = 'd_z' not in dir()\n" - " _t_g_r_in_stored_names = 'g_r' in nsvk\n" - " _t_t_x_in_stored_names = 't_x' in nsvk\n" - " _t_t_y_in_stored_names = 't_y' in nsvk\n" - " _t_r_x_not_in_stored_names = 'r_x' not in nsvk\n" - " _t_m_m_not_in_stored_names = 'm_m' not in nsvk\n" - " _t_f_z_in_stored_names = 'f_z' in nsvk\n" - " _t_d_z_in_stored_names = 'd_z' in nsvk\n" - " _t_g_r_in_passed_names = 'g_r' in tv.names\n" - " _t_m_m_in_passed_names = 'm_m' in tv.names\n" - " _t_t_x_not_in_passed_names = 't_x' not in tv.names\n" - " _t_t_y_not_in_passed_names = 't_y' not in tv.names\n" - " _t_r_x_not_in_passed_names = 'r_x' not in tv.names\n" - " _t_f_z_not_in_passed_names = 'f_z' not in tv.names\n" - " _t_d_z_not_in_passed_names = 'd_z' not in tv.names\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "r_x = 12\n" + "f_z = 59\n" + "d_z = 12\n" + "g_r = 43\n" + "with TempVars(names=['g_r', 'm_m'], starts=['t_'],\n" + " ends=['_z']) as tv:\n" + " nsvk = tv.stored_nsvars.keys()\n" + " _t_g_r_absent = 'g_r' not in dir()\n" + " _t_t_x_absent = 't_x' not in dir()\n" + " _t_t_y_absent = 't_y' not in dir()\n" + " _t_r_x_present = 'r_x' in dir()\n" + " _t_f_z_absent = 'f_z' not in dir()\n" + " _t_d_z_absent = 'd_z' not in dir()\n" + " _t_g_r_in_stored_names = 'g_r' in nsvk\n" + " _t_t_x_in_stored_names = 't_x' in nsvk\n" + " _t_t_y_in_stored_names = 't_y' in nsvk\n" + " _t_r_x_not_in_stored_names = 'r_x' not in nsvk\n" + " _t_m_m_not_in_stored_names = 'm_m' not in nsvk\n" + " _t_f_z_in_stored_names = 'f_z' in nsvk\n" + " _t_d_z_in_stored_names = 'd_z' in nsvk\n" + " _t_g_r_in_passed_names = 'g_r' in tv.names\n" + " _t_m_m_in_passed_names = 'm_m' in tv.names\n" + " _t_t_x_not_in_passed_names = 't_x' not in tv.names\n" + " _t_t_y_not_in_passed_names = 't_y' not in tv.names\n" + " _t_r_x_not_in_passed_names = 'r_x' not in tv.names\n" + " _t_f_z_not_in_passed_names = 'f_z' not in tv.names\n" + " _t_d_z_not_in_passed_names = 'd_z' not in tv.names\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_namesMultiPassed(self): @@ -352,26 +376,28 @@ def test_Good_namesMultiPassed(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "z_x = 14\n" - "z_m = 24\n" - "r_x = 44\n" - "with TempVars(names=['t_x', 'z_m']) as tv:\n" - " _t_inside_t_x_absent = 't_x' not in dir()\n" - " _t_inside_t_y_present = 't_y' in dir()\n" - " _t_inside_z_x_present = 'z_x' in dir()\n" - " _t_inside_z_m_absent = 'z_m' not in dir()\n" - " _t_inside_r_x_present = 'r_x' in dir()\n" - "_t_outside_t_x_present = 't_x' in dir()\n" - "_t_outside_t_y_present = 't_y' in dir()\n" - "_t_outside_z_x_present = 'z_x' in dir()\n" - "_t_outside_z_m_present = 'z_m' in dir()\n" - "_t_outside_r_x_present = 'r_x' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "z_x = 14\n" + "z_m = 24\n" + "r_x = 44\n" + "with TempVars(names=['t_x', 'z_m']) as tv:\n" + " _t_inside_t_x_absent = 't_x' not in dir()\n" + " _t_inside_t_y_present = 't_y' in dir()\n" + " _t_inside_z_x_present = 'z_x' in dir()\n" + " _t_inside_z_m_absent = 'z_m' not in dir()\n" + " _t_inside_r_x_present = 'r_x' in dir()\n" + "_t_outside_t_x_present = 't_x' in dir()\n" + "_t_outside_t_y_present = 't_y' in dir()\n" + "_t_outside_z_x_present = 'z_x' in dir()\n" + "_t_outside_z_m_present = 'z_m' in dir()\n" + "_t_outside_r_x_present = 'r_x' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_startsMultiPassed(self): @@ -379,26 +405,28 @@ def test_Good_startsMultiPassed(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "z_x = 14\n" - "z_m = 24\n" - "r_x = 44\n" - "with TempVars(starts=['t_', 'z_']) as tv:\n" - " _t_inside_t_x_absent = 't_x' not in dir()\n" - " _t_inside_t_y_absent = 't_y' not in dir()\n" - " _t_inside_z_x_absent = 'z_x' not in dir()\n" - " _t_inside_z_m_absent = 'z_m' not in dir()\n" - " _t_inside_r_x_present = 'r_x' in dir()\n" - "_t_outside_t_x_present = 't_x' in dir()\n" - "_t_outside_t_y_present = 't_y' in dir()\n" - "_t_outside_z_x_present = 'z_x' in dir()\n" - "_t_outside_z_m_present = 'z_m' in dir()\n" - "_t_outside_r_x_present = 'r_x' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "z_x = 14\n" + "z_m = 24\n" + "r_x = 44\n" + "with TempVars(starts=['t_', 'z_']) as tv:\n" + " _t_inside_t_x_absent = 't_x' not in dir()\n" + " _t_inside_t_y_absent = 't_y' not in dir()\n" + " _t_inside_z_x_absent = 'z_x' not in dir()\n" + " _t_inside_z_m_absent = 'z_m' not in dir()\n" + " _t_inside_r_x_present = 'r_x' in dir()\n" + "_t_outside_t_x_present = 't_x' in dir()\n" + "_t_outside_t_y_present = 't_y' in dir()\n" + "_t_outside_z_x_present = 'z_x' in dir()\n" + "_t_outside_z_m_present = 'z_m' in dir()\n" + "_t_outside_r_x_present = 'r_x' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_endsMultiPassed(self): @@ -406,26 +434,28 @@ def test_Good_endsMultiPassed(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "t_x = 5\n" - "t_y = 8\n" - "z_x = 14\n" - "z_m = 24\n" - "r_x = 44\n" - "with TempVars(ends=['_x', '_y']) as tv:\n" - " _t_inside_t_x_absent = 't_x' not in dir()\n" - " _t_inside_t_y_absent = 't_y' not in dir()\n" - " _t_inside_z_x_absent = 'z_x' not in dir()\n" - " _t_inside_z_m_present = 'z_m' in dir()\n" - " _t_inside_r_x_absent = 'r_x' not in dir()\n" - "_t_outside_t_x_present = 't_x' in dir()\n" - "_t_outside_t_y_present = 't_y' in dir()\n" - "_t_outside_z_x_present = 'z_x' in dir()\n" - "_t_outside_z_m_present = 'z_m' in dir()\n" - "_t_outside_r_x_present = 'r_x' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "t_x = 5\n" + "t_y = 8\n" + "z_x = 14\n" + "z_m = 24\n" + "r_x = 44\n" + "with TempVars(ends=['_x', '_y']) as tv:\n" + " _t_inside_t_x_absent = 't_x' not in dir()\n" + " _t_inside_t_y_absent = 't_y' not in dir()\n" + " _t_inside_z_x_absent = 'z_x' not in dir()\n" + " _t_inside_z_m_present = 'z_m' in dir()\n" + " _t_inside_r_x_absent = 'r_x' not in dir()\n" + "_t_outside_t_x_present = 't_x' in dir()\n" + "_t_outside_t_y_present = 't_y' in dir()\n" + "_t_outside_z_x_present = 'z_x' in dir()\n" + "_t_outside_z_m_present = 'z_m' in dir()\n" + "_t_outside_r_x_present = 'r_x' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_nestedVarsRestoreOuterOnly(self): @@ -433,27 +463,29 @@ def test_Good_nestedVarsRestoreOuterOnly(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "x = 5\n" - "y = 8\n" - "z = 14\n" - "with TempVars(names=['x'], restore=True) as tv_outer:\n" - " _t_in_1_before_2_x_absent = 'x' not in dir()\n" - " _t_in_1_before_2_y_present = 'y' in dir()\n" - " _t_in_1_before_2_z_present = 'z' in dir()\n" - " with TempVars(names=['y'], restore=False) as tv_inner:\n" - " _t_in_12_x_absent = 'x' not in dir()\n" - " _t_in_12_y_absent = 'y' not in dir()\n" - " _t_in_12_z_present = 'z' in dir()\n" - " _t_in_1_after_2_x_absent = 'x' not in dir()\n" - " _t_in_1_after_2_y_absent = 'y' not in dir()\n" - " _t_in_1_after_2_z_present = 'z' in dir()\n" - "_t_after_12_x_present = 'x' in dir()\n" - "_t_after_12_y_absent= 'y' not in dir()\n" - "_t_after_12_z_present = 'z' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "x = 5\n" + "y = 8\n" + "z = 14\n" + "with TempVars(names=['x'], restore=True) as tv_outer:\n" + " _t_in_1_before_2_x_absent = 'x' not in dir()\n" + " _t_in_1_before_2_y_present = 'y' in dir()\n" + " _t_in_1_before_2_z_present = 'z' in dir()\n" + " with TempVars(names=['y'], restore=False) as tv_inner:\n" + " _t_in_12_x_absent = 'x' not in dir()\n" + " _t_in_12_y_absent = 'y' not in dir()\n" + " _t_in_12_z_present = 'z' in dir()\n" + " _t_in_1_after_2_x_absent = 'x' not in dir()\n" + " _t_in_1_after_2_y_absent = 'y' not in dir()\n" + " _t_in_1_after_2_z_present = 'z' in dir()\n" + "_t_after_12_x_present = 'x' in dir()\n" + "_t_after_12_y_absent= 'y' not in dir()\n" + "_t_after_12_z_present = 'z' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_nestedVarsRestoreInnerOnly(self): @@ -461,27 +493,29 @@ def test_Good_nestedVarsRestoreInnerOnly(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "x = 5\n" - "y = 8\n" - "z = 14\n" - "with TempVars(names=['x'], restore=False) as tv_outer:\n" - " _t_in_1_before_2_x_absent = 'x' not in dir()\n" - " _t_in_1_before_2_y_present = 'y' in dir()\n" - " _t_in_1_before_2_z_present = 'z' in dir()\n" - " with TempVars(names=['y'], restore=True) as tv_inner:\n" - " _t_in_12_x_absent = 'x' not in dir()\n" - " _t_in_12_y_absent = 'y' not in dir()\n" - " _t_in_12_z_present = 'z' in dir()\n" - " _t_in_1_after_2_x_absent = 'x' not in dir()\n" - " _t_in_1_after_2_y_present = 'y' in dir()\n" - " _t_in_1_after_2_z_present = 'z' in dir()\n" - "_t_after_12_x_absent = 'x' not in dir()\n" - "_t_after_12_y_present= 'y' in dir()\n" - "_t_after_12_z_present = 'z' in dir()\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "x = 5\n" + "y = 8\n" + "z = 14\n" + "with TempVars(names=['x'], restore=False) as tv_outer:\n" + " _t_in_1_before_2_x_absent = 'x' not in dir()\n" + " _t_in_1_before_2_y_present = 'y' in dir()\n" + " _t_in_1_before_2_z_present = 'z' in dir()\n" + " with TempVars(names=['y'], restore=True) as tv_inner:\n" + " _t_in_12_x_absent = 'x' not in dir()\n" + " _t_in_12_y_absent = 'y' not in dir()\n" + " _t_in_12_z_present = 'z' in dir()\n" + " _t_in_1_after_2_x_absent = 'x' not in dir()\n" + " _t_in_1_after_2_y_present = 'y' in dir()\n" + " _t_in_1_after_2_z_present = 'z' in dir()\n" + "_t_after_12_x_absent = 'x' not in dir()\n" + "_t_after_12_y_present= 'y' in dir()\n" + "_t_after_12_z_present = 'z' in dir()\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def test_Good_NonMutableNamesStartsEndsArgs(self): @@ -489,26 +523,28 @@ def test_Good_NonMutableNamesStartsEndsArgs(self): # Ensure self.d is actually getting cleared/reset assert len(self.d) == 0 - exec("from tempvars import TempVars\n" - "n = ['xyz', 'fnq']\n" - "s = ['abc_', 'qrs_']\n" - "e = ['_mnp', '_fzr']\n" - "_t_before_n_len = len(n) == 2\n" - "_t_before_s_len = len(s) == 2\n" - "_t_before_e_len = len(e) == 2\n" - "t_fzr = 6\n" - "xyz = 12\n" - "abc_x = 35\n" - "with TempVars(names=n, starts=s, ends=e) as tv:\n" - " _t_inside_n_len = len(n) == 2\n" - " _t_inside_s_len = len(s) == 2\n" - " _t_inside_e_len = len(e) == 2\n" - "_t_outside_n_len = len(n) == 2\n" - "_t_outside_s_len = len(s) == 2\n" - "_t_outside_e_len = len(e) == 2\n", - self.d) - - for _ in [__ for __ in self.d if __.startswith('_t_')]: + exec( + "from tempvars import TempVars\n" + "n = ['xyz', 'fnq']\n" + "s = ['abc_', 'qrs_']\n" + "e = ['_mnp', '_fzr']\n" + "_t_before_n_len = len(n) == 2\n" + "_t_before_s_len = len(s) == 2\n" + "_t_before_e_len = len(e) == 2\n" + "t_fzr = 6\n" + "xyz = 12\n" + "abc_x = 35\n" + "with TempVars(names=n, starts=s, ends=e) as tv:\n" + " _t_inside_n_len = len(n) == 2\n" + " _t_inside_s_len = len(s) == 2\n" + " _t_inside_e_len = len(e) == 2\n" + "_t_outside_n_len = len(n) == 2\n" + "_t_outside_s_len = len(s) == 2\n" + "_t_outside_e_len = len(e) == 2\n", + self.d, + ) + + for _ in [__ for __ in self.d if __.startswith("_t_")]: self.locals_subTest(_, self.d, True) def runtest_Good_NoNamesDupes(self, code): @@ -518,7 +554,7 @@ def runtest_Good_NoNamesDupes(self, code): exec(code, self.d) - for _ in ['nsvars_len_one', 'ret_tempvars_len_one']: + for _ in ["nsvars_len_one", "ret_tempvars_len_one"]: self.locals_subTest(_, self.d, True) def test_Good_NoNamesDupes_NamesDuplicatesPassed(self): @@ -530,7 +566,7 @@ def test_Good_NoNamesDupes_NamesDuplicatesPassed(self): " nsvars_len_one = len(tv.stored_nsvars) == 1\n" " t_y = 35\n" "ret_tempvars_len_one = len(tv.retained_tempvars) == 1\n" - ) + ) def test_Good_NoNamesDupes_StartsMultiMatch(self): """Confirm no dupe var names stored if `starts` matches repeatedly.""" @@ -541,7 +577,7 @@ def test_Good_NoNamesDupes_StartsMultiMatch(self): " nsvars_len_one = len(tv.stored_nsvars) == 1\n" " t_y_f = 35\n" "ret_tempvars_len_one = len(tv.retained_tempvars) == 1\n" - ) + ) def test_Good_NoNamesDupes_EndsMultiMatch(self): """Confirm no dupe var names stored if `ends` matches repeatedly.""" @@ -552,7 +588,7 @@ def test_Good_NoNamesDupes_EndsMultiMatch(self): " nsvars_len_one = len(tv.stored_nsvars) == 1\n" " t_y_f = 35\n" "ret_tempvars_len_one = len(tv.retained_tempvars) == 1\n" - ) + ) def test_Good_NoNamesDupes_StartsEndsBothMatch(self): """Confirm no dupe var names stored if `starts` & `ends` both match.""" @@ -563,7 +599,7 @@ def test_Good_NoNamesDupes_StartsEndsBothMatch(self): " nsvars_len_one = len(tv.stored_nsvars) == 1\n" " t_y = 35\n" "ret_tempvars_len_one = len(tv.retained_tempvars) == 1\n" - ) + ) def test_Good_NoNamesDupes_NamesStartsBothMatch(self): """Confirm no dupe varnames stored if `starts` & `names` both match.""" @@ -574,7 +610,7 @@ def test_Good_NoNamesDupes_NamesStartsBothMatch(self): " nsvars_len_one = len(tv.stored_nsvars) == 1\n" " t_y = 35\n" "ret_tempvars_len_one = len(tv.retained_tempvars) == 1\n" - ) + ) def test_Good_NoNamesDupes_NamesEndsBothMatch(self): """Confirm no dupe var names stored if `names` & `ends` both match.""" @@ -585,7 +621,7 @@ def test_Good_NoNamesDupes_NamesEndsBothMatch(self): " nsvars_len_one = len(tv.stored_nsvars) == 1\n" " t_y = 35\n" "ret_tempvars_len_one = len(tv.retained_tempvars) == 1\n" - ) + ) def test_Good_NoNamesDupes_NamesStartsEndsAllMatch(self): """Confirm no dupe var names if `names`/`starts`/`ends` all match.""" @@ -597,17 +633,17 @@ def test_Good_NoNamesDupes_NamesStartsEndsAllMatch(self): " nsvars_len_one = len(tv.stored_nsvars) == 1\n" " t_y_f = 35\n" "ret_tempvars_len_one = len(tv.retained_tempvars) == 1\n" - ) + ) class TestTempVarsExpectFail(SuperTestTempVars, ut.TestCase): """Testing that code raises expected errors when invoked improperly.""" - list_args = ['names', 'starts', 'ends'] + list_args = ["names", "starts", "ends"] def test_Fail_ArgIsNotListOrNone(self): """Confirm `TypeError` if non-list passed to var arg.""" - code = 'from tempvars import TempVars; TempVars({0}=1)' + code = "from tempvars import TempVars; TempVars({0}=1)" for arg in self.list_args: with self.subTest(arg): @@ -623,27 +659,32 @@ def test_Fail_ArgListHasNonString(self): def test_Fail_UnderArgs(self): """Confirm `ValueError` on (d)under `starts`/`ends` patterns.""" - code = ('from tempvars import TempVars; ' - 'TempVars({0}=["abc", "{1}", "pqr"])') - - for arg in ['starts', 'ends']: - for val in ['_', '__']: - with self.subTest('{0}-{1}'.format(arg, val)): - self.assertRaises(ValueError, exec, - code.format(arg, val), {}) - - with self.subTest('starts-any-dunder-start'): - self.assertRaises(ValueError, exec, - code.format('starts', '__d'), {}) + code = ( + "from tempvars import TempVars; " + 'TempVars({0}=["abc", "{1}", "pqr"])' + ) + + for arg in ["starts", "ends"]: + for val in ["_", "__"]: + with self.subTest("{0}-{1}".format(arg, val)): + self.assertRaises( + ValueError, exec, code.format(arg, val), {} + ) + + with self.subTest("starts-any-dunder-start"): + self.assertRaises( + ValueError, exec, code.format("starts", "__d"), {} + ) - with self.subTest('ends-any-dunder-end'): - self.assertRaises(ValueError, exec, - code.format('ends', 's__'), {}) + with self.subTest("ends-any-dunder-end"): + self.assertRaises(ValueError, exec, code.format("ends", "s__"), {}) def test_Fail_NonBooleanRestore(self): """Confirm `TypeError` if non-boolean `restore` is passed.""" - code = ('from tempvars import TempVars; ' - 'TempVars(names=["abc"], restore=1)') + code = ( + "from tempvars import TempVars; " + 'TempVars(names=["abc"], restore=1)' + ) self.assertRaises(TypeError, exec, code, {}) @@ -652,16 +693,14 @@ def test_Fail_NonGlobalScope(self): from tempvars import TempVars with self.assertRaises(RuntimeError): - with TempVars(names=['abcd']): - pass # pragma: no cover + with TempVars(names=["abcd"]): + pass # pragma: no cover def test_Fail_NoPatternArgsWarning(self): """Confirm `RuntimeWarning` if no pattern arguments are passed.""" code = ( - "from tempvars import TempVars\n" - "with TempVars():\n" - " pass\n" - ) + "from tempvars import TempVars\n" "with TempVars():\n" " pass\n" + ) with self.assertWarns(RuntimeWarning): exec(code, self.d) @@ -669,11 +708,11 @@ def test_Fail_NoPatternArgsWarning(self): def test_Fail_EmptyListArgWarnings(self): """Confirm `RuntimeWarning` if empty list passed.""" code = ( - "from tempvars import TempVars\n" - "with TempVars(**{{'{0}': []}}):\n" - " pass\n" - ) - for _ in ['names', 'starts', 'ends']: + "from tempvars import TempVars\n" + "with TempVars(**{{'{0}': []}}):\n" + " pass\n" + ) + for _ in ["names", "starts", "ends"]: self.d = {} with self.subTest(_): with self.assertWarns(RuntimeWarning): @@ -681,16 +720,16 @@ def test_Fail_EmptyListArgWarnings(self): # Doctest suite for testing README.rst example code -SuiteDoctestReadme = dt.DocFileSuite('README.rst', - module_relative=False) +SuiteDoctestReadme = dt.DocFileSuite("README.rst", module_relative=False) def suite_expect_good(): """Create and return the test suite for expect-good cases.""" s = ut.TestSuite() tl = ut.TestLoader() - s.addTests([tl.loadTestsFromTestCase(TestTempVarsExpectGood), - SuiteDoctestReadme]) + s.addTests( + [tl.loadTestsFromTestCase(TestTempVarsExpectGood), SuiteDoctestReadme] + ) return s @@ -704,5 +743,5 @@ def suite_expect_fail(): return s -if __name__ == '__main__': # pragma: no cover +if __name__ == "__main__": # pragma: no cover print("Module not executable.") diff --git a/tests.py b/tests.py index add7841..9471ac6 100644 --- a/tests.py +++ b/tests.py @@ -20,9 +20,10 @@ class AP(object): Also includes PFX, a helper string for substitution/formatting. """ - ALL = 'all' - GOOD = 'good' - FAIL = 'fail' + + ALL = "all" + GOOD = "good" + FAIL = "fail" PFX = "--{0}" @@ -34,19 +35,27 @@ def get_parser(): prs = argparse.ArgumentParser(description="Run tests for tempvars") # Verbosity argument - prs.add_argument('-v', action='store_true', - help="Show verbose output") + prs.add_argument("-v", action="store_true", help="Show verbose output") # Groups without subgroups - prs.add_argument(AP.PFX.format(AP.ALL), '-a', - action='store_true', - help="Run all tests (overrides any other selections)") - prs.add_argument(AP.PFX.format(AP.GOOD), '-g', - action='store_true', - help="Run all expect-good tests") - prs.add_argument(AP.PFX.format(AP.FAIL), '-f', - action='store_true', - help="Run all expect-fail tests") + prs.add_argument( + AP.PFX.format(AP.ALL), + "-a", + action="store_true", + help="Run all tests (overrides any other selections)", + ) + prs.add_argument( + AP.PFX.format(AP.GOOD), + "-g", + action="store_true", + help="Run all expect-good tests", + ) + prs.add_argument( + AP.PFX.format(AP.FAIL), + "-f", + action="store_true", + help="Run all expect-fail tests", + ) # Return the parser return prs @@ -80,21 +89,21 @@ def addsuiteif(suite, flags): # Commandline tests per-group # Expect-good tests - addsuiteif(tempvars.test.tempvars_base.suite_expect_good(), - [AP.ALL, AP.GOOD]) + addsuiteif( + tempvars.test.tempvars_base.suite_expect_good(), [AP.ALL, AP.GOOD] + ) # Expect-fail tests - addsuiteif(tempvars.test.tempvars_base.suite_expect_fail(), - [AP.ALL, AP.FAIL]) + addsuiteif( + tempvars.test.tempvars_base.suite_expect_fail(), [AP.ALL, AP.FAIL] + ) # Create the test runner and execute - ttr = ut.TextTestRunner(buffer=True, - verbosity=(2 if params['v'] else 1)) + ttr = ut.TextTestRunner(buffer=True, verbosity=(2 if params["v"] else 1)) success = ttr.run(ts).wasSuccessful() # Return based on success result (enables tox) sys.exit(0 if success else 1) -if __name__ == '__main__': +if __name__ == "__main__": main() - From 1d3c9f9e530ac705fd72fc59aa03ba7aba929b44 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Mon, 12 Nov 2018 22:00:05 -0500 Subject: [PATCH 07/20] CLEANUP: Start comments rework Changed the API autodoc around -- need to confirm it still works, fix if not. ISPHX_LOCAL not working correctly on termux in newly cloned repo, even with FALSE -- may be a mismatch between a now-deprecated setup in conf.py and sphinx v1.8.2(?) currently installed? [skip ci] --- doc/source/api.rst | 2 +- tempvars/__init__.py | 43 +++++++++++++++++++++++-------------------- tempvars/tempvars.py | 41 +++++++++++++++++++++++++---------------- 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/doc/source/api.rst b/doc/source/api.rst index aabc520..f64a1b9 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -3,4 +3,4 @@ tempvars API ============ -.. automodule:: tempvars \ No newline at end of file +.. autoclass:: tempvars.TempVars \ No newline at end of file diff --git a/tempvars/__init__.py b/tempvars/__init__.py index c257d0e..6a03f38 100644 --- a/tempvars/__init__.py +++ b/tempvars/__init__.py @@ -1,22 +1,25 @@ -# ------------------------------------------------------------------------------ -# Name: __init__.py -# Purpose: Package information for tempvars -# -# Author: Brian Skinn -# bskinn@alum.mit.edu -# -# Created: 10 Sep 2017 -# Copyright: (c) Brian Skinn 2017 -# License: The MIT License; see "LICENSE.txt" for full license terms. -# -# https://www.github.com/bskinn/tempvars -# -# ------------------------------------------------------------------------------ - -"""Base module of ``tempvars`` package. - -.. autoclass:: TempVars - :members: +r"""*Core package definition module for* ``tempvars``. + +Context manager for handling temporary variables in +Jupyter Notebook, IPython, etc. + +**Author** + Brian Skinn (bskinn@alum.mit.edu) + +**File Created** + 10 Sep 2017 + +**Copyright** + \(c) Brian Skinn 2017-2018 + +**Source Repository** + http://www.github.com/bskinn/tempvars + +**Documentation** + http://tempvars.readthedocs.io + +**License** + The MIT License; see |license_txt|_ for full license terms """ @@ -26,4 +29,4 @@ from .tempvars import TempVars -__version__ = "1.0" +__version__ = "1.0.1.dev1" diff --git a/tempvars/tempvars.py b/tempvars/tempvars.py index fab6498..e61e518 100644 --- a/tempvars/tempvars.py +++ b/tempvars/tempvars.py @@ -1,19 +1,28 @@ -# ------------------------------------------------------------------------------ -# Name: tempvars.py -# Purpose: Module defining the TempVars class -# -# Author: Brian Skinn -# bskinn@alum.mit.edu -# -# Created: 10 Sep 2017 -# Copyright: (c) Brian Skinn 2017 -# License: The MIT License; see "LICENSE.txt" for full license terms. -# -# https://www.github.com/bskinn/tempvars -# -# ------------------------------------------------------------------------------ - -"""Core module defining the TempVars class.""" +r"""``TempVars`` *class definition.* + +This module is part of ``tempvars``, +a context manager for handling temporary variables in +Jupyter Notebook, IPython, etc. + +**Author** + Brian Skinn (bskinn@alum.mit.edu) + +**File Created** + 10 Sep 2017 + +**Copyright** + \(c) Brian Skinn 2017-2018 + +**Source Repository** + http://www.github.com/bskinn/tempvars + +**Documentation** + http://tempvars.readthedocs.io + +**License** + The MIT License; see |license_txt|_ for full license terms + +""" import attr From 93da6e15fa39d55ab8b6e4abb8c13bfbcb383dc0 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 13 Nov 2018 07:02:05 -0500 Subject: [PATCH 08/20] ADMIN: Tests, Travis, various others * Switch Travis to coverage; add codecov * Add codecov, coverage to req'ts-travis (part of #27) * Add coverage to req'ts-dev * Change attrs ref to https * Change all module header comments to docstrings (part of #30) --- .travis.yml | 3 ++- doc/source/conf.py | 2 +- requirements-dev.txt | 1 + requirements-travis.txt | 2 ++ tempvars/tempvars.py | 2 +- tempvars/test/__init__.py | 40 ++++++++++++++++++++-------------- tempvars/test/tempvars_base.py | 39 +++++++++++++++++++-------------- tests.py | 38 ++++++++++++++++++++------------ 8 files changed, 78 insertions(+), 49 deletions(-) diff --git a/.travis.yml b/.travis.yml index 17db2fe..53a21fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,8 @@ python: - 3.6 - 3.7-dev script: - - python tests.py -a + - coverage run tests.py -a - flake8 tempvars + - echo $TRAVIS_PYTHON_VERSION | grep -e '^3\.6' && codecov || echo "No codecov." - sh -c 'cd doc; make doctest' diff --git a/doc/source/conf.py b/doc/source/conf.py index e532157..54359f4 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -276,5 +276,5 @@ def isphx_subst(s): intersphinx_mapping = { 'python': ('https://docs.python.org/3.5', isphx_subst('python')), - 'attrs': ('http://www.attrs.org/en/stable', isphx_subst('attrs')) + 'attrs': ('https://www.attrs.org/en/stable', isphx_subst('attrs')) } diff --git a/requirements-dev.txt b/requirements-dev.txt index f846036..eed04c8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,6 @@ attrs>=17 black +coverage flake8 flake8-docstrings restview diff --git a/requirements-travis.txt b/requirements-travis.txt index dcfeb89..8175f0a 100644 --- a/requirements-travis.txt +++ b/requirements-travis.txt @@ -1,4 +1,6 @@ attrs>=17 +codecov +coverage flake8 flake8-docstrings sphinx diff --git a/tempvars/tempvars.py b/tempvars/tempvars.py index e61e518..d2600e5 100644 --- a/tempvars/tempvars.py +++ b/tempvars/tempvars.py @@ -1,4 +1,4 @@ -r"""``TempVars`` *class definition.* +r"""``TempVars`` *class definition*. This module is part of ``tempvars``, a context manager for handling temporary variables in diff --git a/tempvars/test/__init__.py b/tempvars/test/__init__.py index 02a8f1a..b79199c 100644 --- a/tempvars/test/__init__.py +++ b/tempvars/test/__init__.py @@ -1,19 +1,27 @@ -# ------------------------------------------------------------------------------ -# Name: __init__ -# Purpose: Package submodule definition for the test suite -# -# Author: Brian Skinn -# bskinn@alum.mit.edu -# -# Created: 11 Sep 2017 -# Copyright: (c) Brian Skinn 2017 -# License: The MIT License; see "LICENSE.txt" for full license terms. -# -# https://www.github.com/bskinn/tempvars -# -# ------------------------------------------------------------------------------ - -"""Base submodule for the tempvars test suite.""" +r"""*Testing submodule definition for ``tempvars``. + +Context manager for handling temporary variables in +Jupyter Notebook, IPython, etc. + +**Author** + Brian Skinn (bskinn@alum.mit.edu) + +**File Created** + 11 Sep 2017 + +**Copyright** + \(c) Brian Skinn 2017-2018 + +**Source Repository** + http://www.github.com/bskinn/tempvars + +**Documentation** + http://tempvars.readthedocs.io + +**License** + The MIT License; see |license_txt|_ for full license terms + +""" from __future__ import absolute_import diff --git a/tempvars/test/tempvars_base.py b/tempvars/test/tempvars_base.py index 16de3cd..822383e 100644 --- a/tempvars/test/tempvars_base.py +++ b/tempvars/test/tempvars_base.py @@ -1,20 +1,27 @@ -# ------------------------------------------------------------------------------ -# Name: tempvars_base -# Purpose: Base module for tempvars tests -# -# Author: Brian Skinn -# bskinn@alum.mit.edu -# -# Created: 11 Sep 2017 -# Copyright: (c) Brian Skinn 2017 -# License: The MIT License; see "LICENSE.txt" for full license terms. -# -# https://www.github.com/bskinn/tempvars -# -# ------------------------------------------------------------------------------ - -"""Base module for tempvars tests.""" +r"""Core tests module for ``tempvars``. +Context manager for handling temporary variables in +Jupyter Notebook, IPython, etc. + +**Author** + Brian Skinn (bskinn@alum.mit.edu) + +**File Created** + 11 Sep 2017 + +**Copyright** + \(c) Brian Skinn 2017-2018 + +**Source Repository** + http://www.github.com/bskinn/tempvars + +**Documentation** + http://tempvars.readthedocs.io + +**License** + The MIT License; see |license_txt|_ for full license terms + +""" import doctest as dt import unittest as ut diff --git a/tests.py b/tests.py index 9471ac6..365f88c 100644 --- a/tests.py +++ b/tests.py @@ -1,17 +1,27 @@ -# ------------------------------------------------------------------------------ -# Name: tests -# Purpose: Master script for tempvars testing suite -# -# Author: Brian Skinn -# bskinn@alum.mit.edu -# -# Created: 11 Sep 2017 -# Copyright: (c) Brian Skinn 2017 -# License: The MIT License; see "LICENSE.txt" for full license terms. -# -# http://www.github.com/bskinn/tempvars -# -# ------------------------------------------------------------------------------ +r"""*Test runner module for* ``tempvars``. + +Context manager for handling temporary variables in +Jupyter Notebook, IPython, etc. + +**Author** + Brian Skinn (bskinn@alum.mit.edu) + +**File Created** + 11 Sep 2017 + +**Copyright** + \(c) Brian Skinn 2017-2018 + +**Source Repository** + http://www.github.com/bskinn/tempvars + +**Documentation** + http://tempvars.readthedocs.io + +**License** + The MIT License; see |license_txt|_ for full license terms + +""" class AP(object): From 5f64436ef125174b2935203d01a28131fbd53164 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 13 Nov 2018 10:01:44 -0500 Subject: [PATCH 09/20] ADMIN: Add .coveragerc Closes #34 --- .coveragerc | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..f6ec396 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,13 @@ +[run] +omit = + # Don't do coverage on test code + tempvars/test/* + tests.py + + # Don't cover code in the env + env/* + +[report] +exclude_lines = + pragma: no cover + ^\s*pass\s*$ From 1b4e8614f97185c0ad7c31c71273b2e7c77acd2b Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 13 Nov 2018 14:30:56 -0500 Subject: [PATCH 10/20] REFAC: Switch to copy.copy in initalization Since copy(None) just returns None again, there was no need for the custom function. Closes #28, as long as earlier versions tolerate the change. --- tempvars/tempvars.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tempvars/tempvars.py b/tempvars/tempvars.py index d2600e5..a559bb4 100644 --- a/tempvars/tempvars.py +++ b/tempvars/tempvars.py @@ -161,13 +161,9 @@ def _ns_default(self): def __attrs_post_init__(self): """Process arguments post-init in various ways.""" + from copy import copy import warnings - def copy_if_not_none(v): - """Return a copy of the input argument if it's not None.""" - # This relies on `v` being a finite-size iterable if isn't None - return v if v is None else v[:] - # Trigger a warning if no patterns were passed if all( map( @@ -182,9 +178,9 @@ def copy_if_not_none(v): ) # Copy any arguments that aren't None - self.names = copy_if_not_none(self.names) - self.starts = copy_if_not_none(self.starts) - self.ends = copy_if_not_none(self.ends) + self.names = copy(self.names) + self.starts = copy(self.starts) + self.ends = copy(self.ends) def _pop_to(self, dest_dict, patterns, test_fxn): """Pop matching namespace members to a storage dict. From 17a0ab19fd776d0c65ef720d2d7406500a658962 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 13 Nov 2018 16:32:49 -0500 Subject: [PATCH 11/20] ADMIN: README & docs work README: * Add badges (closes #27; bullet of #31) * Add two-question header (bullet of #31) * Footer revised to match other projects (bullet of #31) DOC: Add :members: to autoclass for TempVars so that members get documented. --- README.rst | 97 ++++++++++++++++++++++++++++++++-------------- doc/source/api.rst | 3 +- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/README.rst b/README.rst index 8603089..6fc0391 100644 --- a/README.rst +++ b/README.rst @@ -1,26 +1,62 @@ -tempvars --------- - -*A context manager for handling temporary variables in Jupyter Notebook, -IPython, etc.* - -A frustrating aspect of working with Jupyter notebooks -is debugging a worksheet for half an hour -and discovering a carried-over variable name was hanging around -in the notebook namespace and causing -the misbehavior, or opening a notebook that "worked fine" the last -time it was used because of random variables lingering in the -namespace. The ``TempVars`` context manager avoids these pitfalls by +tempvars: A context manager for handling temporary variables +============================================================ + +**Current Development Version:** + +.. image:: https://travis-ci.org/bskinn/tempvars.svg?branch=dev + :target: https://travis-ci.org/bskinn/tempvars + +.. image:: https://codecov.io/gh/bskinn/tempvars/branch/dev/graph/badge.svg + :target: https://codecov.io/gh/bskinn/tempvars + +**Most Recent Stable Release:** + +.. image:: https://img.shields.io/pypi/v/tempvars.svg + :target: https://pypi.org/project/tempvars + +.. image:: https://img.shields.io/pypi/pyversions/tempvars.svg + +**Info:** + +.. image:: https://img.shields.io/readthedocs/tempvars/latest.svg + :target: http://tempvars.readthedocs.io/en/latest/ + +.. image:: https://img.shields.io/github/license/mashape/apistatus.svg + :target: https://github.com/bskinn/tempvars/blob/master/LICENSE.txt + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +---- + +**Use Jupyter Notebook?** + +**Constantly run into problems from obsolete variables hanging around +in the namespace?** + +``tempvars`` *can help.* + +Jupyter notebooks can be frustrating. +E.g., debugging a worksheet for half an hour, only to discover +that a carried-over variable name was hanging around +in the notebook namespace and causing problems. +Or, opening a notebook that only "worked fine" the last +time it was used because of random, obsolete variables that happened +to be lingering in the namespace. + +``TempVars`` is a context manager that helps to avoid these pitfalls by clearing selected identifiers from the namespace for the duration of -the ``with`` suite, then restoring them afterwards (or not, if desired). +its scope, then restoring them afterwards (or not, if desired). Further, any variables created within the managed context -that match the criteria passed to ``TempVars`` are removed from +that match the ``TempVars`` filtering criteria are removed from the namespace upon exiting, ensuring these values do not spuriously -contribute to following code. For convenience, all variables +contribute to following code. + +For convenience, all variables that were removed from the namespace at both entry and exit are stored with their values for later reference (see example code below). -**NOTE:** Due to the way Python handles non-global variable scopes, ``TempVars`` +Due to the way Python handles non-global scopes, ``TempVars`` can only be used at the global scope. *Any attempt to use* ``TempVars`` *in non-global contexts will result in a* ``RuntimeError``. Viable use-cases include Jupyter notebooks, @@ -30,7 +66,7 @@ imported modules. Preliminary testing indicates it also works with it may be less helpful there due to the step-local scoping paradigm used (shared values must be passed around via ``cauldron.shared``). -**NOTE ALSO** that ``tempvars`` is *Python 3 only*. +---- After installing with ``pip install tempvars``, import as: @@ -40,6 +76,12 @@ After installing with ``pip install tempvars``, import as: Example usage: + * Screening pre-existing variables, that are restored afterward + * Screening vars, then *not* restoring them + * Clearing vars created in context that match, after exiting + * Demonstrating how vars are stored either in ``.stored_nsvars`` + or in ``.retained_tempvars`` + .. code:: python >>> t_var1 = 5 @@ -82,24 +124,21 @@ Example usage: {'t_var3': -7} -Administrative --------------- +---- + -Branches named with the prefix ``v.`` are volatile. The history there -may be rewritten dramatically, without warning. +Available on `PyPI `__: ``pip install tempvars``. -Available on PyPI: ``pip install tempvars``. +Full documentation at +`Read the Docs `__. -Source on `GitHub `__. Bug reports -and feature requests are welcomed at the +Source on `GitHub `__. +Bug reports and feature requests are welcomed at the `Issues `__ page there. If you like the idea of an enhancement already in the Issues list, please comment to say so; it'll help with prioritization. -Full documentation at -`Read the Docs `__. - -Copyright (c) Brian Skinn 2017 +Copyright (c) Brian Skinn 2017-2018 License: The MIT License. See `LICENSE.txt `__ for full license terms. diff --git a/doc/source/api.rst b/doc/source/api.rst index f64a1b9..5aeafd2 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -3,4 +3,5 @@ tempvars API ============ -.. autoclass:: tempvars.TempVars \ No newline at end of file +.. autoclass:: tempvars.TempVars + :members: \ No newline at end of file From 52d877bfbb15db77cf191a16763bd53b4e19998d Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 13 Nov 2018 17:06:21 -0500 Subject: [PATCH 12/20] DOC: Add 'recommended std usage' to usage.rst Also, revise the way the TOC is presented so that it doesn't lead to weirdness in the RtD sidebar TOC. --- doc/source/usage.rst | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/doc/source/usage.rst b/doc/source/usage.rst index 79190a6..1307646 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -31,14 +31,36 @@ one or more of |arg_names|_, |arg_starts|_, and/or |arg_ends|_ are .. _usage_toc: -Table of Contents -~~~~~~~~~~~~~~~~~ +**Contents** .. contents:: :local: :backlinks: top +.. _recommended_standard_usage: + +Recommended Standard Usage +-------------------------- + +This author's standard approach for using :class:`~tempvars.TempVars` +is to make use of the `starts` argument as follows: + +.. doctest:: recommended + + >>> with TempVars(starts=['t_']): + ... t_foo = foo + ... t_baz = foo + bar + >>> print('t_foo' in dir()) + False + >>> print('t_baz' in dir()) + False + +As shown, any variable desired to be temporary can just be prefixed with +`t_`, and it will not survive beyond the scope of the relevant +:class:`~tempvars.TempVars` suite. + + Masking Specific Variables -------------------------- @@ -90,7 +112,8 @@ the |with| block: Masking Variables by Pattern ---------------------------- -Variables can also be masked by pattern matching. Currently, +As :ref:`noted above `, +variables can also be masked by pattern matching. Currently, only 'starts with' and 'ends with' matching styles are supported: .. doctest:: starts_ends_basic @@ -214,8 +237,7 @@ mutable arguments: .. doctest:: basic_binding_demo >>> names_in = ['foo'] - >>> with TempVars(names=names_in, starts=['baz', 'quux'], - ... ends=['ar']) as tv: + >>> with TempVars(names=names_in, starts=['baz', 'quux'], ends=['ar']) as tv: ... print(tv.starts) ... print(tv.ends) ... print(tv.names) From a38d44c1977d506ca9823bbbd6ac334638b66ff3 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 13 Nov 2018 22:48:54 -0500 Subject: [PATCH 13/20] DOC: Minor buff [skip ci] --- doc/source/usage.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/usage.rst b/doc/source/usage.rst index 1307646..9fd2701 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -44,13 +44,15 @@ Recommended Standard Usage -------------------------- This author's standard approach for using :class:`~tempvars.TempVars` -is to make use of the `starts` argument as follows: +is via the `starts` argument as follows: .. doctest:: recommended >>> with TempVars(starts=['t_']): ... t_foo = foo ... t_baz = foo + bar + ... print(t_foo + t_baz) + 4 >>> print('t_foo' in dir()) False >>> print('t_baz' in dir()) From c4b8f1fa162084b13c1e8f88525f29c1a883099d Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Tue, 13 Nov 2018 23:05:39 -0500 Subject: [PATCH 14/20] CLEANUP/REFAC: Fix comments, verbosity, redundancy Verbose comments either removed or shifted to docstring. Closes #30. Add blank lines for readability in the patterns validator. Refactor __enter__ and __exit__ to avoid repetition of the _pop_to calls. Eliminate the extra call to inspect.currentframe() in _ns_default. --- tempvars/tempvars.py | 71 ++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/tempvars/tempvars.py b/tempvars/tempvars.py index a559bb4..7c0cac1 100644 --- a/tempvars/tempvars.py +++ b/tempvars/tempvars.py @@ -103,13 +103,16 @@ def _var_pattern_validator(self, at, val): for s in val: if type(s) != str: raise te + if at.name != "names" and (s == "_" or s == "__"): raise ValueError( "'_' and '__' are not permitted " "for '{0}'".format(at.name) ) + if at.name == "starts" and s.startswith("__"): raise ValueError("'starts' may not start with '__'") + if at.name == "ends" and s.endswith("__"): raise ValueError("'ends' may not end with '__'") @@ -127,6 +130,18 @@ def _var_pattern_validator(self, at, val): @_ns.default def _ns_default(self): + """Assign the globals() namespace of the instantiating scope. + + This "default value" needs to be crafted this way + because this way it's evaluated at run time, during instantiation. + Putting the globals() retrieval attempt directly in the attr.ib() + signature above would instead make the evaluation occur at + import/definition, which changes the relevant scope in a + significant way: it makes **this module** the scope of + the invocation of globals(), rather than the scope of + the instantiation call. + + """ import inspect # Need two f_back's since this call is inside a method that's @@ -138,15 +153,7 @@ def _ns_default(self): if fm.f_locals is not fm.f_globals: raise RuntimeError("TempVars can only be used in the global scope") - # This default needs to be crafted this way - # because it's evaluated at run time, during instantiation. - # Putting the globals() retrieval attempt directly in the attr.ib() - # signature above would make the evaluation occur at - # definition time(? at import?), which apparently changes - # the relevant scope in a significant way (likely it makes - # this module the accessed scope, rather than the scope of - # the instantiation call). - return inspect.currentframe().f_back.f_back.f_globals + return fm.f_globals # ## Internal vars, not set via the attrs __init__ #: |dict| container for preserving variables masked from @@ -160,11 +167,11 @@ def _ns_default(self): ) def __attrs_post_init__(self): - """Process arguments post-init in various ways.""" + """Proofread identifier-matching arguments and copy for safety.""" from copy import copy import warnings - # Trigger a warning if no patterns were passed + # Raise a warning if no patterns were passed if all( map( lambda a: a is None or len(a) == 0, @@ -191,11 +198,9 @@ def _pop_to(self, dest_dict, patterns, test_fxn): as the second argument. """ - for _ in list(self._ns.keys()): - # Pop the variable over to the destination dictionary if - # any ``map`` result is truthy. - if any(map(lambda p, k=_, t=test_fxn: t(k, p), patterns)): - dest_dict.update({_: self._ns.pop(_)}) + for key in list(self._ns.keys()): + if any(map(lambda p, k=key, t=test_fxn: t(k, p), patterns)): + dest_dict.update({key: self._ns.pop(key)}) def __enter__(self): """Context manager entry function. @@ -205,19 +210,14 @@ def __enter__(self): them in `self.stored_nsvars` for later reference. """ - # Pop variables if they match exactly anything in `names` - if self.names is not None: - self._pop_to(self.stored_nsvars, self.names, str.__eq__) + patterns = (self.names, self.starts, self.ends) + funcs = (str.__eq__, str.startswith, str.endswith) - # Pop variables if they match any starts-with pattern - if self.starts is not None: - self._pop_to(self.stored_nsvars, self.starts, str.startswith) + for p, f in zip(patterns, funcs): + if p is not None: + self._pop_to(self.stored_nsvars, p, f) - # Pop variables if they match any ends-with pattern - if self.ends is not None: - self._pop_to(self.stored_nsvars, self.ends, str.endswith) - - # Return instance so that users can inspect it if desired + # Return instance so that users can inspect/modify it if desired return self def __exit__(self, exc_type, exc_val, exc_tb): @@ -231,20 +231,13 @@ def __exit__(self, exc_type, exc_val, exc_tb): context must handle all errors. """ - # Pop variables if they match exactly anything in `names` - if self.names is not None: - self._pop_to(self.retained_tempvars, self.names, str.__eq__) - - # Pop variables if they match any starts-with pattern - if self.starts is not None: - self._pop_to(self.retained_tempvars, self.starts, str.startswith) + patterns = (self.names, self.starts, self.ends) + funcs = (str.__eq__, str.startswith, str.endswith) - # Pop variables if they match any ends-with pattern - if self.ends is not None: - self._pop_to(self.retained_tempvars, self.ends, str.endswith) + for p, f in zip(patterns, funcs): + if p is not None: + self._pop_to(self.retained_tempvars, p, f) - # If restore is set, then repopulate the namespace with - # the pre-existing values. Otherwise, do nothing. if self.restore: self._ns.update(self.stored_nsvars) From 91653c054b56c65e73ef8d759c0da082fc71a6ad Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Wed, 14 Nov 2018 09:39:04 -0500 Subject: [PATCH 15/20] ADMIN: Add auto-RTD-version update for dist build Closes #33. Also restructure setup.py for consistency with other projects, add py: 3 and py : 3 : 3.7 to classifiers, and implement auto-version-introspection for setup.py (#32). Still need to add version introspection to conf.py, though (#32). --- requirements-dev.txt | 1 + setup.py | 46 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index eed04c8..4ba50af 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,3 +7,4 @@ restview sphinx sphinx-rtd-theme tox +twine diff --git a/setup.py b/setup.py index 36ec25a..2400629 100644 --- a/setup.py +++ b/setup.py @@ -1,36 +1,62 @@ +import re from setuptools import setup +from tempvars import __version__ + +NAME = 'tempvars' def readme(): - with open("README.rst") as f: - return f.read() + with open('README.rst', 'r') as f: + content = f.read() + + # Helper function + def content_update(content, pattern, sub): + return re.sub(pattern, sub, content, flags=re.M | re.I) + + # Docs reference updates to current release version, for PyPI + # This one gets the badge image + content = content_update( + content, + r'(?<=/readthedocs/{0}/)\S+?(?=\.svg$)'.format(NAME), + 'v' + __version__) + + # This one gets the RtD links + content = content_update( + content, + r'(?<={0}\.readthedocs\.io/en/)\S+?(?=[/>])'.format(NAME), + 'v' + __version__) + + return content setup( name="tempvars", - version="1.0", - provides=["tempvars"], - install_requires=["attrs>=17"], - packages=["tempvars"], - url="https://www.github.com/bskinn/tempvars", - license="MIT License", - author="Brian Skinn", - author_email="bskinn@alum.mit.edu", + version=__version__, description=( "Context manager for handling temporary variables " "in Jupyter Notebook, IPython, etc." ), long_description=readme(), + url="https://www.github.com/bskinn/tempvars", + license="MIT License", + author="Brian Skinn", + author_email="bskinn@alum.mit.edu", + packages=["tempvars"], + provides=["tempvars"], + python_requires=">=3.4", + install_requires=["attrs>=17"], classifiers=[ "License :: OSI Approved :: MIT License", "Natural Language :: English", "Intended Audience :: Science/Research", "Intended Audience :: Developers", "Operating System :: OS Independent", + "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", "Topic :: Software Development", "Development Status :: 5 - Production/Stable", ], From fcb25ba4c6a97c42043b785207c0091995e5f202 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Wed, 14 Nov 2018 13:08:03 -0500 Subject: [PATCH 16/20] ADMIN: Re-draft README Closes #31. --- README.rst | 125 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/README.rst b/README.rst index 6fc0391..1294413 100644 --- a/README.rst +++ b/README.rst @@ -37,12 +37,15 @@ in the namespace?** ``tempvars`` *can help.* Jupyter notebooks can be frustrating. -E.g., debugging a worksheet for half an hour, only to discover -that a carried-over variable name was hanging around +For example, it's aggravating to debug a worksheet for half an hour, +only to discover that a carried-over variable name was hanging around in the notebook namespace and causing problems. -Or, opening a notebook that only "worked fine" the last -time it was used because of random, obsolete variables that happened +Or, to open a notebook that "worked fine" the last +time it was used, but only because of random, obsolete variables that happened to be lingering in the namespace. +Wrapping notebook code in functions/classes is an effective way of avoiding +these sorts of problems, but it's rarely effective or efficient to +do this in the initial exploratory phase of in-notebook development. ``TempVars`` is a context manager that helps to avoid these pitfalls by clearing selected identifiers from the namespace for the duration of @@ -50,11 +53,9 @@ its scope, then restoring them afterwards (or not, if desired). Further, any variables created within the managed context that match the ``TempVars`` filtering criteria are removed from the namespace upon exiting, ensuring these values do not spuriously -contribute to following code. - -For convenience, all variables -that were removed from the namespace at both entry and exit -are stored with their values for later reference (see example code below). +contribute to following code. For convenience, all variables +removed from the namespace at entry and exit +are stored for later reference (see example code below). Due to the way Python handles non-global scopes, ``TempVars`` can only be used at the global scope. *Any attempt @@ -63,7 +64,7 @@ result in a* ``RuntimeError``. Viable use-cases include Jupyter notebooks, the IPython and basic Python REPLs, and the outermost scope of executed and imported modules. Preliminary testing indicates it also works with `cauldron-notebook `__, though -it may be less helpful there due to the step-local scoping paradigm used +it may be less helpful there due to its step-local scoping paradigm (shared values must be passed around via ``cauldron.shared``). ---- @@ -74,55 +75,79 @@ After installing with ``pip install tempvars``, import as: >>> from tempvars import TempVars -Example usage: - - * Screening pre-existing variables, that are restored afterward - * Screening vars, then *not* restoring them - * Clearing vars created in context that match, after exiting - * Demonstrating how vars are stored either in ``.stored_nsvars`` - or in ``.retained_tempvars`` +For typical use in a Jupyter notebook cell, the recommended approach +is to pick a marker to use on all variables that are to be temporary, +and enclose the entire cell in a ``TempVars`` context. For example, +one could prefix all temporary variables with `t_` and make use +of the `starts` argument: .. code:: python - >>> t_var1 = 5 - >>> t_var2 = 7 - >>> x = 15 - >>> y = 20 - >>> with TempVars(names=['x']) as tv1: - ... with TempVars(starts=['t_'], restore=False) as tv2: - ... print('x' in dir()) - ... print('t_var1' in dir()) - ... print('t_var2' in dir()) - ... print(y) - ... print(tv1.stored_nsvars) - ... print(sorted(tv2.stored_nsvars.keys())) - ... print(tv2.stored_nsvars['t_var1']) - ... print(tv2.stored_nsvars['t_var2']) - ... x = -3 - ... t_var3 = -7 - ... print((x, t_var3, y)) - False - False + >>> foo = 5 + >>> with TempVars(starts=['t_']): + ... print(foo) + ... t_bar = 8 + ... print(foo + t_bar) + 5 + 13 + >>> print('t_bar' in dir()) False - 20 - {'x': 15} - ['t_var1', 't_var2'] + +A similar effect can be achieved with a suffix such as `_t` and +the `ends` argument. + +Temporary variable masking can also be introduced to existing +code in a more selective fashion via the `names` argument: + +.. code:: python + + >>> foo = 5 + >>> bar = 7 + >>> with TempVars(names=['bar']): + ... print(foo) + ... print('bar' in dir()) 5 - 7 - (-3, -7, 20) - >>> print((x, y)) - (15, 20) - >>> print('t_var1' in dir()) False - >>> print('t_var2' in dir()) + >>> print(foo * bar) + 35 + +Setting the `restore` argument to ``False`` instructs ``TempVars`` +not to restore any masked variables to the namespace after its +context exits. This is potentially useful to avoid carryover of +common helper variables (`arr`, `df`, `i`, etc.) to downstream cells +that may have been created earlier in a notebook: + +.. code:: python + + >>> for k in {'foo': 'bar', 'baz': 'quux'}: + ... print(k) + foo + baz + >>> with TempVars(names=['k'], restore=False): + ... print('k' in dir()) False - >>> print('t_var3' in dir()) + >>> print('k' in dir()) False - >>> print(tv1.retained_tempvars) - {'x': -3} - >>> print(tv2.retained_tempvars) - {'t_var3': -7} +``TempVars`` stores the values of variables it removes from the namespace, +should they need to be accessed. A bound `with`/`as` statement must be +used in order to enable this: + +.. code:: python + + >>> foo = 5 + >>> with TempVars(names=['foo']) as tv: + ... print('foo' in dir()) + ... print(tv.stored_nsvars['foo']) + ... foo = 8 + ... print(foo) + False + 5 + 8 + >>> print(foo) + 5 + >>> print(tv.retained_tempvars['foo']) + 8 ---- From 5069113a67ae7f246a3382607e293c8e2303dd82 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Wed, 14 Nov 2018 13:46:01 -0500 Subject: [PATCH 17/20] DOCFIX: Switch to ordered collection for README Iteration order on dicts not guaranteed for < 3.6. --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 1294413..d767c9e 100644 --- a/README.rst +++ b/README.rst @@ -119,10 +119,10 @@ that may have been created earlier in a notebook: .. code:: python - >>> for k in {'foo': 'bar', 'baz': 'quux'}: - ... print(k) - foo - baz + >>> for k in ['foo', 'bar']: + ... pass + >>> print(k) + bar >>> with TempVars(names=['k'], restore=False): ... print('k' in dir()) False From 0e19024768661c09b3ed19be54856fb70107fe46 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Wed, 14 Nov 2018 13:58:30 -0500 Subject: [PATCH 18/20] ADMIN: Convert conf.py to dynamic version setting Closes #32 --- doc/source/conf.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 54359f4..5125621 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -18,6 +18,7 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # import os +import re import sys sys.path.insert(0, os.path.abspath('../..')) @@ -55,18 +56,21 @@ master_doc = 'index' # General information about the project. -project = 'tempvars' -copyright = '2017, Brian Skinn' -author = 'Brian Skinn' +project = "tempvars" +copyright = "2017-2018, Brian Skinn" +author = "Brian Skinn" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # +from tempvars import __version__ + # The short X.Y version. -version = '1.0' +version = re.match(r"\d+\.\d+", __version__).group() + # The full version, including alpha/beta/rc tags. -release = '1.0' +release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 56deb3b80cb762b5a274bf9eb8fbf5201feec393 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Wed, 14 Nov 2018 14:04:37 -0500 Subject: [PATCH 19/20] README: Tweak language --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d767c9e..2e3bde6 100644 --- a/README.rst +++ b/README.rst @@ -36,7 +36,7 @@ in the namespace?** ``tempvars`` *can help.* -Jupyter notebooks can be frustrating. +Developing in Jupyter notebooks can sometimes be frustrating. For example, it's aggravating to debug a worksheet for half an hour, only to discover that a carried-over variable name was hanging around in the notebook namespace and causing problems. From 2ee1154cbc66787c15bc1f9a2cf345dd9fa60497 Mon Sep 17 00:00:00 2001 From: Brian Skinn Date: Wed, 14 Nov 2018 14:28:24 -0500 Subject: [PATCH 20/20] REL: Update copyright, etc. for release * Version bumped in __init__.py * CHANGELOG updated * Copyright date updated in LICENSE.txt * 'requires' field added in setup.py --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ LICENSE.txt | 2 +- setup.py | 16 ++++++++++------ tempvars/__init__.py | 2 +- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a11685..cf26637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,36 @@ with TempVars(...) as tv: ### [Unreleased] +... + + +### [1.0.1] - 2018-11-14 + +This release ***should not*** affect the behavior of the +package in any way. + +#### Refactored + + * Remove duplicative call to `inspect.currentframe()` in + `TempVars._ns_default` + * Replace custom `copy_if_not_none()` inner function with + built-in `copy.copy()`, in `__attrs_post_init__()` + * Refactor the temporary variable popping operations to + a more concise form in both `__enter__()` + and `__exit__()` + +#### Administrative + + * Switched to [`black`](https://github.com/ambv/black)-style + code formatting + * Switched source file headers from comment blocks to + module docstrings + * Relocated some commentary from code comments to method + docstrings + + +### [1.0.0] - 2017-10-19 + #### Added * `TempVars` now emits a warning if it is instantiated without diff --git a/LICENSE.txt b/LICENSE.txt index 16ed8fc..ac26218 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Brian Skinn +Copyright (c) 2017-2018 Brian Skinn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/setup.py b/setup.py index 2400629..dde984a 100644 --- a/setup.py +++ b/setup.py @@ -3,10 +3,11 @@ from tempvars import __version__ -NAME = 'tempvars' +NAME = "tempvars" + def readme(): - with open('README.rst', 'r') as f: + with open("README.rst", "r") as f: content = f.read() # Helper function @@ -17,14 +18,16 @@ def content_update(content, pattern, sub): # This one gets the badge image content = content_update( content, - r'(?<=/readthedocs/{0}/)\S+?(?=\.svg$)'.format(NAME), - 'v' + __version__) + r"(?<=/readthedocs/{0}/)\S+?(?=\.svg$)".format(NAME), + "v" + __version__, + ) # This one gets the RtD links content = content_update( content, - r'(?<={0}\.readthedocs\.io/en/)\S+?(?=[/>])'.format(NAME), - 'v' + __version__) + r"(?<={0}\.readthedocs\.io/en/)\S+?(?=[/>])".format(NAME), + "v" + __version__, + ) return content @@ -44,6 +47,7 @@ def content_update(content, pattern, sub): packages=["tempvars"], provides=["tempvars"], python_requires=">=3.4", + requires=["attrs (>=17.1)"], install_requires=["attrs>=17"], classifiers=[ "License :: OSI Approved :: MIT License", diff --git a/tempvars/__init__.py b/tempvars/__init__.py index 6a03f38..0f195af 100644 --- a/tempvars/__init__.py +++ b/tempvars/__init__.py @@ -29,4 +29,4 @@ from .tempvars import TempVars -__version__ = "1.0.1.dev1" +__version__ = "1.0.1"