From 5a7b9045b20468da5e8d244337e97bdb407ee589 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sun, 24 Nov 2024 02:15:23 +0100 Subject: [PATCH 01/11] Updated emmocheck to ahead to latest formulation of units --- emmopy/emmocheck.py | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/emmopy/emmocheck.py b/emmopy/emmocheck.py index b8ef02c46..c2a7333ac 100644 --- a/emmopy/emmocheck.py +++ b/emmopy/emmocheck.py @@ -194,6 +194,7 @@ def test_description(self): Exceptions include entities from standard w3c vocabularies. """ + # pylint: disable=invalid-name exceptions = set() exceptions.update(self.get_config("test_description.exceptions", ())) props = self.onto.world._props # pylint: disable=protected-access @@ -313,6 +314,10 @@ def test_unit_dimension(self): "emmo.SIBaseUnit", "emmo.SIUnitSymbol", "emmo.SIUnit", + "emmo.SIDerivedUnit", + "emmo.SIAcceptedPrefixedUnit", + "emmo.SIAcceptedDerivedUnit", + "emmo.SIMetricPrefixedUnit", ) ) if not hasattr(self.onto, "MeasurementUnit"): @@ -431,6 +436,7 @@ def test_quantity_dimension(self): "metrology.ExactConstant", "metrology.MeasuredConstant", "metrology.DerivedQuantity", + "metrology.PhysicalQuantiyByDefinition", "isq.ISQBaseQuantity", "isq.InternationalSystemOfQuantity", "isq.ISQDerivedQuantity", @@ -466,6 +472,7 @@ def test_quantity_dimension(self): "emmo.Intensive", "emmo.Extensive", "emmo.Concentration", + "emmo.PhysicalQuantiyByDefinition", ) ) if not hasattr(self.onto, "PhysicalQuantity"): @@ -500,7 +507,7 @@ def test_quantity_dimension(self): issubclass(cls, self.onto.ISQDimensionlessQuantity) ) - def test_dimensional_unit(self): + def test_dimensional_unit_rc2(self): """Check correct syntax of dimension string of dimensional units.""" # This test requires that the ontology has imported SIDimensionalUnit @@ -520,6 +527,38 @@ def test_dimensional_unit(self): self.assertIsInstance(r, owlready2.Restriction) self.assertRegex(r.value, regex) + def test_dimensional_unit(self): + """Check correct syntax of dimension string of dimensional units.""" + + # This test requires that the ontology has imported SIDimensionalUnit + if "SIDimensionalUnit" not in self.onto: + self.skipTest("SIDimensionalUnit is not imported") + + # pylint: disable=invalid-name + regex = re.compile( + "^T([+-][1-9][0-9]*|0) L([+-][1-9]|0) M([+-][1-9]|0) " + "I([+-][1-9]|0) (H|Θ)([+-][1-9]|0) N([+-][1-9]|0) " + "J([+-][1-9]|0)$" + ) + for cls in self.onto.SIDimensionalUnit.__subclasses__(): + with self.subTest(cls=cls, label=get_label(cls)): + dimstr = [ + r.value + for r in cls.is_a + if isinstance(r, owlready2.Restriction) + and repr(r.property) == "emmo.hasDimensionString" + ] + self.assertEqual( + len(dimstr), + 1, + msg="expect one emmo:hasDimensionString value restriction", + ) + self.assertRegex( + dimstr[0], + regex, + msg=f"invalid dimension string: '{dimstr[0]}'", + ) + def test_physical_quantity_dimension(self): """Check that all physical quantities have `hasPhysicalDimension`. @@ -873,6 +912,7 @@ def main( "test_physical_quantity_dimension_annotation", "test_quantity_dimension_beta3", "test_physical_quantity_dimension", + "test_dimensional_unit_rc2", ] ) msg = {name: "skipped by default" for name in skipped} From 850ad3d4f2d6a5a438bcb052038ec28d06eca9fc Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sun, 24 Nov 2024 02:54:59 +0100 Subject: [PATCH 02/11] Also ignore emmo.CGSUnit when checking unit dimension --- emmopy/emmocheck.py | 1 + 1 file changed, 1 insertion(+) diff --git a/emmopy/emmocheck.py b/emmopy/emmocheck.py index c2a7333ac..9ac7636d1 100644 --- a/emmopy/emmocheck.py +++ b/emmopy/emmocheck.py @@ -318,6 +318,7 @@ def test_unit_dimension(self): "emmo.SIAcceptedPrefixedUnit", "emmo.SIAcceptedDerivedUnit", "emmo.SIMetricPrefixedUnit", + "emmo.CGSUnit", ) ) if not hasattr(self.onto, "MeasurementUnit"): From 8fcc5d80b94f404501aa03eff43860c4e4f034be Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Wed, 11 Dec 2024 00:47:23 +0100 Subject: [PATCH 03/11] Fixing two new pylint issues --- demo/horizontal/emmo2meta.py | 2 +- emmopy/emmocheck.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/horizontal/emmo2meta.py b/demo/horizontal/emmo2meta.py index 91753a914..d5259b154 100644 --- a/demo/horizontal/emmo2meta.py +++ b/demo/horizontal/emmo2meta.py @@ -269,7 +269,7 @@ def get_dim(restriction, name, descr=None): Property( name, type=ptype, - dims=dimensions, + shape=dimensions, unit=unit, description=descr, ) diff --git a/emmopy/emmocheck.py b/emmopy/emmocheck.py index bfc45f7aa..070e2a5de 100644 --- a/emmopy/emmocheck.py +++ b/emmopy/emmocheck.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# pylint: disable=too-many-lines """ A module for testing an ontology against conventions defined for EMMO. From 91794d5aa76335015d8c6bd3ad641f7979df6dc0 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Thu, 12 Dec 2024 16:05:03 +0100 Subject: [PATCH 04/11] Added some exceptions for EMMO unit dimension classes These classes are just providing the structure of the taxonomy and doesn't need to have a physical dimension. --- emmopy/emmocheck.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/emmopy/emmocheck.py b/emmopy/emmocheck.py index 765df491f..b5c46dad3 100644 --- a/emmopy/emmocheck.py +++ b/emmopy/emmocheck.py @@ -314,6 +314,9 @@ def test_unit_dimension(self): "emmo.SIBaseUnit", "emmo.SIUnitSymbol", "emmo.SIUnit", + "emmo.SIAcceptedDerivedUnit", + "emmo.SIDerivedUnit", + "emmo.SIAcceptedPrefixedUnit", ) ) if not hasattr(self.onto, "MeasurementUnit"): From 8ea482c268a0ca0397f83ebdface9d3a7ca20a3f Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Thu, 12 Dec 2024 20:15:28 +0100 Subject: [PATCH 05/11] Also ignore CGSUnit --- emmopy/emmocheck.py | 1 + 1 file changed, 1 insertion(+) diff --git a/emmopy/emmocheck.py b/emmopy/emmocheck.py index 5c6692590..357f7781b 100644 --- a/emmopy/emmocheck.py +++ b/emmopy/emmocheck.py @@ -319,6 +319,7 @@ def test_unit_dimension(self): "emmo.SIAcceptedDerivedUnit", "emmo.SIDerivedUnit", "emmo.SIAcceptedPrefixedUnit", + "emmo.CGSUnit", ) ) if not hasattr(self.onto, "MeasurementUnit"): From 9560af86fadf6359f54b17c2fce5b3b30979f79c Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sun, 2 Feb 2025 19:52:45 +0100 Subject: [PATCH 06/11] Removed Python 3.7 from CI Tests, but added Python 3.12 and 3.13 instead --- .github/workflows/ci_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index daaa282cc..364c9ce65 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - name: Checkout repository From f13883e084fc68b82a01c456d5224971aff0d0e3 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sun, 2 Feb 2025 21:46:00 +0100 Subject: [PATCH 07/11] Removed test for Python 3.13 --- .github/workflows/ci_workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 364c9ce65..8eae7c867 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] steps: - name: Checkout repository From 06463a44cae3af4bb8c20c891243bff47a712a97 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sun, 2 Feb 2025 21:48:18 +0100 Subject: [PATCH 08/11] Removed Python 3.7 and added Python 3.12 to setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4084811b8..37b1acc4b 100644 --- a/setup.py +++ b/setup.py @@ -91,11 +91,11 @@ def fglob(patt): "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Information Analysis", "Topic :: Scientific/Engineering :: Visualization", From 983c116f4e2b0114b24b2b7d3b0c268fb05db871 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Sun, 2 Feb 2025 23:15:21 +0100 Subject: [PATCH 09/11] Allow missing elucidation for unit subclasses with a physical dimension --- emmopy/emmocheck.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/emmopy/emmocheck.py b/emmopy/emmocheck.py index 357f7781b..cf1362710 100644 --- a/emmopy/emmocheck.py +++ b/emmopy/emmocheck.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# pylint: disable=too-many-lines +# pylint: disable=too-many-lines,invalid-name """ A module for testing an ontology against conventions defined for EMMO. @@ -195,6 +195,11 @@ def test_description(self): Exceptions include entities from standard w3c vocabularies. """ + MeasurementUnit = ( + self.onto.MeasurementUnit + if "MeasurementUnit" in self.onto + else None + ) # pylint: disable=invalid-name exceptions = set() exceptions.update(self.get_config("test_description.exceptions", ())) @@ -216,6 +221,19 @@ def test_description(self): if r in exceptions or any(r.startswith(v) for v in vocabs): continue + # Skip units subclasses with a physical dimension + if ( + MeasurementUnit + and issubclass(entity, MeasurementUnit) + and any( + str(r.property.prefLabel.first()) == "hasDimensionString" + for r in entity.get_indirect_is_a() + if hasattr(r, "property") + and hasattr(r.property, "prefLabel") + ) + ): + continue + label = str(get_label(entity)) with self.subTest(entity=entity, label=label): self.assertTrue( From 99e8ada08a8dd8adaf69fe6d77368e082985cb77 Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Mon, 3 Feb 2025 08:19:43 +0100 Subject: [PATCH 10/11] Trying to fix failing test --- tests/test_basic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_basic.py b/tests/test_basic.py index 670ac5de0..3c0ab0922 100755 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -49,7 +49,7 @@ class H2O(emmo.Molecule): emmo.hasSpatialDirectPart.exactly(2, onto.Hydrogen) emmo.hasSpatialDirectPart.exactly(1, Oxygen) - # Create some + # Create some individuals H1 = onto.Hydrogen() H2 = onto.Hydrogen() O = Oxygen() @@ -59,7 +59,7 @@ class H2O(emmo.Molecule): name_prefix = "myonto_" onto.sync_attributes(name_policy="sequential", name_prefix=name_prefix) assert f"{onto.base_iri}{name_prefix}0" in onto - assert f"{onto.base_iri}{name_prefix}6" in onto + assert f"{onto.base_iri}{name_prefix}3" in onto name_prefix = "onto_" onto.sync_attributes(name_policy="uuid", name_prefix=name_prefix) From ed28f351f2be76d2a22543453acdff35c27b77fb Mon Sep 17 00:00:00 2001 From: Jesper Friis Date: Mon, 3 Feb 2025 22:42:54 +0100 Subject: [PATCH 11/11] Added configuration to skip modules --- docs/tools-instructions.md | 29 ++++++++++++++++++++-- emmopy/emmocheck.py | 49 +++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/docs/tools-instructions.md b/docs/tools-instructions.md index 6ef8a32cf..b03df4f9f 100644 --- a/docs/tools-instructions.md +++ b/docs/tools-instructions.md @@ -69,15 +69,40 @@ optional arguments: ``` -### Example configuration file -Example of YAML configuration file provided with the `--configfile` option that will omit `myunits.MyUnitCategory1` and `myunits.MyUnitCategory1` from the *unit dimensions test*. +### Configuration file +The `--configfile` options expects a YAML configuration file that specifies what tests to skip or enable. + +The following keywords are recognised in the YAML file: + + - `skip`: List of tests to skip + - `enable`: List of tests to enable + - ``: A name of a test. Recognised nested keywords are: + - `exceptions`: List of entities in the ontology to skip. Should be written + as `.`, where `` is the last component of the base IRI + and `` is the name of the entity. + - `skipmodules`: List of module names to skip the test for. The module + names may be written either as the full module IRI or as the last + component of the module IRI. + +Example configuration file: ```console +test_description: + skipmodules: + - manufacturing + - conformityassessment + test_unit_dimensions: exceptions: - myunits.MyUnitCategory1 - myunits.MyUnitCategory2 + +skip: + - name_of_test_to_skip + +enable: + - name_of_test_to_enable ``` --- diff --git a/emmopy/emmocheck.py b/emmopy/emmocheck.py index cf1362710..89843d0e5 100644 --- a/emmopy/emmocheck.py +++ b/emmopy/emmocheck.py @@ -5,8 +5,25 @@ A YAML file can be provided with additional test configurations. +Toplevel keywords in the YAML file: + + - `skip`: List of tests to skip + - `enable`: List of tests to enable + - ``: A name of a test. Recognised nested keywords are: + - `exceptions`: List of entities in the ontology to skip. Should be written + as `.`, where `` is the last component of the base IRI + and `` is the name of the entity. + - `skipmodules`: List of module names to skip the test for. The module + names may be written either as the full module IRI or as the last + component of the module IRI. + Example configuration file: + test_description: + skipmodules: + - manufacturing + - conformityassessment + test_unit_dimensions: exceptions: - myunits.MyUnitCategory1 @@ -195,12 +212,12 @@ def test_description(self): Exceptions include entities from standard w3c vocabularies. """ + # pylint: disable=invalid-name MeasurementUnit = ( self.onto.MeasurementUnit if "MeasurementUnit" in self.onto else None ) - # pylint: disable=invalid-name exceptions = set() exceptions.update(self.get_config("test_description.exceptions", ())) props = self.onto.world._props # pylint: disable=protected-access @@ -234,6 +251,10 @@ def test_description(self): ): continue + # Check skipmodules + if skipmodule(self, "test_description", entity): + continue + label = str(get_label(entity)) with self.subTest(entity=entity, label=label): self.assertTrue( @@ -797,6 +818,32 @@ def checker(onto, ignore_namespace): checker(self.onto, self.ignore_namespace) +def skipmodule(testobj, testname, entity): + """Return true if `entity` is in a module that should be skipped.""" + skipmodules = testobj.get_config(f"{testname}.skipmodules") + + if not skipmodules: + return False + + # Infer base iri + if entity.namespace.ontology.base_iri != "https://w3id.org/emmo#": + base_iri = entity.namespace.ontology.base_iri.rstrip("/#") + elif hasattr(entity, "isDefinedBy") and entity.isDefinedBy: + base_iri = entity.isDefinedBy.first().rstrip("/#") + else: + base_iri = entity.namespace.ontology.base_iri.rstrip("/#") + + for module in skipmodules: + module = module.rstrip("/#") + if "/" in module: + if module == base_iri: + return True + elif module == base_iri.rsplit("/", 1)[-1]: + return True + + return False + + def main( argv: list = None, ): # pylint: disable=too-many-locals,too-many-branches,too-many-statements