mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
* fix(ontology): preserve #-terminated namespaces in SHACLGenerator base_uri (#1082) SHACLGenerator.__init__ normalized base_uri with rstrip('/') + '/', turning a #-terminated RDF namespace (e.g. http://example.org/manufacturing#) into ...#/. Every generated URI then landed in a different namespace than the instance data, so SHACL validation silently passed because the shapes targeted nothing. __init__ now preserves a base_uri already ending in '/' or '#', matching the #-aware normalization generate() already applies. shapes_uri inherits the fix. Adds test_hash_namespace_base_uri_is_not_mangled (fails on the old normalization), plus a CHANGELOG entry. Full ontology suite green. Co-Authored-By: Claude <noreply@anthropic.com> * fix(ontology): collapse slash runs, only preserve #-terminated base_uri Qodo review caught that preserving any endswith('/') base left redundant trailing slashes (e.g. .../ns////) intact, leaking a different namespace into emitted IRIs. Now only '#'-terminated bases are kept verbatim; slash runs are collapsed to a single '/', matching generate() normalization. Adds test_slash_run_normalization_regression. --------- Co-authored-by: changshenhan <217217832+changshenhan@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
changshenhan
Claude
parent
59af023447
commit
af3308ad06
@@ -132,6 +132,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- New `tests/export/test_timestamp_timezones.py` and `tests/provenance/test_timestamp_timezones.py`: offset presence on every export and provenance path, PROV-O literals valid as `xsd:dateTimeStamp`, comparison against a timezone-aware instant without `TypeError`, the Oxigraph filter that dropped the naive value (with a bound inside the indeterminate window, so the test cannot pass by accident), and the document `@id` remaining a valid IRI with `+00:00` in it. 11 of the 13 fail on the parent commit
|
||||
- **Fixed during review** (Qodo): once new entries carry `+00:00` and stored ones do not, `ProvenanceManager.query_recorded_between` and `audit_log` compared ISO timestamps as raw strings, so they ordered by spelling rather than by instant — an inclusive naive bound naming a stored offset-bearing timestamp sorted *below* it and dropped the record, and a bound written in another offset landed wherever its digits fell (`19:45+05:30` is 14:15Z, but sorted after 14:19Z). Both now compare instants through a new `to_utc_datetime()` helper that reads a missing offset as UTC, which is what the values written before this change actually were; a bound that cannot be read as a timestamp keeps the historical string comparison rather than raising on a call that used to work
|
||||
- The remaining 147 naive call sites are in `context/`, `vector_store/`, `seed/` and elsewhere, where timestamps are compared against values parsed from previously stored naive strings. Converting those without a read-side migration would raise `TypeError: can't compare offset-naive and offset-aware datetimes` on existing data, so they are deliberately left for a separate change
|
||||
- **`SHACLGenerator` mangles `#`-terminated namespaces into `#/`, so generated shapes target nothing** (#1082) by @changshenhan
|
||||
- `__init__` normalized `base_uri` with `rstrip("/") + "/"`, which turns `http://example.org/manufacturing#` into `...manufacturing#/` — the most common RDF namespace convention. Every generated URI (`sh:targetClass`, `sh:path`, shape URIs) then landed in a different namespace than the instance data, and SHACL validation silently passed because the shapes targeted nothing
|
||||
- `__init__` now preserves a namespace already ending in `/` or `#`, matching the `#`-aware normalization `generate()` already applies; `shapes_uri` inherits the fix
|
||||
- New `test_hash_namespace_base_uri_is_not_mangled` in `tests/ontology/test_ontology_advanced.py` fails on the pre-fix normalization and passes with it; full ontology suite (76 tests) green
|
||||
|
||||
- **`split`/chunking paths bypassed the centralized spaCy model cache, reloading the model on every call** (#1042, closes #998) by @Accute9, reviewed by @Sameer6305
|
||||
- `semantica/split/methods.py`'s `split_by_sentences()` and `semantica/split/semantic_chunker.py`'s `SemanticChunker.__init__` each called `spacy.load()` directly instead of reusing the process-level cache added in #889/`semantic_extract/methods.py`'s `load_spacy_model()` — every call/construction re-paid the ~120ms model-load cost independently of `NERExtractor`, which already used the cache
|
||||
|
||||
@@ -882,7 +882,16 @@ class SHACLGenerator:
|
||||
"""
|
||||
self.logger = get_logger("ontology_shacl")
|
||||
self.progress_tracker = get_progress_tracker()
|
||||
self.base_uri = base_uri.rstrip("/") + "/"
|
||||
# Preserve an RDF namespace that already ends in `#` (the common
|
||||
# convention for vocabularies): `...manufacturing#` must not become
|
||||
# `...manufacturing#/`, or every generated URI lands in the wrong
|
||||
# namespace and SHACL validation silently targets nothing. Matches
|
||||
# the `#`-aware normalization in `generate()`. Slash-terminated
|
||||
# bases are collapsed to a single trailing `/` so redundant runs
|
||||
# (`.../ns////`) cannot leak a different namespace into emitted IRIs.
|
||||
self.base_uri = (
|
||||
base_uri if base_uri.endswith("#") else base_uri.rstrip("/") + "/"
|
||||
)
|
||||
self.shapes_uri = shapes_uri or (self.base_uri + "shapes")
|
||||
self.include_inherited = include_inherited
|
||||
self.severity = severity
|
||||
|
||||
@@ -340,6 +340,27 @@ class TestSHACLHierarchicalAndValidation(unittest.TestCase):
|
||||
ttl = gen.serialize(graph, format="turtle")
|
||||
self.assertIn("myorg.com", ttl)
|
||||
|
||||
# 24a — an RDF namespace ending in `#` must not gain a trailing `/`,
|
||||
# or every generated URI lands in a different namespace and SHACL
|
||||
# validation silently targets nothing.
|
||||
def test_hash_namespace_base_uri_is_not_mangled(self):
|
||||
gen = self._make_gen(base_uri="http://example.org/manufacturing#")
|
||||
self.assertEqual(gen.base_uri, "http://example.org/manufacturing#")
|
||||
self.assertEqual(gen.shapes_uri, "http://example.org/manufacturing#shapes")
|
||||
|
||||
graph = gen.generate(self._HIER_ONTOLOGY)
|
||||
ttl = gen.serialize(graph, format="turtle")
|
||||
self.assertNotIn("#/", ttl)
|
||||
self.assertIn("manufacturing#", ttl)
|
||||
|
||||
# 24b — a `#`-terminated base is preserved verbatim, while slash runs
|
||||
# are collapsed: Qodo review caught that `endswith(("/","#"))` left
|
||||
# `.../ns////` intact, leaking a different namespace into emitted IRIs.
|
||||
def test_slash_run_normalization_regression(self):
|
||||
gen = self._make_gen(base_uri="http://example.org/ns////")
|
||||
self.assertEqual(gen.base_uri, "http://example.org/ns/")
|
||||
self.assertEqual(gen.shapes_uri, "http://example.org/ns/shapes")
|
||||
|
||||
# 25
|
||||
def test_severity_warning(self):
|
||||
gen = self._make_gen(severity="Warning")
|
||||
|
||||
Reference in New Issue
Block a user