Skip to main content

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

# Result.children() — walk direct child elements in document order,
# keeping duplicate tag names.
store = pygxml.get(xml, "store")
[name for name, _ in store.children()]                    # ['book', 'book', 'book']
[c.get("title").to_str() for _, c in store.children()]    # ['XML in a Nutshell', ...]

# 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)
v.children() Lazy ChildrenView of (name, Result) pairs for every direct child element, in document order (raises TypeError for non-dict)
r == "x", r == ["a", "b"], r == other_result Equality with str/list/Result

Result.items() groups same-named siblings under a single key, so the order between differently named siblings is lost. Result.children() yields one pair per direct child element instead: duplicate names are kept and document order is preserved. Names are raw QNames (namespace prefix included), and text, comments, CDATA and processing instructions are not yielded.

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

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.5.tar.gz (61.6 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.5-cp311-abi3-win_arm64.whl (433.7 kB view details)

Uploaded CPython 3.11+Windows ARM64

pygxml-0.0.5-cp311-abi3-win_amd64.whl (448.3 kB view details)

Uploaded CPython 3.11+Windows x86-64

pygxml-0.0.5-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (570.9 kB view details)

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

pygxml-0.0.5-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (567.3 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

pygxml-0.0.5-cp311-abi3-macosx_11_0_arm64.whl (532.7 kB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

pygxml-0.0.5-cp311-abi3-macosx_10_12_x86_64.whl (544.5 kB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

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

File metadata

  • Download URL: pygxml-0.0.5.tar.gz
  • Upload date:
  • Size: 61.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pygxml-0.0.5.tar.gz
Algorithm Hash digest
SHA256 6b40d1d6d1fb2ee83f62dad1bef6aa4aaafc0d22edf3aa4b82539926626614dd
MD5 14114a28221daa4e1822f7aadbb81f5e
BLAKE2b-256 6b19556c007de0d0761226fb92891f1b1f9f076d5b872e10f83abca35da47f98

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.5.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.5-cp311-abi3-win_arm64.whl.

File metadata

  • Download URL: pygxml-0.0.5-cp311-abi3-win_arm64.whl
  • Upload date:
  • Size: 433.7 kB
  • Tags: CPython 3.11+, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pygxml-0.0.5-cp311-abi3-win_arm64.whl
Algorithm Hash digest
SHA256 6fa075b1778f5fd298a3f4e33ad1424272c961128a363c74115215ff8a9586a9
MD5 2bc7fa85429cfe76c1a2d21e63c6c5f2
BLAKE2b-256 1e575c0db7ad70f5f978a851300f58afea507d6ade803c281df56e3c8b8d98ae

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.5-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.5-cp311-abi3-win_amd64.whl.

File metadata

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

File hashes

Hashes for pygxml-0.0.5-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 57a9041bf3d69fda09da5ee51094f7fbd2022c8a804a7ecc6551819862761716
MD5 8ec2f835e7bc202451c341b4f2bf297b
BLAKE2b-256 0ea9790a3bd3f9d9948a93d1bc730c2d23a456debcc0b6c7d01dbbc78fffc9cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.5-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.5-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.5-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 6685fffe452ddd2efa845cd905d3ebe9fb9bfe7c5fc1923d92f2185fac544ccb
MD5 15ca5548cd84e6f90ddc773324eddb9f
BLAKE2b-256 c8ab46a7d7e5f36db8e81225154018015ab12cfc1cfb491ed5ca2225651f00e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.5-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.5-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.5-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 dbea98ee943517a80249f986bbede7eb849295d3aa1f65d55b5dca3493c509ac
MD5 2e3045891dd472593a77bb311f1c0a0e
BLAKE2b-256 d66dc81a77a9a2f0cbba9f2ef62a4175be6a6353e77ca18974a45afedaa2ca15

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.5-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.5-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.5-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c2e7629b317661ab6d957af752fdd96cbf48e558863d3a3eb0a8276f7c08a830
MD5 883e9618b99b46dc88238c7274d9d7c9
BLAKE2b-256 f0a82851a08461f723bf7e64012fd8ad2a4007a914f8af43bf6f81644b58a5d9

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.5-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.5-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pygxml-0.0.5-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 027a5be906879a88e58180c983076b4c56bcde8471a04349effeeaad34076f63
MD5 4f09093f4b19c0d08a313b3b9c1c0211
BLAKE2b-256 0b6e87bbf97758c9b88afa345113b5ae4a2a77945d059510918c6a5b3dbd0569

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygxml-0.0.5-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.

Release history Release notifications | RSS feed

This release

0.0.5 This release

7 files

0.0.4

7 files

0.0.3

7 files

0.0.2

7 files

0.0.1

7 files

Supported by

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