diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f60813..82fd098 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,5 +79,10 @@ jobs: - name: pytest run: ci/pytest_unit.sh - - name: perf + - name: test performance run: ci/pytest_perf.sh + + - name: test compatibility with Cirq + run: | + pip install cirq-core + pytest test/* -m cirq diff --git a/pyproject.toml b/pyproject.toml index 4e24403..5cb2a24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,4 +11,8 @@ requires = ["setuptools", "wheel", "Cython"] testpaths = [ "test", "test_perf", -] \ No newline at end of file +] +markers = [ + "cirq: mark a test as a compatibility test with Cirq.", +] +addopts = '-m "not cirq"' diff --git a/test/cirq_compatibility.py b/test/cirq_compatibility.py new file mode 100644 index 0000000..bc8fc6e --- /dev/null +++ b/test/cirq_compatibility.py @@ -0,0 +1,43 @@ +# Copyright 2025 The TUnits Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +import tunits as tu +import cirq # type: ignore + + +@pytest.mark.cirq +def test_to_json() -> None: + assert ( + cirq.to_json(tu.ns * 3) + == """{ + "cirq_type": "tunits.Value", + "value": 3, + "unit": "ns" +}""" + ) + + assert ( + cirq.to_json(tu.GHz * [1, 2, 3, -1]) + == """{ + "cirq_type": "tunits.ValueArray", + "value": [ + 1.0, + 2.0, + 3.0, + -1.0 + ], + "unit": "GHz" +}""" + ) diff --git a/tunits/core/__init__.pyi b/tunits/core/__init__.pyi index 4f00248..a52feb3 100644 --- a/tunits/core/__init__.pyi +++ b/tunits/core/__init__.pyi @@ -214,6 +214,11 @@ class WithUnit: def __divmod__(self, other: Any) -> tuple[_NUMERICAL_TYPE_OR_ARRAY, 'WithUnit']: ... def _value_class(self) -> type['Value']: ... def _array_class(self) -> type['ValueArray']: ... + def _json_dict_(self) -> dict[str, Any]: ... + @classmethod + def _json_namespace_(cls) -> str: ... + @classmethod + def _from_json_dict_(cls: type[T], **kwargs: Any) -> T: ... class Value(Generic[NumericalT], WithUnit, np.generic, SupportsIndex): """A floating-point value with associated units.""" diff --git a/tunits/core/cython/with_unit.pyx b/tunits/core/cython/with_unit.pyx index c910bd0..4907378 100644 --- a/tunits/core/cython/with_unit.pyx +++ b/tunits/core/cython/with_unit.pyx @@ -674,6 +674,17 @@ cdef class WithUnit: raise ValueError(f'{self} is not dimensionless') return self.value_in_base_units() + def _json_dict_(self) -> dict[str, Any]: + return {"value": self.value, "unit": self.units} + + @classmethod + def _json_namespace_(cls) -> str: + return "tunits" + + @classmethod + def _from_json_dict_(cls, **kwargs): + return cls(kwargs["value"], kwargs["unit"]) + _try_interpret_as_with_unit = None _is_value_consistent_with_default_unit_database = None def init_base_unit_functions(