pygx.symbolic¶
Symbolic Object Model.
symbolic
¶
Symbolic Object Model.
This module enables Symbolic Object Model, which defines and implements the symbolic interfaces for common Python types (e.g. symbolic class, symbolic function and symbolic container types). Based on symbolic types, symbolic objects can be created, which can be then inspected, manipulated symbolically.
ContextualObject
¶
ContextualObject(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Object
Base class for contextual objects.
Contextual objects are objects whose attributes can be dynamically overridden
using pg.contextual.override or resolved through
pg.contextual.Placeholder, allowing them to inherit values from their
containing objects.
Usages:
# Define a contextual object.
class A(pg.ContextualObject):
x: int
y: Any = pg.contextual.Placeholder()
# Create an instance of A
a = A(1)
print(a.x) # Outputs: 1
print(a.y) # Raises an error, as `a` has no containing object.
# Define another contextual object containing an instance of A
class B(pg.ContextualObject):
y: int
a: A
# Create an instance of B, containing "a"
b = B(y=2, a=a)
print(a.y) # Outputs: 2, as "y" is resolved from the containing object (B).
# Contextual overrides are thread-specific
with pg.contextual.override(x=2):
print(a.x) # Outputs: 2
# Thread-specific behavior of `pg.contextual.override`
def foo(a):
print(a.x)
with pg.contextual.override(x=3):
t = threading.Thread(target=foo, args=(a,))
t.start()
t.join()
# Outputs: 1, because `pg.contextual.override` is limited to the current
# thread to avoid clashes in multi-threaded environments.
# To propagate the override to a new thread, use `pg.contextual.with_override`
with pg.contextual.override(x=3):
t = threading.Thread(target=pg.contextual.with_override(foo), args=(a,))
t.start()
t.join()
# Outputs: 3, as the override is explicitly propagated.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
sym_inferred
¶
Override to allow attribute to access scoped value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
attribute name. |
required |
default
|
Any
|
Value to return if inference fails (else the error propagates). |
RAISE_IF_NOT_FOUND
|
**kwargs
|
Any
|
Optional keyword arguments for value inference. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The value of the symbolic attribute. If not available, returns the default value. |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If the attribute does not exist or contextual attribute
is not ready (and no |
Source code in pygx/contextual/_contextual_object.py
DescendantQueryOption
¶
Bases: Enum
Options for querying descendants through sym_descendant.
FieldUpdate
¶
FieldUpdate(
path: KeyPath,
target: Symbolic,
field: Field | None,
old_value: Any,
new_value: Any,
)
Bases: Formattable
Class that describes an update to a field in an object tree.
Constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
KeyPath
|
KeyPath of the field that is updated. |
required |
target
|
Symbolic
|
Parent of updated field. |
required |
field
|
Field | None
|
Specification of the updated field. |
required |
old_value
|
Any
|
Old value of the field. |
required |
new_value
|
Any
|
New value of the field. |
required |
Source code in pygx/symbolic/_base.py
format
¶
Formats this object.
Source code in pygx/symbolic/_base.py
Inferential
¶
Bases: TopologyAware, CustomTyping
Interface for values that could be dynamically inferred upon read.
Inferential values are objects whose values are not determined directly but
are instead derived from other sources, such as references (pg.Ref)
to other objects or computed based on their context
(pg.symbolic.ValueFromParentChain) such as the symbolic tree they
reside in.
When inferential values are utilized as symbolic attributes, we can obtain
their original definition by invoking pg.Symbolic.sym_getattr, and
their inferred values can be retrieved by calling
pg.Symbolic.sym_inferred. The values retrieved from pg.Dict,
pg.List and pg.Object through __getitem__ or
__getattribute__ are all inferred values.
infer
abstractmethod
¶
Returns the inferred value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Optional keyword arguments for inference, which are usually inferential subclass specific. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
Inferred value. |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If the value cannot be inferred. |
Source code in pygx/symbolic/_base.py
Symbolic
¶
Symbolic(
*,
allow_partial: bool,
accessor_writable: bool,
sealed: bool,
root_path: KeyPath | None,
init_super: bool = True
)
Bases: TopologyAware, Formattable, WireConvertible, MaybePartial, HtmlConvertible
Base for all symbolic types.
Symbolic types are types that provide interfaces for symbolic programming, based on which symbolic objects can be created. In PyGX, there are three categories of symbolic types:
- Symbolic classes: Defined by
pygx.Objectsubclasses, including symbolic classes created frompygx.symbolize, which inheritpygx.ClassWrapper, a subclass ofpg.Object. - Symbolic functions: Defined by
pygx.Functor. - Symbolic container types: Defined by
pygx.Listandpygx.Dict.
Constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow_partial
|
bool
|
Whether to allow required fields to be MISSING_VALUE or partial. |
required |
accessor_writable
|
bool
|
Whether to allow write access via attributes. This flag
is useful when we want to enforce update of fields using |
required |
sealed
|
bool
|
Whether object is sealed that cannot be changed. This flag is useful when we don't want downstream to modify the object. |
required |
root_path
|
KeyPath | None
|
KeyPath of current object in its context (object tree). |
required |
init_super
|
bool
|
If True, call super.init, otherwise short-circuit. This
flag is useful when user want to explicitly implement |
True
|
Source code in pygx/symbolic/_base.py
sym_puresymbolic
property
¶
Returns True if current value is or contains subnodes of PureSymbolic.
sym_abstract
property
¶
Returns True if current value is abstract (partial or pure symbolic).
sym_parent
property
¶
sym_parent: Symbolic
Returns the containing symbolic object.
For a sym=False object (which has no symbolic tree, and is held by
reference rather than adopted) this is always None.
sym_path
property
¶
sym_path: KeyPath
Returns the path of current object from the root of its symbolic tree.
For a sym=False object (which has no symbolic tree) this is always
the empty pg.KeyPath().
accessor_writable
property
¶
Returns True if mutation can be made by attribute assignment.
sym_seal
¶
sym_missing
abstractmethod
¶
Returns missing values.
The public override point (there is no protected _sym_missing hook):
concrete subclasses (Object / Dict / List / Functor)
override this directly and route their computation through
self._sym_cached('_sym_missing_values', compute, flatten). A user
subclass overrides it too and may call super().sym_missing() to get
the (cached) parent result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flatten
|
bool
|
If True, flatten nested structures into a key-path dict. |
True
|
Source code in pygx/symbolic/_base.py
sym_nondefault
abstractmethod
¶
Returns non-default values.
The public override point (there is no protected _sym_nondefault
hook); see sym_missing for the override / caching contract.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flatten
|
bool
|
If True, flatten nested structures into a key-path dict. |
True
|
Source code in pygx/symbolic/_base.py
sym_ancestor
¶
sym_ancestor() -> Optional[Symbolic]
sym_ancestor(where: Callable[[Any], bool]) -> Optional[Symbolic]
Returns the nearest ancestor matching where.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
where
|
type[_T] | Callable[[Any], bool] | None
|
One of: - None (default): returns the immediate parent. - A class: returns the nearest ancestor that is an instance of the class. Static type-checkers narrow the result to that class. - A callable: returns the nearest ancestor for which the callable returns True. |
None
|
Source code in pygx/symbolic/_base.py
sym_descendants
¶
sym_descendants(
where: None = None,
option: DescendantQueryOption = ...,
include_self: bool = ...,
) -> list[Any]
sym_descendants(
where: type[_T],
option: DescendantQueryOption = ...,
include_self: bool = ...,
) -> list[_T]
sym_descendants(
where: Callable[[Any], bool],
option: DescendantQueryOption = ...,
include_self: bool = ...,
) -> list[Any]
sym_descendants(
where: type[_T] | Callable[[Any], bool] | None = None,
option: DescendantQueryOption = ALL,
include_self: bool = False,
) -> list[Any]
Returns all descendants matching where.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
where
|
type[_T] | Callable[[Any], bool] | None
|
One of:
- None (default): all descendants are returned.
- A class: descendants that are instances of the class are returned.
Static type-checkers narrow the result to |
None
|
option
|
DescendantQueryOption
|
Descendant query options, indicating whether all matched, immediate matched or only the matched leaf nodes will be returned. |
ALL
|
include_self
|
bool
|
If True, |
False
|
Returns:
| Type | Description |
|---|---|
list[Any]
|
A list of objects that match |
Source code in pygx/symbolic/_base.py
sym_has
¶
sym_has(path: KeyPath | str | int) -> bool
Returns True if a path exists in the sub-tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
KeyPath | str | int
|
A KeyPath object or equivalence. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the path exists in current sub-tree, otherwise False. |
Source code in pygx/symbolic/_base.py
sym_get
¶
sym_get(
path: KeyPath | str | int,
default: Any = RAISE_IF_NOT_FOUND,
use_inferred: bool = False,
) -> Any
Returns a sub-node by path.
NOTE: there is no sym_set, use sym_rebind.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
KeyPath | str | int
|
A KeyPath object or equivalence. |
required |
default
|
Any
|
Default value if path does not exists. If absent, |
RAISE_IF_NOT_FOUND
|
use_inferred
|
bool
|
If True, return inferred value instead of the symbolic form
of |
False
|
Returns:
| Type | Description |
|---|---|
Any
|
Value of symbolic attribute specified by path if found, otherwise the |
Any
|
default value if it's specified. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
Source code in pygx/symbolic/_base.py
sym_hasattr
abstractmethod
¶
sym_getattr
¶
Gets a symbolic attribute.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str | int
|
Key of symbolic attribute. |
required |
default
|
Any
|
Default value if attribute does not exist. If absent, |
RAISE_IF_NOT_FOUND
|
Returns:
| Type | Description |
|---|---|
Any
|
Value of symbolic attribute if found, otherwise the default value |
Any
|
if it's specified. |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If |
Source code in pygx/symbolic/_base.py
sym_inferrable
¶
Returns True if the attribute under key can be inferred.
Source code in pygx/symbolic/_base.py
sym_inferred
¶
Returns the inferred value of the attribute under key.
The public override point (there is no protected _sym_inferred hook):
a subclass overrides this directly and calls super().sym_inferred(...)
for the base infer-unwrap (read the attribute; if it is Inferential,
return infer(**kwargs)). The default / RAISE_IF_NOT_FOUND
fallback — return default instead of propagating an inference error —
is applied here, so a subclass override that defers to super() for the
not-found path inherits it.
Source code in pygx/symbolic/_base.py
sym_keys
abstractmethod
¶
sym_values
abstractmethod
¶
sym_items
abstractmethod
¶
sym_setparent
¶
sym_contains
¶
Returns True if the object contains sub-nodes of given value or type.
Source code in pygx/symbolic/_base.py
sym_setpath
¶
sym_setpath(path: str | KeyPath | None) -> None
Sets the path of current node in its symbolic tree.
Source code in pygx/symbolic/_base.py
sym_rebind
¶
sym_rebind(
path_value_pairs: None | (Mapping[Any, Any] | Callable[..., Any]) = None,
*,
raise_on_no_change: bool = True,
notify_parents: bool = True,
skip_notification: bool | None = None,
**kwargs: Any
) -> Self
Mutates the sub-nodes of current object.
sym_rebind is the recommended way for mutating symbolic objects in
PyGX:
- It allows mutations not only on immediate child nodes, but on the entire sub-tree.
- It allows mutations by rules via passing a callable object as the
value for
path_value_pairs. - It batches the updates from multiple sub-nodes, which triggers the
on_sym_changeoron_sym_boundevent once for recomputing the parent object's internal states. - It respects the "sealed" flag of the object or the
pg.as_sealedcontext manager to trigger permission error.
Example::
# # Rebind on pg.Object subclasses. #
class A(pg.Object): x: pg.typing.Dict([ ('y', pg.typing.Int(default=0)) ]) z: int = 1
a = A() # Rebind using path-value pairs. a.sym_rebind({ 'x.y': 1, 'z': 0 })
# Rebind using **kwargs. a.sym_rebind(x={y: 1}, z=0)
# Rebind using rebinders. # Rebind based on path. a.sym_rebind(lambda k, v: 1 if k == 'x.y' else v) # Rebind based on key. a.sym_rebind(lambda k, v: 1 if k and k.key == 'y' else v) # Rebind based on value. a.sym_rebind(lambda k, v: 0 if v == 1 else v) # Rebind baesd on value and parent. a.sym_rebind(lambda k, v, p: (0 if isinstance(p, A) and isinstance(v, int) else v))
# Rebind on pg.Dict. # d = pg.Dict(value_spec=pg.typing.Dict([ ('a', pg.typing.Dict([ ('b', pg.typing.Int()), ])), ('c', pg.typing.Float()) ])
# Rebind using **kwargs. d.sym_rebind(a={b: 1}, c=1.0)
# Rebind using key path to value dict. d.sym_rebind({ 'a.b': 2, 'c': 2.0 })
# NOT OKAY: **kwargs and dict/rebinder cannot be used at the same time. d.sym_rebind({'a.b': 2}, c=2)
# Rebind with rebinder by path (on subtree). d.sym_rebind(lambda k, v: 1 if k.key == 'b' else v)
# Rebind with rebinder by value (on subtree). d.sym_rebind(lambda k, v: 0 if isinstance(v, int) else v)
# # Rebind on pg.List. # l = pg.List([{ 'a': 'foo', 'b': 0, } ], value_spec = pg.typing.List(pg.typing.Dict([ ('a', pg.typing.Str()), ('b', pg.typing.Int()) ]), max_size=10))
# Rebind using integer as list index: update semantics on list[0]. l.sym_rebind({ 0: { 'a': 'bar', 'b': 1 } })
# Rebind: trigger append semantics when index is larger than list length. l.sym_rebind({ 999: { 'a': 'fun', 'b': 2 } })
# Rebind using key path. l.sym_rebind({ '[0].a': 'bar2' '[1].b': 3 })
# Rebind using function (rebinder). # Change all integers to 0 in sub-tree. l.sym_rebind(lambda k, v: v if not isinstance(v, int) else 0)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path_value_pairs
|
None | (Mapping[Any, Any] | Callable[..., Any])
|
A dictionary of key/or key path to new field value, or a function that generate updates based on the key path, value and parent of each node under current object. We use terminology 'rebinder' for this type of functions. The signature of a rebinder is: |
None
|
raise_on_no_change
|
bool
|
If True, raises |
True
|
notify_parents
|
bool
|
If True (default), parents will be notified upon change.
Otherwisee only the current object and the impacted children will
be notified. A most common use case for setting this flag to False
is when users want to rebind a child within the parent |
True
|
skip_notification
|
bool | None
|
If True, there will be no |
None
|
**kwargs
|
Any
|
For |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
Self. |
Raises:
| Type | Description |
|---|---|
WritePermissionError
|
If object is sealed. |
KeyError
|
If update location specified by key or key path is not aligned with the schema of the object tree. |
TypeError
|
If updated field value type does not conform to field spec. |
ValueError
|
If updated field value is not acceptable according to field
spec, or nothing is updated and |
SymbolicModeError
|
If the object was declared with |
Note
On a sym=False (the default) object, sym_rebind is the
batch mutation surface for the IMMEDIATE fields: plain field-name
keys (dict or **kwargs) are validated and applied through the
same write path as attribute assignment, with one batched
on_sym_change on the object itself (there is no tree to
propagate through). Path keys ('a.b', 'x[0]') and rebinder
callables need the symbolic tree and raise SymbolicModeError.
Sealing follows frozen alone — attr_write gates only the
dotted surface.
Source code in pygx/symbolic/_base.py
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 | |
sym_jsonify
abstractmethod
¶
Converts representation of current object to a plain Python object.
sym_ne
¶
sym_eq
¶
sym_gt
¶
sym_lt
¶
sym_hash
abstractmethod
¶
sym_setorigin
¶
sym_setorigin(
source: Any,
tag: str,
stacktrace: bool | None = None,
stacklimit: int | None = None,
stacktop: int = -1,
)
Sets the symbolic origin of current object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Any
|
Source value for current object. |
required |
tag
|
str
|
A descriptive tag of the origin. Built-in tags are:
|
required |
stacktrace
|
bool | None
|
If True, enable stack trace for the origin. If None, enable
stack trace if |
None
|
stacklimit
|
int | None
|
An optional integer to limit the stack depth. If None, it's
determined by the value passed to |
None
|
stacktop
|
int
|
A negative or zero-value integer indicating the stack top among
the stack frames that we want to present to user, by default it's
1-level up from the stack within current |
-1
|
Example::
def foo(): return bar()
def bar(): s = MyObject() t = s.build() t.sym_setorigin(s, 'builder', stacktrace=True, stacklimit=5, stacktop=-1)
This example sets the origin of t using s as its source with tag
'builder'. We also record the callstack where the sym_setorigin is
called, so users can call t.sym_origin.stacktrace to get the call stack
later. The stacktop -1 indicates that we do not need the stack frame
within sym_setorigin, so users will see the stack top within the
function bar. We also set the max number of stack frames to display to 5,
not including the stack frame inside sym_setorigin.
Source code in pygx/symbolic/_base.py
set_accessor_writable
¶
sym_clone
¶
sym_clone(
deep: bool = False,
memo: Any | None = None,
override: Mapping[Any, Any] | None = None,
) -> Self
Clones current object symbolically (the formalized public entry).
Layers override-rebind + origin-tracking on top of the per-subclass
_sym_clone core, so copy protocols (__copy__ / __deepcopy__),
pg.clone, and child walks compose those exactly once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
deep
|
bool
|
If True, perform deep copy (equivalent to copy.deepcopy). Otherwise shallow copy (equivalent to copy.copy). |
False
|
memo
|
Any | None
|
Memo object for deep clone. |
None
|
override
|
Mapping[Any, Any] | None
|
An optional dict of key path to new values to override cloned
value. Under |
None
|
Returns:
| Type | Description |
|---|---|
Self
|
A copy of self. |
Source code in pygx/symbolic/_base.py
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 | |
to_json
¶
to_json_str
¶
load
classmethod
¶
Loads an instance of this type using the global load handler.
Source code in pygx/symbolic/_base.py
save
¶
Saves current object using the global save handler.
inspect
¶
inspect(
path_regex: str | None = None,
where: None | (Callable[[Any], bool] | Callable[[Any, Any], bool]) = None,
custom_selector: None | (
Callable[[KeyPath, Any], bool] | Callable[[KeyPath, Any, Any], bool]
) = None,
file: IO[str] = stdout,
**kwargs: Any
) -> None
Inspects current object by printing out selected values.
Example::
class A(pg.Object): x: int = 0 y: str
value = { 'a1': A(x=0, y=0), 'a2': [A(x=1, y=1), A(x=1, y=2)], 'a3': { 'p': A(x=2, y=1), 'q': A(x=2, y=2) } }
# Inspect without constraint,
# which is equivalent as print(value.format(exclude_defaults=True))
# Shall print:
# {
# a1 = A(y=0)
# a2 = [
# 0: A(x=1, y=1)
# 1: A(x=1, y=2)
# a3 = {
# p = A(x=2, y=1)
# q = A(x=2, y=2)
# }
# }
value.inspect(exclude_defaults=True)
# Inspect by path regex. # Shall print: # {'a3.p': A(x=2, y=1)} value.inspect(r'.*p')
# Inspect by value. # Shall print: # { # 'a3.p.x': 2, # 'a3.q.x': 2, # 'a3.q.y': 2, # } value.inspect(where=lambda v: v==2)
# Inspect by path, value and parent. # Shall print: # { # 'a2[1].y': 2 # } value.inspect( r'.*y', where=lambda v, p: v > 1 and isinstance(p, A) and p.x == 1))
# Inspect by custom_selector. # Shall print: # { # 'a2[0].x': 1, # 'a2[0].y': 1, # 'a3.q.x': 2, # 'a3.q.y': 2 # } value.inspect( custom_selector=lambda k, v, p: ( len(k) == 3 and isinstance(p, A) and p.x == v))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path_regex
|
str | None
|
Optional regex expression to constrain path. |
None
|
where
|
None | (Callable[[Any], bool] | Callable[[Any, Any], bool])
|
Optional callable to constrain value and parent when path matches
|
None
|
custom_selector
|
None | (Callable[[KeyPath, Any], bool] | Callable[[KeyPath, Any, Any], bool])
|
Optional callable object as custom selector. When
|
None
|
file
|
IO[str]
|
Output file stream. This can be any object with a |
stdout
|
**kwargs
|
Any
|
Wildcard keyword arguments to pass to |
{}
|
Source code in pygx/symbolic/_base.py
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 | |
on_sym_change
abstractmethod
¶
on_sym_change(field_updates: dict[KeyPath, FieldUpdate])
Event that is triggered when field values in the subtree are updated.
This event will be called
* On per-field basis when object is modified via attribute.
* In batch when multiple fields are modified via sym_rebind.
When a field in an object tree is updated, all ancestors' on_sym_change event
will be triggered in order, from the nearest one to furthest one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field_updates
|
dict[KeyPath, FieldUpdate]
|
Updates made to the subtree. Key path is relative to current object. |
required |
Source code in pygx/symbolic/_base.py
SymbolicModeError
¶
Bases: Exception
Raised when a tree-dependent operation is used on a sym=False object.
A pg.Object subclass declared with sym=False is a flat,
reference-semantics object with no symbolic tree. Operations that require a
tree position or tree-propagated mutation — sym_rebind, sym_path,
sym_parent, sym_root, sym_ancestor, sym_setparent /
sym_setpath, and contextual resolution — raise this error. Declare the
class with sym=True to use those features (the default is
sym=False — a flat, validated dataclass; #524).
Subclasses Exception directly (not AttributeError) so contextual
resolution's error handling cannot silently swallow it.
TraverseAction
¶
WritePermissionError
¶
Bases: Exception
Exception raisen when write access to object fields is not allowed.
ClassWrapper
¶
ClassWrapper(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Object
Base class for symbolic class wrapper.
Please see pygx.wrap for details.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
Dict
¶
Dict(
dict_obj: (
None | Iterable[tuple[str | int, Any]] | dict[str | int, Any]
) = None,
*,
value_spec: Dict | None = None,
wrap: bool = True,
onchange_callback: (
None | Callable[[dict[KeyPath, FieldUpdate]], None]
) = None,
allow_partial: bool = False,
accessor_writable: bool = True,
sealed: bool = False,
pass_through: bool = False,
root_path: KeyPath | None = None,
as_object_attributes_container: bool = False,
under_sym_mode: bool = True,
**kwargs: Any
)
Bases: dict[K, V], Symbolic, CustomTyping
Symbolic dict.
pg.Dict implements a dict type whose instances are symbolically
programmable, which is a subclass of the built-in Python dict and
a subclass of pygx.Symbolic.
pg.Dict provides the following features:
- It a symbolic programmable dict with string keys.
- It enables attribute access on dict keys.
- It supports symbolic validation and value completitions based on schema.
- It provides events to handle sub-nodes changes.
pg.Dict can be used as a regular dict with string keys::
# Construct a symbolic dict from key value pairs. d = pg.Dict(x=1, y=2)
or::
# Construct a symbolic dict from a mapping object. d = pg.Dict({'x': 1, 'y': 2})
Besides regular items access using [], it allows attribute access
to its keys::
# Read access to key x.
assert d.x == 1
# Write access to key 'y'. d.y = 1
pg.Dict supports symbolic validation when the value_spec argument
is provided::
d = pg.Dict(x=1, y=2, value_spec=pg.typing.Dict([ ('x', pg.typing.Int(min_value=1)), ('y', pg.typing.Int(min_value=1)), (pg.typing.StrKey('foo.*'), pg.typing.Str()) ])
# Okay: all keys started with 'foo' is acceptable and are strings. d.foo1 = 'abc'
# Raises: 'bar' is not acceptable as keys in the dict. d.bar = 'abc'
Users can mutate the values contained in it::
d = pg.Dict(x=pg.Dict(y=1), p=pg.List([0])) d.sym_rebind({ 'x.y': 2, 'p[0]': 1 })
It also allows the users to subscribe subtree updates::
def on_change(updates): print(updates)
d = pg.Dict(x=1, onchange_callaback=on_change)
# on_change will be triggered on item insertion.
d['y'] = {'z': 1}
# on_change will be triggered on item removal.
del d.x
# on_change will also be triggered on subtree change.
d.sym_rebind({'y.z': 2})
Constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dict_obj
|
None | Iterable[tuple[str | int, Any]] | dict[str | int, Any]
|
A dict as initial value for this Dict. |
None
|
value_spec
|
Dict | None
|
Value spec that applies to this Dict. |
None
|
wrap
|
bool
|
Container-level default for whether nested raw
|
True
|
onchange_callback
|
None | Callable[[dict[KeyPath, FieldUpdate]], None]
|
Callback when sub-tree has been modified. |
None
|
allow_partial
|
bool
|
Whether to allow unbound or partial fields. This takes effect only when value_spec is not None. |
False
|
accessor_writable
|
bool
|
Whether to allow modification of this Dict using accessors (operator[] / attributes). |
True
|
sealed
|
bool
|
Whether to seal this Dict after creation. |
False
|
pass_through
|
bool
|
When True (and |
False
|
root_path
|
KeyPath | None
|
KeyPath of this Dict in its object tree. |
None
|
as_object_attributes_container
|
bool
|
Internal — when True, child item
parents propagate to |
False
|
under_sym_mode
|
bool
|
Internal — the inner |
True
|
**kwargs
|
Any
|
Key value pairs that will be inserted into the dict as
initial value, which provides a syntax sugar for usage as below:
|
{}
|
Source code in pygx/symbolic/_dict.py
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | |
value_spec
property
¶
value_spec: Dict | None
Returns value spec of this dict.
NOTE(daiyip): If this dict is schema-less, value_spec will be None.
partial
classmethod
¶
partial(
dict_obj: dict[str | int, Any] | None = None,
value_spec: Dict | None = None,
*,
onchange_callback: (
None | Callable[[dict[KeyPath, FieldUpdate]], None]
) = None,
**kwargs
) -> Self
Class method that creates a partial Dict object.
Source code in pygx/symbolic/_dict.py
from_json
classmethod
¶
from_json(
json_value: Any,
*,
value_spec: Dict | None = None,
allow_partial: bool = False,
root_path: KeyPath | None = None,
**kwargs: Any
) -> Self
Class method that load an symbolic Dict from a JSON value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_value
|
Any
|
Input JSON value, only JSON dict is acceptable. |
required |
value_spec
|
Dict | None
|
An optional value spec to apply. |
None
|
allow_partial
|
bool
|
Whether to allow members of the dict to be partial. |
False
|
root_path
|
KeyPath | None
|
KeyPath of loaded object in its object tree. |
None
|
**kwargs
|
Any
|
Allow passing through keyword arguments that are not applicable. |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
A schemaless symbolic dict. For example:: d = Dict.from_json({ 'a': { 'type': '__main_.Foo', 'f1': 1, 'f2': { 'f21': True } } }) assert d.value_spec is None Okay:¶d.b = 1 a.f2 is bound by class Foo's field 'f2' definition (assume it defines¶a schema for the Dict field).¶assert d.a.f2.value_spec is not None Not okay:¶d.a.f2.abc = 1 |
Source code in pygx/symbolic/_dict.py
use_value_spec
¶
use_value_spec(value_spec: Dict | None, allow_partial: bool = False) -> Self
Applies a pg.typing.Dict as the value spec for current dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_spec
|
Dict | None
|
A Dict ValueSpec to apply to this Dict.
If current Dict is schema-less (whose immediate members are not
validated against schema), and |
required |
allow_partial
|
bool
|
Whether allow partial dict based on the schema. This flag will override allow_partial flag in init for spec-less Dict. |
False
|
Returns:
| Type | Description |
|---|---|
Self
|
Self. |
Raises:
| Type | Description |
|---|---|
ValueError
|
validation failed due to value error. |
RuntimeError
|
Dict is already bound with another spec. |
TypeError
|
type errors during validation. |
KeyError
|
key errors during validation. |
Source code in pygx/symbolic/_dict.py
sym_missing
¶
Returns missing values.
Returns:
| Type | Description |
|---|---|
dict[str | int, Any]
|
A dict of key to MISSING_VALUE. |
Source code in pygx/symbolic/_dict.py
sym_nondefault
¶
Returns non-default values as key/value pairs in a dict.
Source code in pygx/symbolic/_dict.py
sym_seal
¶
Seals or unseals current object (recursing into children).
Source code in pygx/symbolic/_dict.py
sym_attr_field
¶
sym_attr_field(key: str | int) -> Field | None
Returns the field definition for a symbolic attribute.
Source code in pygx/symbolic/_dict.py
sym_hasattr
¶
sym_keys
¶
Iterates the keys of symbolic attributes.
Source code in pygx/symbolic/_dict.py
sym_values
¶
Iterates the values of symbolic attributes.
Source code in pygx/symbolic/_dict.py
sym_items
¶
Iterates the (key, value) pairs of symbolic attributes.
Source code in pygx/symbolic/_dict.py
sym_setparent
¶
Override set parent of Dict to handle the passing through scenario.
Source code in pygx/symbolic/_dict.py
sym_hash
¶
Symbolic hashing.
Source code in pygx/symbolic/_dict.py
on_sym_change
¶
on_sym_change(field_updates: dict[KeyPath, FieldUpdate])
get
¶
Get item in this Dict.
Source code in pygx/symbolic/_dict.py
keys
¶
items
¶
Returns an iterator of (key, value) items in current dict.
Source code in pygx/symbolic/_dict.py
values
¶
Returns an iterator of values in current dict..
Source code in pygx/symbolic/_dict.py
copy
¶
pop
¶
Pops a key from current dict.
Source code in pygx/symbolic/_dict.py
clear
¶
Removes all the keys in current dict.
Source code in pygx/symbolic/_dict.py
setdefault
¶
Sets default as the value to key if not present.
Source code in pygx/symbolic/_dict.py
update
¶
update(
other: None | dict[str | int, Any] | Iterable[tuple[str | int, Any]] = None,
**kwargs
) -> None
Update Dict with the same semantic as update on standard dict.
Source code in pygx/symbolic/_dict.py
sym_jsonify
¶
sym_jsonify(
*,
exclude_frozen: bool = True,
exclude_defaults: bool = False,
exclude_keys: Sequence[str | int] | None = None,
use_inferred: bool = False,
exclude_none: bool = False,
omit_symbolic_marker: bool = True,
**kwargs
) -> WireValue
Converts current object to a dict with plain Python objects.
exclude_none=True (#519) drops None-valued entries at emission —
uniformly for pg.Object fields and pg.Dict entries (any mapping
key whose value is None; list items are never dropped);
exclude_defaults=True drops entries equal to their schema default
(the pydantic spelling — formerly hide_default_values).
Source code in pygx/symbolic/_dict.py
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 | |
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, Dict]
Implement pg.typing.CustomTyping interface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
KeyPath
|
KeyPath of current object. |
required |
value_spec
|
ValueSpec
|
Origin value spec of the field. |
required |
allow_partial
|
bool
|
Whether allow partial object to be created. |
required |
child_transform
|
None | Callable[[KeyPath, Field, Any], Any]
|
Function to transform child node values in dict_obj into their final values. Transform function is called on leaf nodes first, then on their containers, recursively. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, Dict]
|
A tuple (proceed_with_standard_apply, transformed value) |
Source code in pygx/symbolic/_dict.py
format
¶
format(
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
*,
python_format: bool = False,
exclude_frozen: bool = True,
exclude_defaults: bool = False,
exclude_missing: bool = False,
include_keys: set[str | int] | None = None,
exclude_keys: set[str | int] | None = None,
use_inferred: bool = False,
cls_name: str | None = None,
bracket_type: BracketType = CURLY,
key_as_attribute: bool = False,
extra_blankline_for_field_docstr: bool = False,
extra_fields: list[tuple[Field | None, str | int, Any]] | None = None,
**kwargs
) -> str
Formats this Dict.
extra_fields lets callers (notably Object.format) inject
rendered entries that aren't part of the underlying Dict — e.g.
enable_init=False fields stored on the parent __dict__.
Entries are formatted with the same per-field codepath as
normal entries (description, indentation, exclude_missing).
Source code in pygx/symbolic/_dict.py
Diff
¶
Diff(
left: Any = _DIFF_MISSING,
right: Any = _DIFF_MISSING,
children: dict[str, Diff] | None = None,
**kwargs: Any
)
Bases: PureSymbolic, Object, Extension
A value diff between two objects: a 'left' object and a 'right' object.
If one of them is missing, it may be represented by pg.Diff.MISSING
For example::
pg.Diff(3.14, 1.618) Diff(left=3.14, right=1.618) pg.Diff('hello world', pg.Diff.MISSING) Diff(left='hello world', right=MISSING)
Source code in pygx/symbolic/_diff.py
sym_eq
¶
format
¶
Override format to conditionally print the shared value or the diff.
Source code in pygx/symbolic/_diff.py
Functor
¶
Functor(
*args: Any,
root_path: KeyPath | None = None,
override_args: bool = False,
ignore_extra_args: bool = False,
**kwargs: Any
)
Bases: Object, Functor
Symbolic functions (Functors).
A symbolic function is a symbolic class with a __call__ method, whose
arguments can be bound partially, incrementally bound by attribute
assignment, or provided at call time.
Another useful trait is that a symbolic function is serializable, when its definition is imported by the target program and its arguments are also serializable. Therefore, it is very handy to move a symbolic function around in distributed scenarios.
Symbolic functions can be created from regular function via
pygx.functor::
# Create a functor class using @pg.functor decorator. @pg.functor([ ('a', pg.typing.Int(), 'Argument a'), # No field specification for 'b', which will be treated as any type. ]) def sum(a, b=1, args, *kwargs): return a + b + sum(args + kwargs.values())
sum(1)() # returns 2: prebind a=1, invoke with b=1 (default) sum(a=1)() # returns 2: same as above. sum()(1) # returns 2: bind a=1 at call time, b=1(default)
sum(b=2)(1) # returns 3: prebind b=2, invoke with a=1.
sum(b=2)() # wrong: a is not provided.
sum(1)(2) # wrong: 'a' is provided multiple times.
sum(1)(2, override_args=True) # ok: override a value with 2.
sum()(1, 2, 3, 4) # returns 10: a=1, b=2, args=[3, 4] sum(c=4)(1, 2, 3) # returns 10: a=1, b=2, *args=[3], *kwargs={'c': 4}
Or created by subclassing pg.Functor::
class Sum(pg.Functor): a: int b: int = 1
def _call(self) -> int:
return self.a + self.b
Usage on subclassed functors is the same as functors created from functions.
Constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
prebound positional arguments. |
()
|
root_path
|
KeyPath | None
|
The symbolic path for current object. |
None
|
override_args
|
bool
|
If True, allows arguments provided during |
False
|
ignore_extra_args
|
bool
|
If True, unsupported arguments can be passed in
during |
False
|
**kwargs
|
Any
|
prebound keyword arguments. |
{}
|
Raises:
| Type | Description |
|---|---|
KeyError
|
constructor got unexpected arguments. |
Source code in pygx/symbolic/_functor.py
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | |
non_default_args
property
¶
Returns the names of bound arguments whose values are not the default.
default_args
property
¶
Returns the names of bound argument whose values are the default.
is_subclassed_functor
classmethod
¶
partial
classmethod
¶
sym_inferred
¶
Overrides method to allow member overrides during call.
Source code in pygx/symbolic/_functor.py
on_sym_change
¶
on_sym_change(field_updates: dict[KeyPath, FieldUpdate])
Custom handling field change to update bound args.
Source code in pygx/symbolic/_functor.py
sym_missing
¶
Returns missing values for Functor.
Semantically unbound arguments are not missing, thus we only return partial
bound arguments in sym_missing. As a result, a functor is partial only
when any of its bound arguments is partial.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dict of missing key (or path) to missing value. |
Source code in pygx/symbolic/_functor.py
InferredValue
¶
InferredValue(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Object, Inferential
Base class for inferred values.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
ValueFromParentChain
¶
ValueFromParentChain(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: InferredValue
A value that could inferred from the parent chain.
For example::
class A(pg.Object): x: int y: int = pg.symbolic.ValueFromParentChain()
# Not okay: x is not inferential and is not specified.
A()
# Okay: both x and y are specified.
A(x=1, y=2)
# Okay: y is inferential, hence optional.
a = A(x=1)
# Raises: y is neither specified during init
# nor provided from the context.
a.y
d = pg.Dict(y=2, z=pg.Dict(a=a))
# a.y now refers to d.a since d is in its symbolic parent chain,
# aka. context.
assert a.y == 2
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
Insertion
dataclass
¶
Class that marks a value to insert into a list.
Example::
l = pg.List([0, 1]) l.sym_rebind({ 0: pg.Insertion(2) }) assert l == [2, 0, 1]
List
¶
List(
items: Iterable[Any] | None = None,
*,
value_spec: List | None = None,
wrap: bool = True,
onchange_callback: (
None | Callable[[dict[KeyPath, FieldUpdate]], None]
) = None,
allow_partial: bool = False,
accessor_writable: bool = True,
sealed: bool = False,
pass_through: bool = False,
root_path: KeyPath | None = None
)
Bases: list, Symbolic, CustomTyping
Symbolic list.
pg.List implements a list type whose instances are symbolically
programmable, which is a subclass of the built-in Python list,
and the subclass of pg.Symbolic.
pg.List can be used as a regular list::
# Construct a symbolic list from an iterable object. l = pg.List(range(10))
It also supports symbolic validation through the value_spec argument::
l = pg.List([1, 2, 3], value_spec=pg.typing.List( pg.typing.Int(min_value=1), max_size=10 ))
# Raises: 0 is not in acceptable range. l.append(0)
And can be symbolically manipulated::
l = pg.List([{'foo': 1}]) l.sym_rebind({ '[0].foo': 2 })
pg.query(l, where=lambda x: isinstance(x, int))
The user call also subscribe changes to its sub-nodes::
def on_change(updates): print(updates)
l = pg.List([{'foo': 1}], onchange_callaback=on_change)
# on_change will be triggered on item insertion.
l.append({'bar': 2})
# on_change will be triggered on item removal.
l.pop(0)
# on_change will also be triggered on subtree change.
l.sym_rebind({'[0].bar': 3})
Constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
Iterable[Any] | None
|
A optional iterable object as initial value for this list. |
None
|
value_spec
|
List | None
|
Value spec that applies to this List. |
None
|
wrap
|
bool
|
Container-level default for whether nested raw
|
True
|
onchange_callback
|
None | Callable[[dict[KeyPath, FieldUpdate]], None]
|
Callback when sub-tree has been modified. |
None
|
allow_partial
|
bool
|
Whether to allow unbound or partial fields. This takes effect only when value_spec is not None. |
False
|
accessor_writable
|
bool
|
Whether to allow modification of this List using accessors (operator[]). |
True
|
sealed
|
bool
|
Whether to seal this List after creation. |
False
|
pass_through
|
bool
|
When True (and |
False
|
root_path
|
KeyPath | None
|
KeyPath of this List in its object tree. |
None
|
Source code in pygx/symbolic/_list.py
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | |
partial
classmethod
¶
partial(
items: Iterable[Any] | None = None,
*,
value_spec: List | None = None,
onchange_callback: (
None | Callable[[dict[KeyPath, FieldUpdate]], None]
) = None,
**kwargs
) -> Self
Class method that creates a partial List object.
Source code in pygx/symbolic/_list.py
from_json
classmethod
¶
from_json(
json_value: Any,
*,
value_spec: List | None = None,
allow_partial: bool = False,
root_path: KeyPath | None = None,
**kwargs: Any
) -> Self
Class method that load an symbolic List from a JSON value.
Example::
l = List.from_json([{
'_type': '__main__.Foo',
'f1': 1,
'f2': {
'f21': True
}
},
1
])
assert l.value_spec is None
# Okay:
l.append('abc')
# [0].f2 is bound by class Foo's field 'f2' definition
# (assuming it defines a schema for the Dict field).
assert l[0].f2.value_spec is not None
# Not okay:
l[0].f2.abc = 1
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_value
|
Any
|
Input JSON value, only JSON list is acceptable. |
required |
value_spec
|
List | None
|
An optional |
None
|
allow_partial
|
bool
|
Whether to allow elements of the list to be partial. |
False
|
root_path
|
KeyPath | None
|
KeyPath of loaded object in its object tree. |
None
|
**kwargs
|
Any
|
Allow passing through keyword arguments that are not applicable. |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
A schema-less symbolic list, but its items maybe symbolic. |
Source code in pygx/symbolic/_list.py
use_value_spec
¶
use_value_spec(value_spec: List | None, allow_partial: bool = False) -> Self
Applies a pg.List as the value spec for current list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_spec
|
List | None
|
A List ValueSpec to apply to this List.
If current List is schema-less (whose immediate members are not
validated against schema), and |
required |
allow_partial
|
bool
|
Whether allow partial dict based on the schema. This flag will override allow_partial flag in init for spec-less List. |
False
|
Returns:
| Type | Description |
|---|---|
Self
|
Self. |
Raises:
| Type | Description |
|---|---|
ValueError
|
schema validation failed due to value error. |
RuntimeError
|
List is already bound with another value_spec. |
TypeError
|
type errors during validation. |
KeyError
|
key errors during validation. |
Source code in pygx/symbolic/_list.py
sym_attr_field
¶
sym_attr_field(key: str | int) -> Field | None
Returns the field definition for a symbolic attribute.
sym_hasattr
¶
sym_keys
¶
sym_values
¶
sym_items
¶
sym_hash
¶
Symbolically hashing.
Source code in pygx/symbolic/_list.py
sym_missing
¶
Returns missing fields.
Source code in pygx/symbolic/_list.py
sym_nondefault
¶
Returns non-default values.
Source code in pygx/symbolic/_list.py
sym_seal
¶
Seal or unseal current object (recursing into children).
Source code in pygx/symbolic/_list.py
on_sym_change
¶
on_sym_change(field_updates: dict[KeyPath, FieldUpdate])
On change event of List.
Source code in pygx/symbolic/_list.py
append
¶
Appends an item.
Source code in pygx/symbolic/_list.py
insert
¶
Inserts an item at a given position.
Source code in pygx/symbolic/_list.py
pop
¶
Pop an item and return its value.
Source code in pygx/symbolic/_list.py
remove
¶
Removes the first occurrence of the value.
Source code in pygx/symbolic/_list.py
clear
¶
Clears the list.
Source code in pygx/symbolic/_list.py
sort
¶
Sorts the items of the list in place..
reverse
¶
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, List]
Implement pg.typing.CustomTyping interface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
KeyPath
|
KeyPath of current object. |
required |
value_spec
|
ValueSpec
|
Origin value spec of the field. |
required |
allow_partial
|
bool
|
Whether allow partial object to be created. |
required |
child_transform
|
None | Callable[[KeyPath, Field, Any], Any]
|
Function to transform child node values in dict_obj into their final values. Transform function is called on leaf nodes first, then on their containers, recursively. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, List]
|
A tuple (proceed_with_standard_apply, transformed value) |
Source code in pygx/symbolic/_list.py
sym_jsonify
¶
Converts current list to a list of plain Python objects.
Source code in pygx/symbolic/_list.py
format
¶
format(
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
*,
python_format: bool = False,
use_inferred: bool = False,
cls_name: str | None = None,
bracket_type: BracketType = SQUARE,
**kwargs
) -> str
Formats this List.
Source code in pygx/symbolic/_list.py
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 | |
Object
¶
Object(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Symbolic
Base class for symbolic user classes.
PyGX allow symbolic programming interfaces to be easily added to most Python classes in two ways:
- Developing a dataclass-like symbolic class by subclassing
pg.Object. - Developing a class as usual and decorate it using
pygx.symbolize. This also work with existing classes.
By directly subclassing pg.Object, programmers can create new symbolic
classes with the least effort. For example::
class Greeting(pg.Object):
# Each annotated class attribute defines a symbolic field for __init__.
name: str | None # Name to greet
time_of_day: pg.typing.Enum( # Time of the day.
['morning', 'afternoon', 'evening'], 'morning')
def __call__(self):
# Values for symbolic fields can be accessed
# as public data members of the symbolic object.
print(f'Good {self.time_of_day}, {self.name}')
# Create an object of Greeting and invoke it, # which shall print 'Good morning, Bob'. Greeting('Bob')()
Symbolic fields can be inherited from the base symbolic class: the fields from the base class will be copied to the subclass in their declaration order, while the subclass can override the inherited fields with more restricted validation rules or different default values. For example::
class Foo(pg.Object): x: pg.typing.Int(max_value=10) y: pg.typing.Float(min_value=0)
class Bar(Foo): x: pg.typing.Int(min_value=1, default=1) z: str | None
# Printing Bar's schema will show that there are 3 parameters defined: # x : pg.typing.Int(min_value=1, max_value=10, default=1)) # y : pg.typing.Float(min_value=0) # z : pg.typing.Str().noneable() print(Bar.schema)
Overriding only a default on a subclass. When a subclass overrides an inherited field's default, always re-annotate the name so static type checkers stay in sync::
class Message(pg.Object): text: str sender: str = pg.MISSING_VALUE # required at construction time
class UserMessage(Message): sender: str = 'User' # re-annotate — pyright sees this
A bare assignment (sender = 'User') works at runtime — pygx still
rewrites the default — but PEP 681 dataclass_transform synthesizes
__init__ from class-level annotations only, so pyright would still
see sender as required. pygx emits a UserWarning at class creation
when it detects this pattern; silence it via
pg.warn_on_field_override_no_annotation(False).
Create an Object instance.
pg.Object synthesizes a keyword-only __init__. Subclasses
that need positional arguments must override __init__
explicitly and translate to keyword arguments before forwarding
to super().__init__(**kwargs).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow_partial
|
bool
|
If True, the object can be partial. |
False
|
sealed
|
bool | None
|
If True, seal the object from future modification (unless under
a |
None
|
root_path
|
KeyPath | None
|
The symbolic path for current object. By default it's None, which indicates that newly constructed object does not have a parent. |
None
|
explicit_init
|
bool
|
Should set to |
False
|
**kwargs
|
Any
|
key/value arguments that align with the schema. All required keys in the schema must be specified, and values should be acceptable according to their value spec. |
{}
|
Raises:
| Type | Description |
|---|---|
KeyError
|
When required key(s) are missing. |
ValueError
|
When value(s) are not acceptable by their value spec. |
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
sym_init_args
property
¶
Returns the symbolic attributes which are also the __init__ args.
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A plain dict of the evaluated symbolic attributes, in schema order.
Every inferential value (e.g.
|
sym_partial
property
¶
Whether any field (here or in a child) is unbound.
Inline sym=False fast path: read the native inline fields and decide
directly, WITHOUT materializing into _sym_attributes. The base
Symbolic.sym_partial is bool(self.sym_missing(flatten=False)), and
Object.sym_missing reads _sym_attributes — which materializes the
object. The Object value-spec's apply reads sym_partial on every
Object-typed field value, so on the construct hot path that materialized
every nested sym=False child (the dominant nested-ctor cost). An inline
object stores every field (an unset one as a present MISSING_VALUE), so
sym_partial is: any field is MISSING_VALUE, or any symbolic child is
itself partial (matching sym_missing(flatten=False)'s recursion — a
child's sym_partial is bool(child.sym_missing(flatten=False))). The
child's sym_partial recurses through this same inline path, so a nested
clean tree never materializes.
partial
classmethod
¶
from_json
classmethod
¶
from_json(
json_value: Any,
*,
allow_partial: bool = False,
root_path: KeyPath | None = None,
**kwargs: Any
) -> Self
Class method that load an symbolic Object from a JSON value.
Example::
class Foo(pg.Object):
f1: int
f2: pg.typing.Dict([
('f21', pg.typing.Bool())
])
foo = Foo.from_json({
'f1': 1,
'f2': {
'f21': True
}
})
# or
foo2 = symbolic.from_json({
'_type': '__main__.Foo',
'f1': 1,
'f2': {
'f21': True
}
})
assert foo == foo2
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_value
|
Any
|
Input JSON value, only JSON dict is acceptable. |
required |
allow_partial
|
bool
|
Whether to allow elements of the list to be partial. |
False
|
root_path
|
KeyPath | None
|
KeyPath of loaded object in its object tree. |
None
|
**kwargs
|
Any
|
Additional keyword arguments to pass through. |
{}
|
Returns:
| Type | Description |
|---|---|
Self
|
A symbolic Object instance. |
Source code in pygx/symbolic/_object.py
2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 | |
on_sym_post_init
¶
Event triggered at the end of __init__.
This is the construction-time entry point for the bind sequence: the
default implementation triggers on_sym_bound (and then on_sym_ready
once the object is concrete). An override that does not call
super().on_sym_post_init() therefore suppresses on_sym_bound and
on_sym_ready at construction — call super() unless that is
intended. For most needs, prefer overriding on_sym_ready (or
on_sym_bound) directly rather than this funnel.
Source code in pygx/symbolic/_object.py
on_sym_bound
¶
Event triggered on each (re)bind, even when the subtree is partial.
Fires at the end of __init__ and at most once per rebind (no matter how
many fields changed), regardless of whether every field is present. Use
this only for logic that must run even on a partial or pure-symbolic
object.
For the common case — (re)computing derived members from schema fields —
override on_sym_ready instead: it fires right after on_sym_bound but only
once the object is concrete, so all fields are guaranteed present.
When derived members are expensive to recompute, override on_sym_change
instead: it receives the exact field_updates (both per-field mutations
and batched rebinds, anywhere in the subtree) so you can refresh only
the members impacted by the change.
Source code in pygx/symbolic/_object.py
on_sym_ready
¶
Event triggered after on_sym_bound once the object is concrete.
Fires at the end of __init__ and after each rebind, but only when the
object is not abstract (not sym_abstract — neither partial nor
pure-symbolic). Unlike on_sym_bound, every schema field is guaranteed
present, making this the natural place to set derived members.
It is level-triggered: it fires on every (re)bind that leaves the object concrete, not only on the abstract -> concrete transition. For one-shot setup, guard with your own flag.
Source code in pygx/symbolic/_object.py
on_sym_preinit
classmethod
¶
Model-level before hook: transform raw init kwargs (#516).
Called on every fresh construct (including from_json) with the
raw field kwargs — before unknown-key/missing-required resolution —
and must return the mapping to construct from (mutating and
returning kwargs is fine). The reserved init args
(allow_partial / sealed / root_path / explicit_init)
are never included. Not called on clones (their values come
formalized from a validated source) nor on rebinds (per-field
transform is the write-path before-hook).
The default implementation returns kwargs unchanged.
Source code in pygx/symbolic/_object.py
on_sym_validate
¶
Model-level after validator: check cross-field invariants (#516).
Called with all fields validated and committed — raise to reject.
Unlike on_sym_ready (a lifecycle notification), a raise here is
part of validation:
- Construct: fires after fields are stored, before
on_sym_post_init/on_sym_bound/on_sym_ready, and only when the object is concrete (theon_sym_readygate) — a failing check aborts the construct. - Writes (
self.x = .../sym_rebind): fires after the update commits and before anyon_sym_changenotification; a raise rolls the update back (observers never see the invalid state) and propagates to the writer. It also fires when notifications are suppressed (pg.notify_on_change(False)/skip_notification=True) — those flags mute observers, never validation.
Validate only — do not mutate. A write from inside this hook
re-enters validation (an unconditionally-writing hook recurses
without bound) and escapes the failing write's rollback. Normalize
values with the field-level transform, and compute derived
state in on_sym_ready / on_sym_change.
The default implementation is a no-op.
Source code in pygx/symbolic/_object.py
on_sym_change
¶
on_sym_change(field_updates: dict[KeyPath, FieldUpdate]) -> None
Event that is triggered when field values in the subtree are updated.
This event will be called
* On per-field basis when object is modified via attribute.
* In batch when multiple fields are modified via sym_rebind method.
When a field in an object tree is updated, all ancestors' on_sym_change event
will be triggered in order, from the nearest one to furthest one.
The default implementation triggers on_sym_bound (and then on_sym_ready
when the object is concrete).
Any functools.cached_property (including the pg.typing.cached_property
shim) declared on the class is invalidated automatically just before this
event fires, so a cached value can never outlive a change to the fields it
derives from. The invalidation is a framework guarantee — it runs whether or
not this method is overridden, and whether or not an override calls
super().on_sym_change(...) — so subclasses no longer need to pop their
cached values by hand.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field_updates
|
dict[KeyPath, FieldUpdate]
|
Updates made to the subtree. Key path is relative to current object. |
required |
Source code in pygx/symbolic/_object.py
on_sym_path_change
¶
Event that is triggered after the symbolic path changes.
on_sym_parent_change
¶
Event that is triggered after the symbolic parent changes.
sym_hasattr
¶
Tests if a symbolic attribute exists.
Source code in pygx/symbolic/_object.py
sym_getattr
¶
Gets a symbolic attribute (Object-specific fast path).
Symbolic.sym_getattr would dispatch sym_hasattr then _sym_getattr
and round-trip through the attrs Dict's own dispatchers. Inline that
here: one _sym_attributes lookup, one membership check, one
dict.__getitem__.
Source code in pygx/symbolic/_object.py
sym_attr_field
¶
sym_attr_field(key: str | int) -> Field | None
Returns the field definition for a symbolic attribute.
Source code in pygx/symbolic/_object.py
sym_keys
¶
Iterates the keys of symbolic attributes.
sym_values
¶
Iterates the values of symbolic attributes.
sym_items
¶
Iterates the (key, value) pairs of symbolic attributes.
sym_eq
¶
Tests symbolic equality.
sym_lt
¶
Tests symbolic less-than.
Same-type comparison is lexicographic over the enable_compare
fields in declaration order — the same fields that feed sym_eq.
This keeps sym_eq / sym_lt / sym_gt mutually coherent: for two
same-type values exactly one of == / < / > holds (trichotomy),
and a compare=False field is ignored by all three. Cross-type
comparison defers to base.lt, which orders by type bucket (see
pygx.lt).
Source code in pygx/symbolic/_object.py
sym_hash
¶
Symbolically hashing.
Source code in pygx/symbolic/_object.py
sym_setparent
¶
Sets the parent of current node in the symbolic tree.
Available only when sym=True; a sym=False object is a flat,
reference-held leaf with no tree to attach to and raises
SymbolicModeError.
Source code in pygx/symbolic/_object.py
sym_setpath
¶
sym_setpath(path: str | KeyPath | None) -> None
Sets the path of current node in its symbolic tree.
Available only when sym=True; a sym=False object has no tree
position (sym_path is always the empty KeyPath()) and raises
SymbolicModeError.
Source code in pygx/symbolic/_object.py
sym_missing
¶
Returns missing values.
Source code in pygx/symbolic/_object.py
sym_nondefault
¶
Returns non-default values.
Source code in pygx/symbolic/_object.py
sym_seal
¶
Seal or unseal current object (recursing into fields).
Source code in pygx/symbolic/_object.py
to_json
¶
Serializes to a plain-Python JSON value.
Fast path: a sym=False object whose field values are all plain —
JSON primitives, or list / dict trees recursively of primitives
with no container shared across fields / positions and no cycle — needs
none of the wire.to_json shared-object context (allocation +
per-object bookkeeping + the deref post-pass). Emit {_type, **fields}
directly, with a deep plain-copy of each value — byte-identical to what
wire.to_json would build, since with nothing aliased no __ref__ ever
materializes. _plain_value_to_json shares one seen id-set across the
fields so a container reached twice (or a cycle) bails the WHOLE object
to wire.to_json, which emits the correct __ref__ / __context__
form.
Any genuinely symbolic value (a nested Object, a pg.List / pg.Dict,
a pg.Ref, a hyper-value, a tuple), a shared / cyclic plain container,
a sym=True object, or a shape-changing kwarg falls through to the
general wire.to_json (which honors shared/cyclic-reference dedup,
frozen / non-const fields, and the serialization options).
Dump options (#519, threaded through the recursion like by_alias):
exclude_none=True drops None-valued mapping entries — object
fields, pg.Dict entries, and RAW dict values alike (list items are
never dropped); exclude_defaults=True drops fields equal to their
schema default; exclude_frozen (default True) drops frozen fields;
type_info=False omits the _type class marker
everywhere — the plain-dict export (dataclasses.asdict analog;
one-way — from_json of the result yields plain containers, not the
original classes). Under type_info=False, markers that ARE the
payload still emit: structural wire forms (__tuple__, __ref__),
UnknownTypedObject / opaque-pickle _types, the pg.Ref wrapper,
and classes with a custom sym_jsonify; an unresolved Inferential
dumps to {}, so pair with use_inferred=True when dumping
contextual trees. Pydantic's exclude_unset has
no pygx equivalent — defaults materialize at construction, so
provided-at-default and defaulted are indistinguishable afterwards.
Source code in pygx/symbolic/_object.py
3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 | |
sym_jsonify
¶
Converts current object to a dict of plain Python objects.
Calls the inner Dict's sym_jsonify directly instead of routing
through _sym_attributes.to_json(). That round-trip would otherwise
re-enter wire.to_json and serialize_maybe_shared just to wrap
and immediately unwrap the same content into this outer dict.
type(self) bypasses Object.__getattribute__'s try/except, which shows
up on the hot serialization path. _sym_attributes is read via the
attribute (not vars(self)[...]) so it is storage-agnostic: a vars()
entry under the python core, a Rust struct field under the native core.
Source code in pygx/symbolic/_object.py
4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 | |
format
¶
Formats this object.
Source code in pygx/symbolic/_object.py
4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 | |
ObjectMeta
¶
Bases: ABCMeta
Meta class for pg.Object.
apply_schema
¶
apply_schema(schema: Schema | None = None) -> None
Applies a schema to a symbolic class.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
Schema | None
|
The schema that will be applied to class. If |
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
When REPLACING the schema ( |
Source code in pygx/symbolic/_object_meta.py
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | |
update_schema
¶
update_schema(
fields: list[Field | list[FieldDef] | dict[FieldKeyDef, FieldValueDef]],
extend: bool = True,
*,
init_arg_list: Sequence[str] | None = None,
metadata: dict[str, Any] | None = None
) -> None
Updates the schema of the class.
Only legal BEFORE any instance of the class (or a subclass) exists,
and never for a slots=True class, whose inline-slot layout is
generated from the schema at class creation — both enforced by
apply_schema (#432).
Source code in pygx/symbolic/_object_meta.py
register_for_deserialization
¶
register_for_deserialization(
serialization_key: str | None = None,
additional_keys: list[str] | None = None,
) -> None
Register current symbolic class for deserialization.
Also mirrors the resolved keys onto cls.__json_options__ so
callers that later read the class's JSON conversion options see
the updated state.
Source code in pygx/symbolic/_object_meta.py
Origin
¶
Origin(
source: Any,
tag: str,
stacktrace: bool | None = None,
stacklimit: int | None = None,
stacktop: int = -1,
)
Bases: Formattable
Class that represents the origin of a symbolic value.
Origin is used for debugging the creation chain of a symbolic value, as
well as keeping track of the factory or builder in creational design patterns.
An Origin object records the source value, a string tag, and optional
stack information on where a symbolic value is created.
Built-in tags are 'init', 'clone', 'deepclone' and 'return'.
Users can pass custom tags to the sym_setorigin method of a symbolic value
for tracking its source in their own scenarios.
When origin tracking is enabled by calling pg.track_origin(True), the
sym_setorigin method of symbolic values will be automatically called during
object creation, cloning or being returned from a functor. The stack
information can be obtained by origin.stack or origin.stacktrace.
Constructor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Any
|
Source value for the origin. |
required |
tag
|
str
|
A descriptive tag of the origin. Built-in tags are:
'init', 'clone', 'deepclone', 'return'. Users can manually
call |
required |
stacktrace
|
bool | None
|
If True, enable stack trace for the origin. If None, enable
stack trace if |
None
|
stacklimit
|
int | None
|
An optional integer to limit the stack depth. If None, it's
determined by the value passed to |
None
|
stacktop
|
int
|
A negative integer to indicate the stack top among the stack
frames that we want to present to user, by default it's 2-level up from
the stack within current |
-1
|
Source code in pygx/symbolic/_origin.py
history
¶
Returns a history of origins with an optional filter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
condition
|
Callable[[Origin], bool] | None
|
An optional callable object with signature (origin) -> should_list. If None, all origins will be listed. |
None
|
Returns:
| Type | Description |
|---|---|
list[Origin]
|
A list of filtered origin from the earliest (root) to the most recent. |
Source code in pygx/symbolic/_origin.py
chain
¶
chain(tag: str | None = None) -> list[Origin]
Get the origin list from the neareast to the farthest filtered by tag.
Source code in pygx/symbolic/_origin.py
format
¶
Formats this object.
Source code in pygx/symbolic/_origin.py
NonDeterministic
¶
Bases: PureSymbolic
Base class to mark a class whose objects are considered non-deterministic.
A non-deterministic value represents a value that will be decided later.
In PyGX system, pg.one_of, pg.sublist_of, pg.float_value are
non-deterministic values. Please search NonDeterministic subclasses for more
details.
PureSymbolic
¶
Bases: CustomTyping
Base class to classes whose objects are considered pure symbolic.
Pure symbolic objects can be used for representing abstract concepts - for example, a search space of objects - which cannot be executed but soely representational.
Having pure symbolic object is a key differentiator of symbolic OOP from
regular OOP, which can be used to placehold values in an object as a
high-level expression of ideas. Later, with symbolic manipulation, the
pure symbolic objects are replaced with material values so the object
can be evaluated. This effectively decouples the expression of ideas from
the implementation of ideas. For example: pg.oneof(['a', 'b', 'c'] will
be manipulated into 'a', 'b' or 'c' based on the decision of a search
algorithm, letting the program evolve itself.
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, Any]
Custom apply on a value based on its original value spec.
This implements pg.pg_typing.CustomTyping, allowing a pure symbolic
value to be assigned to any field. To customize this behavior, override
this method in subclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
KeyPath
|
KeyPath of current object under its object tree. |
required |
value_spec
|
ValueSpec
|
Original value spec for this field. |
required |
allow_partial
|
bool
|
Whether allow partial object to be created. |
required |
child_transform
|
None | Callable[[KeyPath, Field, Any], Any]
|
Function to transform child node values into their final values. Transform function is called on leaf nodes first, then on their parents, recursively. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[bool, Any]
|
A tuple (proceed_with_standard_apply, value_to_proceed). If proceed_with_standard_apply is set to False, value_to_proceed will be used as final value. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If the value is not compatible with the value spec. |
Source code in pygx/symbolic/_pure_symbolic.py
Ref
¶
Bases: Object, Inferential, Extension
Symbolic reference.
When adding a symbolic node to a symbolic tree, it undergoes a copy operation
if it already has a parent, ensuring that all symbolic objects have a single
parent. Additionally, list and dict objects are automatically converted to
pg.List and pg.Dict, respectively, to enable symbolic operability.
However, these two conventions come with certain costs. The act of making copies incurs a runtime cost, and it also introduces challenges in sharing states across different symbolic objects. To address this issue, symbolic reference is introduced. This feature allows a symbolic node to refer to value objects without the need for transformation or copying, even when the symbolic node itself is copied. For example::
class A(pg.Object): x: int
a = pg.Ref(A(1)) b = pg.Dict(x=a) c = pg.Dict(y=a)
assert b.x is a assert c.y is a assert b.sym_clone().x is a assert c.sym_clone(deep=True).y is a
In this example, pg.Ref is used to create a symbolic reference to the
object A(1), and the pg.Dict objects b and c can then reference
a without creating additional copies. This mechanism not only mitigates
the runtime cost but also facilitates seamless sharing of states among various
symbolic objects.
Another useful scenario arises when we wish to utilize regular Python list
and dict objects. In this case, pg.Ref enables us to access the list/dict
object as fields in the symbolic tree without requiring them to be transformed
into pg.List and pg.Dict. This allows for seamless integration of
standard Python containers within the symbolic structure::
d = pg.Dict(x=pg.Ref({1: 2})) assert isinstance(d.x, dict) assert not isinstance(d.x, pg.Dict)
e = pg.Dict(x=pg.Ref([0, 1, 2]])) assert isinstance(e.x, list) assert not isinstance(e.x, pg.List)
Please be aware that pg.Ref objects are treated as leaf nodes in the
symbolic tree, even when they reference other symbolic objects. As a result,
the sym_rebind() method cannot modify the value they are pointing to.
For primitive types, pg.Ref() returns their values directly without
creating a reference. For example, pg.Ref(1) and pg.Ref('abc') will
simply return the values 1 and 'abc', respectively, without any additional
referencing.
Source code in pygx/symbolic/_ref.py
infer
¶
custom_apply
¶
custom_apply(
path: KeyPath,
value_spec: ValueSpec,
allow_partial: bool = False,
child_transform: None | Callable[[KeyPath, Field, Any], Any] = None,
) -> tuple[bool, Any]
Validate candidates during value_spec binding time.
Source code in pygx/symbolic/_ref.py
UnknownFunction
¶
UnknownFunction(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: UnknownCallable
Symbolic objject for representing unknown functions.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
UnknownMethod
¶
UnknownMethod(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: UnknownCallable
Symbolic object for representing unknown methods.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
UnknownSymbol
¶
UnknownSymbol(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: Object, CustomTyping
Interface for symbolic representation of unknown symbols.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
UnknownType
¶
UnknownType(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: UnknownSymbol
Symbolic object for representing unknown types.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
UnknownTypedObject
¶
UnknownTypedObject(
*,
allow_partial: bool = False,
sealed: bool | None = None,
root_path: KeyPath | None = None,
explicit_init: bool = False,
**kwargs: Any
)
Bases: UnknownSymbol
Symbolic object for representing objects of unknown-type.
Source code in pygx/symbolic/_object.py
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | |
sym_jsonify
¶
Converts current object to a dict of plain Python objects.
Source code in pygx/symbolic/_unknown_symbols.py
clone
¶
clone(
x: _CloneT,
deep: bool = False,
memo: Any | None = None,
override: Mapping[Any, Any] | None = None,
) -> _CloneT
Clones a value. Use symbolic clone if possible.
Example::
class A(pg.Object): x: int y: Any
# B is not a symbolic object. class B: pass
# Shallow copy on non-symbolic values (by reference). a = A(1, B()) b = pg.clone(a) assert pg.eq(a, b) assert a.y is b.y
# Deepcopy on non-symbolic values. c = pg.clone(a, deep=True) assert pg.ne(a, c) assert a.y is not c.y
# Copy with override d = pg.clone(a, override={'x': 2}) assert d.x == 2 assert d.y is a.y
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
_CloneT
|
value to clone. |
required |
deep
|
bool
|
If True, use deep clone, otherwise use shallow clone. |
False
|
memo
|
Any | None
|
Optional memo object for deep clone. |
None
|
override
|
Mapping[Any, Any] | None
|
Value to override if value is symbolic. |
None
|
Returns:
| Type | Description |
|---|---|
_CloneT
|
Cloned instance. |
Source code in pygx/symbolic/_base.py
contains
¶
contains(
x: Any,
value: Any = None,
type: type[Any] | tuple[type[Any], ...] | None = None,
) -> bool
Returns if a value contains values of specific type.
Example::
class A(pg.Object): x: Any y: Any
# Test if a symbolic tree contains a value. assert pg.contains(A('a', 'b'), 'a') assert not pg.contains(A('a', 'b'), A)
# Test if a symbolic tree contains a type. assert pg.contains({'x': A(1, 2)}, type=A) assert pg.contains({'x': A(1, 2)}, type=int) assert pg.contains({'x': A(1, 2)}, type=(int, float))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Any
|
The source value to query against. |
required |
value
|
Any
|
Value of sub-node to contain. Applicable when |
None
|
type
|
type[Any] | tuple[type[Any], ...] | None
|
A type or a tuple of types for the sub-nodes. Applicable if not None. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if |
bool
|
is an instance of |
Source code in pygx/symbolic/_base.py
eq
¶
Compares if two values are equal. Use symbolic equality if possible.
Example::
class A(pg.Object): x: Any
def sym_eq(self, right):
if super().sym_eq(right):
return True
return pg.eq(self.x, right)
class B: pass
assert pg.eq(1, 1)
assert pg.eq(A(1), A(1))
# This is True since A has override sym_eq.
assert pg.eq(A(1), 1)
# Objects of B are compared by references.
assert not pg.eq(A(B()), A(B()))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
Any
|
The left-hand value to compare. |
required |
right
|
Any
|
The right-hand value to compare. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if left and right is equal or symbolically equal. Otherwise False. |
Source code in pygx/symbolic/_base.py
2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 | |
from_json
¶
from_json(
json_value: Any,
*,
expected_type: type[T],
context: WireConversionContext | None = ...,
wrap: bool = ...,
auto_import: bool = ...,
convert_unknown: bool = ...,
allow_partial: bool = ...,
root_path: KeyPath | None = ...,
value_spec: ValueSpec | None = ...,
**kwargs: Any
) -> T
from_json(
json_value: Any,
*,
expected_type: Any = ...,
context: WireConversionContext | None = ...,
wrap: bool = ...,
auto_import: bool = ...,
convert_unknown: bool = ...,
allow_partial: bool = ...,
root_path: KeyPath | None = ...,
value_spec: ValueSpec | None = ...,
**kwargs: Any
) -> Any
from_json(
json_value: Any,
*,
expected_type: Any = Any,
context: WireConversionContext | None = None,
wrap: bool = True,
auto_import: bool = True,
convert_unknown: bool = False,
allow_partial: bool = False,
root_path: KeyPath | None = None,
value_spec: ValueSpec | None = None,
**kwargs: Any
) -> Any
Deserializes a (maybe) symbolic value from JSON value.
Example::
class A(pg.Object): x: Any
a1 = A(1) json = a1.to_json() a2 = pg.from_json(json) assert pg.eq(a1, a2)
# Narrow the static return type and assert at runtime:
a3 = pg.from_json(json, expected_type=A) # statically typed as A.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_value
|
Any
|
Input JSON value. |
required |
expected_type
|
Any
|
When provided (anything other than the default |
Any
|
context
|
WireConversionContext | None
|
JSON conversion context. |
None
|
wrap
|
bool
|
If True (default), schemaless JSON |
True
|
auto_import
|
bool
|
If True, when a '_type' is not registered, PyGX will identify its parent module and automatically import it. For example, if the type is 'foo.bar.A', PyGX will try to import 'foo.bar' and find the class 'A' within the imported module. |
True
|
convert_unknown
|
bool
|
If True, when a '_type' is not registered and cannot
be imported, PyGX will create objects of:
- |
False
|
allow_partial
|
bool
|
Whether to allow elements of the list to be partial. |
False
|
root_path
|
KeyPath | None
|
KeyPath of loaded object in its object tree. |
None
|
value_spec
|
ValueSpec | None
|
The value spec for the symbolic list or dict. |
None
|
**kwargs
|
Any
|
Allow passing through keyword arguments to from_json of specific types. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
Deserialized value, which is |
Any
|
|
Any
|
|
Any
|
|
Any
|
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
Source code in pygx/symbolic/_base.py
3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 | |
from_json_str
¶
from_json_str(
json_str: bytes | str,
*,
expected_type: type[T],
context: WireConversionContext | None = ...,
auto_import: bool = ...,
convert_unknown: bool = ...,
allow_partial: bool = ...,
root_path: KeyPath | None = ...,
value_spec: ValueSpec | None = ...,
format: str | WireFormat = ...,
**kwargs: Any
) -> T
from_json_str(
json_str: bytes | str,
*,
expected_type: Any = ...,
context: WireConversionContext | None = ...,
auto_import: bool = ...,
convert_unknown: bool = ...,
allow_partial: bool = ...,
root_path: KeyPath | None = ...,
value_spec: ValueSpec | None = ...,
format: str | WireFormat = ...,
**kwargs: Any
) -> Any
from_json_str(
json_str: bytes | str,
*,
expected_type: Any = Any,
context: WireConversionContext | None = None,
auto_import: bool = True,
convert_unknown: bool = False,
allow_partial: bool = False,
root_path: KeyPath | None = None,
value_spec: ValueSpec | None = None,
format: str | WireFormat = "json",
**kwargs: Any
) -> Any
Deserialize (maybe) symbolic object from JSON string.
Example::
class A(pg.Object): x: Any
a1 = A(1) json_str = a1.to_json_str() a2 = pg.from_json_str(json_str) assert pg.eq(a1, a2)
# Narrow the static return type and assert at runtime: a3 = pg.from_json_str(json_str, expected_type=A)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
json_str
|
bytes | str
|
JSON string. |
required |
expected_type
|
Any
|
When provided (anything other than the default |
Any
|
context
|
WireConversionContext | None
|
JSON conversion context. |
None
|
auto_import
|
bool
|
If True, when a '_type' is not registered, PyGX will identify its parent module and automatically import it. For example, if the type is 'foo.bar.A', PyGX will try to import 'foo.bar' and find the class 'A' within the imported module. |
True
|
convert_unknown
|
bool
|
If True, when a '_type' is not registered and cannot
be imported, PyGX will create objects of:
- |
False
|
allow_partial
|
bool
|
If True, allow a partial symbolic object to be created. Otherwise error will be raised on partial value. |
False
|
root_path
|
KeyPath | None
|
The symbolic path used for the deserialized root object. |
None
|
value_spec
|
ValueSpec | None
|
The value spec for the symbolic list or dict. |
None
|
format
|
str | WireFormat
|
The wire format to decode with -- a registered format name
(e.g. |
'json'
|
**kwargs
|
Any
|
Additional keyword arguments that will be passed to
|
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
A deserialized value. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
Source code in pygx/symbolic/_base.py
3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 | |
gt
¶
Returns True if a value is symbolically greater than the other value.
Refer to pygx.lt for the definition of symbolic comparison.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
Any
|
The left-hand value to compare. |
required |
right
|
Any
|
The right-hand value to compare. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the left value is symbolically greater than the right value. |
Source code in pygx/symbolic/_base.py
is_abstract
¶
Returns if the input value is abstract.
Example::
@pg.symbolize class Foo: def init(self, x): pass
class Bar(pg.PureSymbolic): pass
assert not pg.is_abstract(1) assert not pg.is_abstract(Foo(1)) assert pg.is_abstract(Foo.partial()) assert pg.is_abstract(Bar()) assert pg.is_abstract(Foo(Bar())) assert pg.is_abstract(Foo(pg.oneof([1, 2])))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Any
|
Value to query against. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if value itself is partial/PureSymbolic or its child and nested |
bool
|
child fields contain partial/PureSymbolic values. |
Source code in pygx/symbolic/_base.py
is_deterministic
¶
Returns if the input value is deterministic.
Example::
@pg.symbolize def foo(x, y): pass
assert pg.is_deterministic(1) assert pg.is_deterministic(foo(1, 2)) assert not pg.is_deterministic(pg.oneof([1, 2])) assert not pg.is_deterministic(foo(pg.oneof([1, 2]), 3))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Any
|
Value to query against. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if value itself is not NonDeterministic and its child and nested |
bool
|
child fields do not contain NonDeterministic values. |
Source code in pygx/symbolic/_base.py
is_pure_symbolic
¶
Returns if the input value is pure symbolic.
Example::
class Bar(pg.PureSymbolic): pass
@pg.symbolize def foo(x, y): pass
assert not pg.is_pure_symbolic(1) assert not pg.is_pure_symbolic(foo(1, 2)) assert pg.is_pure_symbolic(Bar()) assert pg.is_pure_symbolic(foo(Bar(), 1)) assert pg.is_pure_symbolic(foo(pg.oneof([1, 2]), 1))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Any
|
Value to query against. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if value itself is PureSymbolic or its child and nested |
bool
|
child fields contain PureSymbolic values. |
Source code in pygx/symbolic/_base.py
lt
¶
Returns True if a value is symbolically less than the other value.
Symbolic values are comparable by their symbolic representations. For common types such as numbers and string, symbolic comparison returns the same value as value comparisons. For example::
assert pg.lt(False, True) == Flase < True assert pg.lt(0.1, 1) == 0.1 < 1 assert pg.lt('a', 'ab') == 'a' < 'ab'
However, symbolic comparison can be applied on hierarchical values, for example::
assert pg.lt(['a'], ['a', 'b']) assert pg.lt(['a', 'b', 'c'], ['b']) assert pg.lt({'x': 1}, {'x': 2}) assert pg.lt({'x': 1}, {'y': 1}) assert pg.lt(A(x=1), A(x=2))
Also, symbolic values of different types can be compared, for example::
assert pg.lt(pg.MISSING_VALUE, None) assert pg.lt(None, 1) assert pg.lt(1, 'abc') assert pg.lt('abc', []) assert pg.lt([], {}) assert pg.lt([], A(x=1))
The high-level idea is that a value with lower information entropy is less
than a value with higher information entropy. As a result, we know that
pg.MISSING_VALUE is the smallest among all values.
The order of symbolic representation are defined by the following rules:
1) If x and y are comparable by their values, they will be compared using
operator <. (e.g. bool, int, float, str)
2) If x and y are not directly comparable and are different in their types,
they will be compared based on their types. The order of different types
are: pg.MISSING_VALUE, NoneType, bool, int, float, str, list, tuple, set,
dict, functions/classes. When different functions/classes compare, their
order is determined by their qualified name.
3) If x and y are of the same type, which are symbolic containers (e.g. list,
dict, pg.Symbolic objects), their order will be determined by the order of
their first sub-nodes which are different. Therefore ['b'] is greater than
['a', 'b'], though the later have 2 elements.
4) Non-symbolic classes can define method sym_lt to enable symbolic
comparison.
5) Two pg.Object values of the same type are ordered over their
compare=True fields only (the same fields that feed pg.eq), so a
compare=False field never affects pg.lt / pg.gt — keeping
equality and ordering consistent (exactly one of < / == / >
holds).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
Any
|
The left-hand value to compare. |
required |
right
|
Any
|
The right-hand value to compare. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the left value is symbolically less than the right value. |
Source code in pygx/symbolic/_base.py
2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 | |
ne
¶
Compares if two values are not equal. Use symbolic equality if possible.
Example::
class A(pg.Object): x: Any
def sym_eq(self, right):
if super().sym_eq(right):
return True
return pg.eq(self.x, right)
class B: pass
assert pg.ne(1, 2)
assert pg.ne(A(1), A(2))
# A has override sym_eq.
assert not pg.ne(A(1), 1)
# Objects of B are compared by references.
assert pg.ne(A(B()), A(B()))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
Any
|
The left-hand value to compare. |
required |
right
|
Any
|
The right-hand value to compare. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if left and right is not equal or symbolically equal. Otherwise False. |
Source code in pygx/symbolic/_base.py
query
¶
query(
x: Any,
path_regex: str | None = None,
where: None | (Callable[[Any], bool] | Callable[[Any, Any], bool]) = None,
enter_selected: bool = False,
custom_selector: None | (
Callable[[KeyPath, Any], bool] | Callable[[KeyPath, Any, Any], bool]
) = None,
) -> dict[str, Any]
Queries a (maybe) symbolic value.
Example::
class A(pg.Object):
x: int
y: int
value = {
'a1': A(x=0, y=1),
'a2': [A(x=1, y=1), A(x=1, y=2)],
'a3': {
'p': A(x=2, y=1),
'q': A(x=2, y=2)
}
}
# Query by path regex.
# Shall print:
# {'a3.p': A(x=2, y=1)}
print(pg.query(value, r'.*p'))
# Query by value.
# Shall print:
# {
# 'a2[1].y': 2,
# 'a3.p.x': 2,
# 'a3.q.x': 2,
# 'a3.q.y': 2,
# }
print(pg.query(value, where=lambda v: v==2))
# Query by path, value and parent.
# Shall print:
# {
# 'a2[1].y': 2,
# }
print(pg.query(
value, r'.*y',
where=lambda v, p: v > 1 and isinstance(p, A) and p.x == 1))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Any
|
A nested structure that may contains symbolic value. |
required |
path_regex
|
str | None
|
Optional regex expression to constrain path. |
None
|
where
|
None | (Callable[[Any], bool] | Callable[[Any, Any], bool])
|
Optional callable to constrain value and parent when path matches
with
|
None
|
enter_selected
|
bool
|
If True, if a node is selected, enter the node and query its sub-nodes. |
False
|
custom_selector
|
None | (Callable[[KeyPath, Any], bool] | Callable[[KeyPath, Any, Any], bool])
|
Optional callable object as custom selector. When
|
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A dict of key path to value as results for selected values. |
Source code in pygx/symbolic/_base.py
2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 | |
hash
¶
Returns hash of value. Use symbolic hashing function if possible.
Example::
@pg.symbolize class A: def init(self, x): self.x = x
assert hash(A(1)) != hash(A(1)) assert pg.hash(A(1)) == pg.hash(A(1)) assert pg.hash(pg.Dict(x=[A(1)])) == pg.hash(pg.Dict(x=[A(1)]))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Any
|
Value for computing hash. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The hash value for |
Source code in pygx/symbolic/_base.py
to_json
¶
Serializes a (maybe) symbolic value into a plain Python object.
Example::
class A(pg.Object): x: Any
a1 = A(1) json = a1.to_json() a2 = pg.from_json(json) assert pg.eq(a1, a2)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
value to serialize. Applicable value types are:
|
required |
**kwargs
|
Any
|
Keyword arguments to pass to value.to_json if value is WireConvertible. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
JSON value. |
Source code in pygx/symbolic/_base.py
to_json_str
¶
to_json_str(
value: Any,
*,
json_indent: int | None = None,
format: str | WireFormat = "json",
**kwargs: Any
) -> str
Serializes a (maybe) symbolic value into a wire-format string.
Example::
class A(pg.Object): x: Any
a1 = A(1) json_str = a1.to_json_str() a2 = pg.from_json_str(json_str) assert pg.eq(a1, a2)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
Value to serialize. |
required |
json_indent
|
int | None
|
The size of indentation passed to the format's encoder. |
None
|
format
|
str | WireFormat
|
The wire format to encode with -- a registered format name
(e.g. |
'json'
|
**kwargs
|
Any
|
Additional keyword arguments that are passed to |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A serialized string. |
Source code in pygx/symbolic/_base.py
traverse
¶
traverse(
x: Any,
preorder_visitor_fn: (
None | Callable[[KeyPath, Any, Any], TraverseAction | None]
) = None,
postorder_visitor_fn: (
None | Callable[[KeyPath, Any, Any], TraverseAction | None]
) = None,
root_path: KeyPath | None = None,
parent: Any | None = None,
) -> bool
Traverse a (maybe) symbolic value using visitor functions.
Example::
class A(pg.Object): x: int
v = [{'a': A(1)}, A(2)] integers = [] def track_integers(k, v, p): if isinstance(v, int): integers.append((k, v)) return pg.TraverseAction.ENTER
pg.traverse(v, track_integers) assert integers == [('[0].a.x', 1), ('[1].x', 2)]
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Any
|
Maybe symbolic value. |
required |
preorder_visitor_fn
|
None | Callable[[KeyPath, Any, Any], TraverseAction | None]
|
preorder visitor function. Function signature is
|
None
|
postorder_visitor_fn
|
None | Callable[[KeyPath, Any, Any], TraverseAction | None]
|
postorder visitor function. Function signature is
|
None
|
root_path
|
KeyPath | None
|
KeyPath of root value. |
None
|
parent
|
Any | None
|
Optional parent of the root node. |
None
|
Returns:
| Type | Description |
|---|---|
bool
|
True if both |
Source code in pygx/symbolic/_base.py
2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 | |
boilerplate_class
¶
boilerplate_class(
cls_name: str,
value: Object,
init_arg_list: list[str] | None = None,
**kwargs: Any
) -> type[Object]
Create a boilerplate class using a symbolic object.
As the name indicates, a boilerplate class is a class that can be used as a boilerplate to create object.
Implementation-wise it's a class that extends the type of input value, while setting the default values of its (inherited) schema using the value from input.
An analogy to boilerplate class is prebound function.
For example::
# A regular function: correspond to a pg.Object subclass.
def f(a, b, c)
return a + b + c
# A partially bound function: correspond to a boilerplate class created
# from a partially bound object.
def g(c):
return f(1, 2, c)
# A fully bound function: correspond to a boilerplate class created from
# a fully bound object.
def h():
return f(1, 2, 3)
Boilerplate class can be created with a value that is fully bound
(like function h above), or partially bound (like function g above).
Since boilerplate class extends the type of the input, we can rebind members
of its instances as we modify the input.
Here are a few examples::
class A(pg.Object): a: str # Field A. b: int # Field B.
A1 = pg.boilerplate_class('A1', A.partial(a='foo')) assert A1(b=1) == A(a='foo', b=1)
A2 = pg.boilerplate_class('A2', A(a='bar', b=2)) assert A2() == A(a='bar', b=2)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cls_name
|
str
|
Name of the boilerplate class. |
required |
value
|
Object
|
Value that is used as the default value of the boilerplate class. |
required |
init_arg_list
|
list[str] | None
|
An optional list of strings as init positional arguments names. |
None
|
**kwargs
|
Any
|
Keyword arguments for infrequently used options. Acceptable
keywords are: * |
{}
|
Returns:
| Type | Description |
|---|---|
type[Object]
|
A class which extends the input value's type, with its schema's default values set from the input value. |
Raises:
| Type | Description |
|---|---|
TypeError
|
Keyword argumment provided is not supported. |
Source code in pygx/symbolic/_boilerplate.py
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
apply_wrappers
¶
apply_wrappers(
wrapper_classes: Sequence[type[ClassWrapper]] | None = None,
where: Callable[[type[ClassWrapper]], bool] | None = None,
) -> AbstractContextManager[Any]
Context manager for swapping user classes with their class wrappers.
This helper method is a handy tool to swap user classes with their wrappers within a code block, without modifying exisiting code.
For example::
def foo(): return A()
APrime = pg.wrap(A)
with pg.apply_wrappers([APrime]):
# Direct creation of an instance of A will be detoured to APrime.
assert isinstance(A(), APrime)
Indirect creation of an instance of `A` will be detoured too.
assert isinstance(foo(), APrime)
# Out of the scope, direct/indirect creation of A will be restored.
assert not isinstance(A(), APrime)
assert not isinstance(foo(), APrime)
pg.apply_wrappers can be nested, under which the inner context will apply
the wrappers from the outter context. pg.apply_wrappers is NOT
thread-safe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wrapper_classes
|
Sequence[type[ClassWrapper]] | None
|
Wrapper classes to use. If None, sets it to all registered wrapper classes. |
None
|
where
|
Callable[[type[ClassWrapper]], bool] | None
|
An optional filter function in signature (wrapper_class) -> bool.
If not None, only filtered |
None
|
Returns:
| Type | Description |
|---|---|
AbstractContextManager[Any]
|
A context manager that detours the original classes to the wrapper classes. |
Source code in pygx/symbolic/_class_wrapper.py
wrap
¶
wrap(
cls: type[Any],
init_args: (
list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef] | None
) = None,
*,
reset_state_fn: Callable[[Any], None] | None = None,
repr: bool = True,
eq: bool = False,
class_name: str | None = None,
module_name: str | None = None,
auto_doc: bool = False,
auto_typing: bool = False,
serialization_key: str | None = None,
additional_keys: list[str] | None = None,
override: dict[str, Any] | None = None
) -> type[ClassWrapper]
Makes a symbolic class wrapper from a regular Python class.
pg.wrap is called by pygx.symbolize for symbolizing existing
Python classes. For example::
class A: def init(self, x): self.x = x
# The following two lines are equivalent. A1 = pg.symbolize(A) A2 = pg.wrap(A)
Besides passing the source class, pg.wrap allows the user to pass symbolic
field definitions for the init arguments. For example::
A3 = pg.wrap(A, [ ('x', pg.typing.Int()) ])
Moreover, multiple flags are provided to determine whether or not to use the symbolic operations as the default behaviors. For example::
A4 = pg.wrap( A, [], # Instead clearing out all internal states (default), # do not reset internal state. reset_state_fn=lambda self: None, # Use symbolic representation for repr and str. repr=True, # use symbolic equality for eq, ne and hash. eq=True, # Customize the class name obtained (the default behaivor # is to use the source class name). class_name='A4' # Customize the module name for created class (the default # behavior is to use the source module name). module_name='my_module')
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cls
|
type[Any]
|
Class to wrap. |
required |
init_args
|
list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef] | None
|
An optional list of field definitions for the arguments of
init. It can be a sparse value specifications for argument in the
init method of |
None
|
reset_state_fn
|
Callable[[Any], None] | None
|
An optional callable object to reset the internal state of the user class when rebind happens. |
None
|
repr
|
bool
|
Options for generating |
True
|
eq
|
bool
|
Options for generating |
False
|
class_name
|
str | None
|
An optional string used as class name for the wrapper class. If None, the wrapper class will use the class name of the wrapped class. |
None
|
module_name
|
str | None
|
An optional string used as module name for the wrapper class. If None, the wrapper class will use the module name of the wrapped class. |
None
|
auto_doc
|
bool
|
If True, the descriptions for init argument fields will be extracted from docstring if present. |
False
|
auto_typing
|
bool
|
If True, PyGX typing (runtime-typing) will be enabled based
on type annotations inspected from the |
False
|
serialization_key
|
str | None
|
An optional string to be used as the serialization key
for the class during |
None
|
additional_keys
|
list[str] | None
|
An optional list of strings as additional keys to deserialize an object of the registered class. This can be useful when we need to relocate or rename the registered class while being able to load existing serialized JSON values. |
None
|
override
|
dict[str, Any] | None
|
Additional class attributes to override. |
None
|
Returns:
| Type | Description |
|---|---|
type[ClassWrapper]
|
A subclass of |
Raises:
| Type | Description |
|---|---|
TypeError
|
input |
Source code in pygx/symbolic/_class_wrapper.py
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | |
wrap_module
¶
wrap_module(
module: ModuleType,
names: Sequence[str] | None = None,
where: Callable[[type[ClassWrapper]], bool] | None = None,
export_to: ModuleType | None = None,
**kwargs: Any
) -> list[type[ClassWrapper]]
Wrap classes from a module.
For example, users can wrap all subclasses of xxx.Base under module xxx::
import xxx
pg.wrap_module( xxx, where=lambda c: isinstance(c, xxx.Base))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
ModuleType
|
A container that contains classes to wrap. |
required |
names
|
Sequence[str] | None
|
An optional list of class names. If not provided, all classes under
|
None
|
where
|
Callable[[type[ClassWrapper]], bool] | None
|
An optional filter function in signature (user_class) -> bool.
Only the classes under |
None
|
export_to
|
ModuleType | None
|
An optional module to export the wrapper classes. |
None
|
**kwargs
|
Any
|
Keyword arguments passed to |
{}
|
Returns:
| Type | Description |
|---|---|
list[type[ClassWrapper]]
|
Wrapper classes. |
Source code in pygx/symbolic/_class_wrapper.py
compound
¶
compound(
base_class: type[Object] | None = None,
args: (
None
| list[
tuple[tuple[str, KeySpec], ValueSpec, str]
| tuple[tuple[str, KeySpec], ValueSpec, str, Any]
]
) = None,
**kwargs: Any
) -> Callable[[Callable[..., Any]], type[Compound]]
Function decorator to create compound class.
Example::
@dataclasses.dataclass class Foo: x: int y: int
def sum(self):
return self.x + self.y
@pg.compound def foo_with_equal_x_y(v: int) -> Foo: return Foo(v, v)
f = foo_with_equal_x_y(1)
# First of all, the objects of compound classes can be used as an in-place # replacement for the objects of regular classes, as they are subclasses of # the regular classes. assert issubclass(foo_with_equal_x_y, Foo) assert isinstance(f, Foo)
# We can access symbolic attributes of the compound object. assert f.v == 1
# We can also access the public APIs of the decomposed object. assert f.x == 1 assert f.y == 1 assert f.sum() == 2
# Or explicit access the decomposed object. assert f.decomposed == Foo(1, 1)
# Moreover, symbolic power is fully unleashed to the compound class. f.sym_rebind(v=2) assert f.x == 2 assert f.y == 2 assert f.sum() == 4
# Err with runtime type check: 2.5 is not an integer. f.sym_rebind(v=2.5)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_class
|
type[Object] | None
|
The base class of the compond class, which should be a
|
None
|
args
|
None | list[tuple[tuple[str, KeySpec], ValueSpec, str] | tuple[tuple[str, KeySpec], ValueSpec, str, Any]]
|
Symbolic args specification. See |
None
|
**kwargs
|
Any
|
Keyword arguments. See |
{}
|
Returns:
A symbolic compound class that subclasses base_class.
Source code in pygx/symbolic/_compounding.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | |
compound_class
¶
compound_class(
factory_fn: FunctionType,
base_class: type[Object] | None = None,
args: (
list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef] | None
) = None,
*,
lazy_build: bool = True,
auto_doc: bool = True,
auto_typing: bool = True,
serialization_key: str | None = None,
additional_keys: list[str] | None = None,
add_to_registry: bool = False
) -> type[Compound]
Creates a compound class from a factory function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
factory_fn
|
FunctionType
|
A function that produces a compound object. |
required |
base_class
|
type[Object] | None
|
The base class of the compond class, which should be a
|
None
|
args
|
list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef] | None
|
Symbolic args specification. |
None
|
lazy_build
|
bool
|
If True, |
True
|
auto_doc
|
bool
|
If True, the descriptions of argument fields will be inherited
from |
True
|
auto_typing
|
bool
|
If True, the value spec for constraining each argument will be
inferred from its annotation. Otherwise the value specs for all arguments
will be |
True
|
serialization_key
|
str | None
|
An optional string to be used as the serialization key
for the class during |
None
|
additional_keys
|
list[str] | None
|
An optional list of strings as additional keys to deserialize an object of the registered class. This can be useful when we need to relocate or rename the registered class while being able to load existing serialized JSON values. |
None
|
add_to_registry
|
bool
|
If True, the newly created functor class will be added to the registry for deserialization. |
False
|
Returns:
| Type | Description |
|---|---|
type[Compound]
|
A callable that converts a factory function into a subclass of the base class. |
Source code in pygx/symbolic/_compounding.py
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
field
¶
field(
*,
value_spec: ValueSpec | None = None,
default: Any = MISSING_VALUE,
default_factory: Callable[[], Any] | None = None,
doc: str | None = None,
metadata: dict[str, Any] | None = None,
transform: Callable[[Any], Any] | None = None,
validator: Callable[[Any], None] | None = None,
alias: str | None = None,
repr: bool | None = None,
compare: bool | None = None,
hash: bool | None = None,
init: bool | None = None,
clone: bool | None = None,
validate: bool | None = None,
wrap: bool | None = None
) -> Any
Field descriptor for pg.Object dataclass-like fields.
Registered as a field_specifier on pg.ObjectMeta so that type
checkers honoring :pep:681 (pyright/Pylance/mypy) read the field's
static properties from the keyword arguments here.
pg.Object.__init__ is keyword-only by default
(dataclass_transform(kw_only_default=True)), so this helper exists
primarily to attach doc or metadata to an annotation-style
field — matching the third/fourth tuple slots in pg.members::
class A(pg.Object):
x: int = pg.field(
default=1,
doc='Initial count.',
metadata={'serialize_as': 'count'},
)
A value_spec may be supplied to apply richer runtime constraints
than the bare annotation can express (e.g. regex / numeric bounds)::
class A(pg.Object):
x: str = pg.field(value_spec=pg.typing.Str(regex='a.*'))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value_spec
|
ValueSpec | None
|
Optional explicit |
None
|
default
|
Any
|
The field's default value. If omitted (and no
|
MISSING_VALUE
|
default_factory
|
Callable[[], Any] | None
|
Zero-argument callable producing the default
value. Stored on the field's value spec and invoked once per
instance (stdlib- |
None
|
doc
|
str | None
|
Field docstring — equivalent to the third tuple slot in
|
None
|
metadata
|
dict[str, Any] | None
|
Field metadata dict — equivalent to the fourth tuple
slot in |
None
|
transform
|
Callable[[Any], Any] | None
|
Optional |
None
|
validator
|
Callable[[Any], None] | None
|
Optional |
None
|
alias
|
str | None
|
Optional wire-layer alias for this field's key. Accepted by
|
None
|
repr
|
bool | None
|
Whether the field appears in |
None
|
compare
|
bool | None
|
Whether the field participates in symbolic equality
( |
None
|
hash
|
bool | None
|
Whether the field participates in symbolic hashing. Same
|
None
|
init
|
bool | None
|
If False, drop this field from the generated |
None
|
clone
|
bool | None
|
Only meaningful when |
None
|
validate
|
bool | None
|
Whether values are validated against the field's value
spec. Same |
None
|
wrap
|
bool | None
|
Whether raw |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A |
Any
|
doc/metadata/flags. The return is typed as |
Any
|
declared annotation is what type checkers surface. |
Source code in pygx/symbolic/_dataclass_field.py
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | |
diff
¶
diff(
left: Any,
right: Any,
flatten: bool = False,
collapse: bool | str | Callable[[Any, Any], bool] = "same_type",
mode: str = "diff",
) -> Nestable[Diff]
Inspect the symbolic diff between two objects.
For example::
class A(pg.Object): x: Any y: Any
class B(A): z: Any | None = None
# Diff the same object. pg.diff(A(1, 2), A(1, 2))
No diff
# Diff the same object with mode 'same'. pg.diff(A(1, 2), A(1, 2), mode='same')
A(1, 2)
# Diff different objects of the same type. pg.diff(A(1, 2), A(1, 3))
A( y = Diff( left=2, right=3 ) )
# Diff objects of different type. pg.diff(A(1, 2), B(1, 3))
Diff( left = A( x = 1, y = 2 ), right = B( x = 1, y = 3, z = None )
# Diff objects of different type with collapse. pg.diff(A(1, 2), B(1, 3), collapse=True)
A|B ( y = Diff( left = 2, right = 3, ), z = Diff( left = MISSING, right = None ) )
# Diff objects of different type with collapse and flatten. # Object type is included in key '_type'. pg.diff(A(1, pg.Dict(a=1)), B(1, pg.Dict(a=2)), collapse=True, flatten=True)
{ 'y.a': Diff(1, 2), 'z', Diff(MISSING, None), '_type': Diff(A, B) }
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
Any
|
The left object to compare. |
required |
right
|
Any
|
The right object to compare. |
required |
flatten
|
bool
|
If True, returns a level-1 dict with diff keys flattened. Otherwise preserve the hierarchy of the diff result. |
False
|
collapse
|
bool | str | Callable[[Any, Any], bool]
|
One of a boolean value, string or a callable object that indicates whether to collapse two different values. The default value 'same_type' means only collapse when the two values are of the same type. |
'same_type'
|
mode
|
str
|
Diff mode, should be one of ['diff', 'same', 'both']. For 'diff' mode (the default), the return value contains only different values. For 'same' mode, the return value contains only same values. For 'both', the return value contains both different and same values. |
'diff'
|
Returns:
| Type | Description |
|---|---|
Nestable[Diff]
|
A |
Nestable[Diff]
|
to |
Source code in pygx/symbolic/_diff.py
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 | |
allow_empty_field_description
¶
Allow empty field description, which is useful for testing purposes.
allow_partial
¶
Returns a context manager that allows partial values in scope.
This function is thread-safe and can be nested. In the nested use case, the allow flag of immediate parent context is effective.
Example::
class A(pg.Object): x: int y: int
with pg.allow_partial(True):
a = A(x=1) # Missing y, but OK
with pg.allow_partial(False):
a.sym_rebind(x=pg.MISSING_VALUE) # NOT OK
a.sym_rebind(x=pg.MISSING_VALUE) # OK
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
allow
|
bool | None
|
If True, allow partial symbolic values in scope.
If False, do not allow partial symbolic values in scope even if
individual objects allow so. If None, honor object-level
|
True
|
Returns:
| Type | Description |
|---|---|
ContextManager[None]
|
A context manager that allows/disallow partial symbolic values in scope.
After leaving the scope, the |
Source code in pygx/symbolic/_flags.py
allow_repeated_class_registration
¶
Allow repeated class registration, which is useful for testing purposes.
allow_writable_accessors
¶
Returns a context manager that makes accessor writable in scope.
This function is thread-safe and can be nested. In the nested use case, the writable flag of immediate parent context is effective.
Example::
sd1 = pg.Dict() sd2 = pg.Dict(accessor_writable=False) with pg.allow_writable_accessors(False): sd1.a = 2 # NOT OK sd2.a = 2 # NOT OK with pg.allow_writable_accessors(True): sd1.a = 2 # OK sd2.a = 2 # OK with pg.allow_writable_accessors(None): sd1.a = 1 # OK sd2.a = 1 # NOT OK
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
writable
|
bool | None
|
If True, allow write access with accessors (setattr,
setitem) for all symbolic values in scope.
If False, disallow write access via accessors for all symbolic values
in scope, even if individual objects allow so.
If None, honor object-level |
True
|
Returns:
| Type | Description |
|---|---|
ContextManager[None]
|
A context manager that allows/disallows writable accessors of all
symbolic values in scope. After leaving the scope, the
|
Source code in pygx/symbolic/_flags.py
as_sealed
¶
Returns a context manager to treat symbolic values as sealed/unsealed.
While the user can use Symbolic.sym_seal to seal or unseal an individual object.
This context manager is useful to create a readonly zone for operations on
all existing symbolic objects.
This function is thread-safe and can be nested. In the nested use case, the sealed flag of immediate parent context is effective.
Example::
sd1 = pg.Dict() sd2 = pg.Dict().sym_seal()
with pg.as_sealed(True): sd1.a = 2 # NOT OK sd2.a = 2 # NOT OK with pg.as_sealed(False): sd1.a = 2 # OK sd2.a = 2 # OK with pg.as_sealed(None): sd1.a = 1 # OK sd2.a = 1 # NOT OK
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sealed
|
bool | None
|
If True, treats all symbolic values as sealed in scope.
If False, treats all as unsealed.
If None, honor object-level |
True
|
Returns:
| Type | Description |
|---|---|
ContextManager[None]
|
A context manager that treats all symbolic values as sealed/unsealed in scope. After leaving the scope, the sealed state of individual objects will remain intact. |
Source code in pygx/symbolic/_flags.py
auto_call_functors
¶
Returns a context manager to enable or disable auto call for functors.
auto_call_functors is thread-safe and can be nested. For example::
@pg.symbolize def foo(x, y): return x + y
with pg.auto_call_functors(True): a = foo(1, 2) assert a == 3 with pg.auto_call_functors(False): b = foo(1, 2) assert isinstance(b, foo)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled
|
bool
|
If True, enable auto call for functors. Otherwise, auto call will be disabled. |
True
|
Returns:
| Type | Description |
|---|---|
ContextManager[None]
|
A context manager for enabling/disabling auto call for functors. |
Source code in pygx/symbolic/_flags.py
enable_type_debug
¶
Toggle inclusion of pygx-internal frames in type-error tracebacks.
When disabled (the default), tracebacks for type-related errors raised
during pg.Object construction (and similar entry points) are
rewritten to drop frames that live inside the pygx package, leaving
only the user's call sites visible. When enabled, the full traceback
(including every pygx-internal frame) is preserved — useful when
debugging the typing machinery itself.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled
|
bool
|
If True, keep pygx-internal frames; if False, hide them. |
True
|
Source code in pygx/symbolic/_flags.py
get_load_handler
¶
get_origin_stacktrace_limit
¶
get_save_handler
¶
is_change_notification_enabled
¶
is_empty_field_description_allowed
¶
is_repeated_class_registration_allowed
¶
is_tracking_origin
¶
is_type_debug_enabled
¶
is_under_accessor_writable_scope
¶
Return True if symbolic values are treated as sealed in current context.
is_under_partial_scope
¶
is_under_sealed_scope
¶
Return True if symbolic values are treated as sealed in current context.
is_warn_on_capability_narrowing_enabled
¶
is_warn_on_field_override_no_annotation_enabled
¶
Returns True if bare-assignment field-override warnings are emitted.
notify_on_change
¶
Returns a context manager to enable or disable notification upon change.
notify_on_change is thread-safe and can be nested. For example, in the
following code, on_sym_change (thus on_sym_bound) method of a will be
triggered due to the rebind in the inner with statement, and those of b
will not be triggered as the outer with statement disables the
notification::
with pg.notify_on_change(False): with pg.notify_on_change(True): a.sym_rebind(b=1) b.sym_rebind(x=2)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled
|
bool
|
If True, enable change notification in current scope. Otherwise, disable notification. |
True
|
Returns:
| Type | Description |
|---|---|
ContextManager[None]
|
A context manager for allowing/disallowing change notification in scope. |
Source code in pygx/symbolic/_flags.py
notify_once
¶
Returns a context manager that coalesces change notifications.
Within the scope, every object touched by sym_rebind (or accessor writes
such as obj.x = ...) receives its on_sym_change event when the
scope exits, instead of once per mutation. Repeated updates to the same
field collapse into a single FieldUpdate that keeps the original
old_value and the final new_value. This is useful when applying a
batch of related edits that should be observed as one change.
For example, x and y below are each notified a single time::
with pg.notify_once():
x.sym_rebind(a=2, c=3)
y.sym_rebind(p=1)
x.sym_rebind(a=1)
# x.on_sym_change fires once (with a:
Nesting. notify_once is thread-safe and nestable. Each object is
flushed by the scope it was rebound in — the scope where sym_rebind (or
an accessor write) was called on that object. So a helper that wraps its
own edits in notify_once always observes them settled when it returns,
regardless of how it was called::
def configure(x):
with pg.notify_once():
x.sym_rebind(a=1)
x.sym_rebind(b=2)
return x # x is fully notified here, even if called within a scope
with pg.notify_once():
configure(x) # x flushed when configure returns
y = Y(p=x.derived) # reads x's settled state
y.sym_rebind(q=1)
# y flushed here
A parent reached only by walking up from a rebound child is not owned by the inner scope; it is notified by whichever scope rebinds the parent, or by the outermost scope::
with pg.notify_once():
p.sym_rebind(w=1) # p rebound here -> owned by this scope
with pg.notify_once():
p.child.sym_rebind(v=2) # p.child rebound here -> owned by inner
# inner exit: p.child is notified; p is not (it is the outer's)
# outer exit: p is notified once (with w and the child change)
Children are therefore always notified before their parents, and an object is notified once per scope that directly rebinds it (each event reports a correct, coalesced transition — no event ever carries a stale value).
Coalescing nested scopes. Pass coalesce_nested=True to absorb any
scopes nested inside this one: they no longer flush on their own exit, and
every accumulated change is flushed once when this scope exits. Use it to
coalesce edits made by nested scopes, e.g. in a loop::
with pg.notify_once(coalesce_nested=True):
for item in items:
with pg.notify_once(): # absorbed; does not flush here
y.children.append(item)
# y is notified once here, for all appended children.
Deferred side effects. Because notifications are deferred to scope
exit, side effects driven by on_sym_change (e.g.
pg.Object.on_sym_bound recomputation, or pg.List removal of deleted
items) are also deferred. Caches derived from content (including
functools.cached_property) are still invalidated eagerly, so most reads
within the scope stay consistent; only state recomputed as a side effect of
on_sym_change lags until the flush. When change notification is disabled
via pygx.notify_on_change(False),
there is nothing to batch and notify_once has no effect.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coalesce_nested
|
bool
|
If True, |
False
|
Yields:
| Type | Description |
|---|---|
None
|
None, within the coalescing scope. |
Source code in pygx/symbolic/_flags.py
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 | |
set_load_handler
¶
Sets global load handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
load_handler
|
Callable[..., Any] | None
|
A callable object that takes arbitrary arguments and returns
a value. |
required |
Returns:
| Type | Description |
|---|---|
Callable[..., Any] | None
|
Previous global load handler. |
Source code in pygx/symbolic/_flags.py
set_origin_stacktrace_limit
¶
set_save_handler
¶
Sets global save handler.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
save_handler
|
Callable[..., Any] | None
|
A callable object that takes at least one argument as value to
save. |
required |
Returns:
| Type | Description |
|---|---|
Callable[..., Any] | None
|
Previous global save handler. |
Source code in pygx/symbolic/_flags.py
should_call_functors_during_init
¶
Return True functors should be automatically called during init.
track_origin
¶
Returns a context manager to enable or disable origin tracking.
track_origin is thread-safe and can be nested. For example::
a = pg.Dict(x=1)
with pg.track_origin(False):
with pg.track_origin(True):
# b's origin will be tracked, which can be accessed by b.sym_origin.
b = a.sym_clone()
# c's origin will not be tracked, c.sym_origin returns None.
c = a.sym_clone()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled
|
bool
|
If True, the origin of symbolic values will be tracked during object cloning and retuning from functors under current scope. |
True
|
Returns:
| Type | Description |
|---|---|
ContextManager[None]
|
A context manager for enable or disable origin tracking. |
Source code in pygx/symbolic/_flags.py
warn_on_capability_narrowing
¶
Toggle the warning emitted when a subclass narrows a class capability.
A subclass that explicitly removes a class-level capability its base
granted — attr_read / attr_write / sym True → False, or
frozen False → True — breaks substitutability: code that uses an
instance through its base type may then fail (e.g. reading obj.x, or
calling sym_rebind). When enabled (the default), pygx emits a one-shot
UserWarning at class creation. See the flag-combination matrix in
pg_object_semantics.md for the rationale.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled
|
bool
|
If True, emit the warning; if False, silence it. |
True
|
Source code in pygx/symbolic/_flags.py
warn_on_field_override_no_annotation
¶
Toggle the warning emitted for bare-assignment field overrides.
A subclass that shadows an inherited pg.Object field via bare class-level
assignment (e.g. sender = 'User' instead of sender: str = 'User') is
handled correctly at runtime, but pyright/PEP 681 dataclass_transform
synthesizes __init__ from class-level annotations only — the override
is invisible to static type checkers. When enabled (the default), pygx emits
a one-shot UserWarning at class creation pointing at the offending name
and suggesting the re-annotated form.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enabled
|
bool
|
If True, emit the warning; if False, silence it. |
True
|
Source code in pygx/symbolic/_flags.py
as_functor
¶
as_functor(func: Callable, ignore_extra_args: bool = False) -> Functor
Make a functor object from a regular python function.
NOTE(daiyip): This method is designed to create on-the-go functor object,
usually for lambdas. To create a reusable functor class, please use
functor_class method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable
|
A regular python function. |
required |
ignore_extra_args
|
bool
|
If True, extra argument which is not acceptable by |
False
|
Returns:
| Type | Description |
|---|---|
Functor
|
Functor object from input function. |
Source code in pygx/symbolic/_functor.py
functor
¶
functor(
args: (
None
| list[
tuple[str | KeySpec, ValueSpec, str]
| tuple[str | KeySpec, ValueSpec, str, Any]
]
) = None,
returns: ValueSpec | None = None,
base_class: type[Functor] | None = None,
**kwargs: Any
) -> Callable[..., type[Functor]] | type[Functor]
Function/Decorator for creating symbolic function from regular function.
Example::
# Create a symbolic function without specifying the # validation rules for arguments. @pg.functor def foo(x, y): return x + y
f = foo(1, 2) assert f() == 3
# Create a symbolic function with specifying the # the validation rules for argument 'a', 'args', and 'kwargs'. @pg.functor([ ('a', pg.typing.Int()), ('b', pg.typing.Float()), ('args', pg.List(pg.typing.Int())), (pg.typing.StrKey(), pg.typing.Int()) ]) def bar(a, b, c, args, *kwargs): return a * b / c + sum(args) + sum(kwargs.values())
See pygx.Functor for more details on symbolic function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
args
|
None | list[tuple[str | KeySpec, ValueSpec, str] | tuple[str | KeySpec, ValueSpec, str, Any]]
|
A list of tuples that defines the schema for function arguments.
Please see |
None
|
returns
|
ValueSpec | None
|
Optional value spec for return value. |
None
|
base_class
|
type[Functor] | None
|
Optional base class derived from |
None
|
**kwargs
|
Any
|
Keyword arguments for infrequently used options: Acceptable
keywords are: * |
{}
|
Returns:
| Type | Description |
|---|---|
Callable[..., type[Functor]] | type[Functor]
|
A function that converts a regular function into a symbolic function. |
Source code in pygx/symbolic/_functor.py
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | |
functor_class
¶
functor_class(
func: FunctionType,
args: (
list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef] | None
) = None,
returns: ValueSpec | None = None,
base_class: type[Functor] | None = None,
*,
auto_doc: bool = False,
auto_typing: bool = False,
serialization_key: str | None = None,
additional_keys: list[str] | None = None,
add_to_registry: bool = False
) -> type[Functor]
Returns a functor class from a function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
FunctionType
|
Function to be wrapped into a functor. |
required |
args
|
list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef] | None
|
Symbolic args specification. @pg.functor([('c', pg.typing.Int(min_value=0), 'Arg c')]) def foo(a, b, c=1, **kwargs): return a + b + c + sum(kwargs.values()) |
None
|
returns
|
ValueSpec | None
|
Optional schema specification for the return value. |
None
|
base_class
|
type[Functor] | None
|
Optional base class (derived from |
None
|
auto_doc
|
bool
|
If True, the descriptions of argument fields will be inherited
from funciton docstr if they are not explicitly specified through
|
False
|
auto_typing
|
bool
|
If True, the value spec for constraining each argument will be
inferred from its annotation. Otherwise the value specs for all arguments
will be |
False
|
serialization_key
|
str | None
|
An optional string to be used as the serialization key
for the class during |
None
|
additional_keys
|
list[str] | None
|
An optional list of strings as additional keys to deserialize an object of the registered class. This can be useful when we need to relocate or rename the registered class while being able to load existing serialized JSON values. |
None
|
add_to_registry
|
bool
|
If True, the newly created functor class will be added to the registry for deserialization. |
False
|
Returns:
| Type | Description |
|---|---|
type[Functor]
|
|
Raises:
| Type | Description |
|---|---|
KeyError
|
names of symbolic arguments are not compatible with function signature. |
TypeError
|
types of symbolic arguments are not compatible with function signature. |
ValueError
|
default values of symbolic arguments are not compatible with function signature. |
Source code in pygx/symbolic/_functor.py
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 | |
default_load_handler
¶
default_load_handler(
path: str, file_format: str | WireFormat = "json", **kwargs: Any
) -> Any
Default load handler from file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
File path to read. |
required |
file_format
|
str | WireFormat
|
The literal |
'json'
|
**kwargs
|
Any
|
Forwarded to |
{}
|
Source code in pygx/symbolic/_io.py
default_save_handler
¶
default_save_handler(
value: Any,
path: str,
*,
indent: int | None = None,
file_format: str | WireFormat = "json",
**kwargs: Any
) -> None
Default save handler to file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
Value to save. |
required |
path
|
str
|
File path to write. |
required |
indent
|
int | None
|
Indentation passed to the format's encoder. |
None
|
file_format
|
str | WireFormat
|
The literal |
'json'
|
**kwargs
|
Any
|
Forwarded to |
{}
|
Source code in pygx/symbolic/_io.py
load
¶
Load a symbolic value using the global load handler.
Example::
class A(pg.Object):
x: Any
a1 = A(1)
file = 'my_file.json'
a1.save(file)
a2 = pg.load(file)
assert pg.eq(a1, a2)
# Narrow the static return type and assert at runtime:
a3 = pg.load(file, expected_type=A)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
A path string for loading an object. |
required |
*args
|
Any
|
Positional arguments that will be passed through to the global load handler. |
()
|
expected_type
|
Any
|
When provided (anything other than the default |
Any
|
**kwargs
|
Any
|
Keyword arguments that will be passed through to the global load handler. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
Return value from the global load handler. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
Source code in pygx/symbolic/_io.py
open_jsonl
¶
open_jsonl(path: str, mode: str = 'r', **kwargs: Any) -> Sequence
Open a JSONL file for reading or writing.
Example::
with pg.open_jsonl('my_file.jsonl', 'w') as f: f.add(1) f.add('foo') f.add(dict(x=1))
with pg.open_jsonl('my_file.jsonl', 'r') as f: for value in f: print(value)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
The path to the file. |
required |
mode
|
str
|
The mode of the file. |
'r'
|
**kwargs
|
Any
|
Additional keyword arguments that will be passed to
|
{}
|
Returns:
| Type | Description |
|---|---|
Sequence
|
A sequence for PyGX objects. |
Source code in pygx/symbolic/_io.py
save
¶
Save a symbolic value using the global save handler.
Example::
class A(pg.Object):
x: Any
a1 = A(1)
file = 'my_file.json'
a1.save(file)
a2 = pg.load(file)
assert pg.eq(a1, a2)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
value to save. |
required |
path
|
str
|
A path string for saving |
required |
*args
|
Any
|
Positional arguments that will be passed through to the global save handler. |
()
|
**kwargs
|
Any
|
Keyword arguments that will be passed through to the global save handler. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
Return value from the global save handler. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
if global save handler is not set. |
Source code in pygx/symbolic/_io.py
members
¶
members(
fields: list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef],
metadata: dict[str, Any] | None = None,
serialization_key: str | None = None,
additional_keys: list[str] | None = None,
add_to_registry: bool = True,
) -> Decorator
Function/Decorator for declaring symbolic fields for pg.Object.
Example::
@pg.members([
# Declare symbolic fields. Each field produces a symbolic attribute
# for its object, which can be accessed by self.<field_name>.
# Description is optional.
('x', pg.typing.Int(min_value=0, default=0), 'Description for x.'),
('y', pg.typing.Str(), 'Description for y.')
])
class A(pg.Object):
def sum(self):
return self.x + self.y
@pg.members([ # Override field 'x' inherited from class A and make it more restrictive. ('x', pg.typing.Int(max_value=10, default=5)), # Add field 'z'. ('z', pg.typing.Bool().noneable()) ]) class B(A): pass
@pg.members([
# Declare dynamic fields: any keyword can be acceptable during __init__
# and can be accessed using self.<field_name>.
(pg.typing.StrKey(), pg.typing.Int())
])
class D(B):
pass
@pg.members([ # Declare dynamic fields: keywords started with 'foo' is acceptable. (pg.typing.StrKey('foo.*'), pg.typing.Int()) ]) class E(pg.Object): pass
See pygx.typing.ValueSpec for supported value specifications.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fields
|
list[Field | FieldDef] | dict[FieldKeyDef, FieldValueDef]
|
A list of pg.typing.Field or equivalent tuple representation as
( |
required |
metadata
|
dict[str, Any] | None
|
Optional dict of user objects as class-level metadata which will be attached to class schema. |
None
|
serialization_key
|
str | None
|
An optional string to be used as the
serialization key for the class during |
None
|
additional_keys
|
list[str] | None
|
An optional list of strings as additional keys to deserialize an object of the registered class. This can be useful when we need to relocate or rename the registered class while being able to load existing serialized JSON values. |
None
|
add_to_registry
|
bool
|
If True, register serialization keys and additional keys with the class. |
True
|
Returns:
| Type | Description |
|---|---|
Decorator
|
a decorator function that register the class or function with schema created from the fields. |
Raises:
| Type | Description |
|---|---|
TypeError
|
Decorator cannot be applied on target class or keyword argument provided is not supported. |
KeyError
|
If type has already been registered in the registry. |
ValueError
|
schema cannot be created from fields. |
Source code in pygx/symbolic/_object.py
4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 | |
deref
¶
deref(value: Symbolic, recursive: bool = False) -> Any
Dereferences a symbolic value that may contain pg.Ref.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Symbolic
|
The input symbolic value. |
required |
recursive
|
bool
|
If True, dereference |
False
|
Returns:
| Type | Description |
|---|---|
Any
|
The dereferenced root, or dereferenced tree if recursive is True. |
Source code in pygx/symbolic/_ref.py
maybe_ref
¶
Returns a reference if a value is not symbolic or already has a parent.
symbolize
¶
Make a symbolic class/function out of a regular Python class/function.
pg.symbolize is introduced for the purpose of making existing
classes/functions symbolically programmable. For use cases that build
symbolic classes from scratch (native PyGX classes), extending pg.Object
with dataclass-like class annotations that declare the symbolic properties
is the recommended way, which automatically generates the __init__
method and allow symbolic attributes to be accessed via self.<member>.
pg.symbolize can be invoked as a class/function decorator, or as a
function. When it is used as a decorator, the decorated class or function
will be converted to a symbolic type (via pygx.wrap and
pygx.functor_class). This is preferred when user can modify the
files of existing classes/functions. For example::
@pg.symbolize def foo(a, b): return a + b
f = foo(1, 2) f.sym_rebind(a=2) f() # Returns 4
@pg.symbolize([
# (Optional) add symbolic constraint for init argument 'a'.
('a', pg.typing.Int(min_value=0), 'Description for a.')
])
class Foo:
def init(self, a, b):
self._a = a
self._b = b
def result(self):
return self._a + self._b
f = Foo(1, 2) f.sym_rebind(a=2, b=3) f.result() # Returns 5
When it used as a function, the input class or function will not be modified. Instead, a new symbolic type will be created and returned. This is helpful when users want to create new symbolic types from existing classes/functions without modifying their original source code. For example::
def foo(a, b): return a + b
# Create a new symbolic type with constraint on 'a'. symbolic_foo = pg.symbolize(foo, [ ('a', pg.typing.Int(min_value=0)) ], returns=pg.typing.Int()) foo(1, 2) # Returns 3 (foo is kept intact).
f = symbolic_foo(1, 2) f.sym_rebind(a=2) f() # Returns 4.
class Foo: def init(self, a, b): self._a = a self._b = b
def result(self):
return self._a + self._b
SymbolicFoo = pg.symbolize(Foo) f = SymbolicFoo(2, 2) f.sym_rebind(a=3) f.result() # Returns 5.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*args
|
Any
|
The positional arguments for
Where |
()
|
**kwargs
|
Any
|
Keyword arguments will be passsed through to |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
A Symbolic subclass for the decorated/input type. |
Raises:
| Type | Description |
|---|---|
TypeError
|
input type cannot be symbolized, or it's not a type. |
Source code in pygx/symbolic/_symbolize.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | |
from_uri
¶
from_uri(
uri: str,
class_resolver: ClassSource[T],
*,
case_sensitive: bool = True,
allow_scheme_names: bool = False
) -> T
Build a symbolic object from a URI-like string.
Two equivalent top-level forms are accepted:
- URI form:
<ClassName>?<arg1>=<v1>&<arg2>=<v2> - Call form:
<ClassName>(<arg1>=<v1>, <arg2>=<v2>)
Use whichever reads better at the call site. The forms can also be
mixed: nested values inside a URI-form call always use call form, since
& is the URI's arg separator (see Special characters below).
Positional arguments are accepted (mapped to the class's
init_arg_list) and may appear before any keyword arguments.
Suppose we have::
class Optimizer(pg.Object):
lr: float
momentum: float = 0.0
class Schedule(pg.Object):
name: str
steps: list[int] = []
class Trainer(pg.Object):
optimizer: Optimizer
epochs: int = 10
schedules: list[Schedule] = []
tags: dict[str, str] = {}
notes: str | None = None
The class_resolver argument can be a dict, a list, or a callable::
# 1. Mapping: explicit name -> class lookup.
registry = {
'Optimizer': Optimizer,
'Schedule': Schedule,
'Trainer': Trainer,
}
# 2. Sequence: each class is keyed by ``cls.__name__``.
registry = [Optimizer, Schedule, Trainer]
# 3. Callable: resolve a name on demand (e.g. import lazily).
def registry(name):
return {'Optimizer': Optimizer, 'Trainer': Trainer}.get(name)
All three are interchangeable in the examples below.
Primitives — ints, floats, bools (True/False, yes/no),
None, and strings (quoted or unquoted)::
pg.from_uri('Optimizer?lr=0.1', registry)
# Optimizer(lr=0.1, momentum=0.0)
pg.from_uri('Optimizer?lr=0.1&momentum=0.9', registry)
# Optimizer(lr=0.1, momentum=0.9)
pg.from_uri('Schedule?name=cosine', registry)
pg.from_uri('Schedule?name="cosine annealing"', registry)
# Schedule(name='cosine annealing', steps=[])
Positional arguments map through the class's init_arg_list::
pg.from_uri('Optimizer?0.1&0.9', registry)
pg.from_uri('Optimizer(0.1, 0.9)', registry)
# Optimizer(lr=0.1, momentum=0.9)
Lists use [...]; dicts use {...} with : or =::
pg.from_uri('Schedule?name=warmup&steps=[1, 2, 3]', registry)
# Schedule(name='warmup', steps=[1, 2, 3])
pg.from_uri('Trainer?optimizer=Optimizer(lr=0.1)&'
'tags={env: "prod", team: "ml"}', registry)
# Trainer(optimizer=Optimizer(lr=0.1), tags={'env': 'prod', 'team': 'ml'})
Nested objects are spelled with the call form anywhere a value is expected; arbitrary depth is supported::
pg.from_uri(
'Trainer?epochs=5&optimizer=Optimizer(lr=0.1, momentum=0.9)&'
'schedules=[Schedule(name="warmup", steps=[1, 2]),'
' Schedule(name="cosine", steps=[10, 20])]',
registry,
)
Equivalently, in call form::
pg.from_uri(
'Trainer(epochs=5,'
' optimizer=Optimizer(lr=0.1, momentum=0.9),'
' schedules=[Schedule(name="warmup", steps=[1, 2])])',
registry,
)
None and noneable fields::
pg.from_uri('Trainer?optimizer=Optimizer(lr=0.1)¬es=None',
registry)
# Trainer(..., notes=None)
Case-insensitive matching for both class and field names::
pg.from_uri('TRAINER?optimizer=OPTIMIZER(LR=0.1, MoMentum=0.9)',
registry, case_sensitive=False)
# Trainer(optimizer=Optimizer(lr=0.1, momentum=0.9))
Special characters in string values:
-
Inside double or single quotes, content is preserved verbatim — spaces, real newlines, parens, commas, and
&are all fine::pg.from_uri('Schedule(name="my favorite schedule")', registry)
pg.from_uri('Schedule(name="line1\nline2")', registry)
Note:
\nhere is a real newline in the Python source. The¶parser does NOT interpret escape sequences — only
\"(an¶escaped quote) and
\\(an escaped backslash) are useful.¶To embed a newline, pass a real newline character.¶
-
Unquoted top-level URI-form values may contain spaces (the URI form splits only on
&)::pg.from_uri('Schedule?name=cosine annealing', registry)
Schedule(name='cosine annealing')¶
However, inside any structured context ([...], {...},
Name(...)), whitespace is a token separator — quote any value
that contains spaces.
-
&is the URI form's arg separator and is not quote-aware. If a value contains&, use the call form (or wrap the value in[...]/Name(...)so the surrounding context isn't URI-style)::Doesn't work — URI form splits on
&before quotes are seen:¶pg.from_uri('Note?text="a&b"', registry) # ValueError
Works — call form splits on
,:¶pg.from_uri('Note(text="a&b")', registry)
Scheme-style names for registry-style factories — pass
allow_scheme_names=True and a callable resolver::
def resolve(model_id):
factory = lookup(model_id) # exact + regex registry, etc.
def instantiate(**kw):
return factory(model=model_id, **kw)
return instantiate
pg.from_uri('mock://test?temperature=0.1', resolve,
allow_scheme_names=True)
pg.from_uri('claude-opus-4-6@latest(temperature=0.1)', resolve,
allow_scheme_names=True)
The leading name extends to the first ? or ( and is passed
verbatim to the resolver. When the resolver returns a plain callable
(no __schema__), value-spec validation is skipped — kwargs are
forwarded with best-effort coercion so the callable can accept
**kwargs for fields it routes itself.
Note
pg.patching.from_uri is a different function that resolves a URI
to a registered pygx.patching.Patcher; this
function builds a symbolic object instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uri
|
str
|
URI-like string. Either |
required |
class_resolver
|
ClassSource[T]
|
Source used to resolve both the top-level name and
any nested object calls. The return type
|
required |
case_sensitive
|
bool
|
When |
True
|
allow_scheme_names
|
bool
|
When The resolver may return either a Only the top-level name is affected; nested |
False
|
Returns:
| Type | Description |
|---|---|
T
|
A new instance of |
T
|
constructed from the parsed arguments. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the top-level class name is not resolvable via the resolver. |
ValueError
|
If the URI cannot be parsed or violates the class schema. |
Source code in pygx/symbolic/_uri.py
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 | |
parse_uri_value
¶
parse_uri_value(
raw: str,
value_spec: ValueSpec | None = None,
class_resolver: ClassSource[Any] | _ClassResolver | None = None,
*,
source_kind: str = "Source",
source_id: str = "",
arg_name: str = "",
case_sensitive: bool = True
) -> Any
Parse one value string into a typed Python value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
str
|
The raw value string. |
required |
value_spec
|
ValueSpec | None
|
If provided, the parsed value is validated/coerced through
|
None
|
class_resolver
|
ClassSource[Any] | _ClassResolver | None
|
Optional source for resolving nested object calls
|
None
|
source_kind
|
str
|
Label used in error messages (e.g. |
'Source'
|
source_id
|
str
|
Identifier (patcher/class name) used in error messages. |
''
|
arg_name
|
str
|
Argument name used in error messages. |
''
|
case_sensitive
|
bool
|
When |
True
|
Returns:
| Type | Description |
|---|---|
Any
|
The parsed value (after optional |
Raises:
| Type | Description |
|---|---|
ValueError
|
If parsing fails or the value violates |
Source code in pygx/symbolic/_uri.py
options: show_root_heading: false show_root_toc_entry: false members_order: source heading_level: 2