--- /dev/null
+import types
+import functools
+
+from ._itertools import always_iterable
+
+
+def parameterize(names, value_groups):
+ """
+ Decorate a test method to run it as a set of subtests.
+
+ Modeled after pytest.parametrize.
+ """
+
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapped(self):
+ for values in value_groups:
+ resolved = map(Invoked.eval, always_iterable(values))
+ params = dict(zip(always_iterable(names), resolved))
+ with self.subTest(**params):
+ func(self, **params)
+
+ return wrapped
+
+ return decorator
+
+
+class Invoked(types.SimpleNamespace):
+ """
+ Wrap a function to be invoked for each usage.
+ """
+
+ @classmethod
+ def wrap(cls, func):
+ return cls(func=func)
+
+ @classmethod
+ def eval(cls, cand):
+ return cand.func() if isinstance(cand, cls) else cand
import pathlib
import unittest
import string
-import functools
+import pickle
+import itertools
+
+from ._test_params import parameterize, Invoked
+from ._functools import compose
+
from test.support.os_helper import temp_dir
return zf
-def pass_alpharep(meth):
- """
- Given a method, wrap it in a for loop that invokes method
- with each subtest.
- """
-
- @functools.wraps(meth)
- def wrapper(self):
- for alpharep in self.zipfile_alpharep():
- meth(self, alpharep=alpharep)
+alpharep_generators = [
+ Invoked.wrap(build_alpharep_fixture),
+ Invoked.wrap(compose(add_dirs, build_alpharep_fixture)),
+]
- return wrapper
+pass_alpharep = parameterize(['alpharep'], alpharep_generators)
class TestPath(unittest.TestCase):
self.fixtures = contextlib.ExitStack()
self.addCleanup(self.fixtures.close)
- def zipfile_alpharep(self):
- with self.subTest():
- yield build_alpharep_fixture()
- with self.subTest():
- yield add_dirs(build_alpharep_fixture())
-
def zipfile_ondisk(self, alpharep):
tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir()))
buffer = alpharep.fp
@pass_alpharep
def test_inheritance(self, alpharep):
cls = type('PathChild', (zipfile.Path,), {})
- for alpharep in self.zipfile_alpharep():
- file = cls(alpharep).joinpath('some dir').parent
- assert isinstance(file, cls)
+ file = cls(alpharep).joinpath('some dir').parent
+ assert isinstance(file, cls)
+
+ @parameterize(
+ ['alpharep', 'path_type', 'subpath'],
+ itertools.product(
+ alpharep_generators,
+ [str, pathlib.Path],
+ ['', 'b/'],
+ ),
+ )
+ def test_pickle(self, alpharep, path_type, subpath):
+ zipfile_ondisk = path_type(self.zipfile_ondisk(alpharep))
+
+ saved_1 = pickle.dumps(zipfile.Path(zipfile_ondisk, at=subpath))
+ restored_1 = pickle.loads(saved_1)
+ first, *rest = restored_1.iterdir()
+ assert first.read_text().startswith('content of ')
return itertools.filterfalse(set(subtrahend).__contains__, minuend)
-class CompleteDirs(zipfile.ZipFile):
+class InitializedState:
+ """
+ Mix-in to save the initialization state for pickling.
+ """
+
+ def __init__(self, *args, **kwargs):
+ self.__args = args
+ self.__kwargs = kwargs
+ super().__init__(*args, **kwargs)
+
+ def __getstate__(self):
+ return self.__args, self.__kwargs
+
+ def __setstate__(self, state):
+ args, kwargs = state
+ super().__init__(*args, **kwargs)
+
+
+class CompleteDirs(InitializedState, zipfile.ZipFile):
"""
A ZipFile subclass that ensures that implied directories
are always included in the namelist.