From f04c67d9605c87952aa1e64b20a0fc8c8ce4fa9a Mon Sep 17 00:00:00 2001 From: pwwang <1188067+pwwang@users.noreply.github.com> Date: Thu, 15 Dec 2022 10:51:49 -0700 Subject: [PATCH] 0.11.0 (#163) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 📝 Add testimonials in README.md * ✅ Fix test coverage * ✅ Fix test coverage * 📝 Add backend badges in README * 🐛 Load entrypoint plugins only when APIs are called * 💥 Rename `other` module to `misc` * 🔖 0.11.0 --- .coveragerc | 1 + README.md | 18 ++++++++++++++++++ datar/__init__.py | 2 +- datar/all.py | 3 ++- datar/apis/{other.py => misc.py} | 0 datar/base.py | 3 ++- datar/core/import_names_conflict.py | 3 ++- datar/core/load_plugins.py | 23 +++++++++++++++++++++++ datar/core/plugin.py | 26 ++------------------------ datar/data/__init__.py | 2 +- datar/dplyr.py | 3 ++- datar/forcats.py | 3 ++- datar/misc.py | 4 ++++ datar/other.py | 4 ---- datar/tibble.py | 3 ++- datar/tidyr.py | 3 ++- docs/CHANGELOG.md | 6 ++++++ poetry.lock | 14 +++++++------- pyproject.toml | 6 +++--- tests/test_array_ufunc.py | 4 ++-- tests/test_plugin.py | 20 ++++++++++---------- 21 files changed, 92 insertions(+), 59 deletions(-) rename datar/apis/{other.py => misc.py} (100%) create mode 100644 datar/core/load_plugins.py create mode 100644 datar/misc.py delete mode 100644 datar/other.py diff --git a/.coveragerc b/.coveragerc index 68773a741..6c35e9d9f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,3 +4,4 @@ exclude_lines = if TYPE_CHECKING: omit = datar/datasets.py + */site-packages/* diff --git a/README.md b/README.md index 557823373..925aa7149 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,13 @@ pip install -U datar[pandas] # More backends support will be added in the future ``` +## Backends + +|Repo|Badges| +|-|-| +|[datar-numpy][1]|![3] ![18]| +|[datar-pandas][2]|![4] ![19]| + ## Example usage ```python @@ -107,6 +114,15 @@ iris >> pull(f.Sepal_Length) >> dist_plot() ![example](./example2.png) +## Testimonials + +[@coforfe](https://github.com/coforfe): +> Thanks for your excellent package to port R (`dplyr`) flow of processing to Python. I have been using other alternatives, and yours is the one that offers the most extensive and equivalent to what is possible now with `dplyr`. + +[1]: https://github.com/pwwang/datar-numpy +[2]: https://github.com/pwwang/datar-pandas +[3]: https://img.shields.io/codacy/coverage/0a7519dad44246b6bab30576895f6766?style=flat-square +[4]: https://img.shields.io/codacy/coverage/45f4ea84ae024f1a8cf84be54dd144f7?style=flat-square [5]: https://pwwang.github.io/datar/ [6]: https://img.shields.io/pypi/v/datar?style=flat-square [7]: https://pypi.org/project/datar/ @@ -120,3 +136,5 @@ iris >> pull(f.Sepal_Length) >> dist_plot() [15]: https://pwwang.github.io/datar/reference-maps/ALL/ [16]: https://pwwang.github.io/datar/notebooks/across/ [17]: https://pwwang.github.io/datar/api/datar/ +[18]: https://img.shields.io/pypi/v/datar-numpy?style=flat-square +[19]: https://img.shields.io/pypi/v/datar-pandas?style=flat-square diff --git a/datar/__init__.py b/datar/__init__.py index c63ede7d8..50f6caf58 100644 --- a/datar/__init__.py +++ b/datar/__init__.py @@ -4,7 +4,7 @@ from .core.defaults import f from .core.options import options, get_option, options_context -__version__ = "0.10.3" +__version__ = "0.11.0" def get_versions(prnt: bool = True) -> _Mapping[str, str]: diff --git a/datar/all.py b/datar/all.py index cc5d73466..99cc02b02 100644 --- a/datar/all.py +++ b/datar/all.py @@ -2,6 +2,7 @@ _locs = locals() +from .core import load_plugins as _ from .core.defaults import f from .core.import_names_conflict import ( handle_import_names_conflict as _handle_import_names_conflict, @@ -12,7 +13,7 @@ from .forcats import * from .tibble import * from .tidyr import * -from .other import * +from .misc import * _locs.update( { diff --git a/datar/apis/other.py b/datar/apis/misc.py similarity index 100% rename from datar/apis/other.py rename to datar/apis/misc.py diff --git a/datar/base.py b/datar/base.py index c8d707149..4a445d136 100644 --- a/datar/base.py +++ b/datar/base.py @@ -1,4 +1,5 @@ -from .core.plugin import plugin as _plugin + +from .core.load_plugins import plugin as _plugin from .apis.base import * locals().update(_plugin.hooks.base_api()) diff --git a/datar/core/import_names_conflict.py b/datar/core/import_names_conflict.py index 2a0347cc1..334c4d430 100644 --- a/datar/core/import_names_conflict.py +++ b/datar/core/import_names_conflict.py @@ -27,7 +27,8 @@ def handle_import_names_conflict(imports, conflict_names): `sum` to `sum_`. """ _import_names_conflict = get_option("import_names_conflict") - if _import_names_conflict == "underscore_suffixed": + if _import_names_conflict == "underscore_suffixed": # pragma: no cover + # Test in subprocess.Popen return [name for name in imports if not name.startswith("_")], None import sys diff --git a/datar/core/load_plugins.py b/datar/core/load_plugins.py new file mode 100644 index 000000000..225f99da1 --- /dev/null +++ b/datar/core/load_plugins.py @@ -0,0 +1,23 @@ +from pipda import register_array_ufunc + +from .options import get_option +from .plugin import plugin + + +def _array_ufunc_to_register(ufunc, x, *args, **kwargs): + """Register the array ufunc to pipda""" + from ..apis.misc import array_ufunc + + return array_ufunc( + x, + ufunc, + *args, + **kwargs, + __backend=array_ufunc.backend, + ) + + +plugin.load_entrypoints(only=get_option("backends")) + +plugin.hooks.setup() +register_array_ufunc(_array_ufunc_to_register) diff --git a/datar/core/plugin.py b/datar/core/plugin.py index 062da69c4..944284dd0 100644 --- a/datar/core/plugin.py +++ b/datar/core/plugin.py @@ -2,9 +2,6 @@ from typing import Any, List, Mapping, Tuple, Callable from simplug import Simplug, SimplugResult, makecall -from pipda import register_array_ufunc - -from .options import get_option plugin = Simplug("datar") @@ -19,19 +16,6 @@ def _collect(calls: List[Tuple[Callable, Tuple, Mapping]]) -> Mapping[str, Any]: return collected -def _array_ufunc_to_register(ufunc, x, *args, **kwargs): - """Register the array ufunc to pipda""" - from ..apis.other import array_ufunc - - return array_ufunc( - x, - ufunc, - *args, - **kwargs, - __backend=array_ufunc.backend, - ) - - @plugin.spec def setup(): """Initialize the backend""" @@ -73,8 +57,8 @@ def tidyr_api(): @plugin.spec(result=_collect) -def other_api(): - """What is implemented the other APIs.""" +def misc_api(): + """What is implemented the misc APIs.""" @plugin.spec(result=SimplugResult.SINGLE) @@ -85,9 +69,3 @@ def c_getitem(item): @plugin.spec(result=SimplugResult.SINGLE) def operate(op: str, x: Any, y: Any = None): """Operate on x and y""" - - -plugin.load_entrypoints(only=get_option("backends")) - -plugin.hooks.setup() -register_array_ufunc(_array_ufunc_to_register) diff --git a/datar/data/__init__.py b/datar/data/__init__.py index dc1dc4c08..ea1395c1a 100644 --- a/datar/data/__init__.py +++ b/datar/data/__init__.py @@ -46,7 +46,7 @@ def load_dataset(name: str, __backend: str = None): def __getattr__(name: str): # mkapi accesses quite a lot of attributes starting with _ - if not name.isidentifier() or name.startswith("__"): + if not name.isidentifier() or name.startswith("__"): # pragma: no cover raise AttributeError(name) return load_dataset(name.lower()) diff --git a/datar/dplyr.py b/datar/dplyr.py index 9378b58c3..7ef954f19 100644 --- a/datar/dplyr.py +++ b/datar/dplyr.py @@ -1,4 +1,5 @@ -from .core.plugin import plugin as _plugin + +from .core.load_plugins import plugin as _plugin from .apis.dplyr import * locals().update(_plugin.hooks.dplyr_api()) diff --git a/datar/forcats.py b/datar/forcats.py index 2163cb531..91af92143 100644 --- a/datar/forcats.py +++ b/datar/forcats.py @@ -1,4 +1,5 @@ -from .core.plugin import plugin as _plugin + +from .core.load_plugins import plugin as _plugin from .apis.forcats import * _additional_imports = _plugin.hooks.forcats_api() diff --git a/datar/misc.py b/datar/misc.py new file mode 100644 index 000000000..27db9ee54 --- /dev/null +++ b/datar/misc.py @@ -0,0 +1,4 @@ +from .core.load_plugins import plugin as _plugin + +_additional_imports = _plugin.hooks.misc_api() +locals().update(_additional_imports) diff --git a/datar/other.py b/datar/other.py deleted file mode 100644 index 7e02bc3e0..000000000 --- a/datar/other.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core.plugin import plugin as _plugin - -_additional_imports = _plugin.hooks.other_api() -locals().update(_additional_imports) diff --git a/datar/tibble.py b/datar/tibble.py index f08c12cd5..b46e8930a 100644 --- a/datar/tibble.py +++ b/datar/tibble.py @@ -1,4 +1,5 @@ -from .core.plugin import plugin as _plugin + +from .core.load_plugins import plugin as _plugin from .apis.tibble import * _additional_imports = _plugin.hooks.tibble_api() diff --git a/datar/tidyr.py b/datar/tidyr.py index 55be7a7b8..7c835ec76 100644 --- a/datar/tidyr.py +++ b/datar/tidyr.py @@ -1,4 +1,5 @@ -from .core.plugin import plugin as _plugin + +from .core.load_plugins import plugin as _plugin from .apis.tidyr import * _additional_imports = _plugin.hooks.tidyr_api() diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9f7564b33..5e4adf430 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 0.11.0 + +- 📝 Add testimonials and backend badges in README.md +- 🐛 Load entrypoint plugins only when APIs are called (#162) +- 💥 Rename `other` module to `misc` + ## 0.10.3 - ⬆️ Bump simplug to 0.2.2 diff --git a/poetry.lock b/poetry.lock index 85f1f7679..fed97fbf2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -48,14 +48,14 @@ numpy = ">=1.17,<2.0" [[package]] name = "datar-pandas" -version = "0.0.0" +version = "0.1.1" description = "The pandas backend for datar" category = "main" optional = true python-versions = ">=3.7.1,<4.0.0" [package.dependencies] -datar = ">=0.10,<0.11" +datar = ">=0.10.3,<0.11.0" datar-numpy = ">=0.0,<0.1" pdtypes = ">=0.0.4,<0.0.5" @@ -269,7 +269,7 @@ yaml = ["pyyaml (>=6,<7)"] name = "python-slugify" version = "7.0.0" description = "A Python slugify application that also handles Unicode" -category = "main" +category = "dev" optional = false python-versions = ">=3.7" @@ -319,7 +319,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" name = "text-unidecode" version = "1.3" description = "The most basic Text::Unidecode port" -category = "main" +category = "dev" optional = false python-versions = "*" @@ -358,7 +358,7 @@ pandas = ["datar-pandas"] [metadata] lock-version = "1.1" python-versions = "^3.7.1" -content-hash = "38a41a6f0a3e19a57aa07e638abdc5cfed9693ecd0ec5363c435f694b2394c28" +content-hash = "b0839cf2320971b52efddd0b3f75ffb1f5755fd59cbada6e53dd8ee896e011d5" [metadata.files] attrs = [ @@ -426,8 +426,8 @@ datar-numpy = [ {file = "datar_numpy-0.0.0.tar.gz", hash = "sha256:d60f997ec6d36d4dfb726e8472d8fb21b41619af24c657a31b47a314a794d5c9"}, ] datar-pandas = [ - {file = "datar_pandas-0.0.0-py3-none-any.whl", hash = "sha256:6f646d1ac3332b1745ba39e3dc130fecd6da8abeae5f84f9b99a22d8b440ede2"}, - {file = "datar_pandas-0.0.0.tar.gz", hash = "sha256:2c478edd409d5f8188c899a2f54176fac02376c817bf54c6e795f52e6870ca25"}, + {file = "datar_pandas-0.1.1-py3-none-any.whl", hash = "sha256:ca04c2cfa7a72123c6780dc20391e97127e503f3b8cf827cf81d4c985d5c8df6"}, + {file = "datar_pandas-0.1.1.tar.gz", hash = "sha256:b00c618d9bfdb0177cb33f7635ab47fd32e5a31e01826c1cb45ea2202017bf2c"}, ] diot = [ {file = "diot-0.1.6-py3-none-any.whl", hash = "sha256:1e7f97bbc92cbbdd51d8c535885f5e2a71762b364c1b2410dfc84f3a4572d551"}, diff --git a/pyproject.toml b/pyproject.toml index 4b1420a39..d658516a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "datar" -version = "0.10.3" +version = "0.11.0" description = "A Grammar of Data Manipulation in python" authors = ["pwwang "] license = "MIT" @@ -13,8 +13,8 @@ python = "^3.7.1" simplug = "^0.2.2" pipda = "^0.11" python-simpleconf = {version = "^0.5", extras = ["toml"]} -datar-numpy = {version = "^0.0", optional = true} -datar-pandas = {version = "^0.1", optional = true} +datar-numpy = {version = "^0.1", optional = true} +datar-pandas = {version = "^0.2", optional = true} # datar-polars = {version = "^0.0.0", optional = true} # datar-pyarrow = {version = "^0.0.0", optional = true} diff --git a/tests/test_array_ufunc.py b/tests/test_array_ufunc.py index 01adac95a..a43e12fa0 100644 --- a/tests/test_array_ufunc.py +++ b/tests/test_array_ufunc.py @@ -4,7 +4,7 @@ from pipda import Context from datar import f from datar.core import plugin as _ # noqa: F401 -from datar.apis.other import array_ufunc +from datar.apis.misc import array_ufunc def test_default(): @@ -12,7 +12,7 @@ def test_default(): assert out.tolist() == [1, 2, 3] -def test_other_obj(): +def test_misc_obj(): class Foo(list): pass diff --git a/tests/test_plugin.py b/tests/test_plugin.py index cb1559b8e..d16379b48 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -20,8 +20,8 @@ def load_dataset(name, metadata): return name * 2 @plugin.impl - def other_api(): - from datar.apis.other import array_ufunc + def misc_api(): + from datar.apis.misc import array_ufunc @array_ufunc.register(object, backend="testplugin1") def _array_ufunc(x, ufunc, *args, **kwargs): @@ -86,23 +86,23 @@ def test_get_versions(with_test_plugin1, capsys): assert "datar" in capsys.readouterr().out -def test_other_api(with_test_plugin1): - from datar import all, other - plugin.hooks.other_api() +def test_misc_api(with_test_plugin1): + from datar import all, misc + plugin.hooks.misc_api() from importlib import reload - reload(other) - assert other.other_var == 1 + reload(misc) + assert misc.other_var == 1 reload(all) from datar.all import other_var assert other_var == 1 -def test_other_api_array_ufunc(with_test_plugin1): +def test_misc_api_array_ufunc(with_test_plugin1): from datar import f - from datar.apis.other import array_ufunc + from datar.apis.misc import array_ufunc - plugin.hooks.other_api() + plugin.hooks.misc_api() with pytest.warns(MultiImplementationsWarning): out = np.sqrt(f)._pipda_eval([3, 12, 27], Context.EVAL)