Skip to main content

streaming parser using gjson-style path queries over XML

Project description

pygxml

streaming parser using gjson-style path queries over XML. Rust core (quick-xml) + PyO3.

The original GJSON: tidwall/gjson

Installation

pip install pygxml

Usage examples

import pygxml

xml = """<store>
  <book id="b1"><title>XML in a Nutshell</title><price>30</price></book>
  <book id="b2"><title>The Cathedral and the Bazaar</title><price>20</price></book>
  <book id="b3"><title>Programming Rust</title><price>45</price></book>
</store>"""

# Single-shot path query — returns a typed Result.
pygxml.get(xml, "store.book").type_                         # list (3 books)
pygxml.get(xml, "store.book.0.title").to_str()             # 'XML in a Nutshell'
pygxml.get(xml, "store.book.1.@id").to_str()               # 'b2'
pygxml.get(xml, "store.book.#").to_int()                   # 3
[str(r) for r in pygxml.get(xml, "store.book.#.title")]    # ['XML in a Nutshell', ...]

# A bare child name with multiple matches AND a follow-on step is rejected:
# the user must pick `.N` (single) or `.#` (each).
pygxml.get(xml, "store.book.title")                         # ValueError

# Filters
pygxml.get(xml, "store.book.#(price>=30).title").to_str()           # 'XML in a Nutshell'
[str(r) for r in pygxml.get(xml, "store.book.#(price>=30)#.title")] # all matches
pygxml.get(xml, 'store.book.#(@id=="b2").title').to_str()           # 'The Cathedral...'

# Modifiers
pygxml.get(xml, "store.book.#.title|@count").to_int()               # 3

# Result.get(...) — descend into a captured element fragment.
book = pygxml.get(xml, "store.book.0")
book.get("title").to_str()                                # 'XML in a Nutshell'
book.get("@id").to_str()                                  # 'b1'
book.get("price").to_int()                                # 30

# parse(data) — wrap the input as a top-level Result for chained navigation.
r = pygxml.parse(xml)
r.get("store.book.0.title").to_str()                      # 'XML in a Nutshell'
r.get("store.book.#(price>=30)#.title").value             # [Result('XML in a Nutshell'), Result('Programming Rust')]

# get_many — scan the document once and return multiple Results.
title, price = pygxml.get_many(xml, ["store.book.0.title", "store.book.0.price"])

# compile() — pre-compile a path for reuse across many documents.
path = pygxml.compile("store.book.0.title")
path.get(xml).to_str()                                    # 'XML in a Nutshell'

# Compiled paths can also be passed to get_many / get_many_bytes / get_many_buffer.
title_path = pygxml.compile("store.book.0.title")
price_path = pygxml.compile("store.book.0.price")
title, price = pygxml.get_many(xml, [title_path, price_path])

# mmap input — true zero-copy on huge files. parse(mm) keeps the mmap by
# reference, so subsequent .get() calls re-borrow it without copying.
import mmap
with open('huge.xml', 'rb') as f, mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
    title = pygxml.parse(mm).get("store.book.0.title").to_str()

# Namespace prefix-aware match
pygxml.get(atom_xml, "atom:feed.atom:entry.atom:title").to_str()

# Validate well-formedness without raising.
pygxml.validate(xml)                                        # True

API

Module-level functions

Function Description
get(xml, path) Query xml (str) at path; returns Result
get_bytes(xml, path) Query xml (bytes) at path; returns Result
get_buffer(xml, path) Query xml (buffer protocol) at path; returns Result
get_many(xml, paths) Query xml (str) at each path (str or Path); returns list[Result]
get_many_bytes(xml, paths) Query xml (bytes) at each path (str or Path); returns list[Result]
get_many_buffer(xml, paths) Query xml (buffer protocol) at each path (str or Path); returns list[Result]
parse(xml) Parse the entire XML document into a Result
validate(xml) True if xml is syntactically valid
compile(path) Pre-compile a path expression; returns a Path

Result

get and parse return a Result. Result accessors

Properties

Property Description
r.type_ Python type for this value: None, bool, int, float, str, list, dict
r.value Value converted to the corresponding Python type: None / int / float / str / list[Result] / dict[str, Result]

gjson-style methods

Method Description
r.exists() True if the value was found in the XML
r.to_str() String representation (text content for elements, or full XML for dict/list elements)
r.to_int(),r.to_float() Typed coercions; return 0 / 0.0 when empty
r.to_bool() gjson-style boolean coercion (see below); returns False when empty
r.get(path) Sub-query relative to this value
r.get_many(paths) Sub-query at multiple paths; returns list[Result]

Result.to_bool() follows gjson semantics:

  • "1" / "true"True; "0" / "false"False
  • "\"t\"" / "\"T\"" / "\"1\""True; "\"f\"" / "\"F\"" / "\"0\""False
  • "\"true\"" / "\"TRUE\"" / "\"True\""True; "\"false\"" / "\"FALSE\"" / "\"False\""False
  • Any other value: to_int() != 0 (non-numeric strings → False)
  • Non-empty dict or list Result → True; empty Result → False

Result.get(path) only descends into element items — scalar items (attributes, #text, counts, modifier aggregates like @sum) have no children, so .get(...) against them yields an empty Result.

Pythonic methods

Syntax Description
str(v),repr(v) dict: <Result type=dict, keys=[...]>; list: <Result type=list, value=[...]>; others: str(v.value)
int(v) 64-bit Integer
float(v) 64-bit float
bool(v) Equivalent to bool(v.value)False for null/false/0/""/[]/{}
len(v) Chars for String; element count for list/dict elements
v[key] Subscript access
key in v Key membership for dict; string match for list
iter(v) Lazy iterator: chars for str; Results for list; keys for dict
v.keys() Lazy KeysView of dict keys (raises TypeError for non-dict)
v.values() Lazy ValuesView of dict values (raises TypeError for non-dict)
v.items() Lazy ItemsView of (key, Result) pairs (raises TypeError for non-dict)
r == "x", r == ["a", "b"], r == other_result Equality with str/list/Result

Path

compile(path) returns a Path — a compiled, reusable path expression. Using a pre-compiled Path avoids re-parsing the path string on every call, which matters when querying many documents with the same path.

Method Description
p.get(data) Query XML string data; equivalent to get(data, path)
p.get_bytes(data) Query XML bytes data
p.get_buffer(data) Query buffer-protocol data (mmap, bytearray, memoryview)

Path objects are also accepted wherever a path string is accepted: get_many, get_many_bytes, get_many_buffer, and Result.get_many.

Path syntax

syntax meaning
a.b.c Descend into child elements (local-name match, ignores namespaces)
a.0, a.1 N-th same-named sibling
a.# Count of same-named siblings
a.#.b Project b over all same-named siblings
*, ? Wildcards in element name
@name Attribute reference
#text Explicit text content
\., \@ Escape
a.#(expr) Filter, first match. expr ::= path op value
a.#(expr)# Filter, all matches
path | @modifier Apply modifier (@reverse, @first, @last, @count, @sort, @sort_n, @unique/@uniq, @flatten, @tostr, @sum, @avg/@mean, @min, @max)
prefix:local Prefix-aware match — qualified-name literal compare (matches <atom:title>, not <rss:title>)
a.**.b Descendant: match every b at any depth under a (XPath // equivalent)

A bare child name (no .N/.#/filter) is implicit: at the terminal position it returns every match (a list-shaped Result), but at a non-terminal position the engine raises ValueError if more than one element matches. To chain past a multi-match step, pick one (.0, .1, …) or project explicitly with .#. Single-match elements (e.g., a unique root element) chain transparently.

Result.get(path) follows the same rule: it requires the receiver to hold at most one element. To process every element of a multi-match Result, iterate (for item in result: item.get(...)).

Filter operators: == = != < <= > >= % (glob) !% (negative glob). Filter values: "string", number, true/false, or bare unquoted text.

Inputs

bytes, str, mmap.mmap, bytearray, memoryview — anything implementing the buffer protocol. bytes and mmap are zero-copy on the way in; str is copied once to UTF-8.

pygxml.parse(data) keeps a reference to the input object instead of copying it. Two consequences worth knowing:

  • For mmap input, do not close the mmap while a Result derived from it is still in use — re-borrows during .get() / .str() will fault.
  • For bytearray and other mutable buffers, mutations after parse(...) are observed by subsequent Result accesses.

pygxml.get(data, path) does not retain the input; captured element fragments inside the returned Result are owned copies.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pygxml-0.0.4.tar.gz (60.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pygxml-0.0.4-cp311-abi3-win_arm64.whl (433.0 kB view details)

Uploaded CPython 3.11+Windows ARM64

pygxml-0.0.4-cp311-abi3-win_amd64.whl (445.8 kB view details)

Uploaded CPython 3.11+Windows x86-64

pygxml-0.0.4-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (573.0 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

pygxml-0.0.4-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (569.2 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

pygxml-0.0.4-cp311-abi3-macosx_11_0_arm64.whl (534.5 kB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

pygxml-0.0.4-cp311-abi3-macosx_10_12_x86_64.whl (546.2 kB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file pygxml-0.0.4.tar.gz.

File metadata

  • Download URL: pygxml-0.0.4.tar.gz
  • Upload date:
  • Size: 60.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pygxml-0.0.4.tar.gz
Algorithm Hash digest
SHA256 07e402f46c006d325739d5e6d78641855676ee0dfe5533643e0fc54a02556aec
MD5 0451eaba1c477c162b5b40b69404fff4
BLAKE2b-256 cdd90053bccd38d5dcba45c9e025999b14f8b1dc9ffe4ffec98c200b65511ee1

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.4.tar.gz:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.4-cp311-abi3-win_arm64.whl.

File metadata

  • Download URL: pygxml-0.0.4-cp311-abi3-win_arm64.whl
  • Upload date:
  • Size: 433.0 kB
  • Tags: CPython 3.11+, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pygxml-0.0.4-cp311-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 0821609e59b0ea7523fa15710abca3f0579ec2338abf48407306a262efa5574b
MD5 2631094aa54a4283239fb97d5a4bfeaa
BLAKE2b-256 0394994dfd01388e6fdfd4146959f7ed8fe53ae8f29c0a7e64bfdbd0773a42f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.4-cp311-abi3-win_arm64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.4-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: pygxml-0.0.4-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 445.8 kB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pygxml-0.0.4-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 99c864fa89f7453b59d68a870c07f5ec28b232aa0b0d72eebcfd5008d35db11b
MD5 97055897db2f949cf2cac0be3d40ecb3
BLAKE2b-256 c0b2f137a5eedde8828349ea1d80cba9af30e94562fa06c0a4f13683dc71589d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.4-cp311-abi3-win_amd64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.4-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.4-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cd23c0cb46b5a95cc02c1b4e033192dbc49ea3849573207790dceb7fae5063b6
MD5 1948a498cda588a076dff57e44159187
BLAKE2b-256 4a050642a94a0e029c1f85665add7ae2bcd024fae7cb775ce69770cd283656f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.4-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.4-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.4-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 7c5ce8dfca81aace7e0a60d65f82ef07b1851df8bf100859136202e5f8891718
MD5 41713b1060b3076200fe16b764befbf6
BLAKE2b-256 0f08212d3ce7b6e0fcc4b6c5b81e43969e0d7800550a373fc70df7f38d14a422

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.4-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.4-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.4-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e5e45c26ab3a07cccd74285b295d71a2524df8db79d6de515a9fa16ec69c19c3
MD5 60b9d5f8ae801360a2ea43d09720e70d
BLAKE2b-256 e8c8be29efec245e55a3365978cf95f1a1e33e065619a13098a94755b352d565

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.4-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygxml-0.0.4-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.4-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 144da41f5acc4c4715e6f001e7a9cf0f425bd47de414c2005997df1de8f334f1
MD5 5c716fa27eb0ed7b9a0e01b3b3b04283
BLAKE2b-256 8a0bbeb1888f3f4012a507c86c053735f4fb41389fc30a86f8974d96d76d8ce3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.4-cp311-abi3-macosx_10_12_x86_64.whl:

Publisher: publish.yml on minefuto/pygxml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page