pg.Schema

Accessible via pg.Schema, pg.typing.Schema.

class Schema(fields, name=None, base_schema_list=None, description=None, *, allow_nonconst_keys=False, metadata=None)[source]

Bases: pg.Formattable, pg.JSONConvertible

Class that represents a schema.

PyGlove’s runtime type system is based on the concept of Schema ( class pg.Schema), which defines what symbolic attributes are held by a symbolic type (e.g. a symbolic dict, a symbolic list or a symbolic class) and what values each attribute accepts. A Schema object consists of a list of Field (class pg.Field), which define the acceptable keys and their values for these attributes. A Schema object is usually created automatically and associated with a symbolic type upon its declaration, through decorators such as pg.members, pg.symbolize or pg.functor. For example:

@pg.members([
    ('x', pg.typing.Int(default=1)),
    ('y', pg.typing.Float().noneable())
])
class A(pg.Object):
  pass

print(A.__schema__)

@pg.symbolize([
    ('a', pg.typing.Int()),
    ('b', pg.typing.Float())
])
def foo(a, b):
  return a + b

print(foo.__schema__)

Implementation-wise it holds an ordered dictionary of a field key (pg.KeySpec) to its field definition (pg.Field). The key specification describes what keys/attributes are acceptable for the field, and value specification within the Field describes the value type of the field and their validation rules, default values, and etc.

Symbolic attributes can be inherited during subclassing. Accordingly, the schema that defines a symbolic class’ attributes can be inherited too by its subclasses. The fields from the bases’ schema will be carried over into the subclasses’ schema, while the subclass can override, by redefining that field with the same key. The subclass cannot override its base classes’ field with arbitrary value specs, it must be overriding non-frozen fields with more restrictive validation rules of the same type, or change their default values. See pg.ValueSpec for more details.

The code snippet below illustrates schema inheritance during subclassing:

@pg.members([
    ('x', pg.typing.Int(min_value=1)),
    ('y', pg.typing.Float()),
])
class A(pg.Object):
  pass

@pg.members([
    # Further restrict inherited 'x' by specifying the max value, as well
    # as providing a default value.
    ('x', pg.typing.Int(max_value=5, default=2)),
    ('z', pg.typing.Str('foo').freeze())
])
class B(A):
  pass

assert B.schema.fields.keys() == ['x', 'y', 'z']

@pg.members([
    # Raises: 'z' is frozen in class B and cannot be extended further.
    ('z', pg.typing.Str())
])
class C(B):
  pass

With a schema, an input dict can be validated and completed by the schema via apply. If required a field is missing from the schema, and the object’s allow_partial is set to False, a KeyError will raise. Otherwise a partially validated/transformed dict will be returned. Missing values in the object will be placeheld by pg.MISSING_VALUE.

Attributes:

allow_nonconst_keys

Returns whether to allow non-const keys.

description

Returns the description for the schema.

dynamic_field

Returns the field that matches multiple keys if any.

fields

Returns fields of this schema.

metadata

Returns metadata of this schema.

name

Name of this schema.

Methods:

apply(dict_obj[, allow_partial, ...])

Apply this schema to a dict object, validate and transform it.

extend(base)

Extend current schema based on a base schema.

format([compact, verbose, root_indent, ...])

Format current Schema into nicely printed string.

get(key[, default])

Returns field by key with default value if not found.

get_field(key)

Get field definition (Field) for a key.

is_compatible(other)

Returns whether current schema is compatible with the other schema.

items()

Returns an iterable of (KeySpec, Field) tuple in declaration order.

keys()

Return an iteratable of KeySpecs in declaration order.

merge(schema_list[, name, description])

Merge multiple schemas into one.

resolve(keys)

Resolve keys by grouping them by their matched fields.

set_description(description)

Sets the description for the schema.

set_name(name)

Sets the name of this schema.

to_json(**kwargs)

Returns a plain Python value as a representation for this object.

validate(dict_obj[, allow_partial, root_path])

Validates whether dict object is conformed with the schema.

values()

Returns an iterable of Field in declaration order.

property allow_nonconst_keys: bool[source]

Returns whether to allow non-const keys.

apply(dict_obj, allow_partial=False, child_transform=None, root_path=None)[source]

Apply this schema to a dict object, validate and transform it.

Return type:

Dict[str, Any]

Parameters:
  • dict_obj – JSON dict type that (maybe) conform to the schema.

  • allow_partial – Whether allow partial object to be created.

  • child_transform

    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. The signature of transform_fn is: (path, field, value) -> new_value Argument path is a KeyPath object to the field. Argument field is on which Field the value should apply. Argument value is the value from input that matches a Field from the schema, with child fields already transformed by this function. There are possible values for these two arguments:

    ------------------------------------------------------------
                            |   field       | value
    ------------------------------------------------------------
    The value with          |               |
    applicable Field is     |   Not None    | Not MISSING_VALUE
    found in schema.        |               |
    value.                  |               |
    ------------------------------------------------------------
    The value is            |               |
    not present for a       |   Not None    | MISSING_VALUE
    key defined in schema.  |               |
    ------------------------------------------------------------
    

    Return value will be inserted to the parent dict under path, unless return value is MISSING_VALUE.

  • root_path – KeyPath of root element of dict_obj.

Returns:

A dict filled by the schema with transformed values.

Raises:
  • KeyError – Key is not allowed in schema.

  • TypeError – Type of dict values are not aligned with schema.

  • ValueError – Value of dict values are not aligned with schema.

property description: str | None[source]

Returns the description for the schema.

property dynamic_field: Field | None[source]

Returns the field that matches multiple keys if any.

extend(base)[source]

Extend current schema based on a base schema.

Return type:

pg.Schema

property fields: Dict[KeySpec, Field][source]

Returns fields of this schema.

format(compact=False, verbose=True, root_indent=0, *, markdown=False, cls_name=None, bracket_type=BracketType.ROUND, **kwargs)[source]

Format current Schema into nicely printed string.

Return type:

str

get(key, default=None)[source]

Returns field by key with default value if not found.

Return type:

Optional[pg.Field]

get_field(key)[source]

Get field definition (Field) for a key.

Return type:

Optional[pg.Field]

Parameters:

key – string as input key.

Returns:

  • Its key spec is a ConstStrKey that equals to the input key.

  • Or it’s the first field whose key spec is a NonConstKey which matches the input key.

Return type:

Matched field. A field is considered a match when

is_compatible(other)[source]

Returns whether current schema is compatible with the other schema.

NOTE(daiyip): schema A is compatible with schema B when: schema A and schema B have the same keys, with compatible values specs.

Return type:

bool

Parameters:

other – Other schema.

Returns:

True if values that is acceptable to the other schema is acceptable to

current schema.

Raises:

TypeError – If other is not a schema object.

items()[source]

Returns an iterable of (KeySpec, Field) tuple in declaration order.

Return type:

Iterable[Tuple[pg.KeySpec, pg.Field]]

keys()[source]

Return an iteratable of KeySpecs in declaration order.

Return type:

Iterable[pg.KeySpec]

classmethod merge(schema_list, name=None, description=None)[source]

Merge multiple schemas into one.

For fields shared by multiple schemas, the first appeared onces will be used in the merged schema.

Return type:

pg.Schema

Parameters:
  • schema_list – A list of schemas to merge.

  • name – (Optional) name of the merged schema.

  • description – (Optinoal) description of the schema.

Returns:

The merged schema.

property metadata: Dict[str, Any][source]

Returns metadata of this schema.

property name: str | None[source]

Name of this schema.

resolve(keys)[source]

Resolve keys by grouping them by their matched fields.

Return type:

Tuple[Dict[pg.KeySpec, List[str]], List[str]]

Parameters:

keys – A list of string keys.

Returns:

A tuple of matched key results and unmatched keys.

Matched key results are an ordered dict of KeySpec to matched keys, in field declaration order. Unmatched keys are strings from input.

set_description(description)[source]

Sets the description for the schema.

Return type:

None

set_name(name)[source]

Sets the name of this schema.

Return type:

None

to_json(**kwargs)[source]

Returns a plain Python value as a representation for this object.

A plain Python value are basic python types that can be serialized into JSON, e.g: bool, int, float, str, dict (with string keys), list, tuple where the container types should have plain Python values as their values.

Return type:

Dict[str, Any]

Parameters:

**kwargs – Keyword arguments as flags to control JSON conversion.

Returns:

A plain Python value.

validate(dict_obj, allow_partial=False, root_path=None)[source]

Validates whether dict object is conformed with the schema.

Return type:

None

values()[source]

Returns an iterable of Field in declaration order.

Return type:

Iterable[pg.Field]