Linked-data-python

Latest version: v0.6.5

Safety actively analyzes 1036830 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 1 of 3

0.6.5

- **`for bindings` takes namedtuple rows.** Reported by Erdem Onal (issue
1): `pandas.DataFrame.itertuples()` yields namedtuples, which are neither
`Bindings` nor mappings, so they were refused and needed an `_asdict()`
adapter. Two row shapes are now accepted — an object with `_fields`, or
failing that one with `_asdict()`. Both are needed: CPython's namedtuple
has `_fields`, MicroPython's has only `_asdict`. ldpy's own `Row`, what an
`m{ … }` of arity ≥ 2 yields, already carries `_fields`, so
`for bindings in list(m{ … })` works now too.

- **A prefix declared while a graph is current binds on it too.** `graph`
gave the designated graph the prefixes in scope at that moment, and only
those: declaring `prefix` on the next line left it unbound, so swapping
two lines changed the serialisation. It was a silent trap — nothing
reported the missing prefix, the output was merely less readable. The
position of a declaration no longer decides; both orders now serialise
alike.

- **`STRLANG` and `STRDT` in `e{ }`.** The refusal of `?ven` and `?v^^dt`
(below) is only defensible if saying it explicitly is possible. These two
standard SPARQL 1.1 functions are that way — take the lexical form a
variable is bound to, read it as a tagged or typed literal — and they were
missing from the expression island. They work in `+{ }` and `-{ }`, with a
`bindings` in scope like any `e{ }` used as a term there; `m{ }` keeps
refusing expression islands, which is a decision and not an omission.
Both refuse an argument that already carries a language tag or a datatype.

- **An RDF suffix on a variable is refused, and the refusal names itself.**
`{expr}en` sticks a language tag to an interpolation, which is meaningful:
the Python value is the lexical form. `?ven` never parsed, but the parser
only complained that the island's closing brace was missing. It is now a
decision the language states — a variable is already bound to a complete
term, there is nothing left to interpret — with a message that points at
`{expr}en`. Same for `?v^^dt`.

0.6.4

- **`as` works on a dynamic prefix too**: `prefix p: f<http://{host}/ns#> as
P .` binds the `Namespace` built from the IRI resolved at that point — the
same object the prefix itself uses, so the two can never disagree. This
belongs with 0.6.3 and missed its tag by a few minutes; it is not an
afterthought but the form the corpus needed first, since every IRI of the
region that motivated `as` is computed at run time.

0.6.3

Two answers to the limit the corpus study attested most: `prefix` is
lexical, so it produces no Python value, and code that needs the `Namespace`
as an **object** had no translation at all (record ldpy/027).

- **A designated graph now inherits the block's prefixes.** A graph created
by `graph as g` already serialised with them; one merely designated by
`graph mine` carried none, so the same block filling two graphs the same
way serialised them differently. That was an inconsistency rather than a
decision. The prefixes are *added* to the graph's own bindings — it belongs
to its caller, and nothing it had is discarded. This removes the reason for
most hand-written `g.bind()` calls; one function in the corpus had
nineteen.
- **`prefix ex: <IRI> as EX .`** binds the `Namespace` object to a Python
name, for the code that must keep it: to `bind()` it on a graph it manages,
to export it, to put it in a registry. A bare `ex:` is still never a value.
The name follows Python's scope, and `global` / `nonlocal` widen it like
any other declaration.

You rarely need `as`. A `Namespace` that merely *produces* IRIs needs
nothing — the `URIRef`s it produces are ordinary values, and two regions in
three of the corpus stratum that looked blocked were expressible for exactly
that reason.

0.6.2

Bug fixes only, all found by the corpus study and all reproduced by hand
before being fixed.

- **A `` inside a full IRI is no longer read as a comment in `s{ }`.** The
SPARQL island scanner knew strings and comments but not IRIs, so the
fragment of `<http://example.org/ns#X>` — the most ordinary shape an IRI
takes in RDF — swallowed the rest of the line, closing brace included, and
the island failed to transpile with "'}' expected". `m{ }` and `g{ }` parse
IRIs as terms and never had the defect. A `<` that does not open an IRI is
still SPARQL's less-than.
- **An island inside an f-string interpolation hole is now refused by name.**
The scanner does not descend into those holes, so the island used to be
copied verbatim into the generated Python, which CPython then rejected with
a message from its f-string parser that named nothing. It now says which
island, where, and what to do instead. Supporting islands there remains
open; refusing them clearly does not foreclose it.
- **Two documentation corrections in the hover table**, which also feeds the
corpus study's translator instructions: `f<...>` does **not** percent-encode
(only `e<...>` does — `firi()` concatenates and resolves, nothing more), and
`.execute()` is not just for updates but for any query whose rdflib
`Result` you then call a method on.
- **Two silent traps documented** in `how-to/migrate-from-rdflib.md`, both
with runnable proof: interpolating a bare `str` into `s{ }` coerces it to a
literal, so the query matches nothing without raising; and `"{x}"` inside a
literal does not interpolate — `f"{x}"` does.

0.6.1

- **A tutorial that is a program**: `docs/tutorials/tour.ldpy`, one file you
run, whose comments introduce the notation section by section — declarations,
terms, `f<…>`, `g{ }`, the current graph with `+{ }` and `-{ }`, `m{ }`,
`e{ }`, `bindings` over a CSV, `s{ }`, and back to rdflib. Each section
builds, prints and asserts what it claims, so running it to the end is the
proof. The page that shows it *includes* the file rather than copying it, and
the test suite runs it.

0.6.0

- **Documentation for the device.** A tutorial (run a programme on
MicroPython, on the Unix port, no hardware needed), a how-to guide (build
for MicroPython: the target, the bundle, `mpy-cross`, the seam between
programme and device), and an explanation (running on a device: the
façade, the backend, why SPARQL is two capabilities); the command-line and
API references, the rationale's R6 and the limitations list say the same.

- **The façade has a backend, and the backend can be urdflib.** The
generated code always went through `ldpy.runtime`; that module now takes
its terms and graphs from `ldpy.backend`, which is rdflib when rdflib
imports and [urdflib](https://github.com/linked-data-python/urdflib) on a
MicroPython device. `Namespace`, `RDF` and `XSD` are provided in Python
where the C module has none; the emitted graph is lazy over rdflib and
eager over urdflib, whose graphs add in place at C speed; the `Row` of an
`m{ }` no longer needs `tuple.__new__`, which MicroPython lacks.
- **`--target micropython`** (`ldpy`, `ldpy.build`, `transpile(...,
target=)`): `s{ }` is refused at build time, on the host, where the message
can be read — a SPARQL query needs rdflib's engine, and the device has
none; `e{ }` and `m{ }` are pure Python over the terms and stay. The build
copies the device runtime (`runtime.py`, `backend.py`, `sparql.py`, a
minimal `__init__.py`) next to the emitted files, so that what is shipped
and what it imports travel together (record ldpy/026).
- **The same programme runs on MicroPython.** `tests/test_target_micropython.py`
transpiles a programme with `+{ }`, `m{ }`, `e{ }` and `serialize()`,
runs it on a MicroPython built with urdflib (`LDPY_MICROPYTHON`) and gets
the host's answer.

Page 1 of 3

© 2026 Safety CLI Cybersecurity Inc. All Rights Reserved.