Typing

The typing system is managed using a meta data wrapper for the Python types along with the appropriate extraction logic to parse a signature to extract the types. The entry point into the type meta-data system is:

class HgTypeMetaData[source]

The type meta-data provides reflective information describing a supplied type. It contains a collection of useful methods and properties that are used when wiring and reasoning about HGraph signatures.

The key entry-point into this class is the parse_type and parse_value methods. These methods construct instances of HgTypeMetaData from the given inputs. This is the top level class and can construct any of the supported types. It is possible to use either the HgScalarTypeMetaData or HgTimeSeriesTypeMetaData to parse the types with if you know what type is expected.

This defines the following useful properties:

is_resolved

If this instance of type is resolved, or in other words, does this type or any of its children contain an unresolved type. (Unresolved means has a TypeVar in the type declaration)

is_scalar

True if this represents a scalar type.

is_atomic

True if this represents a type has no child elements. For example, a str or int type. An example of a NON-ATOMIC element could be tuple[int, …] where this has additional child type information (in this case an int type).

is_generic

True if this represents a generic type or in other words, a TypeVar element.

is_injectable

True if this represents an injectable type. These are types that are required to be injected into the function signature, for example, STATE.

is_reference

True if this type is a reference type.

is_context_manager

True if this type represents a context manager.

is_context_wired

Is this an auto-wired input from a context.

py_type

The python type this meta-data type represents.

classmethod parse_type(value_tp) HgTypeMetaData | None[source]

This accepts a python type (value_tp) and returns the HgTypeMetaData instance that represents the type supplied. If the type does not resolve to a valid HGraph type, ParseError is raised.

Return type:

Optional[HgTypeMetaData]

classmethod parse_value(value) HgTypeMetaData | None[source]

Attempts to determine the HGraph type from the value supplied. This is not as reliable as parsing the type and could result in an incorrect result or may be unable to extract the type at all. But it is useful for auto-resolution and validation.

Return type:

Optional[HgTypeMetaData]

as_reference() HgREFTypeMetaData[source]

Converts the type meta-data to a reference type if the type is not already a reference type. If the type is already a reference type, it will be returned as is.

Note

This DOES NOT recurse the type hierarchy.

Return type:

HgREFTypeMetaData

Returns:

The reference variation of this type (or itself).

build_resolution_dict(resolution_dict, wired_type: HgTypeMetaData)[source]

Attempts to resolve any unresolved types using the wired type supplied. Any resolutions made are added to the resolution_dict. This is used to:

  1. Validate that resolutions made previously for the same TypeVar instances are still valid.

  2. When resolution is made to a different type, determine if the types are convertible, if so, pick the lowest conversion to bind to.

Once all the types have had a go at determining the resolution_dict, the types are resolved for real in a second pass. The outputs are fully reliant on types to be resolved using the wired_type s on the inputs to resolve the output types.

Parameters:
  • resolution_dict – A dictionary of TypeVar to HgTypeMetaData instances.

  • wired_type (HgTypeMetaData) – The wired type provided to assist in building the type-resolution dictionary.

dereference() HgTypeMetaData[source]

Returns the dereferenced the type.

Return type:

HgTypeMetaData

Returns:

The dereferenced value of the type, this is performed recursively. The resultant type will represent the type without any reference value included.

do_build_resolution_dict(resolution_dict, wired_type: HgTypeMetaData)[source]

Implementation method for build_resolution_dict - to be overridden by the derived classes. Do not override the build_resolution_dict method in derived classes.

Parameters:
  • resolution_dict – A dictionary of TypeVar to HgTypeMetaData instances.

  • wired_type (HgTypeMetaData) – The wired type provided to assist in building the type-resolution dictionary.

matches(tp: HgTypeMetaData) bool[source]

Can this instance of meta-data match the supplied type? This is used to determine if a type can be wired to another type. It does not provide a guarantee that the types are compatible, only that they could match. For example:

add_(lhs: TS[NUMERIC], rhs: TS[NUMERIC])

in this case TS[int] and TS[float] could match, but if the inputs to lhs and rhs were TS[int] and TS[float] respectively, then the types would not be a match for each input but not for the function as a whole.

Return type:

bool

matches_type(tp: TYPE)[source]

Will match a standard python type (tp) to this HGraph type. The function is effectively a call to parse_type and then calls match on the result.

resolve(resolution_dict, weak: bool = False) HgTypeMetaData[source]

Resolve any TypeVar s that are found in this type using the resolution_dict provided. If weak is False and a type can’t be resolved, it will raise an Exception. If weak is set to True then any types that are unresolved remain unresolved in the returned type.

Parameters:
  • resolution_dict – The dictionary containing the currently resolved types so far (TypeVar->HgTypeMetaData).

  • weak (bool) – When True this will self when an exact resolution is not possible.

Return type:

HgTypeMetaData

Returns:

The resolved HGraph type instance.

property generic_rank: dict[type, float]

The generic rank indicates how imprecision exists in the type. The higher the rank, the more imprecise. With the highest rank being 1.0 and the smallest being 0.0. This ranking is used to determine the best match when wiring types by summing up the ranks and picking the lowest sum of the inputs as the best match.

property has_references: bool

True if this type is or has a reference in it.

Type:

return

is_atomic: bool = False
is_context_manager: bool = False
is_context_wired: bool = False
is_generic: bool = False
is_injectable: bool = False
is_reference: bool = False
is_resolved: bool
is_scalar: bool
py_type: Type
property type_vars: set

The set of type-vars that are associated to this type instance.

Type:

return

The support for schema based types has as it’s base:

class AbstractSchema[source]

The base class for the two schema-based types supported by HGraph, namely: CompoundScalar and TimeSeriesSchema.

This class provides the key meta-data describing the schema which is extracted from the class type annotations. The information is tracked at class level and is stored in the attribute __meta_data_schema__. This attribute contains the name of the property, and it’s HgTypeMetaData representation.

Schemas can also contain unresolved generic types. The resolution and any partial information is also tracked on class level attributes in this class. These make use of the attributes: __resolved__, __partial_resolution__ and __partial_resolution_parent__.

For scalar values, use:

class CompoundScalar[source]

Use this to construct scalar values with more complex structure. Below is an example of the use of this:

@dataclass(frozen=True)
class MyCompoundScalar(CompoundScalar):
    p1: str
    p2: int

The compound scalar can contain other compound scalar types, but as of now it cannot support a recursive definition, i.e. not possible to have a property of type MyCompoundScalar.

It is possible to create a generic compound scalar, for example:

@dataclass(frozen=True)
class MyTemplateScalar(CompoundScalar, Generic[SCALAR]):
    p1: SCALAR

To enable C++ field expansion (storing fields in C++ memory rather than as opaque Python objects), use the cpp_native flag:

@dataclass(frozen=True)
class MyCppScalar(CompoundScalar, cpp_native=True):
    p1: int
    p2: float

Alternatively, use the CppNative[T] wrapper in type annotations:

def my_node(ts: TS[CppNative[MyScalar]]) -> TS[int]:
    ...
classmethod from_dict(d: dict) CompoundScalar[source]

Creates an instance of the compound scalar from a dictionary of values.

Parameters:

d (dict) – Dictionary of values matching the schema

Return type:

CompoundScalar

Returns:

New instance of the compound scalar

to_dict()[source]

Converts the value of the compound scalar into a dictionary. This will allow for construction of the type using **kwargs pattern. For example:

my_cs = MyCompundScalar(p1="test", p2=42)
my_cs_copy = MyCompundScalar(**my_cs.to_dict())
Returns:

The dictionary of the values.

For time-series schema to be used with TSD use this as a base:

class TimeSeriesSchema[source]

Describes a time series schema that can be used to describe the structure of a TSB time-series composite. To construct a schema, do something along the lines of:

@dataclass(frozen=True)
class MySchema(TimeSeriesSchema):
    p1: TS[int]
    p2: TS[str]

...
tsb: TSB[MySchema]

The TSB type takes the schema type as an input to describe the structure.

The schema can also make use of template types to describe the structure, for example:

@dataclass(frozen=True)
class MySchema(TimeSeriesSchema, Generic[SCALAR, TIME_SERIES_TYPE]):
    p1: TS[SCALAR]
    p2: TIME_SERIES_TYPE

This can then be resolved to specific types when used.

classmethod scalar_type() Type[SCALAR][source]

The scalar type that represents the CompoundScalar of the time-series schema. This is generated using the method: to_scalar_schema.

This allows for mapping from a TSB to a scalar value.

Return type:

Type[TypeVar(SCALAR, bound= object)]

Returns:

The CompoundScalar type representing this schema.

classmethod to_scalar_schema() Type[CompoundScalar][source]

Converts a time series schema to a scalar schema.

Return type:

Type[CompoundScalar]

static from_scalar_schema(schema: Type[AbstractSchema]) Type[TimeSeriesSchema][source]

Creates a new time-series schema from the scalar schema provided. This has some limitations since it is hard to distinguish between TS[frozendict] and TSD[…,…].

Return type:

Type[TimeSeriesSchema]

To assist with dynamic schema construction the following are provided:

compound_scalar(**kwargs) Type[CompoundScalar][source]

Provides a mechanism to create a CompoundScalar instance without creating a class first. This is useful for either dynamically creating a compound scalar or when creating a convenience result. Here is an example of its use:

@compute_node
def combine_inputs(ts_1: TS[int], ts_2: TS[str]) -> TS[compound_scalar(p1=int, p2=str)]:
    return {"p1": ts_1.value, "p2": ts_2.value}
Return type:

Type[CompoundScalar]

and for time-series schemas:

ts_schema(**kwargs) Type[TimeSeriesSchema][source]

Provides a mechanism to create a TimeSeriesSchema instance without creating a class first. This is useful for either dynamically creating a compound time-series schema or when creating a convenience result. Here is an example of its use:

@compute_node
def route(condition: TS[bool], ts: TS[int]) -> TSB[ts_schema(on_true=TS[int], on_false=TS[int])]:
    if condition.value:
        return {"on_true": ts.value}
    else:
        return {"on_false": ts.value}

This can also be useful to dynamically create an input or output for example:

def process(**kwargs):
    input_schema = ts_schema(**{k: v.output_type for k, v in kwargs.items()})

    @graph
    def _process(tsb: TSB[input_schema]) -> TS[str]:
        ...

In the above example, the input schema is dynamically constructed from the supplied inputs to allow us to create a valid input.

Return type:

Type[TimeSeriesSchema]