diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d33cad..c22bb77 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,6 +17,14 @@ repos: args: [pipda] types: [python] language: system + - id: mypy + name: Run mypy + files: ^pipda/.+$ + pass_filenames: false + entry: mypy + args: [-p, pipda] + types: [python] + language: system - id: versionchecker name: Check version agreement in pyproject and __version__ entry: bash -c diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index daa21f7..0d6ca0d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 0.8.1 + +- ✨ Allow verb to be a placeholder (without any types registered) +- ✅ Add test for npufuncs to be used in verbs + ## 0.8.0 - patch classes if they have piping operator method diff --git a/pipda/__init__.py b/pipda/__init__.py index 6d75d80..71d7768 100644 --- a/pipda/__init__.py +++ b/pipda/__init__.py @@ -12,4 +12,4 @@ ) from .piping import register_piping -__version__ = "0.8.0" +__version__ = "0.8.1" diff --git a/pipda/verb.py b/pipda/verb.py index 87a1562..3ef21fa 100644 --- a/pipda/verb.py +++ b/pipda/verb.py @@ -141,8 +141,9 @@ def generic(__data, *args, **kwargs): self._generic = generic wrapped = singledispatch(self._generic) - update_wrapper(self, func) - update_wrapper(wrapped, func) + if callable(func): + update_wrapper(self, func) + update_wrapper(wrapped, func) update_user_wrapper( self, name=name, @@ -160,7 +161,8 @@ def generic(__data, *args, **kwargs): if types is not None: for t in types: - wrapped.register(t, func) + if callable(func): + wrapped.register(t, func) self.func = wrapped self.registry = wrapped.registry @@ -239,9 +241,22 @@ def register_verb( """Register a verb Args: + func: The function works as a verb. + If `None`, this function will return a decorator. + If non-callable, an auto-generated generic function that + raises `NotImplementedError()` will be used. This is useful when + you want to register a verb as a placeholder. We may not know + which types will come in. We can register the types later using + `Verb.register()`. types: The types of the data allowed to pipe in If `None`, then `func` is a generic function instead of an automatically created one to raise `NotImplementedError` by default + You can also use `object` to register a function, then any type + will be registered. + >>> f = register_verb(object, func=lambda x: x) + >>> f.registered(int) # True + >>> g = register_verb(None, func=lambda x: x) + >>> g.registered(int) # False context: The context to evaluate the arguments extra_contexts: Extra contexts to evaluate the keyword arguments name: and @@ -265,7 +280,6 @@ def register_verb( piping_warning - Suppose piping call, but show a warning normal_warning - Suppose normal call, but show a warning raise - Raise an error - func: The function works as a verb. """ if func is None: return lambda fun: register_verb( # type: ignore diff --git a/pyproject.toml b/pyproject.toml index ba38a54..d054064 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pipda" -version = "0.8.0" +version = "0.8.1" readme = "README.md" description = "A framework for data piping in python" authors = ["pwwang "] diff --git a/tests/test_verb.py b/tests/test_verb.py index af73c0f..211c083 100644 --- a/tests/test_verb.py +++ b/tests/test_verb.py @@ -225,6 +225,12 @@ def sum2(x): assert sum2.registered(int) +def test_register_non_callable(): + + fun = register_verb(None, func="none") + assert not fun.registered(object) + + def test_types_none(): @register_verb(None) @@ -291,3 +297,16 @@ def sum2(data, n): s = np.array([1, 2, 3]) >> sum2(1) assert s == 7 and isinstance(s, np.integer) + + +def test_npufunc_as_verb_args(): + + f = Symbolic() + + @register_verb(np.ndarray, context=Context.EVAL) + def add(data, n): + return data + n + + out = np.array([1, -2, 3]) >> add(np.abs(f[1])) + out = list(out) + assert out == [3, 0, 5] and isinstance(out, list)