diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-10-26 18:05:42 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-26 18:05:42 +0000 |
commit | cf1a80220dfc058bba4b127beb32a335a1f89248 (patch) | |
tree | 7fbd2929abaa0783b7bd2956c57a5ee3ec1b25f8 | |
parent | 984923a90c5fefb69783d5d26ad215820ede0559 (diff) | |
parent | 5e3c937a4782c1fc55561e90c157eeb68211d203 (diff) | |
download | pint-staging.tar.gz |
Merge #1633staging
1633: Simplify registry subclassing r=hgrecco a=hgrecco
- [x] Closes #1631
- [x] Executed ``pre-commit run --all-files`` with no errors
- [x] The change is fully covered by automated unit tests
- [x] Documented in docs/ as appropriate
- [x] Added an entry to the CHANGES file
Co-authored-by: Hernan <hernan.grecco@gmail.com>
Co-authored-by: Hernan Grecco <hernan.grecco@gmail.com>
-rw-r--r-- | .github/workflows/ci.yml | 74 | ||||
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | docs/advanced_guides.rst | 1 | ||||
-rw-r--r-- | docs/user/custom-registry-class.rst | 83 | ||||
-rw-r--r-- | pint/testsuite/test_issues.py | 21 | ||||
-rw-r--r-- | pint/util.py | 15 | ||||
-rw-r--r-- | setup.cfg | 1 |
7 files changed, 182 insertions, 15 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e70fd13..8e44023 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,18 +92,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8, 3.9, "3.10"] + python-version: [3.8, 3.9, "3.10", "3.11"] numpy: [ "numpy>=1.19,<2.0.0" ] - # uncertainties: [null, "uncertainties==3.1.6", "uncertainties>=3.1.6,<4.0.0"] - # extras: [null] - # include: - # - python-version: 3.8 # Minimal versions - # numpy: numpy==1.19.5 - # extras: matplotlib==2.2.5 - # - python-version: 3.8 - # numpy: "numpy" - # uncertainties: "uncertainties" - # extras: "sparse xarray netCDF4 dask[complete] graphviz babel==2.8" runs-on: windows-latest env: @@ -159,6 +149,68 @@ jobs: - name: Run tests run: pytest ${env:TEST_OPTS} + test-macos: + strategy: + fail-fast: false + matrix: + python-version: [3.8, 3.9, "3.10", "3.11"] + numpy: [ "numpy>=1.19,<2.0.0" ] + runs-on: macos-latest + + env: + TEST_OPTS: "-rfsxEX -s --cov=pint --cov-config=.coveragerc" + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 100 + + - name: Get tags + run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: echo "::set-output name=dir::$(pip cache dir)" + + - name: Setup caching + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: pip-${{ matrix.python-version }} + restore-keys: | + pip-${{ matrix.python-version }} + + - name: Install numpy + if: ${{ matrix.numpy != null }} + run: pip install "${{matrix.numpy}}" + + - name: Install dependencies + run: | + pip install pytest pytest-cov pytest-subtests + pip install . + + - name: Run Tests + run: | + pytest $TEST_OPTS + + - name: Coverage report + run: coverage report -m + + - name: Coveralls Parallel + env: + COVERALLS_FLAG_NAME: ${{ matrix.test-number }} + COVERALLS_PARALLEL: true + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_SERVICE_NAME: github + run: | + pip install coveralls + coveralls + coveralls: needs: test-linux runs-on: ubuntu-latest @@ -4,7 +4,7 @@ Pint Changelog 0.21 (unreleased) ----------------- -- Nothing changed yet. +- Simplify registry subclassing and document it. 0.20 (2022-10-25) diff --git a/docs/advanced_guides.rst b/docs/advanced_guides.rst index cc2c495..580ddb7 100644 --- a/docs/advanced_guides.rst +++ b/docs/advanced_guides.rst @@ -12,3 +12,4 @@ Advanced Guides user/measurement user/pitheorem user/currencies + user/custom-registry-class diff --git a/docs/user/custom-registry-class.rst b/docs/user/custom-registry-class.rst new file mode 100644 index 0000000..31f3d76 --- /dev/null +++ b/docs/user/custom-registry-class.rst @@ -0,0 +1,83 @@ +.. _custom_registry_class: + +Custom registry class +===================== + +Pay as you go +------------- + +Pint registry functionality is divided into facets. The default +UnitRegistry inherits from all of them, providing a full fledged +and feature rich registry. However, in certain cases you might want +to have a simpler and light registry. Just pick what you need +and create your own. + +- FormattingRegistry: adds the capability to format quantities and units into string. +- SystemRegistry: adds the capability to work with system of units. +- GroupRegistry: adds the capability to group units. +- MeasurementRegistry: adds the capability to handle measurements (quantities with uncertainties). +- NumpyRegistry: adds the capability to interoperate with NumPy. +- DaskRegistry: adds the capability to interoperate with Dask. +- ContextRegistry: the capability to contexts: predefined conversions + between incompatible dimensions. +- NonMultiplicativeRegistry: adds the capability to handle nonmultiplicative units (offset, logarithmic). +- PlainRegistry: base implementation for registry, units and quantities. + +The only required one is `PlainRegistry`, the rest are completely +optional. + +For example: + +.. doctest:: + + >>> import pint + >>> class MyRegistry(pint.facets.NonMultiplicativeRegistry, pint.facets.PlainRegistry): + ... pass + + +Subclassing +----------- + +If you want to add the default registry class some specific functionality, +you can subclass it: + +.. doctest:: + + >>> import pint + >>> class MyRegistry(pint.UnitRegistry): + ... + ... def my_specific_function(self): + ... """Do something + ... """ + + +If you want to create your own Quantity class, you must tell +your registry about it: + +.. doctest:: + + >>> import pint + >>> class MyQuantity: + ... + ... # Notice that subclassing pint.Quantity + ... # is not necessary. + ... # Pint will inspect the Registry class and create + ... # a Quantity class that contains all the + ... # required parents. + ... + ... def to_my_desired_format(self): + ... """Do something else + ... """ + >>> + >>> class MyRegistry(pint.UnitRegistry): + ... + ... _quantity_class = MyQuantity + ... + ... # The same you can be done with + ... # _unit_class + ... # _measurement_class + + +While these examples demonstrate how to add functionality to the default +registry class, you can actually subclass just the PlainRegistry or any +combination of facets. diff --git a/pint/testsuite/test_issues.py b/pint/testsuite/test_issues.py index 80e2ebb..a07e850 100644 --- a/pint/testsuite/test_issues.py +++ b/pint/testsuite/test_issues.py @@ -1014,3 +1014,24 @@ def test_backcompat_speed_velocity(func_registry): get = func_registry.get_dimensionality assert get("[velocity]") == UnitsContainer({"[length]": 1, "[time]": -1}) assert get("[speed]") == UnitsContainer({"[length]": 1, "[time]": -1}) + + +def test_issue1631(): + import pint + + # Test registry subclassing + class MyRegistry(pint.UnitRegistry): + pass + + assert MyRegistry.Quantity is pint.UnitRegistry.Quantity + assert MyRegistry.Unit is pint.UnitRegistry.Unit + + ureg = MyRegistry() + + u = ureg.meter + assert isinstance(u, ureg.Unit) + assert isinstance(u, pint.Unit) + + q = 2 * ureg.meter + assert isinstance(q, ureg.Quantity) + assert isinstance(q, pint.Quantity) diff --git a/pint/util.py b/pint/util.py index eba747e..3d00175 100644 --- a/pint/util.py +++ b/pint/util.py @@ -10,6 +10,7 @@ from __future__ import annotations +import functools import inspect import logging import math @@ -1021,6 +1022,13 @@ def sized(y) -> bool: return True +@functools.lru_cache( + maxsize=None +) # TODO: replace with cache when Python 3.8 is dropped. +def _build_type(class_name: str, bases): + return type(class_name, bases, dict()) + + def build_dependent_class(registry_class, class_name: str, attribute_name: str) -> Type: """Creates a class specifically for the given registry that subclass all the classes named by the registry bases in a @@ -1036,9 +1044,10 @@ def build_dependent_class(registry_class, class_name: str, attribute_name: str) for base in inspect.getmro(registry_class) if attribute_name in base.__dict__ ) - bases = dict.fromkeys(bases, None) - newcls = type(class_name, tuple(bases.keys()), dict()) - return newcls + bases = tuple(dict.fromkeys(bases, None).keys()) + if len(bases) == 1 and bases[0].__name__ == class_name: + return bases[0] + return _build_type(class_name, bases) def create_class_with_registry(registry, base_class) -> Type: @@ -75,3 +75,4 @@ line_length=88 [zest.releaser] python-file-with-version = version.py +create-wheel = yes |