Skip to content

Commit 9dd34bc

Browse files
committed
Add docs and changelog
1 parent 83f0c8e commit 9dd34bc

3 files changed

Lines changed: 43 additions & 3 deletions

File tree

CHANGELOG.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ Changelog
44
*unreleased*
55
~~~~~~~~~~~~
66

7-
No unreleased changes.
7+
Features:
8+
9+
* Expose a public structured marker tree via ``Marker.as_ast()`` (:pull:`1145`)
810

911
26.0 - 2026-01-20
1012
~~~~~~~~~~~~~~~~~

docs/markers.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,41 @@ when building markers dynamically from separate conditions.
6565

6666
.. versionadded:: 26.1
6767

68+
You can combine existing :class:`Marker` instances with ``&`` and ``|`` instead of
69+
parsing one long marker string. The string form preserves PEP 508 ``and`` / ``or``
70+
precedence. :meth:`Marker.as_ast` returns an immutable tree of
71+
:class:`MarkerCompare`, :class:`MarkerAnd`, and :class:`MarkerOr` nodes (see
72+
:class:`MarkerNode`).
73+
74+
.. doctest::
75+
76+
>>> from packaging.markers import Marker, MarkerCompare, MarkerAnd, MarkerOr
77+
>>> py_at_least_310 = Marker('python_version >= "3.10"')
78+
>>> posix = Marker('os_name == "posix"')
79+
>>> py_at_least_310 & posix
80+
<Marker('python_version >= "3.10" and os_name == "posix"')>
81+
>>> windows = Marker('sys_platform == "win32"')
82+
>>> macos = Marker('sys_platform == "darwin"')
83+
>>> windows | macos
84+
<Marker('sys_platform == "win32" or sys_platform == "darwin"')>
85+
>>> expr = Marker(
86+
... "python_version > '3.12' or (python_version == '3.12' and os_name == 'unix')"
87+
... )
88+
>>> node = expr.as_ast()
89+
>>> isinstance(node, MarkerOr)
90+
True
91+
>>> len(node.operands)
92+
2
93+
>>> isinstance(node.operands[0], MarkerCompare)
94+
True
95+
>>> (node.operands[0].left, node.operands[0].op, node.operands[0].right)
96+
('python_version', '>', '3.12')
97+
>>> isinstance(node.operands[1], MarkerAnd)
98+
True
99+
>>> [type(p).__name__ for p in node.operands[1].operands]
100+
['MarkerCompare', 'MarkerCompare']
101+
102+
.. versionadded:: 26.1
68103

69104
Reference
70105
---------
@@ -73,3 +108,6 @@ Reference
73108
:members:
74109
:special-members: __and__, __or__
75110
:exclude-members: __init__
111+
112+
.. autodata:: MarkerCompareOp
113+
:no-value:

src/packaging/markers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,8 @@ def as_ast(self) -> MarkerNode | None:
511511
"""Return a structured tree for this marker.
512512
513513
The tree uses :class:`MarkerCompare`, :class:`MarkerAnd`, and
514-
:class:`MarkerOr`, preserving PEP 508 ``and`` / ``or`` precedence;
515-
parenthesized sub-expressions are nested nodes.
514+
:class:`MarkerOr`. It preserves PEP 508 ``and`` / ``or`` precedence.
515+
Parenthesized sub-expressions are nested nodes.
516516
517517
:returns: The root node, or ``None`` if the internal marker list is empty
518518
(a vacuous marker, which :meth:`evaluate` treats as true).

0 commit comments

Comments
 (0)