The Register

class strcs.CreateRegister

The register is a central object that holds knowledge of how to transform data into different types. It is used to get a decorator that is used to add those creators and also used to then do a conversion:

Usage looks like:

import strcs

reg = strcs.CreateRegister()
creator = reg.make_decorator()

# Then the creator may be used as a decorator to add knowledge about custom
# transformations

# Then objects may be created
instance = reg.create(MyKls, some_data)
create(typ: type[T] | strcs.Type[T], value: object = strcs.NotSpecified, meta: strcs.Meta | None = None, once_only_creator: strcs.ConvertFunction[T] | None = None) T

Create an instance of the specified type by transforming the provided value.

If no meta is provided, then an empty meta is created.

If once_only_creator is provided then it will be used as the entry point for conversion.

create_annotated(typ: type[T] | strcs.Type[T],, ann: MetaAnnotation | MergedMetaAnnotation | AdjustableMeta | AdjustableCreator | ConvertFunction[T], value: object = strcs.NotSpecified, meta: strcs.Meta | None = None, once_only_creator: strcs.ConvertFunction[T] | None = None) T

This is the same as reg.create but the type will be wrapped with the provided annotation.

make_decorator() strcs.Creator

Return an object that can be used to register Creators:

import attrs
import strcs

reg = strcs.CreateRegister()
creator = reg.make_decorator()


@attrs.define
class Thing:
    one: int


@creator(Thing)
def make_thing(value: object, /) -> strcs.ConvertResponse[Thing]:
    ...

thing = reg.create(Thing, ...)

The decorator is instantiated with the object the creator should be making. As well as optional a boolean called assume_unchanged_converted which defaults to True.

When assume_unchanged_converted is True then the creator is not called if the value is already the desired type. If it is False then it will always be called.

Multiple registers

It is easy to have multiple registers as the creator functions can ask for the current register with the special _register: strcs.CreateRegister in the signature:

import attrs
import strcs

reg = strcs.CreateRegister()
creator = reg.make_decorator()


@attrs.define
class MyKls:
    one: int


@creator(MyKls)
def create_mykls(value: object, /, _register: strcs.CreateRegister) -> bool:
    assert _register is reg
    return True


instance = reg.create(MyKls, {"one": 2})
assert isinstance(instance, MyKls)

Resolving type annotations

There is a limitation whereby unresolved string type annotations will cause errors as strcs won’t know what object the string represents. strcs offers a helper function based off typing.get_type_hints for resolving string type annotations. It will automatically be used on any class that strcs needs to work with unless the auto_resolve_string_annotations=False is given to strcs.CreateRegister.

strcs.resolve_types(cls: C, globalns: dict[str, object] | None = None, localns: dict[str, object] | None = None, *, type_cache: Union[CreateRegister, TypeCache]) C

Resolve any strings and forward annotations in type annotations.

This is equivalent to attrs.resolve_types except it doesn’t erase Annotations.

It is automatically used by strcs.CreateRegister unless the auto_resolve_string_annotations=False option is used when it’s created.

Assumes that the string annotations have been defined when you call this function:

import attrs
# or ``dataclasses`` equivalent
import strcs


@attrs.define
class One:
    two: "Two"


@attrs.define
class Two:
    ...


strcs.resolve_types(One)

Note that if from __future__ import annotations is used then all types are strings and require resolution. In that case if auto resolution on the register is turned off then strcs.resolve_types may be used as a decorator in any situation where types are already available at definition:

from __future__ import annotations
import attrs
import strcs


@strcs.resolve_types
class Stuff:
    one: int


@attrs.define
class Thing:
    stuff: "Stuff"
    other: "Other"


@strcs.resolve_types
@attrs.define
class Other:
    thing: Thing | None


strcs.resolve_types(Thing)

Note

Calling resolve_types will modify the fields on the class in place.