diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0f7438b2..43ad16fa 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -48,6 +48,10 @@ jobs: with: submodules: recursive persist-credentials: false + - name: Install system libraries for lxml + run: | + sudo apt-get update + sudo apt-get install -y libxml2-dev libxslt1-dev - name: Use Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: @@ -78,6 +82,10 @@ jobs: with: submodules: recursive persist-credentials: false + - name: Install system libraries for lxml + run: | + sudo apt-get update + sudo apt-get install -y libxml2-dev libxslt1-dev - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: diff --git a/AGENTS.md b/AGENTS.md index 3ba8d1ab..4ef186d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,8 @@ Read [CONTRIBUTING.rst](CONTRIBUTING.rst) for code style, linting (e.g. `make li ## Testing -- Prefer **function-based** pytest tests (module-level `def test_...`) over class-based tests (`class Test...`). +- When adding tests to a file that already uses class-based pytest structure (e.g. `tests/test_jsonld.py`), add them as methods on the appropriate existing class — do not introduce a parallel standalone function-based test. A repo-wide refactor to function-based tests is planned but not yet landed; until then, match each file's existing structure. +- For brand-new test files, prefer function-based pytest tests (module-level `def test_...`) over class-based tests. - Use descriptive test names that reflect behavior (e.g. `test_remote_context_via_link_alternate`). ## Committing diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..dc1d0803 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +See [AGENTS.md](AGENTS.md) for project-specific agent guidance. diff --git a/lib/pyld/jsonld.py b/lib/pyld/jsonld.py index ce18f0d3..46c6a45e 100644 --- a/lib/pyld/jsonld.py +++ b/lib/pyld/jsonld.py @@ -5842,8 +5842,8 @@ def _expand_iri( # resolve against base rval = value - # if there is a base in the active context, use that - if '@base' in active_ctx: + # if a base was passed and the active context has a base, use that + if base is not None and '@base' in active_ctx: # the None case preserves rval as potentially relative if active_ctx['@base'] is not None: resolved_base = ( diff --git a/tests/test_jsonld.py b/tests/test_jsonld.py index a12016fe..578e3a79 100644 --- a/tests/test_jsonld.py +++ b/tests/test_jsonld.py @@ -180,6 +180,36 @@ def test_missing_base(self): got = jsonld.expand(input) assert got == expected + def test_base_does_not_expand_property_terms(self): + """ + Regression test: @base must not be used to expand property keys. + + Property names are expanded vocabulary-relative: @vocab, term + definitions, compact IRIs with a defined prefix, etc. The active + context's @base is for document-relative IRI resolution where the + algorithms pass that flag (e.g. certain @id and @type values), not + for turning arbitrary keys into absolute IRIs. Here the context sets + only @base; `name` has no term definition and no @vocab, so it + cannot become an absolute property IRI and must be dropped. + + See: https://www.w3.org/TR/json-ld11-api/#iri-expansion + """ + doc = { + '@context': {'@base': 'https://schema.org/'}, + '@id': 'https://w3.org/yaml-ld/', + '@type': 'WebContent', + 'name': 'YAML-LD', + } + result = jsonld.expand(doc) + # `name` has no vocabulary-relative mapping (@vocab or term + # definition); @base must not supply one. The key is dropped. + assert result == [ + { + '@id': 'https://w3.org/yaml-ld/', + '@type': ['https://schema.org/WebContent'], + } + ] + class TestFrame: # Issue 11 - PR: https://github.com/digitalbazaar/pyld/issues/149