@@ -1310,14 +1310,12 @@ Changelog
and this project adheres to Semantic Versioning.
-Unreleased
-
Compare with latest
+v0.1.6 - 2024-10-30
+Compare with v0.1.5
Bug Fixes
- Unwrap
TypeAliasType
for all nested types in a type graph (c837838 by Sean Stewart).
-
-
v0.1.5 - 2024-10-30
Compare with v0.1.4
Bug Fixes
diff --git a/v0.1/search/search_index.json b/v0.1/search/search_index.json
index 588f1f1..bfe89b1 100644
--- a/v0.1/search/search_index.json
+++ b/v0.1/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to typelib
","text":""},{"location":"#pythons-typing-toolkit","title":"Python's Typing Toolkit","text":"typelib
provides a sensible, non-invasive, production-ready toolkit for leveraging Python type annotations at runtime.
"},{"location":"#quickstart","title":"Quickstart","text":""},{"location":"#installation","title":"Installation","text":"poetry add 'typelib[json]'\n
"},{"location":"#bring-your-own-models","title":"Bring Your Own Models","text":"We don't care how your data model is implemented - you can use dataclasses
, TypedDict
, NamedTuple
, a plain collection, a custom class, or any other modeling library. As long as your type is valid at runtime, we'll support it.
"},{"location":"#the-how-and-the-where","title":"The How and the Where","text":""},{"location":"#how-the-high-level-api","title":"How: The High-Level API","text":"We have a simple high-level API which should handle most production use-cases:
from __future__ import annotations\n\nimport dataclasses\nimport datetime\nimport decimal\n\n\nimport typelib\n\n@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\nclass BusinessModel:\n op: str\n value: decimal.Decimal\n id: int | None = None\n created_at: datetime.datetime | None = None\n\n\ncodec = typelib.codec(BusinessModel)\ninstance = codec.decode(b'{\"op\":\"add\",\"value\":\"1.0\"}')\nprint(instance)\n#> BusinessModel(op='add', value=decimal.Decimal('1.0'), id=None, created_at=None)\nencoded = codec.encode(instance)\nprint(encoded)\n#> b'{\"op\":\"add\",\"value\":\"1.0\",\"id\":null,\"created_at\":null}'\n
Tip
Looking for more? Check out our API Reference for the high-level API.
"},{"location":"#where-at-the-edges-of-your-code","title":"Where: At the Edges of Your Code","text":"You can integrate this library at the \"edges\" of your code - e.g., at the integration points between your application and your client or you application and your data-store:
from __future__ import annotations\n\nimport dataclasses\nimport datetime\nimport decimal\nimport operator\nimport random\n\nimport typelib\n\n\nclass ClientRPC:\n def __init__(self):\n self.codec = typelib.codec(BusinessModel)\n\n def call(self, inp: bytes) -> bytes:\n model = self.receive(inp)\n done = self.op(model)\n return self.send(done)\n\n @staticmethod\n def op(model: BusinessModel) -> BusinessModel:\n op = getattr(operator, model.op)\n return dataclasses.replace(\n model,\n value=op(model.value, model.value),\n id=random.getrandbits(64),\n created_at=datetime.datetime.now(tz=datetime.UTC)\n )\n\n def send(self, model: BusinessModel) -> bytes:\n return self.codec.encode(model)\n\n def receive(self, data: bytes) -> BusinessModel:\n return self.codec.decode(data)\n\n\n@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\nclass BusinessModel:\n op: str\n value: decimal.Decimal\n id: int | None = None\n created_at: datetime.datetime | None = None\n
"},{"location":"#where-between-layers-in-your-code","title":"Where: Between Layers in Your Code","text":"You can integrate this library to ease the translation of one type to another:
from __future__ import annotations\n\nimport dataclasses\nimport datetime\nimport decimal\nimport typing as t\n\n\nimport typelib\n\n@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\nclass BusinessModel:\n op: str\n value: decimal.Decimal\n id: int | None = None\n created_at: datetime.datetime | None = None\n\n\nclass ClientRepr(t.TypedDict):\n op: str\n value: str\n id: str | None\n created_at: datetime.datetime | None\n\n\nbusiness_codec = typelib.codec(BusinessModel)\nclient_codec = typelib.codec(ClientRepr)\n# Initialize your business model directly from your input.\ninstance = business_codec.decode(\n b'{\"op\":\"add\",\"value\":\"1.0\",\"id\":\"10\",\"created_at\":\"1970-01-01T00:00:00+0000}'\n)\nprint(instance)\n#> BusinessModel(op='add', value=Decimal('1.0'), id=10, created_at=datetime.datetime(1970, 1, 1, 0, 0, fold=1, tzinfo=Timezone('UTC')))\n# Encode your business model into the format defined by your ClientRepr.\nencoded = client_codec.encode(instance)\nprint(encoded)\n#> b'{\"op\":\"add\",\"value\":\"1.0\",\"id\":\"10\",\"created_at\":\"1970-01-01T00:00:00+00:00\"}'\n
Tip
There's no need to initialize your ClientRepr instance to leverage its codec, as long as:
- The instance you pass in has the same overlap of required fields.
- The values in the overlapping fields can be translated to the target type.
"},{"location":"#why-typelib","title":"Why typelib
","text":"typelib
provides a simple, non-invasive API to make everyday data wrangling in your production applications easy and reliable.
"},{"location":"#we-do","title":"We DO","text":" - Provide an API for marshalling and unmarshalling data based upon type annotations.
- Provide an API for integrating our marshalling with over-the-wire serialization and deserialization.
- Provide fine-grained, high-performance, runtime introspection of Python types.
- Provide future-proofing to allow for emerging type annotation syntax.
"},{"location":"#we-dont","title":"We DON'T","text":" - Require you to inherit from a custom base class.
- Require you to use custom class decorators.
- Rely upon generated code.
"},{"location":"#how-it-works","title":"How It Works","text":"typelib
's implementation is unique among runtime type analyzers - we use an iterative, graph-based resolver to build a predictable, static ordering of the types represented by an annotation. We have implemented our type-resolution algorithm in isolation from our logic for marshalling and unmarshalling as a simple iterative loop, making the logic simple to reason about.
Tip
Read a detailed discussion here.
"},{"location":"changelog/","title":"Changelog","text":"All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
"},{"location":"changelog/#unreleased","title":"Unreleased","text":"Compare with latest
"},{"location":"changelog/#bug-fixes","title":"Bug Fixes","text":" - Unwrap
TypeAliasType
for all nested types in a type graph (c837838 by Sean Stewart).
"},{"location":"changelog/#v015-2024-10-30","title":"v0.1.5 - 2024-10-30","text":"Compare with v0.1.4
"},{"location":"changelog/#bug-fixes_1","title":"Bug Fixes","text":" - extract the real
__value__
from a TypeAliasType
when calling origin(...)
(94218a4 by Sean Stewart).
"},{"location":"changelog/#v014-2024-10-26","title":"v0.1.4 - 2024-10-26","text":"Compare with v0.1.2
"},{"location":"changelog/#bug-fixes_2","title":"Bug Fixes","text":" - remove use of
graphlib.TypeNode
in type context (e4742c0 by Sean Stewart). - correct handling optional types (79e431a by Sean Stewart).
"},{"location":"changelog/#v012-2024-10-16","title":"v0.1.2 - 2024-10-16","text":"Compare with v0.1.1
"},{"location":"changelog/#bug-fixes_3","title":"Bug Fixes","text":" - handle case where a resolved type reference can't match up to the original hint (a5ddf68 by Sean Stewart).
- inspect types when resolving field marshallers for structured types (78d4896 by Sean Stewart).
"},{"location":"changelog/#v011-2024-10-16","title":"v0.1.1 - 2024-10-16","text":"Compare with v0.1.0
"},{"location":"changelog/#features","title":"Features","text":" - support
enum.Enum
subtypes (d2a699a by Sean Stewart).
"},{"location":"changelog/#bug-fixes_4","title":"Bug Fixes","text":" - allow optional and union types to be marks as \"stdlib\" (bf4ad13 by Sean Stewart).
"},{"location":"changelog/#v010-2024-09-05","title":"v0.1.0 - 2024-09-05","text":"Compare with first commit
"},{"location":"changelog/#features_1","title":"Features","text":" - implement the top-level API (611f590 by Sean Stewart).
- rename some inspections and rework imports (3a5946e by Sean Stewart).
- re-organize utility modules (5019468 by Sean Stewart).
- codec interface (6996275 by Sean Stewart).
- type-enforce signature binding (a56418b by Sean Stewart).
- add high-level API for creating marshal/unmarshal protocols (2fa5345 by Sean Stewart).
- Implement marshallers. (ed159ef by Sean Stewart).
- Support TypeAliasType (e235f43 by Sean Stewart).
- Support for cyclic types. (4422413 by Sean Stewart).
- Defining the unmarshal API. (8117e0c by Sean Stewart).
- Better generics interface. (0f96785 by Sean Stewart).
- Initial pass of complex types for unmarshalling. (82b566c by Sean Stewart).
- Unmarshallers for initial primitive types. (1c6aa1c by Sean Stewart).
- Core utilities and graph resolver, with test coverage. (108faa1 by Sean Stewart).
"},{"location":"changelog/#bug-fixes_5","title":"Bug Fixes","text":" - treat sequences as unique from iterables in iteritems (1f1b0fd by Sean Stewart).
- update param name for type input in dateparse (1779b4e by Sean Stewart).
- weakref bug with slotted Codec (0887083 by Sean Stewart).
- mypy type hinting for non py312 in compat.py (b36c7d6 by Sean Stewart).
- use
datetime.timestamp
when converting date/time to numeric values (ecdc908 by Sean Stewart). - reliable UTC timestamps for date objects. (582686d by Sean Stewart).
- use compat for future types. (2e8aa24 by Sean Stewart).
- Fix type-hints for lower versions of python (7c08c8c by Sean Stewart).
- Fix type hints for marshalled values (f4742e0 by Sean Stewart).
- Enforce utc for tz-naive datetime.date during number conversion (afe79fb by Sean Stewart).
- The factory function can handle strings/forwardrefs. (34dd7dd by Sean Stewart).
- Tweaking root node assignment. (6b1f141 by Sean Stewart).
- Fix passing var names to unmarshaller (38c2002 by Sean Stewart).
"},{"location":"conduct/","title":"Contributor Covenant Code of Conduct","text":""},{"location":"conduct/#our-pledge","title":"Our Pledge","text":"In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
"},{"location":"conduct/#our-standards","title":"Our Standards","text":"Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
"},{"location":"conduct/#our-responsibilities","title":"Our Responsibilities","text":"Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
"},{"location":"conduct/#scope","title":"Scope","text":"This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
"},{"location":"conduct/#enforcement","title":"Enforcement","text":"Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by creating a new issue with the label conduct-review
. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
"},{"location":"conduct/#attribution","title":"Attribution","text":"This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
"},{"location":"graph/","title":"Your Annotation is a Graph","text":"Graph theory is a fundamental aspect of computer programming - nearly every problem can be modeled as a graph and in most cases, doing so can drastically reduce the complexity of the solution. We use graph theory to map your type annotations into a reliable resolution order for building out your marshalling and unmarshalling logic.
Read More
- Wikipedia - Graph Theory
- StackOverflow - Beginners Guide to Graph Theory
"},{"location":"graph/#handling-data-models","title":"Handling Data Models","text":"Below we map out some examples of the type graph for some common annotations:
---\ntitle: \"A Simple Mapping\"\n---\nerDiagram\n \"dict[str, int]\" ||--|{ str : contains\n \"dict[str, int]\" ||--|{ int : contains
Given the above graph, the static order for resolving the type dict[str, int]
would be: (0: str, 1: int, 2: dict[str, int])
.
---\ntitle: \"A Data Class Definition\"\n---\nerDiagram\n Customer ||--|| str : contains\n Customer ||--|| uuid : contains\n Customer ||--|| datetime : contains\n Customer {\n str name\n uuid id\n datetime created_at\n }
Given the above graph, the static order for resolving type Customer
would be: (0: str, 1: uuid, 2: datetime, 3: Customer)
.
Implementers can iterate over the static order, building a localized context for the type definition as we traverse from outer edges to the root node.
Note
As an implementation detail, edge types will be resolved in the order they are declared within the containing type. However, we only guarantee that all edges will be provided before the containing type, the field-order of these edges is not guaranteed.
"},{"location":"graph/#handling-cyclic-data-models","title":"Handling Cyclic Data Models","text":"Graphs may have cycles - if not addressed, this can result in infinite recursion. When we encounter a cyclic or direct recursive type, we wrap the cycle in a typing.ForwardRef
and terminate that branch. This provides another guarantee to implementations which leverage our graph resolver - all forward references are cyclic types and should be delayed.
---\ntitle: \"A Recursive Type Definition\"\n---\nerDiagram\n Node ||--o| Node : contains\n Node {\n parent Node\n }
---\ntitle: \"A Cyclic Type Definition\"\n---\nerDiagram\n ClassA ||--|| ClassB : contains\n ClassB ||--o| ClassA : contains\n ClassA {\n ClassB attr\n }\n ClassB {\n ClassA attr\n }
"},{"location":"graph/#further-reading","title":"Further Reading","text":""},{"location":"reference/typelib/","title":"Index","text":""},{"location":"reference/typelib/#typelib","title":"typelib","text":"The top-level API for typelib.
Typical Usage
>>> import dataclasses\n>>> import typelib\n>>>\n>>> @dataclasses.dataclass\n... class Data:\n... attr: int\n... key: str\n...\n>>>\n>>> typelib.unmarshal(Data, '{\"key\":\"string\",\"attr\":\"1\"}')\nData(attr=1, key='string')\n>>> typelib.marshal(Data(attr=1, key='string'))\n{'attr': 1, 'key': 'string'}\n>>> codec = typelib.codec(Data)\n>>> codec.encode(Data(attr=1, key='string'))\nb'{\"attr\":1,\"key\":\"string\"}'\n>>> codec.decode(b'{\"key\":\"string\",\"attr\":1}')\nData(attr=1, key='string')\n
"},{"location":"reference/typelib/#typelib.AbstractMarshaller","title":"AbstractMarshaller","text":"AbstractMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for marshallers.
Marshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic reducing Python objects into their primitive representations for over-the-wire encoding.
Marshallers support contextual serialization, which enables the marshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/#typelib.AbstractUnmarshaller","title":"AbstractUnmarshaller","text":"AbstractUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for unmarshallers.
Unmarshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic for decoding and converting primtive Python objects or JSON-endcoded data into their target type.
Unmarshallers support contextual deserialization, which enables the unmarshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/#typelib.AbstractUnmarshaller.__call__","title":"__call__ abstractmethod
","text":"__call__(val: Any) -> T\n
Unmarshall a Python object into its target type.
Not implemented for the abstract base class.
Source code in src/typelib/unmarshals/routines.py
@abc.abstractmethod\ndef __call__(self, val: tp.Any) -> T:\n \"\"\"Unmarshall a Python object into its target type.\n\n Not implemented for the abstract base class.\n \"\"\"\n
"},{"location":"reference/typelib/#typelib.Codec","title":"Codec dataclass
","text":"Codec(marshal: AbstractMarshaller[T], unmarshal: AbstractUnmarshaller[T], encoder: EncoderT, decoder: DecoderT)\n
Bases: Generic[T]
A standard wire protocol (codec).
This codec enables you to directly encode and decode your data model into your wire protocol.
"},{"location":"reference/typelib/#typelib.Codec.decoder","title":"decoder instance-attribute
","text":"decoder: DecoderT\n
The decoder used to deserialize a bytes-like object into a Python data structure for marshalling into T
.
"},{"location":"reference/typelib/#typelib.Codec.encoder","title":"encoder instance-attribute
","text":"encoder: EncoderT\n
The encoder used to serialize a marshalled T
into bytes.
"},{"location":"reference/typelib/#typelib.Codec.marshal","title":"marshal instance-attribute
","text":"marshal: AbstractMarshaller[T]\n
The marshaller used to convert an instance of T
to a serializable object.
"},{"location":"reference/typelib/#typelib.Codec.unmarshal","title":"unmarshal instance-attribute
","text":"unmarshal: AbstractUnmarshaller[T]\n
The unmarshaller used to convert a deserialized object into an instance of T
.
"},{"location":"reference/typelib/#typelib.Codec.decode","title":"decode","text":"decode(value: bytes) -> T\n
Decode an instance of T
from bytes.
We will first decode the data from bytes using the decoder
, then unmarshal the data into an instance of T
using unmarshal
.
Parameters:
-
value
(bytes
) \u2013 The bytes to decode.
Source code in src/typelib/codecs.py
def decode(self, value: bytes) -> T:\n \"\"\"Decode an instance of `T` from bytes.\n\n We will first decode the data from bytes using the\n [`decoder`][typelib.Codec.decoder], then unmarshal the data into an\n instance of `T` using [`unmarshal`][typelib.Codec.unmarshal].\n\n Args:\n value: The bytes to decode.\n \"\"\"\n decoded = self.decoder(value)\n unmarshalled = self.unmarshal(decoded)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.Codec.encode","title":"encode","text":"encode(value: T) -> bytes\n
Encode an instance of T
to bytes.
We will first marshal the given instance using the marshal
, then encode the marshalled data into bytes using the encoder
.
Parameters:
-
value
(T
) \u2013 The instance to encode.
Source code in src/typelib/codecs.py
def encode(self, value: T) -> bytes:\n \"\"\"Encode an instance of `T` to bytes.\n\n We will first marshal the given instance using the\n [`marshal`][typelib.Codec.marshal], then encode the marshalled data\n into bytes using the [`encoder`][typelib.Codec.encoder].\n\n Args:\n value: The instance to encode.\n \"\"\"\n marshalled = self.marshal(value)\n encoded = self.encoder(marshalled)\n return encoded\n
"},{"location":"reference/typelib/#typelib.codec","title":"codec","text":"codec(t: type[T], *, marshaller: AbstractMarshaller[T] | None = None, unmarshaller: AbstractUnmarshaller[T] | None = None, encoder: EncoderT = compat.json.dumps, decoder: DecoderT = compat.json.loads, codec_cls: type[CodecT[T]] | None = None) -> CodecT[T]\n
Factory function for creating a Codec
instance.
Note In the simplest case, all that needs be provided is the first parameter (t
). We will generate a marshaller, unmarshaller and build a codec. However, we provide ample means for customization:
- You can pass in a subclass of
Codec
to codec_cls
. - You may supply custom
marshaller
or unmarshaller
callables - we will generate one using the high-level APIs from marshals
and unmarshals
if not supplied. - The
encoder
and decoder
default to JSON, using either stdlib json
or orjson
if available. You may provide custom encoder
and decoder
callables, the only requirement is they ser/des to/from bytes
.
Tip
If you installed the json
extra when you installed this library, then you have installed orjson
.
Parameters:
-
t
(type[T]
) \u2013 The type to create the interchange protocol for.
-
marshaller
(AbstractMarshaller[T] | None
, default: None
) \u2013 The marshaller used to marshal inputs into the associated type. (optional)
-
unmarshaller
(AbstractUnmarshaller[T] | None
, default: None
) \u2013 The unmarshaller used to unmarshal inputs into the associated type. (optional)
-
encoder
(EncoderT
, default: dumps
) \u2013 The encoder for encoding data for over-the-wire (defaults to JSON).
-
decoder
(DecoderT
, default: loads
) \u2013 The decoder for decoding data from over-the-wire (defaults to JSON).
-
codec_cls
(type[CodecT[T]] | None
, default: None
) \u2013 The codec class definition, if overloading (optional).
Source code in src/typelib/codecs.py
@compat.cache\ndef codec(\n t: type[T],\n *,\n marshaller: marshals.AbstractMarshaller[T] | None = None,\n unmarshaller: unmarshals.AbstractUnmarshaller[T] | None = None,\n encoder: EncoderT = compat.json.dumps,\n decoder: DecoderT = compat.json.loads,\n codec_cls: type[CodecT[T]] | None = None,\n) -> CodecT[T]:\n \"\"\"Factory function for creating a [`Codec`][typelib.Codec] instance.\n\n Note:\n In the simplest case, all that needs be provided is the first parameter (`t`). We will\n generate a marshaller, unmarshaller and build a codec. However, we provide ample\n means for customization:\n\n - You can pass in a subclass of [`Codec`][typelib.codecs.Codec] to `codec_cls`.\n - You may supply custom `marshaller` or `unmarshaller` callables - we will generate\n one using the high-level APIs from [`marshals`][typelib.marshals] and\n [`unmarshals`][typelib.unmarshals] if not supplied.\n - The `encoder` and `decoder` default to JSON, using either\n stdlib [`json`][] or [`orjson`](https://github.com/ijl/orjson){.external}\n if available. You may provide custom `encoder` and `decoder` callables, the only\n requirement is they ser/des to/from `bytes`.\n\n /// tip\n If you installed the `json` extra when you installed this library, then you have\n installed [`orjson`](https://github.com/ijl/orjson){.external}.\n ///\n\n Args:\n t: The type to create the interchange protocol for.\n marshaller: The marshaller used to marshal inputs into the associated type. (optional)\n unmarshaller: The unmarshaller used to unmarshal inputs into the associated type. (optional)\n encoder: The encoder for encoding data for over-the-wire (defaults to JSON).\n decoder: The decoder for decoding data from over-the-wire (defaults to JSON).\n codec_cls: The codec class definition, if overloading (optional).\n\n \"\"\"\n marshal = marshaller or marshals.marshaller(t=t)\n unmarshal = unmarshaller or unmarshals.unmarshaller(t=t)\n cls = codec_cls or Codec\n if inspection.isbytestype(t):\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=lambda v: v, # type: ignore[arg-type,return-value]\n decoder=lambda v: v, # type: ignore[arg-type,return-value]\n )\n return cdc\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=encoder,\n decoder=decoder,\n )\n return cdc\n
"},{"location":"reference/typelib/#typelib.decode","title":"decode","text":"decode(t: type[T] | ForwardRef | str, value: bytes, *, decoder: DecoderT = compat.json.loads) -> T\n
Decode a bytes object into an instance of t
.
Parameters:
Source code in src/typelib/api.py
def decode(\n t: type[T] | refs.ForwardRef | str,\n value: bytes,\n *,\n decoder: DecoderT = compat.json.loads,\n) -> T:\n \"\"\"Decode a bytes object into an instance of `t`.\n\n Args:\n t: A type hint for resolving the unmarshaller.\n value: The value to decode.\n decoder: A callable that takes a bytes object and returns a Python value.\n \"\"\"\n decoded = decoder(value)\n unmarshalled = unmarshal(t=t, value=decoded)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.encode","title":"encode","text":"encode(value: T, *, t: type[T] | ForwardRef | str | None = None, encoder: EncoderT = compat.json.dumps) -> bytes\n
Encode a value into a bytes object.
Parameters:
Source code in src/typelib/api.py
def encode(\n value: T,\n *,\n t: type[T] | refs.ForwardRef | str | None = None,\n encoder: EncoderT = compat.json.dumps,\n) -> bytes:\n \"\"\"Encode a value into a bytes object.\n\n Args:\n value: The value to encode.\n t: A type hint for resolving the marshaller.\n encoder: A callable that takes a value and returns a bytes object.\n \"\"\"\n marshalled = marshal(value=value, t=t)\n encoded = encoder(marshalled)\n return encoded\n
"},{"location":"reference/typelib/#typelib.marshal","title":"marshal","text":"marshal(value: Any, *, t: type[T] | ForwardRef | str | None = None) -> MarshalledValueT\n
Marshal value
from typ
into MarshalledValueT
.
Parameters:
-
value
(Any
) \u2013 The value to reduce to a simple, encode-able type.
-
t
(type[T] | ForwardRef | str | None
, default: None
) \u2013 The type to use for building the marshaller (optional). If not provided, we'll default to the type of the input value.
Source code in src/typelib/marshals/api.py
def marshal(\n value: tp.Any, *, t: type[T] | refs.ForwardRef | str | None = None\n) -> serdes.MarshalledValueT:\n \"\"\"Marshal `value` from `typ` into [`MarshalledValueT`][typelib.serdes.MarshalledValueT].\n\n Args:\n value: The value to reduce to a simple, encode-able type.\n t:\n The type to use for building the marshaller (optional).\n If not provided, we'll default to the type of the input value.\n \"\"\"\n typ = value.__class__ if t is None else t\n routine: routines.AbstractMarshaller[T] = marshaller(typ)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.marshaller","title":"marshaller","text":"marshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractMarshaller[T]\n
Get a marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate a marshaller for. Can be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/marshals/api.py
@compat.cache\ndef marshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractMarshaller[T]:\n \"\"\"Get a marshaller routine for a given type.\n\n Args:\n t:\n The type annotation to generate a marshaller for. Can be a type, type alias,\n [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractMarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpMarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"},{"location":"reference/typelib/#typelib.unmarshal","title":"unmarshal","text":"unmarshal(t: type[T] | ForwardRef | str, value: Any) -> T\n
Unmarshal value
into typ
.
Parameters:
Source code in src/typelib/unmarshals/api.py
def unmarshal(t: type[T] | refs.ForwardRef | str, value: tp.Any) -> T:\n \"\"\"Unmarshal `value` into `typ`.\n\n Args:\n t: The type annotation or reference to unmarshal into.\n value: The value to unmarshal.\n \"\"\"\n routine = unmarshaller(t)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.unmarshaller","title":"unmarshaller","text":"unmarshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractUnmarshaller[T]\n
Get an un-marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate an unmarshaller for. May be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/unmarshals/api.py
@compat.cache\ndef unmarshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractUnmarshaller[T]:\n \"\"\"Get an un-marshaller routine for a given type.\n\n Args:\n t: The type annotation to generate an unmarshaller for.\n May be a type, type alias, [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractUnmarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpUnmarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"},{"location":"reference/typelib/binding/","title":"Binding","text":""},{"location":"reference/typelib/binding/#typelib.binding","title":"binding","text":"Utilities for automatic unmarshalling of inputs according to a callable object's signature.
Typical Usage
>>> from typelib import binding\n>>>\n>>> def foo(val: int) -> int:\n... return val * 2\n...\n>>> bound = binding.bind(foo)\n>>> bound(\"2\")\n4\n>>> bound.call(\"3\")\n'33'\n
"},{"location":"reference/typelib/binding/#typelib.binding.AbstractBinding","title":"AbstractBinding","text":"AbstractBinding(*, signature: Signature, binding: BindingT, varkwd: AbstractUnmarshaller | None = None, varpos: AbstractUnmarshaller | None = None, startpos: int | None = None)\n
Bases: ABC
, Generic[P]
The abstract base class for all type-enforced bindings.
Note \"Bindings\" are callables which leverage the type annotations in a signature to unmarshal inputs.
We differentiate each subclass based upon the possible combinations of parameter kinds:
- Positional-only arguments
- Keyword-only arguments
- Positional-or-Keyword arguments
- Variable-positional arguments (
*args
) - Variable-keyword arguments (
**kwargs
)
This allows us to micro-optimize the call for each subclass to exactly what is necessary for the that combination, which can lead to a significant speedup in hot loops.
Parameters:
-
signature
(Signature
) \u2013 The signature for the binding.
-
binding
(BindingT
) \u2013 A mapping of parameter names and positions to unmarshallers. This accounts for positional, keyword, or positional-or-keyword arguments.
-
varkwd
(AbstractUnmarshaller | None
, default: None
) \u2013 The unmarshaller for var-keyword arguments (**kwargs
).
-
varpos
(AbstractUnmarshaller | None
, default: None
) \u2013 The unmarshaller for var-positional arguments (*args
).
-
startpos
(int | None
, default: None
) \u2013 The start position of var-positional arguments (*args
). This accounts for the fact that var-positional comes after positional-only.
Source code in src/typelib/binding.py
def __init__(\n self,\n *,\n signature: inspect.Signature,\n binding: BindingT,\n varkwd: unmarshals.AbstractUnmarshaller | None = None,\n varpos: unmarshals.AbstractUnmarshaller | None = None,\n startpos: int | None = None,\n):\n \"\"\"Constructor.\n\n Args:\n signature: The signature for the binding.\n binding: A mapping of parameter names and positions to unmarshallers.\n This accounts for positional, keyword, or positional-or-keyword arguments.\n varkwd: The unmarshaller for var-keyword arguments (`**kwargs`).\n varpos: The unmarshaller for var-positional arguments (`*args`).\n startpos: The start position of var-positional arguments (`*args`).\n This accounts for the fact that var-positional comes after positional-only.\n \"\"\"\n self.signature = signature\n self.binding = binding\n self.varkwd = varkwd\n self.varpos = varpos\n self.startpos = startpos\n
"},{"location":"reference/typelib/binding/#typelib.binding.AbstractBinding.__call__","title":"__call__ abstractmethod
","text":"__call__(args: tuple[Any], kwargs: dict[str, Any]) -> tuple[args, kwargs]\n
Inspect the given args
and kwargs
and unmarshal them.
Parameters:
Source code in src/typelib/binding.py
@abc.abstractmethod\ndef __call__(\n self, args: tuple[tp.Any], kwargs: dict[str, tp.Any]\n) -> tuple[P.args, P.kwargs]:\n \"\"\"Inspect the given `args` and `kwargs` and unmarshal them.\n\n Args:\n args: The positional arguments.\n kwargs: The keyword arguments.\n \"\"\"\n
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine","title":"BoundRoutine dataclass
","text":"BoundRoutine(call: Callable[P, R], binding: AbstractBinding[P])\n
Bases: Generic[P, R]
A type-enforced, bound routine for a callable object.
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine.binding","title":"binding instance-attribute
","text":"binding: AbstractBinding[P]\n
The parameter->type binding.
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine.call","title":"call instance-attribute
","text":"call: Callable[P, R]\n
The callable object.
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine.__call__","title":"__call__","text":"__call__(*args: Any, **kwargs: Any) -> R\n
Binding an input to the parameters of call
,
then call the callable and return the result.
Source code in src/typelib/binding.py
def __call__(self, *args: tp.Any, **kwargs: tp.Any) -> R:\n \"\"\"Binding an input to the parameters of `call`,\n\n then call the callable and return the result.\"\"\"\n bargs, bkwargs = self.binding(args=args, kwargs=kwargs)\n return self.call(*bargs, **bkwargs)\n
"},{"location":"reference/typelib/binding/#typelib.binding.bind","title":"bind","text":"bind(obj: Callable[P, R]) -> BoundRoutine[P, R]\n
Create a type-enforced, bound routine for a callable object.
Note In contrast to typelib.binding.wrap
, this function creates a new, type-enforced BoundRoutine
instance. Rather than masquerading as the given obj
, we encapsulate it in the routine instance, which is more obvious and provides developers with the ability to side-step type enforcement when it is deemed unnecessary, which should be most of the time if your code is strongly typed and statically analyzed.
TL;DR This function returns an object that walks like your duck and quacks like your duck, but doesn't look like your duck.
Parameters:
Source code in src/typelib/binding.py
def bind(obj: tp.Callable[P, R]) -> BoundRoutine[P, R]:\n \"\"\"Create a type-enforced, bound routine for a callable object.\n\n Note:\n In contrast to [`typelib.binding.wrap`][], this function creates a new,\n type-enforced [`BoundRoutine`][typelib.binding.BoundRoutine] instance. Rather than\n masquerading as the given `obj`, we encapsulate it in the routine\n instance, which is more obvious and provides developers with the ability to\n side-step type enforcement when it is deemed unnecessary, which should be\n most of the time if your code is strongly typed and statically analyzed.\n\n Tip: TL;DR\n This function returns an object that walks like your duck and quacks like your duck,\n but doesn't look like your duck.\n\n Args:\n obj: The callable object to bind.\n \"\"\"\n binding: AbstractBinding[P] = _get_binding(obj)\n routine: BoundRoutine[P, R] = BoundRoutine(\n call=obj,\n binding=binding,\n )\n return routine\n
"},{"location":"reference/typelib/binding/#typelib.binding.wrap","title":"wrap","text":"wrap(obj: Callable[P, R]) -> Callable[..., R]\n
Wrap a callable object for runtime type coercion of inputs.
Note If a class is given, we will attempt to wrap the init method.
Warning This is a useful feature. It is also very surprising. By wrapping a callable in this decorator, we end up with implicit behavior that's not obvious to the caller or a fellow developer.
You're encouraged to prefer typelib.binding.bind
for similar functionality, less the implicit nature, especially when a class is given.
TL;DR This function returns an object walks, quacks, and tries to look like your duck.
Parameters:
Source code in src/typelib/binding.py
def wrap(obj: tp.Callable[P, R]) -> tp.Callable[..., R]:\n \"\"\"Wrap a callable object for runtime type coercion of inputs.\n\n Note:\n If a class is given, we will attempt to wrap the init method.\n\n Warning:\n This is a useful feature. It is also very *surprising*. By wrapping a callable\n in this decorator, we end up with *implicit* behavior that's not obvious to the\n caller or a fellow developer.\n\n You're encouraged to prefer [`typelib.binding.bind`][] for similar\n functionality, less the implicit nature, especially when a class is given.\n\n Tip: TL;DR\n This function returns an object walks, quacks, and tries to look like your duck.\n\n Args:\n obj: The callable object to wrap.\n Maybe be a function, a callable class instance, or a class.\n \"\"\"\n\n binding: AbstractBinding[P] = _get_binding(obj)\n\n if inspect.isclass(obj):\n obj.__init__ = wrap(obj.__init__)\n return obj\n\n @functools.wraps(obj) # type: ignore[arg-type]\n def binding_wrapper(*args: tp.Any, __binding=binding, **kwargs: tp.Any) -> R:\n bargs, bkwargs = __binding(args, kwargs)\n return obj(*bargs, **bkwargs)\n\n return binding_wrapper\n
"},{"location":"reference/typelib/codecs/","title":"Codecs","text":""},{"location":"reference/typelib/codecs/#typelib.codecs","title":"codecs","text":"Interfaces for managing type-enforced wire protocols (codecs).
"},{"location":"reference/typelib/codecs/#typelib.codecs.CodecT","title":"CodecT module-attribute
","text":"CodecT = TypeAliasType('CodecT', Codec, type_params=(T))\n
Generic type alias with an upper bound of Codec
.
"},{"location":"reference/typelib/codecs/#typelib.codecs.DecoderT","title":"DecoderT module-attribute
","text":"DecoderT: TypeAlias = Callable[[bytes], MarshalledValueT]\n
Protocol for a wire deserializer.
"},{"location":"reference/typelib/codecs/#typelib.codecs.EncoderT","title":"EncoderT module-attribute
","text":"EncoderT: TypeAlias = Callable[[MarshalledValueT], bytes]\n
Protocol for a wire serializer.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec","title":"Codec dataclass
","text":"Codec(marshal: AbstractMarshaller[T], unmarshal: AbstractUnmarshaller[T], encoder: EncoderT, decoder: DecoderT)\n
Bases: Generic[T]
A standard wire protocol (codec).
This codec enables you to directly encode and decode your data model into your wire protocol.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.decoder","title":"decoder instance-attribute
","text":"decoder: DecoderT\n
The decoder used to deserialize a bytes-like object into a Python data structure for marshalling into T
.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.encoder","title":"encoder instance-attribute
","text":"encoder: EncoderT\n
The encoder used to serialize a marshalled T
into bytes.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.marshal","title":"marshal instance-attribute
","text":"marshal: AbstractMarshaller[T]\n
The marshaller used to convert an instance of T
to a serializable object.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.unmarshal","title":"unmarshal instance-attribute
","text":"unmarshal: AbstractUnmarshaller[T]\n
The unmarshaller used to convert a deserialized object into an instance of T
.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.decode","title":"decode","text":"decode(value: bytes) -> T\n
Decode an instance of T
from bytes.
We will first decode the data from bytes using the decoder
, then unmarshal the data into an instance of T
using unmarshal
.
Parameters:
-
value
(bytes
) \u2013 The bytes to decode.
Source code in src/typelib/codecs.py
def decode(self, value: bytes) -> T:\n \"\"\"Decode an instance of `T` from bytes.\n\n We will first decode the data from bytes using the\n [`decoder`][typelib.Codec.decoder], then unmarshal the data into an\n instance of `T` using [`unmarshal`][typelib.Codec.unmarshal].\n\n Args:\n value: The bytes to decode.\n \"\"\"\n decoded = self.decoder(value)\n unmarshalled = self.unmarshal(decoded)\n return unmarshalled\n
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.encode","title":"encode","text":"encode(value: T) -> bytes\n
Encode an instance of T
to bytes.
We will first marshal the given instance using the marshal
, then encode the marshalled data into bytes using the encoder
.
Parameters:
-
value
(T
) \u2013 The instance to encode.
Source code in src/typelib/codecs.py
def encode(self, value: T) -> bytes:\n \"\"\"Encode an instance of `T` to bytes.\n\n We will first marshal the given instance using the\n [`marshal`][typelib.Codec.marshal], then encode the marshalled data\n into bytes using the [`encoder`][typelib.Codec.encoder].\n\n Args:\n value: The instance to encode.\n \"\"\"\n marshalled = self.marshal(value)\n encoded = self.encoder(marshalled)\n return encoded\n
"},{"location":"reference/typelib/codecs/#typelib.codecs.codec","title":"codec","text":"codec(t: type[T], *, marshaller: AbstractMarshaller[T] | None = None, unmarshaller: AbstractUnmarshaller[T] | None = None, encoder: EncoderT = compat.json.dumps, decoder: DecoderT = compat.json.loads, codec_cls: type[CodecT[T]] | None = None) -> CodecT[T]\n
Factory function for creating a Codec
instance.
Note In the simplest case, all that needs be provided is the first parameter (t
). We will generate a marshaller, unmarshaller and build a codec. However, we provide ample means for customization:
- You can pass in a subclass of
Codec
to codec_cls
. - You may supply custom
marshaller
or unmarshaller
callables - we will generate one using the high-level APIs from marshals
and unmarshals
if not supplied. - The
encoder
and decoder
default to JSON, using either stdlib json
or orjson
if available. You may provide custom encoder
and decoder
callables, the only requirement is they ser/des to/from bytes
.
Tip
If you installed the json
extra when you installed this library, then you have installed orjson
.
Parameters:
-
t
(type[T]
) \u2013 The type to create the interchange protocol for.
-
marshaller
(AbstractMarshaller[T] | None
, default: None
) \u2013 The marshaller used to marshal inputs into the associated type. (optional)
-
unmarshaller
(AbstractUnmarshaller[T] | None
, default: None
) \u2013 The unmarshaller used to unmarshal inputs into the associated type. (optional)
-
encoder
(EncoderT
, default: dumps
) \u2013 The encoder for encoding data for over-the-wire (defaults to JSON).
-
decoder
(DecoderT
, default: loads
) \u2013 The decoder for decoding data from over-the-wire (defaults to JSON).
-
codec_cls
(type[CodecT[T]] | None
, default: None
) \u2013 The codec class definition, if overloading (optional).
Source code in src/typelib/codecs.py
@compat.cache\ndef codec(\n t: type[T],\n *,\n marshaller: marshals.AbstractMarshaller[T] | None = None,\n unmarshaller: unmarshals.AbstractUnmarshaller[T] | None = None,\n encoder: EncoderT = compat.json.dumps,\n decoder: DecoderT = compat.json.loads,\n codec_cls: type[CodecT[T]] | None = None,\n) -> CodecT[T]:\n \"\"\"Factory function for creating a [`Codec`][typelib.Codec] instance.\n\n Note:\n In the simplest case, all that needs be provided is the first parameter (`t`). We will\n generate a marshaller, unmarshaller and build a codec. However, we provide ample\n means for customization:\n\n - You can pass in a subclass of [`Codec`][typelib.codecs.Codec] to `codec_cls`.\n - You may supply custom `marshaller` or `unmarshaller` callables - we will generate\n one using the high-level APIs from [`marshals`][typelib.marshals] and\n [`unmarshals`][typelib.unmarshals] if not supplied.\n - The `encoder` and `decoder` default to JSON, using either\n stdlib [`json`][] or [`orjson`](https://github.com/ijl/orjson){.external}\n if available. You may provide custom `encoder` and `decoder` callables, the only\n requirement is they ser/des to/from `bytes`.\n\n /// tip\n If you installed the `json` extra when you installed this library, then you have\n installed [`orjson`](https://github.com/ijl/orjson){.external}.\n ///\n\n Args:\n t: The type to create the interchange protocol for.\n marshaller: The marshaller used to marshal inputs into the associated type. (optional)\n unmarshaller: The unmarshaller used to unmarshal inputs into the associated type. (optional)\n encoder: The encoder for encoding data for over-the-wire (defaults to JSON).\n decoder: The decoder for decoding data from over-the-wire (defaults to JSON).\n codec_cls: The codec class definition, if overloading (optional).\n\n \"\"\"\n marshal = marshaller or marshals.marshaller(t=t)\n unmarshal = unmarshaller or unmarshals.unmarshaller(t=t)\n cls = codec_cls or Codec\n if inspection.isbytestype(t):\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=lambda v: v, # type: ignore[arg-type,return-value]\n decoder=lambda v: v, # type: ignore[arg-type,return-value]\n )\n return cdc\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=encoder,\n decoder=decoder,\n )\n return cdc\n
"},{"location":"reference/typelib/constants/","title":"Constants","text":""},{"location":"reference/typelib/constants/#typelib.constants","title":"constants","text":"Constants used throughout the library.
"},{"location":"reference/typelib/constants/#typelib.constants.empty","title":"empty","text":"A singleton for signalling no input.
"},{"location":"reference/typelib/ctx/","title":"Ctx","text":""},{"location":"reference/typelib/ctx/#typelib.ctx","title":"ctx","text":"A simple hashmap for working with types in a contextual manner.
"},{"location":"reference/typelib/ctx/#typelib.ctx.TypeContext","title":"TypeContext","text":" Bases: dict[KeyT, ValueT]
, Generic[ValueT]
A key-value mapping which can map between forward references and real types.
"},{"location":"reference/typelib/ctx/#typelib.ctx.TypeContext.__missing__","title":"__missing__","text":"__missing__(key: type | ForwardRef)\n
Hook to handle missing type references.
Allows for sharing lookup results between forward references and real types.
Parameters:
-
key
(type | ForwardRef
) \u2013 The type or reference.
Source code in src/typelib/ctx.py
def __missing__(self, key: type | refs.ForwardRef):\n \"\"\"Hook to handle missing type references.\n\n Allows for sharing lookup results between forward references and real types.\n\n Args:\n key: The type or reference.\n \"\"\"\n # If we missed a ForwardRef, we've already tried this, bail out.\n if isinstance(key, refs.ForwardRef):\n raise KeyError(key)\n\n ref = refs.forwardref(key)\n return self[ref]\n
"},{"location":"reference/typelib/graph/","title":"Graph","text":""},{"location":"reference/typelib/graph/#typelib.graph","title":"graph","text":"Utilities for working with types as graphs.
Typical Usage
>>> import dataclasses\n>>> from typelib import graph\n>>> graph.static_order(dict[str, str])\n[TypeNode(type=<class 'str'>, var=None, cyclic=False), TypeNode(type=dict[str, str], var=None, cyclic=False)]\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> graph.static_order(Class)\n[TypeNode(type=<class 'str'>, var='attr', cyclic=False), TypeNode(type=<class '__main__.Class'>, var=None, cyclic=False)]\n
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode","title":"TypeNode dataclass
","text":"TypeNode(type: Any, var: str | None = None, cyclic: bool = False)\n
A \"node\" in a type graph.
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode.cyclic","title":"cyclic class-attribute
instance-attribute
","text":"cyclic: bool = field(default=False, hash=False, compare=False)\n
Whether this type annotation is cyclic.
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode.type","title":"type instance-attribute
","text":"type: Any\n
The type annotation for this node.
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode.var","title":"var class-attribute
instance-attribute
","text":"var: str | None = None\n
The variable or parameter name associated to the type annotation for this node.
"},{"location":"reference/typelib/graph/#typelib.graph.get_type_graph","title":"get_type_graph","text":"get_type_graph(t: type) -> TopologicalSorter[TypeNode]\n
Get a directed graph of the type(s) this annotation represents,
Parameters:
-
t
(type
) \u2013 A type annotation.
Returns:
Note A key aspect of building a directed graph of a given type is pre-emptive detection and termination of cycles in the graph. If we detect a cycle, we will wrap the type in a typing.ForwardRef
and mark the TypeNode
instance as cyclic=True
.
Consumers of the graph can \"delay\" the resolution of a forward reference until the graph's static_order()
has been exhausted, at which point they have enough type information to resolve into the real type. (At least one layer down).
Resolution of cyclic/recursive types is always (necessarily) lazy and should only resolve one level deep on each attempt, otherwise we will find ourselves stuck in a closed loop which never terminates (infinite recursion).
Source code in src/typelib/graph.py
def get_type_graph(t: type) -> graphlib.TopologicalSorter[TypeNode]:\n \"\"\"Get a directed graph of the type(s) this annotation represents,\n\n Args:\n t: A type annotation.\n\n Returns:\n [`graphlib.TopologicalSorter`][]\n\n Note:\n A key aspect of building a directed graph of a given type is pre-emptive\n detection and termination of cycles in the graph. If we detect a cycle, we\n will wrap the type in a [`typing.ForwardRef`][] and mark the\n [`TypeNode`][typelib.graph.TypeNode] instance as `cyclic=True`.\n\n Consumers of the graph can \"delay\" the resolution of a forward reference until\n the graph's `static_order()` has been exhausted, at which point they have\n enough type information to resolve into the real type. (At least one layer down).\n\n Resolution of cyclic/recursive types is always (necessarily) lazy and should only\n resolve one level deep on each attempt, otherwise we will find ourselves stuck\n in a closed loop which never terminates (infinite recursion).\n \"\"\"\n if inspection.istypealiastype(t):\n t = t.__value__\n\n graph: graphlib.TopologicalSorter = graphlib.TopologicalSorter()\n root = TypeNode(t)\n stack = collections.deque([root])\n visited = {root.type}\n while stack:\n parent = stack.popleft()\n if inspection.isliteral(parent.type):\n graph.add(parent)\n continue\n\n predecessors = []\n for var, child in _level(parent.type):\n # If no type was provided, there's no reason to do further processing.\n if child in (constants.empty, typing.Any):\n continue\n if inspection.istypealiastype(child):\n child = child.__value__\n\n # Only subscripted generics or non-stdlib types can be cyclic.\n # i.e., we may get `str` or `datetime` any number of times,\n # that's not cyclic, so we can just add it to the graph.\n is_visited = child in visited\n is_subscripted = inspection.issubscriptedgeneric(child)\n is_stdlib = inspection.isstdlibtype(child)\n can_be_cyclic = is_subscripted or is_stdlib is False\n # We detected a cyclic type,\n # wrap in a ForwardRef and don't add it to the stack\n # This will terminate this edge to prevent infinite cycles.\n if is_visited and can_be_cyclic:\n qualname = inspection.qualname(child)\n *rest, refname = qualname.split(\".\", maxsplit=1)\n is_argument = var is not None\n module = \".\".join(rest) or getattr(child, \"__module__\", None)\n if module in (None, \"__main__\") and rest:\n module = rest[0]\n is_class = inspect.isclass(child)\n ref = refs.forwardref(\n refname, is_argument=is_argument, module=module, is_class=is_class\n )\n node = TypeNode(ref, var=var, cyclic=True)\n # Otherwise, add the type to the stack and track that it's been seen.\n else:\n node = TypeNode(type=child, var=var)\n visited.add(node.type)\n stack.append(node)\n # Flag the type as a \"predecessor\" of the parent type.\n # This lets us resolve child types first when we iterate over the graph.\n predecessors.append(node)\n # Add the parent type and its predecessors to the graph.\n graph.add(parent, *predecessors)\n\n return graph\n
"},{"location":"reference/typelib/graph/#typelib.graph.itertypes","title":"itertypes","text":"itertypes(t: type | str | ForwardRef | TypeAliasType) -> Iterable[TypeNode]\n
Iterate over the type-graph represented by t
from edges to root.
Parameters:
-
t
(type | str | ForwardRef | TypeAliasType
) \u2013 The \"root\" type.
Yields:
-
Iterable[TypeNode]
\u2013 TypeNode
Note We will build a graph of types with the given type t
as the root node, then iterate from the outermost leaves back to the root using BFS.
This is computationally expensive, so you are encouraged to use static_order
instead of itertypes
.
Source code in src/typelib/graph.py
def itertypes(\n t: type | str | refs.ForwardRef | compat.TypeAliasType,\n) -> typing.Iterable[TypeNode]:\n \"\"\"Iterate over the type-graph represented by `t` from edges to root.\n\n Args:\n t: The \"root\" type.\n\n Yields:\n [`TypeNode`][typelib.graph.TypeNode]\n\n Note:\n We will build a graph of types with the given type `t` as the root node,\n then iterate from the outermost leaves back to the root using BFS.\n\n This is computationally expensive, so you are encouraged to use\n [`static_order`][typelib.graph.static_order] instead of\n [`itertypes`][typelib.graph.itertypes].\n \"\"\"\n if inspection.istypealiastype(t):\n t = t.__value__\n if isinstance(t, (str, refs.ForwardRef)): # pragma: no cover\n ref = refs.forwardref(t) if isinstance(t, str) else t\n t = refs.evaluate(ref)\n\n graph = get_type_graph(t) # type: ignore[arg-type]\n yield from graph.static_order()\n
"},{"location":"reference/typelib/graph/#typelib.graph.static_order","title":"static_order","text":"static_order(t: type | str | ForwardRef | TypeAliasType) -> Sequence[TypeNode]\n
Get an ordered iterable of types which resolve into the root type provided.
Parameters:
Note The order of types is guaranteed to rank from edges to root. If there are multiple edges, the order of those edges is not guaranteed.
This function is memoized to avoid the cost of re-computing a type annotation multiple times at runtime, which would be wasted effort, as types don't change at runtime.
To avoid memoization, you can make use of itertypes
.
Source code in src/typelib/graph.py
@compat.cache\ndef static_order(\n t: type | str | refs.ForwardRef | compat.TypeAliasType,\n) -> typing.Sequence[TypeNode]:\n \"\"\"Get an ordered iterable of types which resolve into the root type provided.\n\n Args:\n t: The type to extract an ordered stack from.\n\n Note:\n The order of types is guaranteed to rank from edges to root. If there are\n multiple edges, the order of those edges is not guaranteed.\n\n This function is memoized to avoid the cost of re-computing a type annotation\n multiple times at runtime, which would be wasted effort, as types don't change\n at runtime.\n\n To avoid memoization, you can make use of [`itertypes`][typelib.graph.itertypes].\n \"\"\"\n # We want to leverage the cache if possible, hence the recursive call.\n # Shouldn't actually recurse more than once or twice.\n if inspection.istypealiastype(t):\n return static_order(t.__value__)\n if isinstance(t, (str, refs.ForwardRef)):\n ref = refs.forwardref(t) if isinstance(t, str) else t\n t = refs.evaluate(ref)\n return static_order(t)\n\n return [*itertypes(t)]\n
"},{"location":"reference/typelib/serdes/","title":"Serdes","text":""},{"location":"reference/typelib/serdes/#typelib.serdes","title":"serdes","text":"Utilities for type translation, serialization, and deserialization.
Typical Usage
>>> from typelib import serdes\n>>>\n>>> serdes.load(\"1\")\n1\n
>>> import datetime\n>>> from typelib import serdes\n>>>\n>>> serdes.unixtime(datetime.datetime(2020, 1, 1))\n1577854800.0\n>>> serdes.isoformat(datetime.timedelta(hours=1))\n'PT1H'\n
>>> import dataclasses\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> dict(serdes.iteritems(instance))\n{'attr': 'value'}\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.MarshalledValueT","title":"MarshalledValueT module-attribute
","text":"MarshalledValueT: TypeAlias = 'PythonPrimitiveT | dict[PythonPrimitiveT, MarshalledValueT] | list[MarshalledValueT]'\n
Type alias for a Python value which is ready for over-the-wire serialization.
"},{"location":"reference/typelib/serdes/#typelib.serdes.PythonPrimitiveT","title":"PythonPrimitiveT module-attribute
","text":"PythonPrimitiveT: TypeAlias = 'bool | int | float | str | None'\n
Type alias for serializable, non-container Python types.
"},{"location":"reference/typelib/serdes/#typelib.serdes.PythonValueT","title":"PythonValueT module-attribute
","text":"PythonValueT: TypeAlias = 'PythonPrimitiveT | dict[PythonPrimitiveT, PythonValueT] | list[PythonValueT] | tuple[PythonValueT, ...] | set[PythonValueT]'\n
Type alias for any Python builtin type.
"},{"location":"reference/typelib/serdes/#typelib.serdes.dateparse","title":"dateparse","text":"dateparse(val: str, t: type[DateTimeT]) -> DateTimeT\n
Parse a date string into a datetime object.
Examples:
>>> import datetime\n>>> from typelib import serdes\n>>> serdes.dateparse(\"1970-01-01\",t=datetime.datetime)\nDateTime(1970, 1, 1, 0, 0, 0, tzinfo=Timezone('UTC'))\n
Parameters:
Returns:
Raises:
Source code in src/typelib/serdes.py
@compat.lru_cache(maxsize=100_000)\ndef dateparse(val: str, t: type[DateTimeT]) -> DateTimeT:\n \"\"\"Parse a date string into a datetime object.\n\n Examples:\n >>> import datetime\n >>> from typelib import serdes\n >>> serdes.dateparse(\"1970-01-01\",t=datetime.datetime)\n DateTime(1970, 1, 1, 0, 0, 0, tzinfo=Timezone('UTC'))\n\n Args:\n val: The date string to parse.\n t: The target datetime type.\n\n Returns:\n The parsed datetime object.\n\n Raises:\n ValueError:\n If `val` is not a date string or does not resolve to an instance of\n the target datetime type.\n \"\"\"\n try:\n # When `exact=False`, the only two possibilities are DateTime and Duration.\n parsed: pendulum.DateTime | pendulum.Duration = pendulum.parse(val) # type: ignore[assignment]\n normalized = _nomalize_dt(val=val, parsed=parsed, td=t)\n return normalized\n except ValueError:\n if val.isdigit() or val.isdecimal():\n return _normalize_number(numval=float(val), td=t)\n raise\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.decode","title":"decode","text":"decode(val: Any, *, encoding: str = constants.DEFAULT_ENCODING) -> Any\n
Decode a bytes-like object into a str.
Note If a non-bytes-like object is passed, it will be returned unchanged.
Examples:
>>> from typelib import serdes\n>>> serdes.decode(b\"abc\")\n'abc'\n>>> serdes.decode(memoryview(b\"abc\"))\n'abc'\n
Parameters:
Source code in src/typelib/serdes.py
def decode(val: t.Any, *, encoding: str = constants.DEFAULT_ENCODING) -> t.Any:\n \"\"\"Decode a bytes-like object into a str.\n\n Note:\n If a non-bytes-like object is passed, it will be returned unchanged.\n\n Examples:\n >>> from typelib import serdes\n >>> serdes.decode(b\"abc\")\n 'abc'\n >>> serdes.decode(memoryview(b\"abc\"))\n 'abc'\n\n Args:\n val: The object to be decoded.\n encoding: The encoding to use when decoding the object (defaults \"utf8\").\n \"\"\"\n val = val.tobytes() if isinstance(val, memoryview) else val\n if isinstance(val, (bytes, bytearray)):\n decoded = val.decode(encoding)\n return decoded\n return val\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.get_items_iter","title":"get_items_iter","text":"get_items_iter(tp: type) -> Callable[[Any], Iterable[tuple[Any, Any]]]\n
Given a type, return a callable which will produce an iterator over (field, value) pairs.
Examples:
>>> import dataclasses\n>>> from typelib import serdes\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> iteritems = get_items_iter(Class)\n>>> next(iteritems(instance))\n('attr', 'value')\n
Parameters:
Source code in src/typelib/serdes.py
@compat.cache\ndef get_items_iter(tp: type) -> t.Callable[[t.Any], t.Iterable[tuple[t.Any, t.Any]]]:\n \"\"\"Given a type, return a callable which will produce an iterator over (field, value) pairs.\n\n Examples:\n >>> import dataclasses\n >>> from typelib import serdes\n >>>\n >>> @dataclasses.dataclass\n ... class Class:\n ... attr: str\n ...\n >>> instance = Class(attr=\"value\")\n >>> iteritems = get_items_iter(Class)\n >>> next(iteritems(instance))\n ('attr', 'value')\n\n Args:\n tp: The type to create an iterator for.\n \"\"\"\n ismapping, isnamedtuple, isiterable = (\n inspection.ismappingtype(tp),\n inspection.isnamedtuple(tp),\n inspection.isiterabletype(tp),\n )\n if ismapping:\n return _itemscaller\n if isnamedtuple:\n return _namedtupleitems\n if isiterable:\n return enumerate\n return _make_fields_iterator(tp)\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.isoformat","title":"isoformat","text":"isoformat(dt: date | time | timedelta) -> str\n
Format any date/time object into an ISO-8601 string.
Note While the standard library includes isoformat()
methods for datetime.date
, datetime.time
, & datetime.datetime
, they do not include a method for serializing datetime.timedelta
, even though durations are included in the ISO 8601 specification. This function fills that gap.
Examples:
>>> import datetime\n>>> from typelib import serdes\n>>> serdes.isoformat(datetime.date(1970, 1, 1))\n'1970-01-01'\n>>> serdes.isoformat(datetime.time())\n'00:00:00'\n>>> serdes.isoformat(datetime.datetime(1970, 1, 1))\n'1970-01-01T00:00:00'\n>>> serdes.isoformat(datetime.timedelta(hours=1))\n'PT1H'\n
Source code in src/typelib/serdes.py
@compat.lru_cache(maxsize=100_000)\ndef isoformat(dt: datetime.date | datetime.time | datetime.timedelta) -> str:\n \"\"\"Format any date/time object into an ISO-8601 string.\n\n Note:\n While the standard library includes `isoformat()` methods for\n [`datetime.date`][], [`datetime.time`][], &\n [`datetime.datetime`][], they do not include a method for serializing\n [`datetime.timedelta`][], even though durations are included in the\n ISO 8601 specification. This function fills that gap.\n\n Examples:\n >>> import datetime\n >>> from typelib import serdes\n >>> serdes.isoformat(datetime.date(1970, 1, 1))\n '1970-01-01'\n >>> serdes.isoformat(datetime.time())\n '00:00:00'\n >>> serdes.isoformat(datetime.datetime(1970, 1, 1))\n '1970-01-01T00:00:00'\n >>> serdes.isoformat(datetime.timedelta(hours=1))\n 'PT1H'\n \"\"\"\n if isinstance(dt, (datetime.date, datetime.time)):\n return dt.isoformat()\n dur: pendulum.Duration = (\n dt\n if isinstance(dt, pendulum.Duration)\n else pendulum.duration(\n days=dt.days,\n seconds=dt.seconds,\n microseconds=dt.microseconds,\n )\n )\n datepart = \"\".join(\n f\"{p}{s}\"\n for p, s in ((dur.years, \"Y\"), (dur.months, \"M\"), (dur.remaining_days, \"D\"))\n if p\n )\n timepart = \"\".join(\n f\"{p}{s}\"\n for p, s in (\n (dur.hours, \"H\"),\n (dur.minutes, \"M\"),\n (\n f\"{dur.remaining_seconds}.{dur.microseconds:06}\"\n if dur.microseconds\n else dur.remaining_seconds,\n \"S\",\n ),\n )\n if p\n )\n period = f\"P{datepart}T{timepart}\"\n return period\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.iteritems","title":"iteritems","text":"iteritems(val: Any) -> Iterable[tuple[Any, Any]]\n
Iterate over (field, value) pairs for any object.
Examples:
>>> import dataclasses\n>>> from typelib import serdes\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> [*serdes.iteritems(instance)]\n[('attr', 'value')]\n>>> [*serdes.iteritems(\"string\")]\n[(0, 's'), (1, 't'), (2, 'r'), (3, 'i'), (4, 'n'), (5, 'g')]\n>>> [*serdes.iteritems(serdes.iteritems(instance))]\n[('attr', 'value')]\n
Note If the given item is detected to be an iterable of pairs (e.g., [('a', 1), ('b', 2)]
), we will iterate directly over that.
Otherwise, we will create an iterator over (field, value) pairs with the following strategy:
- For mappings ->
((key, value), ...)
- For structured objects (user-defined classes) ->
((field, value), ...)
- For all other iterables ->
((index, value), ...)
.
Parameters:
Source code in src/typelib/serdes.py
def iteritems(val: t.Any) -> t.Iterable[tuple[t.Any, t.Any]]:\n \"\"\"Iterate over (field, value) pairs for any object.\n\n Examples:\n >>> import dataclasses\n >>> from typelib import serdes\n >>>\n >>> @dataclasses.dataclass\n ... class Class:\n ... attr: str\n ...\n >>> instance = Class(attr=\"value\")\n >>> [*serdes.iteritems(instance)]\n [('attr', 'value')]\n >>> [*serdes.iteritems(\"string\")]\n [(0, 's'), (1, 't'), (2, 'r'), (3, 'i'), (4, 'n'), (5, 'g')]\n >>> [*serdes.iteritems(serdes.iteritems(instance))]\n [('attr', 'value')]\n\n Note:\n If the given item is detected to be an iterable of pairs (e.g., `[('a', 1), ('b', 2)]`),\n we will iterate directly over that.\n\n Otherwise, we will create an iterator over (field, value) pairs with the following\n strategy:\n\n - For mappings -> `((key, value), ...)`\n - For structured objects (user-defined classes) -> `((field, value), ...)`\n - For all other iterables -> `((index, value), ...)`.\n\n Args:\n val: The object to iterate over.\n \"\"\"\n # If the given value is an iterable, we will create a `peekable` so we can inspect it.\n # In that case, we want to continue using the peekable, since the act of peeking\n # is destructive to the original iterable in the event it is an iterator.\n is_pairs, it = _is_iterable_of_pairs(val)\n if is_pairs:\n return iter(it)\n\n iterate = get_items_iter(val.__class__)\n return iterate(it)\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.itervalues","title":"itervalues","text":"itervalues(val: Any) -> Iterator[Any]\n
Iterate over the contained values for any object.
Examples:
>>> import dataclasses\n>>> from typelib import serdes\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> [*serdes.itervalues(instance)]\n['value']\n
Parameters:
Source code in src/typelib/serdes.py
def itervalues(val: t.Any) -> t.Iterator[t.Any]:\n \"\"\"Iterate over the contained values for any object.\n\n Examples:\n >>> import dataclasses\n >>> from typelib import serdes\n >>>\n >>> @dataclasses.dataclass\n ... class Class:\n ... attr: str\n ...\n >>> instance = Class(attr=\"value\")\n >>> [*serdes.itervalues(instance)]\n ['value']\n\n Args:\n val: The object to iterate over.\n \"\"\"\n iterate = get_items_iter(val.__class__)\n return (v for k, v in iterate(val))\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.load","title":"load","text":"load(val: _T) -> PythonValueT | _T\n
Attempt to decode val
if it is a text-like object.
Otherwise, return val
unchanged.
Examples:
>>> from typelib import serdes\n>>> serdes.load(1)\n1\n>>> serdes.load(\"1\")\n1\n>>> serdes.load(\"1,2\")\n(1, 2)\n>>> serdes.load(b'{\"a\": 1, \"b\": 2}')\n{'a': 1, 'b': 2}\n
Parameters:
-
val
(_T
) \u2013 The value to decode.
Source code in src/typelib/serdes.py
def load(val: _T) -> PythonValueT | _T:\n \"\"\"Attempt to decode `val` if it is a text-like object.\n\n Otherwise, return `val` unchanged.\n\n Examples:\n >>> from typelib import serdes\n >>> serdes.load(1)\n 1\n >>> serdes.load(\"1\")\n 1\n >>> serdes.load(\"1,2\")\n (1, 2)\n >>> serdes.load(b'{\"a\": 1, \"b\": 2}')\n {'a': 1, 'b': 2}\n\n Args:\n val: The value to decode.\n \"\"\"\n return strload(val) if inspection.istexttype(val.__class__) else val # type: ignore[arg-type]\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.strload","title":"strload","text":"strload(val: str | bytes | bytearray | memoryview) -> PythonValueT\n
Attempt to decode a string-like input into a Python value.
Examples:
>>> from typelib import serdes\n>>> serdes.strload(\"1\")\n1\n>>> serdes.strload(\"1,2\")\n(1, 2)\n>>> serdes.strload(b'{\"a\": 1, \"b\": 2}')\n{'a': 1, 'b': 2}\n
Tip This function is memoized and only safe for text-type inputs.
See Also Parameters:
Source code in src/typelib/serdes.py
@compat.lru_cache(maxsize=100_000)\ndef strload(val: str | bytes | bytearray | memoryview) -> PythonValueT:\n \"\"\"Attempt to decode a string-like input into a Python value.\n\n Examples:\n >>> from typelib import serdes\n >>> serdes.strload(\"1\")\n 1\n >>> serdes.strload(\"1,2\")\n (1, 2)\n >>> serdes.strload(b'{\"a\": 1, \"b\": 2}')\n {'a': 1, 'b': 2}\n\n\n Tip:\n This function is memoized and only safe for text-type inputs.\n\n See Also:\n - [`load`][typelib.serdes.load]\n\n Args:\n val: The string-like input to be decoded.\n \"\"\"\n with contextlib.suppress(ValueError):\n return compat.json.loads(val)\n\n decoded = decode(val)\n with contextlib.suppress(ValueError, TypeError, SyntaxError):\n return ast.literal_eval(decoded)\n\n return decoded\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.unixtime","title":"unixtime","text":"unixtime(dt: date | time | timedelta) -> float\n
Convert a date/time object to a unix timestamp.
Note Time is messy. Here is how we've decided to make this work:
datetime.datetime
instances will preserve the current tzinfo (even if naive). datetime.time
instances will default to today, preserving the tzinfo (even if naive). datetime.date
instances will assume UTC. datetime.timedelta
instances be reflected as total seconds since epoch (January 1, 1970).
If you find yourself in a situation where this does not work for you, your best bet is to stop using tz-naive date/time objects. It's always best to keep your time explicit!
Parameters:
Examples:
>>> import datetime\n>>> from typelib import serdes\n>>>\n>>> serdes.unixtime(datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc))\n0.0\n>>> serdes.unixtime(datetime.date(1970, 1, 1))\n0.0\n
Source code in src/typelib/serdes.py
def unixtime(dt: datetime.date | datetime.time | datetime.timedelta) -> float:\n \"\"\"Convert a date/time object to a unix timestamp.\n\n Note:\n Time is messy. Here is how we've decided to make this work:\n\n - `datetime.datetime` instances will preserve the current tzinfo (even if naive).\n - `datetime.time` instances will default to today, preserving the tzinfo (even if naive).\n - `datetime.date` instances will assume UTC.\n - `datetime.timedelta` instances be reflected as total seconds since epoch (January 1, 1970).\n\n If you find yourself in a situation where this does not work for you, your best\n bet is to stop using tz-naive date/time objects. *It's always best to keep your time\n explicit!*\n\n Args:\n dt: The object to be converted.\n\n Examples:\n >>> import datetime\n >>> from typelib import serdes\n >>>\n >>> serdes.unixtime(datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc))\n 0.0\n >>> serdes.unixtime(datetime.date(1970, 1, 1))\n 0.0\n \"\"\"\n if isinstance(dt, datetime.timedelta):\n return dt.total_seconds()\n\n if isinstance(dt, datetime.time):\n dt = datetime.datetime.now(tz=dt.tzinfo).replace(\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n )\n if isinstance(dt, datetime.date) and not isinstance(dt, datetime.datetime):\n dt = datetime.datetime(\n year=dt.year,\n month=dt.month,\n day=dt.day,\n tzinfo=datetime.timezone.utc,\n )\n\n return dt.timestamp()\n
"},{"location":"reference/typelib/marshals/","title":"Index","text":""},{"location":"reference/typelib/marshals/#typelib.marshals","title":"marshals","text":"Support for marshalling Python data structures into primitive equivalents.
Notes \"Marshalling\" your data structure prepares it to be serialized into binary and sent over the wire, but does not serialize it. We keep these stages isolated to ensure maximum flexibility and simplicity.
We ensure that your marshalled data is compatible with Python's built-in json
module. This provides maximum compatibility with most serialization protocols by limiting the output to simple Python builtin types:
bool
int
float
str
None
list
dict
Tip You may use this package directly, but we encourage you to work with our high-level API provided by the top-level typelib
module.
Typical Usage
>>> import dataclasses\n>>> import decimal\n>>> from typelib import marshals\n>>>\n>>> @dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\n... class Struct:\n... key: str\n... number: decimal.Decimal\n...\n>>>\n>>> data = Struct(key=\"some-key\", number=decimal.Decimal(\"1.0\"))\n>>> marshals.marshal(data)\n{'key': 'some-key', 'number': '1.0'}\n>>> marshaller = marshals.marshaller(Struct)\n>>> marshaller(data)\n{'key': 'some-key', 'number': '1.0'}\n
See Also marshal
marshaller
typelib.codec
"},{"location":"reference/typelib/marshals/#typelib.marshals.AbstractMarshaller","title":"AbstractMarshaller","text":"AbstractMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for marshallers.
Marshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic reducing Python objects into their primitive representations for over-the-wire encoding.
Marshallers support contextual serialization, which enables the marshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.DelayedMarshaller","title":"DelayedMarshaller","text":"DelayedMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[T]
Delayed proxy for a given type's marshaller, used when we encounter a typing.ForwardRef
.
Notes This allows us to delay the resolution of the given type reference until call-time, enabling support for cyclic and recursive types.
Source code in src/typelib/marshals/api.py
def __init__(\n self, t: type[T], context: routines.ContextT, *, var: str | None = None\n):\n super().__init__(t, context, var=var)\n self._resolved: routines.AbstractMarshaller[T] | None = None\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.DelayedMarshaller.resolved","title":"resolved property
","text":"resolved: AbstractMarshaller[T]\n
The resolved marshaller.
"},{"location":"reference/typelib/marshals/#typelib.marshals.EnumMarshaller","title":"EnumMarshaller","text":"EnumMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[EnumT]
, Generic[EnumT]
A marshaller that converts an enum.Enum
instance to its assigned value.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.EnumMarshaller.__call__","title":"__call__","text":"__call__(val: EnumT) -> MarshalledValueT\n
Marshal an enum.Enum
instance into a serdes.MarshalledValueT
.
Parameters:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: EnumT) -> serdes.MarshalledValueT:\n \"\"\"Marshal an [`enum.Enum`][] instance into a [`serdes.MarshalledValueT`][].\n\n Args:\n val: The enum instance to marshal.\n \"\"\"\n return val.value\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.FixedTupleMarshaller","title":"FixedTupleMarshaller","text":"FixedTupleMarshaller(t: type[TupleT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[TupleT]
A marshaller for dumping a \"fixed\" tuple to a simple list
.
Values are marshalled according to the value-type in the order they are defined.
See Also Parameters:
-
t
(type[TupleT]
) \u2013 The type to unmarshal from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(\n self, t: type[compat.TupleT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n self.ordered_routines = [self.context[vt] for vt in self.stack]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.FixedTupleMarshaller.__call__","title":"__call__","text":"__call__(val: TupleT) -> MarshalledIterableT\n
Marshal a tuple into a simple list
.
Parameters:
-
val
(TupleT
) \u2013 The tuple to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: compat.TupleT) -> MarshalledIterableT:\n \"\"\"Marshal a tuple into a simple [`list`][].\n\n Args:\n val: The tuple to marshal.\n \"\"\"\n return [\n routine(v)\n for routine, v in zip(self.ordered_routines, serdes.itervalues(val))\n ]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.IterableMarshaller","title":"IterableMarshaller","text":"IterableMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[IterableT]
, Generic[IterableT]
A marshaller for dumping any iterable into a simple list
.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.IterableMarshaller.__call__","title":"__call__","text":"__call__(val: IterableT) -> MarshalledIterableT\n
Marshal an iterable into a simple list
.
Parameters:
-
val
(IterableT
) \u2013 The iterable to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: IterableT) -> MarshalledIterableT:\n \"\"\"Marshal an iterable into a simple [`list`][].\n\n Args:\n val: The iterable to marshal.\n \"\"\"\n return [*val]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.LiteralMarshaller","title":"LiteralMarshaller","text":"LiteralMarshaller(t: type[LiteralT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[LiteralT]
, Generic[LiteralT]
A marshaller that enforces the given value be one of the values in the defined typing.Literal
Parameters:
-
t
(type[LiteralT]
) \u2013 The Literal type to enforce.
-
context
(ContextT
) \u2013 Nested type context (unused).
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[LiteralT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The Literal type to enforce.\n context: Nested type context (unused).\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.values = inspection.args(t)\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.LiteralMarshaller.__call__","title":"__call__","text":"__call__(val: LiteralT) -> MarshalledValueT\n
Enforce the given value is a member of the bound Literal
type.
Parameters:
-
val
(LiteralT
) \u2013 The value to enforce.
Raises:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: LiteralT) -> serdes.MarshalledValueT:\n \"\"\"Enforce the given value is a member of the bound `Literal` type.\n\n Args:\n val: The value to enforce.\n\n Raises:\n ValueError: If `val` is not a member of the bound `Literal` type.\n \"\"\"\n if val in self.values:\n return val # type: ignore[return-value]\n\n raise ValueError(f\"{val!r} is not one of {self.values!r}\")\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.MappingMarshaller","title":"MappingMarshaller","text":"MappingMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[MappingT]
, Generic[MappingT]
A marshaller for dumping any mapping into a simple dict
.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.MappingMarshaller.__call__","title":"__call__","text":"__call__(val: MappingT) -> MarshalledMappingT\n
Marshal a mapping into a simple dict
.
Parameters:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: MappingT) -> MarshalledMappingT:\n \"\"\"Marshal a mapping into a simple [`dict`][].\n\n Args:\n val: The mapping object to marshal.\n \"\"\"\n return {**val}\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.PatternMarshaller","title":"PatternMarshaller","text":"PatternMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[PatternT]
A marshaller that converts a re.Pattern
to a string.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.PatternMarshaller.__call__","title":"__call__","text":"__call__(val: PatternT) -> str\n
Marshal a compiled regex pattern into a string.
Parameters:
-
val
(PatternT
) \u2013 The pattern to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: PatternT) -> str:\n \"\"\"Marshal a compiled regex pattern into a string.\n\n Args:\n val: The pattern to marshal.\n \"\"\"\n return val.pattern\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.StructuredTypeMarshaller","title":"StructuredTypeMarshaller","text":"StructuredTypeMarshaller(t: type[_ST], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[_ST]
A marshaller for dumping a structured (user-defined) type to a simple dict
.
See Also StructuredTypeUnmarshaller
Parameters:
-
t
(type[_ST]
) \u2013 The type to unmarshals from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[_ST], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshals from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.fields_by_var = self._fields_by_var()\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.StructuredTypeMarshaller.__call__","title":"__call__","text":"__call__(val: _ST) -> MarshalledMappingT\n
Marshal a structured type into a simple dict
.
Parameters:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: _ST) -> MarshalledMappingT:\n \"\"\"Marshal a structured type into a simple [`dict`][].\n\n Args:\n val: The structured type to marshal.\n \"\"\"\n fields = self.fields_by_var\n return {f: fields[f](v) for f, v in serdes.iteritems(val) if f in fields}\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.SubscriptedIterableMarshaller","title":"SubscriptedIterableMarshaller","text":"SubscriptedIterableMarshaller(t: type[IterableT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[IterableT]
, Generic[IterableT]
A marshaller for dumping a subscripted iterable into a simple list
.
Values are marshalled according to the defined value-type.
See Also SubscriptedIterableUnmarshaller
Parameters:
-
t
(type[IterableT]
) \u2013 The type to unmarshals from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(\n self, t: type[IterableT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshals from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t=t, context=context, var=var)\n # supporting tuple[str, ...]\n (value_t, *_) = inspection.args(t)\n self.values = context[value_t]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.SubscriptedIterableMarshaller.__call__","title":"__call__","text":"__call__(val: IterableT) -> MarshalledIterableT\n
Marshal an iterable into a simple list
.
Parameters:
-
val
(IterableT
) \u2013 The iterable to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: IterableT) -> MarshalledIterableT:\n \"\"\"Marshal an iterable into a simple [`list`][].\n\n Args:\n val: The iterable to marshal.\n \"\"\"\n # Always decode bytes.\n values = self.values\n return [values(v) for v in serdes.itervalues(val)]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.SubscriptedMappingMarshaller","title":"SubscriptedMappingMarshaller","text":"SubscriptedMappingMarshaller(t: type[MappingT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[MappingT]
, Generic[MappingT]
A marshaller for dumping a subscripted mapping into a simple dict
.
Keys are marshalled according to the defined key-type, values according to the defined value-type.
See Also SubscriptedMappingUnmarshaller
Parameters:
-
t
(type[MappingT]
) \u2013 The type to unmarshals from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[MappingT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshals from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n key_t, value_t = inspection.args(t)\n self.keys = context[key_t]\n self.values = context[value_t]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.UnionMarshaller","title":"UnionMarshaller","text":"UnionMarshaller(t: type[UnionT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[UnionT]
, Generic[UnionT]
A marshaller for dumping a given value via one of the types in the defined bound union.
See Also Parameters:
-
t
(type[UnionT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[UnionT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n self.nullable = inspection.isoptionaltype(t)\n self.ordered_routines = [self.context[typ] for typ in self.stack]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.UnionMarshaller.__call__","title":"__call__","text":"__call__(val: UnionT) -> MarshalledValueT\n
Marshal a value into the bound UnionT
.
Parameters:
Raises:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: UnionT) -> serdes.MarshalledValueT:\n \"\"\"Marshal a value into the bound `UnionT`.\n\n Args:\n val: The input value to unmarshal.\n\n Raises:\n ValueError: If `val` cannot be marshalled via any member type.\n \"\"\"\n if self.nullable and val is None:\n return val\n\n for routine in self.ordered_routines:\n with contextlib.suppress(\n ValueError, TypeError, SyntaxError, AttributeError\n ):\n unmarshalled = routine(val)\n return unmarshalled\n\n raise ValueError(f\"{val!r} is not one of types {self.stack!r}\")\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.marshal","title":"marshal","text":"marshal(value: Any, *, t: type[T] | ForwardRef | str | None = None) -> MarshalledValueT\n
Marshal value
from typ
into MarshalledValueT
.
Parameters:
-
value
(Any
) \u2013 The value to reduce to a simple, encode-able type.
-
t
(type[T] | ForwardRef | str | None
, default: None
) \u2013 The type to use for building the marshaller (optional). If not provided, we'll default to the type of the input value.
Source code in src/typelib/marshals/api.py
def marshal(\n value: tp.Any, *, t: type[T] | refs.ForwardRef | str | None = None\n) -> serdes.MarshalledValueT:\n \"\"\"Marshal `value` from `typ` into [`MarshalledValueT`][typelib.serdes.MarshalledValueT].\n\n Args:\n value: The value to reduce to a simple, encode-able type.\n t:\n The type to use for building the marshaller (optional).\n If not provided, we'll default to the type of the input value.\n \"\"\"\n typ = value.__class__ if t is None else t\n routine: routines.AbstractMarshaller[T] = marshaller(typ)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.marshaller","title":"marshaller","text":"marshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractMarshaller[T]\n
Get a marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate a marshaller for. Can be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/marshals/api.py
@compat.cache\ndef marshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractMarshaller[T]:\n \"\"\"Get a marshaller routine for a given type.\n\n Args:\n t:\n The type annotation to generate a marshaller for. Can be a type, type alias,\n [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractMarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpMarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"},{"location":"reference/typelib/py/","title":"Index","text":""},{"location":"reference/typelib/py/#typelib.py","title":"py","text":"Components for Python compatibility, introspection, and reflection.
Notes The functionality here enables us to support multiple versions of Python to maintain forwards- and backwards-compatibility. This enables developers to leverage the bleeding edge of Python typing as new features and innovations are introduced, without having to upgrade to a new version of the language.
Think of this package as a polyfill for Python typing.
"},{"location":"reference/typelib/py/classes/","title":"Classes","text":""},{"location":"reference/typelib/py/classes/#typelib.py.classes","title":"classes","text":"Vendored class decorators for dataclasses.
Notes This module is unnecessary for Python versions >= 3.10.
Typical Usage >>> import dataclasses\n>>> from typelib.py import classes\n>>>\n>>> @classes.slotted\n>>> @dataclasses.dataclass\n>>> class Slotted:\n... attr: str\n...\n>>> Slotted.__slots__\n('attr',)\n
"},{"location":"reference/typelib/py/classes/#typelib.py.classes.slotted","title":"slotted","text":"slotted(_cls: _ClsT | None = None, *, dict: bool = False, weakref: bool = True) -> Callable[[_ClsT], _ClsT] | _ClsT\n
Decorator to create a \"slotted\" version of the provided class.
Parameters:
Warning This function returns new class object as it's not possible to add __slots__
after class creation.
See Also Source code in src/typelib/py/classes.py
def slotted( # noqa: C901\n _cls: _ClsT | None = None,\n *,\n dict: bool = False,\n weakref: bool = True,\n) -> Callable[[_ClsT], _ClsT] | _ClsT:\n \"\"\"Decorator to create a \"slotted\" version of the provided class.\n\n Args:\n _cls: The class to decorate.\n dict: Whether to add a slot for `__dict__`.\n weakref: Whether to add a slot for `__weakref__`.\n\n Warning:\n This function returns new class object as it's not possible to add `__slots__`\n after class creation.\n\n See Also:\n - [dataslots](https://github.com/starhel/dataslots/blob/master/src/dataslots/__init__.py)\n \"\"\"\n\n def _slots_setstate(self, state):\n for param_dict in filter(None, state):\n for slot, value in param_dict.items():\n object.__setattr__(self, slot, value)\n\n def wrap(cls):\n key = repr(cls)\n if key in _stack: # pragma: no cover\n raise TypeError(\n f\"{cls!r} uses a custom metaclass {cls.__class__!r} \"\n \"which is not compatible with automatic slots. \"\n \"See Issue !typical#104 on GitHub for more information.\"\n ) from None\n\n _stack.add(key)\n\n if (\n sys.version_info >= (3, 10) and constants.PKG_NAME not in cls.__module__\n ): # pragma: no cover\n warnings.warn(\n f\"You are using Python {sys.version}. \"\n \"Python 3.10 introduced native support for slotted dataclasses. \"\n \"This is the preferred method for adding slots.\",\n stacklevel=2,\n )\n\n cls_dict = {**cls.__dict__}\n # Create only missing slots\n inherited_slots = set().union(*(getattr(c, \"__slots__\", ()) for c in cls.mro()))\n\n field_names = {f.name: ... for f in dataclasses.fields(cls) if f.name}\n if dict:\n field_names[\"__dict__\"] = ...\n if weakref:\n field_names[\"__weakref__\"] = ...\n cls_dict[\"__slots__\"] = (*(f for f in field_names if f not in inherited_slots),)\n\n # Erase filed names from class __dict__\n for f in field_names:\n cls_dict.pop(f, None)\n\n # Erase __dict__ and __weakref__\n cls_dict.pop(\"__dict__\", None)\n cls_dict.pop(\"__weakref__\", None)\n\n # Pickle fix for frozen dataclass as mentioned in https://bugs.python.org/issue36424\n # Use only if __getstate__ and __setstate__ are not declared and frozen=True\n if (\n all(param not in cls_dict for param in [\"__getstate__\", \"__setstate__\"])\n and cls.__dataclass_params__.frozen\n ):\n cls_dict[\"__setstate__\"] = _slots_setstate\n\n # Prepare new class with slots\n new_cls = cls.__class__(cls.__name__, cls.__bases__, cls_dict)\n new_cls.__qualname__ = cls.__qualname__\n new_cls.__module__ = cls.__module__\n\n _stack.clear()\n return new_cls\n\n return wrap if _cls is None else wrap(_cls)\n
"},{"location":"reference/typelib/py/compat/","title":"Compat","text":""},{"location":"reference/typelib/py/compat/#typelib.py.compat","title":"compat","text":""},{"location":"reference/typelib/py/contrib/","title":"Contrib","text":""},{"location":"reference/typelib/py/contrib/#typelib.py.contrib","title":"contrib","text":""},{"location":"reference/typelib/py/frames/","title":"Frames","text":""},{"location":"reference/typelib/py/frames/#typelib.py.frames","title":"frames","text":"Utilities for working with stack traces and frames.
Typical Usage
>>> import inspect\n>>> from typelib.py import frames\n>>> var = 1\n>>> frames.extract(\"var\")\n1\n>>> current_frame = inspect.currentframe()\n>>> frames.getcaller() == current_frame\nTrue\n
"},{"location":"reference/typelib/py/frames/#typelib.py.frames.extract","title":"extract","text":"extract(name: str, *, frame: FrameType = None) -> Any | None\n
Extract name
from the stacktrace of frame
.
If frame
is not provided, this function will use the current frame.
Parameters:
Source code in src/typelib/py/frames.py
def extract(name: str, *, frame: types.FrameType = None) -> Any | None:\n \"\"\"Extract `name` from the stacktrace of `frame`.\n\n If `frame` is not provided, this function will use the current frame.\n\n Args:\n name: The name of the object to extract from the stacktrace.\n frame: The [`types.FrameType`][] instance to start from (optional).\n \"\"\"\n frame = frame or inspect.currentframe()\n seen: set[types.FrameType] = set()\n add = seen.add\n while frame and frame not in seen:\n if name in frame.f_globals:\n return frame.f_globals[name]\n if name in frame.f_locals:\n return frame.f_locals[name]\n add(frame)\n frame = frame.f_back\n\n return None\n
"},{"location":"reference/typelib/py/frames/#typelib.py.frames.getcaller","title":"getcaller","text":"getcaller(frame: FrameType = None) -> FrameType\n
Get the caller of the current scope, excluding this library.
If frame
is not provided, this function will use the current frame.
Parameters:
Source code in src/typelib/py/frames.py
def getcaller(frame: types.FrameType = None) -> types.FrameType:\n \"\"\"Get the caller of the current scope, excluding this library.\n\n If `frame` is not provided, this function will use the current frame.\n\n Args:\n frame: The [`types.FrameType`][] instance to start from (optional).\n \"\"\"\n\n frame = frame or inspect.currentframe()\n while frame.f_back:\n frame = frame.f_back\n module = inspect.getmodule(frame)\n if module and module.__name__.startswith(PKG_NAME):\n continue\n\n code = frame.f_code\n if getattr(code, \"co_qualname\", \"\").startswith(PKG_NAME):\n continue\n if PKG_NAME in code.co_filename:\n continue\n return frame\n\n return frame\n
"},{"location":"reference/typelib/py/future/","title":"Future","text":""},{"location":"reference/typelib/py/future/#typelib.py.future","title":"future","text":"Utilities for maintaining runtime compatibility with emerging type annotation operations.
Notes This module's functionality is unnecessary for Python versions >= 3.10
Typical Usage
>>> from typelib.py import future\n>>> future.transform_annotation(\"str | int\")\n'typing.Union[str, int]'\n>>> future.transform_annotation(\"dict[str, int]\")\n'typing.Dict[str, int]'\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation","title":"TransformAnnotation","text":"TransformAnnotation(union: str = 'typing.Union')\n
Bases: NodeTransformer
A ast.NodeTransformer
that transforms typing.Union
.
Source code in src/typelib/py/future.py
def __init__(self, union: str = \"typing.Union\") -> None:\n self.union = union\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_BinOp","title":"visit_BinOp","text":"visit_BinOp(node: BinOp)\n
Transform a ast.BinOp
to typing.Union
.
Source code in src/typelib/py/future.py
def visit_BinOp(self, node: ast.BinOp):\n \"\"\"Transform a [`ast.BinOp`][] to [`typing.Union`][].\"\"\"\n # Ignore anything but a bitwise OR `|`\n if not isinstance(node.op, ast.BitOr):\n return node\n # Build a stack of args to the bitor\n args = collections.deque([node.right])\n left = node.left\n while isinstance(left, ast.BinOp):\n args.appendleft(left.right)\n left = left.left\n args.appendleft(left)\n # Visit each node in the stack\n elts = [self.visit(n) for n in args]\n # Write the old-style `Union`.\n union = ast.Subscript(\n value=ast.Name(id=self.union, ctx=ast.Load()),\n slice=ast.Index(value=ast.Tuple(elts=elts, ctx=ast.Load())), # type: ignore[call-arg,arg-type]\n ctx=ast.Load(),\n )\n ast.copy_location(union, node)\n ast.fix_missing_locations(union)\n return union\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_Name","title":"visit_Name","text":"visit_Name(node: Name)\n
Transform a builtin ast.Name
to the typing
equivalent.
Source code in src/typelib/py/future.py
def visit_Name(self, node: ast.Name):\n \"\"\"Transform a builtin [`ast.Name`][] to the `typing` equivalent.\"\"\"\n # Re-write new-style builtin generics as old-style typing generics\n if node.id not in _GENERICS:\n return node\n\n new = ast.Name(id=_GENERICS[node.id], ctx=ast.Load())\n ast.copy_location(new, node)\n return new\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_Subscript","title":"visit_Subscript","text":"visit_Subscript(node: Subscript)\n
Transform all subscripts within a ast.Subscript
.
Source code in src/typelib/py/future.py
def visit_Subscript(self, node: ast.Subscript):\n \"\"\"Transform all subscripts within a [`ast.Subscript`][].\"\"\"\n # Scan all subscripts to we transform nested new-style types.\n transformed = self.visit(node.slice)\n new = ast.Subscript(\n value=self.visit(node.value),\n slice=transformed,\n ctx=node.ctx,\n )\n ast.copy_location(new, node)\n ast.fix_missing_locations(new)\n return new\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_Tuple","title":"visit_Tuple","text":"visit_Tuple(node: Tuple)\n
Transform all values within a ast.Tuple
.
Source code in src/typelib/py/future.py
def visit_Tuple(self, node: ast.Tuple):\n \"\"\"Transform all values within a [`ast.Tuple`][].\"\"\"\n # Scan all tuples to ensure we transform nested new-style types.\n transformed = [self.visit(n) for n in node.elts]\n new = ast.Tuple(elts=transformed, ctx=node.ctx)\n ast.copy_location(new, node)\n ast.fix_missing_locations(new)\n return new\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.transform","title":"transform cached
","text":"transform(annotation: str, *, union: str = 'typing.Union') -> str\n
Transform a modern annotations into their typing
equivalent:
types.UnionType
into a typing.Union
(str | int
-> typing.Union[str, int]
) - builtin generics into typing generics (
dict[str, int]
-> typing.Dict[str, int]
)
Parameters:
-
annotation
(str
) \u2013 The annotation to transform, as a string.
-
union
(str
, default: 'typing.Union'
) \u2013 The name of the Union type to subscript (defaults \"typing.Union\"
).
Note While this transformation requires your expression be valid Python syntax, it doesn't make sure the type annotation is valid.
Source code in src/typelib/py/future.py
@functools.cache\ndef transform(annotation: str, *, union: str = \"typing.Union\") -> str:\n \"\"\"Transform a modern annotations into their [`typing`][] equivalent:\n\n - [`types.UnionType`][] into a [`typing.Union`][] (`str | int` -> `typing.Union[str, int]`)\n - builtin generics into typing generics (`dict[str, int]` -> `typing.Dict[str, int]`)\n\n Args:\n annotation: The annotation to transform, as a string.\n union: The name of the Union type to subscript (defaults `\"typing.Union\"`).\n\n Note:\n While this transformation requires your expression be valid Python syntax, it\n doesn't make sure the type annotation is valid.\n \"\"\"\n parsed = ast.parse(annotation, mode=\"eval\")\n transformed = TransformAnnotation(union=union).generic_visit(parsed)\n unparsed = ast.unparse(transformed).strip()\n return unparsed\n
"},{"location":"reference/typelib/py/inspection/","title":"Inspection","text":""},{"location":"reference/typelib/py/inspection/#typelib.py.inspection","title":"inspection","text":"High-performance, Fine-grained runtime type inspections.
Typical Usage
>>> from typelib.py import inspection\n>>> inspection.ismappingtype(dict)\nTrue\n>>> inspection.isfixedtupletype(tuple[int, str])\nTrue\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.args","title":"args","text":"args(annotation: Any) -> Tuple[Any, ...]\n
Get the args supplied to an annotation, normalizing typing.TypeVar
.
Note TypeVar normalization follows this strategy:
-> If the TypeVar is bound\n-----> return the bound type\n-> Else If the TypeVar has constraints\n-----> return a Union of the constraints\n-> Else\n-----> return Any\n
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, TypeVar, Any\n>>> T = TypeVar(\"T\")\n>>> args(Dict)\n()\n>>> args(Dict[str, int])\n(<class 'str'>, <class 'int'>)\n>>> args(Dict[str, T])\n(<class 'str'>, typing.Any)\n
Source code in src/typelib/py/inspection.py
def args(annotation: tp.Any) -> tp.Tuple[tp.Any, ...]:\n \"\"\"Get the args supplied to an annotation, normalizing [`typing.TypeVar`][].\n\n Note:\n TypeVar normalization follows this strategy:\n\n -> If the TypeVar is bound\n -----> return the bound type\n -> Else If the TypeVar has constraints\n -----> return a Union of the constraints\n -> Else\n -----> return Any\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, TypeVar, Any\n >>> T = TypeVar(\"T\")\n >>> args(Dict)\n ()\n >>> args(Dict[str, int])\n (<class 'str'>, <class 'int'>)\n >>> args(Dict[str, T])\n (<class 'str'>, typing.Any)\n \"\"\"\n a = tp.get_args(annotation)\n if not a:\n a = getattr(annotation, \"__args__\", a)\n\n return (*_normalize_typevars(*a),)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.get_type_hints","title":"get_type_hints","text":"get_type_hints(obj: Union[type, Callable], exhaustive: bool = True) -> dict[str, type[Any]]\n
Wrapper for typing.get_type_hints
.
If typing.get_type_hints
raises ([NameError][], [TypeError][])
, we will default to an empty dict.
Parameters:
Source code in src/typelib/py/inspection.py
def get_type_hints(\n obj: tp.Union[type, tp.Callable], exhaustive: bool = True\n) -> dict[str, type[tp.Any]]:\n \"\"\"Wrapper for [`typing.get_type_hints`][].\n\n If [`typing.get_type_hints`][] raises `([NameError][], [TypeError][])`, we will\n default to an empty dict.\n\n Args:\n obj: The object to inspect.\n exhaustive:\n Whether to pull type hints from the signature of the object if\n none can be found via [`typing.get_type_hints`][]. (defaults True)\n \"\"\"\n try:\n hints = tp.get_type_hints(obj)\n except (NameError, TypeError):\n hints = {}\n # KW_ONLY is a special sentinel to denote kw-only params in a dataclass.\n # We don't want to do anything with this hint/field. It's not real.\n hints = {f: t for f, t in hints.items() if t is not compat.KW_ONLY}\n if not hints and exhaustive:\n hints = _hints_from_signature(obj)\n return hints\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isabstract","title":"isabstract","text":"isabstract(o) -> TypeIs[ABC]\n
Test whether the given object is an abstract type.
Examples:
>>> import abc\n>>> import numbers\n
>>>\n>>> isabstract(numbers.Number)\nTrue\n>>>\n>>> class MyABC(abc.ABC): ...\n...\n>>> isabstract(MyABC)\nTrue\n
Source code in src/typelib/py/inspection.py
def isabstract(o) -> compat.TypeIs[abc.ABC]:\n \"\"\"Test whether the given object is an abstract type.\n\n Examples:\n >>> import abc\n >>> import numbers\n\n >>>\n >>> isabstract(numbers.Number)\n True\n >>>\n >>> class MyABC(abc.ABC): ...\n ...\n >>> isabstract(MyABC)\n True\n\n \"\"\"\n return inspect.isabstract(o) or o in _ABCS\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbuiltininstance","title":"isbuiltininstance","text":"isbuiltininstance(o: Any) -> TypeIs[BuiltIntypeT]\n
Test whether an object is an instance of a builtin type.
Examples:
>>> isbuiltininstance(\"\")\nTrue\n
Source code in src/typelib/py/inspection.py
def isbuiltininstance(o: tp.Any) -> compat.TypeIs[BuiltIntypeT]:\n \"\"\"Test whether an object is an instance of a builtin type.\n\n Examples:\n >>> isbuiltininstance(\"\")\n True\n \"\"\"\n return builtins.isinstance(o, BUILTIN_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbuiltinsubtype","title":"isbuiltinsubtype","text":"isbuiltinsubtype(t: type) -> TypeIs[type[BuiltIntypeT]]\n
Check whether the provided type is a subclass of a builtin-type.
Examples:
>>> from typing import NewType, Mapping\n>>> class SuperStr(str): ...\n...\n>>> isbuiltinsubtype(SuperStr)\nTrue\n>>> isbuiltinsubtype(NewType(\"MyStr\", SuperStr))\nTrue\n>>> class Foo: ...\n...\n>>> isbuiltintype(Foo)\nFalse\n>>> isbuiltintype(Mapping)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isbuiltinsubtype(t: type) -> compat.TypeIs[type[BuiltIntypeT]]:\n \"\"\"Check whether the provided type is a subclass of a builtin-type.\n\n Examples:\n >>> from typing import NewType, Mapping\n >>> class SuperStr(str): ...\n ...\n >>> isbuiltinsubtype(SuperStr)\n True\n >>> isbuiltinsubtype(NewType(\"MyStr\", SuperStr))\n True\n >>> class Foo: ...\n ...\n >>> isbuiltintype(Foo)\n False\n >>> isbuiltintype(Mapping)\n False\n \"\"\"\n return issubclass(resolve_supertype(t), BUILTIN_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbuiltintype","title":"isbuiltintype","text":"isbuiltintype(obj: type | FunctionType) -> TypeIs[type[BuiltIntypeT]]\n
Check whether the provided object is a builtin-type.
Note Python stdlib and Python documentation have no \"definitive list\" of builtin-types, despite the fact that they are well-known. The closest we have is https://docs.python.org/3.7/library/functions.html, which clumps the builtin-types with builtin-functions. Despite clumping these types with functions in the documentation, these types eval as False when compared to types.BuiltinFunctionType
, which is meant to be an alias for the builtin-functions listed in the documentation.
All this to say, here we are with a custom check to determine whether a type is a builtin.
Examples:
>>> from typing import NewType, Mapping\n>>> isbuiltintype(str)\nTrue\n>>> isbuiltintype(NewType(\"MyStr\", str))\nTrue\n>>> class Foo: ...\n...\n>>> isbuiltintype(Foo)\nFalse\n>>> isbuiltintype(Mapping)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isbuiltintype(\n obj: type | types.FunctionType,\n) -> compat.TypeIs[type[BuiltIntypeT]]:\n \"\"\"Check whether the provided object is a builtin-type.\n\n Note:\n Python stdlib and Python documentation have no \"definitive list\" of\n builtin-**types**, despite the fact that they are well-known. The closest we have\n is https://docs.python.org/3.7/library/functions.html, which clumps the\n builtin-types with builtin-functions. Despite clumping these types with functions\n in the documentation, these types eval as False when compared to\n [`types.BuiltinFunctionType`][], which is meant to be an alias for the\n builtin-functions listed in the documentation.\n\n All this to say, here we are with a custom check to determine whether a type is a\n builtin.\n\n Examples:\n >>> from typing import NewType, Mapping\n >>> isbuiltintype(str)\n True\n >>> isbuiltintype(NewType(\"MyStr\", str))\n True\n >>> class Foo: ...\n ...\n >>> isbuiltintype(Foo)\n False\n >>> isbuiltintype(Mapping)\n False\n \"\"\"\n return (\n resolve_supertype(obj) in BUILTIN_TYPES\n or resolve_supertype(type(obj)) in BUILTIN_TYPES\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbytestype","title":"isbytestype","text":"isbytestype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]\n
Test whether the given type is a subclass of text or bytes.
Examples:
>>> class MyStr(str): ...\n...\n>>> istexttype(MyStr)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isbytestype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:\n \"\"\"Test whether the given type is a subclass of text or bytes.\n\n Examples:\n >>> class MyStr(str): ...\n ...\n >>> istexttype(MyStr)\n True\n \"\"\"\n return _safe_issubclass(t, (bytes, bytearray, memoryview))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.iscallable","title":"iscallable","text":"iscallable(t: Any) -> TypeIs[Callable]\n
Test whether the given type is a callable.
Examples:
>>> import typing\n>>> import collections.abc\n>>> iscallable(lambda: None)\nTrue\n>>> iscallable(typing.Callable)\nTrue\n>>> iscallable(collections.abc.Callable)\nTrue\n>>> iscallable(1)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache # type: ignore[arg-type]\ndef iscallable(t: tp.Any) -> compat.TypeIs[tp.Callable]:\n \"\"\"Test whether the given type is a callable.\n\n Examples:\n >>> import typing\n >>> import collections.abc\n >>> iscallable(lambda: None)\n True\n >>> iscallable(typing.Callable)\n True\n >>> iscallable(collections.abc.Callable)\n True\n >>> iscallable(1)\n False\n \"\"\"\n return inspect.isroutine(t) or t is tp.Callable or _safe_issubclass(t, abc_Callable) # type: ignore[arg-type]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isclassvartype","title":"isclassvartype","text":"isclassvartype(obj: type) -> bool\n
Test whether an annotation is a ClassVar annotation.
Examples:
>>> from typing import ClassVar, NewType\n>>> isclassvartype(ClassVar[str])\nTrue\n>>> isclassvartype(NewType(\"Foo\", ClassVar[str]))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isclassvartype(obj: type) -> bool:\n \"\"\"Test whether an annotation is a ClassVar annotation.\n\n Examples:\n >>> from typing import ClassVar, NewType\n >>> isclassvartype(ClassVar[str])\n True\n >>> isclassvartype(NewType(\"Foo\", ClassVar[str]))\n True\n \"\"\"\n obj = resolve_supertype(obj)\n return getattr(obj, \"__origin__\", obj) is tp.ClassVar\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.iscollectiontype","title":"iscollectiontype","text":"iscollectiontype(obj: type) -> TypeIs[type[Collection]]\n
Test whether this annotation is a subclass of typing.Collection
.
Includes builtins.
Examples:
>>> from typing import Collection, Mapping, NewType\n>>> iscollectiontype(Collection)\nTrue\n>>> iscollectiontype(Mapping[str, str])\nTrue\n>>> iscollectiontype(str)\nTrue\n>>> iscollectiontype(list)\nTrue\n>>> iscollectiontype(NewType(\"Foo\", dict))\nTrue\n>>> iscollectiontype(int)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef iscollectiontype(obj: type) -> compat.TypeIs[type[tp.Collection]]:\n \"\"\"Test whether this annotation is a subclass of [`typing.Collection`][].\n\n Includes builtins.\n\n Examples:\n >>> from typing import Collection, Mapping, NewType\n >>> iscollectiontype(Collection)\n True\n >>> iscollectiontype(Mapping[str, str])\n True\n >>> iscollectiontype(str)\n True\n >>> iscollectiontype(list)\n True\n >>> iscollectiontype(NewType(\"Foo\", dict))\n True\n >>> iscollectiontype(int)\n False\n \"\"\"\n obj = origin(obj)\n return obj in _COLLECTIONS or builtins.issubclass(obj, tp.Collection)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdatetimetype","title":"isdatetimetype","text":"isdatetimetype(obj: type) -> TypeIs[type[Union[datetime, date]]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> isdatetype(datetime.datetime)\nTrue\n>>> isdatetype(datetime.date)\nTrue\n>>> isdatetype(NewType(\"Foo\", datetime.datetime))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isdatetimetype(\n obj: type,\n) -> compat.TypeIs[type[tp.Union[datetime.datetime, datetime.date]]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> isdatetype(datetime.datetime)\n True\n >>> isdatetype(datetime.date)\n True\n >>> isdatetype(NewType(\"Foo\", datetime.datetime))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.datetime)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdatetype","title":"isdatetype","text":"isdatetype(obj: type) -> TypeIs[type[Union[datetime, date]]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> isdatetype(datetime.datetime)\nTrue\n>>> isdatetype(datetime.date)\nTrue\n>>> isdatetype(NewType(\"Foo\", datetime.datetime))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isdatetype(\n obj: type,\n) -> compat.TypeIs[type[tp.Union[datetime.datetime, datetime.date]]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> isdatetype(datetime.datetime)\n True\n >>> isdatetype(datetime.date)\n True\n >>> isdatetype(NewType(\"Foo\", datetime.datetime))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.date)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdecimaltype","title":"isdecimaltype","text":"isdecimaltype(obj: type) -> TypeIs[type[Decimal]]\n
Test whether this annotation is a Decimal object.
Examples:
>>> import decimal\n>>> from typing import NewType\n>>> isdecimaltype(decimal.Decimal)\nTrue\n>>> isdecimaltype(NewType(\"Foo\", decimal.Decimal))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isdecimaltype(obj: type) -> compat.TypeIs[type[decimal.Decimal]]:\n \"\"\"Test whether this annotation is a Decimal object.\n\n Examples:\n >>> import decimal\n >>> from typing import NewType\n >>> isdecimaltype(decimal.Decimal)\n True\n >>> isdecimaltype(NewType(\"Foo\", decimal.Decimal))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), decimal.Decimal)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdescriptor","title":"isdescriptor","text":"isdescriptor(obj) -> TypeIs[DescriptorT]\n
Test whether the given object is a types.GetSetDescriptorType
Examples:
>>> class StringDescriptor:\n... __slots__ = (\"value\",)\n...\n... def __init__(self, default: str = \"value\"):\n... self.value = default\n...\n... def __get__(self, instance: Any, value: str) -> str:\n... return self.value\n...\n>>> isdescriptor(StringDescriptor)\nTrue\n
Source code in src/typelib/py/inspection.py
def isdescriptor(obj) -> compat.TypeIs[DescriptorT]:\n \"\"\"Test whether the given object is a [`types.GetSetDescriptorType`][]\n\n Examples:\n >>> class StringDescriptor:\n ... __slots__ = (\"value\",)\n ...\n ... def __init__(self, default: str = \"value\"):\n ... self.value = default\n ...\n ... def __get__(self, instance: Any, value: str) -> str:\n ... return self.value\n ...\n >>> isdescriptor(StringDescriptor)\n True\n \"\"\"\n intersection = {*dir(obj)} & _DESCRIPTOR_METHODS\n return bool(intersection)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isenumtype","title":"isenumtype","text":"isenumtype(obj: type) -> TypeIs[type[Enum]]\n
Test whether this annotation is a subclass of enum.Enum
Examples:
>>> import enum\n>>>\n>>> class FooNum(enum.Enum): ...\n...\n>>> isenumtype(FooNum)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isenumtype(obj: type) -> compat.TypeIs[type[enum.Enum]]:\n \"\"\"Test whether this annotation is a subclass of [`enum.Enum`][]\n\n Examples:\n >>> import enum\n >>>\n >>> class FooNum(enum.Enum): ...\n ...\n >>> isenumtype(FooNum)\n True\n \"\"\"\n return _safe_issubclass(obj, enum.Enum)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfinal","title":"isfinal","text":"isfinal(obj: type) -> bool\n
Test whether an annotation is typing.Final
.
Examples:
>>> from typing import NewType\n>>> from typelib.py.compat import Final\n>>> isfinal(Final[str])\nTrue\n>>> isfinal(NewType(\"Foo\", Final[str]))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfinal(obj: type) -> bool:\n \"\"\"Test whether an annotation is [`typing.Final`][].\n\n Examples:\n >>> from typing import NewType\n >>> from typelib.py.compat import Final\n >>> isfinal(Final[str])\n True\n >>> isfinal(NewType(\"Foo\", Final[str]))\n True\n \"\"\"\n return origin(obj) is compat.Final\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfixedtupletype","title":"isfixedtupletype","text":"isfixedtupletype(obj: type) -> TypeIs[type[tuple]]\n
Check whether an object is a \"fixed\" tuple, e.g., tuple[int, int]
.
Examples:
>>> from typing import Tuple\n>>>\n>>>\n>>> isfixedtupletype(Tuple[str, int])\nTrue\n>>> isfixedtupletype(Tuple[str, ...])\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfixedtupletype(obj: type) -> compat.TypeIs[type[tuple]]:\n \"\"\"Check whether an object is a \"fixed\" tuple, e.g., `tuple[int, int]`.\n\n Examples:\n >>> from typing import Tuple\n >>>\n >>>\n >>> isfixedtupletype(Tuple[str, int])\n True\n >>> isfixedtupletype(Tuple[str, ...])\n False\n \"\"\"\n a = args(obj)\n origin = tp.get_origin(obj)\n if not a or a[-1] is ...:\n return False\n return _safe_issubclass(origin, tuple)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfloattype","title":"isfloattype","text":"isfloattype(t: type[Any]) -> TypeIs[type[float]]\n
Test whether t
is a subclass of the numbers.Number
protocol.
Examples:
>>> import decimal\n
>>> isnumbertype(int)\nFalse\n>>> isnumbertype(float)\nTrue\n>>> isnumbertype(decimal.Decimal)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfloattype(t: type[tp.Any]) -> compat.TypeIs[type[float]]:\n \"\"\"Test whether `t` is a subclass of the [`numbers.Number`][] protocol.\n\n Examples:\n >>> import decimal\n\n >>> isnumbertype(int)\n False\n >>> isnumbertype(float)\n True\n >>> isnumbertype(decimal.Decimal)\n False\n \"\"\"\n return _safe_issubclass(t, float)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isforwardref","title":"isforwardref","text":"isforwardref(obj: Any) -> TypeIs[ForwardRef]\n
Tests whether the given object is a typing.ForwardRef
.
Source code in src/typelib/py/inspection.py
def isforwardref(obj: tp.Any) -> compat.TypeIs[refs.ForwardRef]:\n \"\"\"Tests whether the given object is a [`typing.ForwardRef`][].\"\"\"\n return obj.__class__ is refs.ForwardRef\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfractiontype","title":"isfractiontype","text":"isfractiontype(obj: type) -> TypeIs[type[Fraction]]\n
Test whether this annotation is a Decimal object.
Examples:
>>> import fractions\n>>> from typing import NewType\n>>> isdecimaltype(fractions.Fraction)\nTrue\n>>> isdecimaltype(NewType(\"Foo\", fractions.Fraction))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfractiontype(obj: type) -> compat.TypeIs[type[fractions.Fraction]]:\n \"\"\"Test whether this annotation is a Decimal object.\n\n Examples:\n >>> import fractions\n >>> from typing import NewType\n >>> isdecimaltype(fractions.Fraction)\n True\n >>> isdecimaltype(NewType(\"Foo\", fractions.Fraction))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), fractions.Fraction)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfromdictclass","title":"isfromdictclass","text":"isfromdictclass(obj: type) -> TypeIs[type[_FromDict]]\n
Test whether this annotation is a class with a from_dict()
method.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfromdictclass(obj: type) -> compat.TypeIs[type[_FromDict]]:\n \"\"\"Test whether this annotation is a class with a `from_dict()` method.\"\"\"\n return inspect.isclass(obj) and hasattr(obj, \"from_dict\")\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfrozendataclass","title":"isfrozendataclass","text":"isfrozendataclass(obj: type) -> TypeIs[type[_FrozenDataclass]]\n
Test whether this is a dataclass and whether it's frozen.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfrozendataclass(obj: type) -> compat.TypeIs[type[_FrozenDataclass]]:\n \"\"\"Test whether this is a dataclass and whether it's frozen.\"\"\"\n return getattr(getattr(obj, \"__dataclass_params__\", None), \"frozen\", False)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isgeneric","title":"isgeneric","text":"isgeneric(t: Any) -> bool\n
Test whether the given type is a typing generic.
Examples:
>>> from typing import Tuple, Generic, TypeVar\n
>>>\n>>> isgeneric(Tuple)\nTrue\n>>> isgeneric(tuple)\nFalse\n>>> T = TypeVar(\"T\")\n>>> class MyGeneric(Generic[T]): ...\n>>> isgeneric(MyGeneric[int])\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isgeneric(t: tp.Any) -> bool:\n \"\"\"Test whether the given type is a typing generic.\n\n Examples:\n >>> from typing import Tuple, Generic, TypeVar\n\n >>>\n >>> isgeneric(Tuple)\n True\n >>> isgeneric(tuple)\n False\n >>> T = TypeVar(\"T\")\n >>> class MyGeneric(Generic[T]): ...\n >>> isgeneric(MyGeneric[int])\n True\n \"\"\"\n strobj = str(t)\n is_generic = (\n strobj.startswith(\"typing.\")\n or strobj.startswith(\"typing_extensions.\")\n or \"[\" in strobj\n or _safe_issubclass(t, tp.Generic) # type: ignore[arg-type]\n )\n return is_generic\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ishashable","title":"ishashable","text":"ishashable(obj: Any) -> TypeIs[Hashable]\n
Check whether an object is hashable.
An order of magnitude faster than isinstance
with typing.Hashable
Examples:
>>> ishashable(str())\nTrue\n>>> ishashable(frozenset())\nTrue\n>>> ishashable(list())\nFalse\n
Source code in src/typelib/py/inspection.py
def ishashable(obj: tp.Any) -> compat.TypeIs[tp.Hashable]:\n \"\"\"Check whether an object is hashable.\n\n An order of magnitude faster than [`isinstance`][] with\n [`typing.Hashable`][]\n\n Examples:\n >>> ishashable(str())\n True\n >>> ishashable(frozenset())\n True\n >>> ishashable(list())\n False\n \"\"\"\n return __hashgetter(obj) is not None\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isintegertype","title":"isintegertype","text":"isintegertype(t: type[Any]) -> TypeIs[type[int]]\n
Test whether t
is a subclass of the numbers.Number
protocol.
Examples:
>>> import decimal\n
>>> isnumbertype(int)\nTrue\n>>> isnumbertype(float)\nFalse\n>>> isnumbertype(decimal.Decimal)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isintegertype(t: type[tp.Any]) -> compat.TypeIs[type[int]]:\n \"\"\"Test whether `t` is a subclass of the [`numbers.Number`][] protocol.\n\n Examples:\n >>> import decimal\n\n >>> isnumbertype(int)\n True\n >>> isnumbertype(float)\n False\n >>> isnumbertype(decimal.Decimal)\n False\n \"\"\"\n return _safe_issubclass(t, int)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isiterabletype","title":"isiterabletype","text":"isiterabletype(obj: type) -> TypeIs[type[Iterable]]\n
Test whether the given type is iterable.
Examples:
>>> from typing import Sequence, Collection\n>>> isiterabletype(Sequence[str])\nTrue\n>>> isiterabletype(Collection)\nTrue\n>>> isiterabletype(str)\nTrue\n>>> isiterabletype(tuple)\nTrue\n>>> isiterabletype(int)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isiterabletype(obj: type) -> compat.TypeIs[type[tp.Iterable]]:\n \"\"\"Test whether the given type is iterable.\n\n Examples:\n >>> from typing import Sequence, Collection\n >>> isiterabletype(Sequence[str])\n True\n >>> isiterabletype(Collection)\n True\n >>> isiterabletype(str)\n True\n >>> isiterabletype(tuple)\n True\n >>> isiterabletype(int)\n False\n \"\"\"\n obj = origin(obj)\n return builtins.issubclass(obj, tp.Iterable)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isiteratortype","title":"isiteratortype","text":"isiteratortype(obj: type) -> TypeIs[type[Iterator]]\n
Check whether the given object is a subclass of an Iterator.
Examples:
>>> def mygen(): yield 1\n...\n>>> isiteratortype(mygen().__class__)\nTrue\n>>> isiteratortype(iter([]).__class__)\nTrue\n>>> isiteratortype(mygen)\nFalse\n>>> isiteratortype(list)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isiteratortype(obj: type) -> compat.TypeIs[type[tp.Iterator]]:\n \"\"\"Check whether the given object is a subclass of an Iterator.\n\n Examples:\n >>> def mygen(): yield 1\n ...\n >>> isiteratortype(mygen().__class__)\n True\n >>> isiteratortype(iter([]).__class__)\n True\n >>> isiteratortype(mygen)\n False\n >>> isiteratortype(list)\n False\n \"\"\"\n obj = origin(obj)\n return builtins.issubclass(obj, tp.Iterator)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isliteral","title":"isliteral","text":"isliteral(obj) -> bool\n
Test whether an annotation is typing.Literal
.
Examples:
>>>\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isliteral(obj) -> bool:\n \"\"\"Test whether an annotation is [`typing.Literal`][].\n\n Examples:\n >>>\n \"\"\"\n return origin(obj) is tp.Literal or (\n obj.__class__ is refs.ForwardRef and obj.__forward_arg__.startswith(\"Literal\")\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ismappingtype","title":"ismappingtype","text":"ismappingtype(obj: type) -> TypeIs[type[Mapping]]\n
Test whether this annotation is a subtype of typing.Mapping
.
Examples:
>>> from typing import Mapping, Dict, DefaultDict, NewType\n>>> ismappingtype(Mapping)\nTrue\n>>> ismappingtype(Dict[str, str])\nTrue\n>>> ismappingtype(DefaultDict)\nTrue\n>>> ismappingtype(dict)\nTrue\n>>> class MyDict(dict): ...\n...\n>>> ismappingtype(MyDict)\nTrue\n>>> class MyMapping(Mapping): ...\n...\n>>> ismappingtype(MyMapping)\nTrue\n>>> ismappingtype(NewType(\"Foo\", dict))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef ismappingtype(obj: type) -> compat.TypeIs[type[tp.Mapping]]:\n \"\"\"Test whether this annotation is a subtype of [`typing.Mapping`][].\n\n Examples:\n >>> from typing import Mapping, Dict, DefaultDict, NewType\n >>> ismappingtype(Mapping)\n True\n >>> ismappingtype(Dict[str, str])\n True\n >>> ismappingtype(DefaultDict)\n True\n >>> ismappingtype(dict)\n True\n >>> class MyDict(dict): ...\n ...\n >>> ismappingtype(MyDict)\n True\n >>> class MyMapping(Mapping): ...\n ...\n >>> ismappingtype(MyMapping)\n True\n >>> ismappingtype(NewType(\"Foo\", dict))\n True\n \"\"\"\n obj = origin(obj)\n return builtins.issubclass(obj, _MAPPING_TYPES) or builtins.issubclass(\n obj, tp.Mapping\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isnamedtuple","title":"isnamedtuple","text":"isnamedtuple(obj: type) -> TypeIs[type[NamedTuple]]\n
Check whether an object is a \"named\" tuple (collections.namedtuple
).
Examples:
>>> from collections import namedtuple\n>>>\n>>> FooTup = namedtuple(\"FooTup\", [\"bar\"])\n>>> isnamedtuple(FooTup)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isnamedtuple(obj: type) -> compat.TypeIs[type[tp.NamedTuple]]:\n \"\"\"Check whether an object is a \"named\" tuple ([`collections.namedtuple`][]).\n\n Examples:\n >>> from collections import namedtuple\n >>>\n >>> FooTup = namedtuple(\"FooTup\", [\"bar\"])\n >>> isnamedtuple(FooTup)\n True\n \"\"\"\n return inspect.isclass(obj) and issubclass(obj, tuple) and hasattr(obj, \"_fields\")\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isnonetype","title":"isnonetype","text":"isnonetype(t: Any) -> TypeIs[None]\n
Detect if the given type is a types.NoneType
.
Examples:
>>> isnonetype(None)\nTrue\n>>> isnonetype(type(None))\nTrue\n>>> isnonetype(1)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isnonetype(t: tp.Any) -> compat.TypeIs[None]:\n \"\"\"Detect if the given type is a [`types.NoneType`][].\n\n Examples:\n >>> isnonetype(None)\n True\n >>> isnonetype(type(None))\n True\n >>> isnonetype(1)\n False\n \"\"\"\n return t in (None, type(None))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isnumbertype","title":"isnumbertype","text":"isnumbertype(t: type[Any]) -> TypeIs[type[Number]]\n
Test whether t
is a subclass of the numbers.Number
protocol.
Examples:
>>> import decimal\n
>>> isnumbertype(int)\nTrue\n>>> isnumbertype(float)\nTrue\n>>> isnumbertype(decimal.Decimal)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isnumbertype(t: type[tp.Any]) -> compat.TypeIs[type[numbers.Number]]:\n \"\"\"Test whether `t` is a subclass of the [`numbers.Number`][] protocol.\n\n Examples:\n >>> import decimal\n\n >>> isnumbertype(int)\n True\n >>> isnumbertype(float)\n True\n >>> isnumbertype(decimal.Decimal)\n True\n \"\"\"\n return _safe_issubclass(t, numbers.Number)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isoptionaltype","title":"isoptionaltype","text":"isoptionaltype(obj: type[_OT]) -> TypeIs[type[Optional[_OT]]]\n
Test whether an annotation is typing.Optional
, or can be treated as.
typing.Optional
is an alias for typing.Union[<T>, None]
, so both are \"optional\".
Examples:
>>> from typing import Optional, Union, Dict, Literal\n>>> isoptionaltype(Optional[str])\nTrue\n>>> isoptionaltype(Union[str, None])\nTrue\n>>> isoptionaltype(Literal[\"\", None])\nTrue\n>>> isoptionaltype(Dict[str, None])\n
False
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isoptionaltype(obj: type[_OT]) -> compat.TypeIs[type[tp.Optional[_OT]]]:\n \"\"\"Test whether an annotation is [`typing.Optional`][], or can be treated as.\n\n [`typing.Optional`][] is an alias for `typing.Union[<T>, None]`, so both are\n \"optional\".\n\n Examples:\n >>> from typing import Optional, Union, Dict, Literal\n >>> isoptionaltype(Optional[str])\n True\n >>> isoptionaltype(Union[str, None])\n True\n >>> isoptionaltype(Literal[\"\", None])\n True\n >>> isoptionaltype(Dict[str, None])\n False\n \"\"\"\n args = getattr(obj, \"__args__\", ())\n tname = name(origin(obj))\n nullarg = next((a for a in args if a in (type(None), None)), ...)\n isoptional = tname == \"Optional\" or (\n nullarg is not ... and tname in (\"Union\", \"Uniontype\", \"Literal\")\n )\n return isoptional\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ispathtype","title":"ispathtype","text":"ispathtype(t: Any) -> TypeIs[Path]\n
Detect if the given type is a pathlib.Path
.
Examples:
>>> import pathlib\n>>> ispathtype(pathlib.Path.cwd())\nTrue\n>>> ispathtype(\".\")\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef ispathtype(t: tp.Any) -> compat.TypeIs[pathlib.Path]:\n \"\"\"Detect if the given type is a [`pathlib.Path`][].\n\n Examples:\n >>> import pathlib\n >>> ispathtype(pathlib.Path.cwd())\n True\n >>> ispathtype(\".\")\n False\n \"\"\"\n return _safe_issubclass(t, pathlib.PurePath)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ispatterntype","title":"ispatterntype","text":"ispatterntype(t: Any) -> TypeIs[Pattern]\n
Detect if the given type is a re.Pattern
.
Examples:
>>> import re\n>>> ispatterntype(re.compile(r\"^[a-z]+$\"))\nTrue\n>>> ispatterntype(r\"^[a-z]+$\")\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef ispatterntype(t: tp.Any) -> compat.TypeIs[re.Pattern]:\n \"\"\"Detect if the given type is a [`re.Pattern`][].\n\n Examples:\n >>> import re\n >>> ispatterntype(re.compile(r\"^[a-z]+$\"))\n True\n >>> ispatterntype(r\"^[a-z]+$\")\n False\n \"\"\"\n return _safe_issubclass(t, re.Pattern)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isproperty","title":"isproperty","text":"isproperty(obj) -> TypeIs[DynamicClassAttribute]\n
Test whether the given object is an instance of [property
] or functools.cached_property
.
Examples:
>>> import functools\n
>>> class Foo:\n... @property\n... def prop(self) -> int:\n... return 1\n...\n... @functools.cached_property\n... def cached(self) -> str:\n... return \"foo\"\n...\n>>> isproperty(Foo.prop)\nTrue\n>>> isproperty(Foo.cached)\nTrue\n
Source code in src/typelib/py/inspection.py
def isproperty(obj) -> compat.TypeIs[types.DynamicClassAttribute]:\n \"\"\"Test whether the given object is an instance of [`property`] or [`functools.cached_property`][].\n\n Examples:\n >>> import functools\n\n >>> class Foo:\n ... @property\n ... def prop(self) -> int:\n ... return 1\n ...\n ... @functools.cached_property\n ... def cached(self) -> str:\n ... return \"foo\"\n ...\n >>> isproperty(Foo.prop)\n True\n >>> isproperty(Foo.cached)\n True\n \"\"\"\n\n return builtins.issubclass(obj.__class__, (property, functools.cached_property))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issequencetype","title":"issequencetype","text":"issequencetype(obj: type) -> TypeIs[type[Collection]]\n
Test whether this annotation is a subclass of typing.Collection
.
Includes builtins.
Examples:
>>> from typing import Collection, Mapping, NewType, Sequence\n>>> issequencetype(Sequence)\nTrue\n>>> issequencetype(Mapping[str, str])\nTrue\n>>> issequencetype(str)\nTrue\n>>> issequencetype(list)\nTrue\n>>> issequencetype(NewType(\"Foo\", dict))\nTrue\n>>> issequencetype(int)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef issequencetype(obj: type) -> compat.TypeIs[type[tp.Collection]]:\n \"\"\"Test whether this annotation is a subclass of [`typing.Collection`][].\n\n Includes builtins.\n\n Examples:\n >>> from typing import Collection, Mapping, NewType, Sequence\n >>> issequencetype(Sequence)\n True\n >>> issequencetype(Mapping[str, str])\n True\n >>> issequencetype(str)\n True\n >>> issequencetype(list)\n True\n >>> issequencetype(NewType(\"Foo\", dict))\n True\n >>> issequencetype(int)\n False\n \"\"\"\n obj = origin(obj)\n return obj in _COLLECTIONS or builtins.issubclass(obj, tp.Sequence)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issimpleattribute","title":"issimpleattribute","text":"issimpleattribute(v) -> bool\n
Test whether the given object is a static value
(e.g., not a function, class, or descriptor).
Examples:
>>> class MyOperator:\n... type = str\n...\n... def operate(self, v) -> type:\n... return self.type(v)\n...\n... @property\n... def default(self) -> type:\n... return self.type()\n...\n>>> issimpleattribute(MyOperator.type)\nFalse\n>>> issimpleattribute(MyOperator.operate)\nFalse\n>>> issimpleattribute(MyOperator.default)\nFalse\n
Source code in src/typelib/py/inspection.py
def issimpleattribute(v) -> bool:\n \"\"\"Test whether the given object is a static value\n\n (e.g., not a function, class, or descriptor).\n\n Examples:\n >>> class MyOperator:\n ... type = str\n ...\n ... def operate(self, v) -> type:\n ... return self.type(v)\n ...\n ... @property\n ... def default(self) -> type:\n ... return self.type()\n ...\n >>> issimpleattribute(MyOperator.type)\n False\n >>> issimpleattribute(MyOperator.operate)\n False\n >>> issimpleattribute(MyOperator.default)\n False\n \"\"\"\n return not any(c(v) for c in _ATTR_CHECKS)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstdlibinstance","title":"isstdlibinstance","text":"isstdlibinstance(o: Any) -> TypeIs[STDLibtypeT]\n
Test whether an object is an instance of a type in the standard-lib.
Source code in src/typelib/py/inspection.py
def isstdlibinstance(o: tp.Any) -> compat.TypeIs[STDLibtypeT]:\n \"\"\"Test whether an object is an instance of a type in the standard-lib.\"\"\"\n return builtins.isinstance(o, STDLIB_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstdlibsubtype","title":"isstdlibsubtype","text":"isstdlibsubtype(t: type) -> TypeIs[type[STDLibtypeT]]\n
Test whether the given type is a subclass of a standard-lib type.
Examples:
>>> import datetime\n
>>> class MyDate(datetime.date): ...\n...\n>>> isstdlibsubtype(MyDate)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isstdlibsubtype(t: type) -> compat.TypeIs[type[STDLibtypeT]]:\n \"\"\"Test whether the given type is a subclass of a standard-lib type.\n\n Examples:\n >>> import datetime\n\n >>> class MyDate(datetime.date): ...\n ...\n >>> isstdlibsubtype(MyDate)\n True\n \"\"\"\n return _safe_issubclass(resolve_supertype(t), STDLIB_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstringtype","title":"isstringtype","text":"isstringtype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]\n
Test whether the given type is a subclass of text or bytes.
Examples:
>>> class MyStr(str): ...\n...\n>>> istexttype(MyStr)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isstringtype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:\n \"\"\"Test whether the given type is a subclass of text or bytes.\n\n Examples:\n >>> class MyStr(str): ...\n ...\n >>> istexttype(MyStr)\n True\n \"\"\"\n return _safe_issubclass(t, str)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstructuredtype","title":"isstructuredtype","text":"isstructuredtype(t: type[Any]) -> bool\n
Test whether the given type has a fixed set of fields.
Examples:
>>> import dataclasses\n>>> from typing import Tuple, NamedTuple, TypedDict, Union, Literal, Collection\n
>>>\n>>> isstructuredtype(Tuple[str, int])\nTrue\n>>> isstructuredtype(class MyDict(TypedDict): ...)\nTrue\n>>> isstructuredtype(class MyTup(NamedTuple): ...)\nTrue\n>>> isstructuredtype(class MyClass: ...)\nTrue\n>>> isstructuredtype(Union[str, int])\nFalse\n>>> isstructuredtype(Literal[1, 2])\nFalse\n>>> isstructuredtype(tuple)\nFalse\n>>> isstructuredtype(Collection[str])\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isstructuredtype(t: type[tp.Any]) -> bool:\n \"\"\"Test whether the given type has a fixed set of fields.\n\n Examples:\n >>> import dataclasses\n >>> from typing import Tuple, NamedTuple, TypedDict, Union, Literal, Collection\n\n >>>\n >>> isstructuredtype(Tuple[str, int])\n True\n >>> isstructuredtype(class MyDict(TypedDict): ...)\n True\n >>> isstructuredtype(class MyTup(NamedTuple): ...)\n True\n >>> isstructuredtype(class MyClass: ...)\n True\n >>> isstructuredtype(Union[str, int])\n False\n >>> isstructuredtype(Literal[1, 2])\n False\n >>> isstructuredtype(tuple)\n False\n >>> isstructuredtype(Collection[str])\n False\n \"\"\"\n return (\n isfixedtupletype(t)\n or isnamedtuple(t)\n or istypeddict(t)\n or (not isstdlibsubtype(origin(t)) and not isuniontype(t) and not isliteral(t))\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issubscriptedcollectiontype","title":"issubscriptedcollectiontype","text":"issubscriptedcollectiontype(obj: type[Generic[_ArgsT]]) -> TypeIs[type[Collection[_ArgsT]]]\n
Test whether this annotation is a collection type and is subscripted.
Examples:
>>> from typing import Collection, Mapping, NewType\n>>> issubscriptedcollectiontype(Collection)\nFalse\n>>> issubscriptedcollectiontype(Mapping[str, str])\nTrue\n>>> issubscriptedcollectiontype(str)\nFalse\n>>> issubscriptedcollectiontype(NewType(\"Foo\", Collection[int]))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef issubscriptedcollectiontype(\n obj: type[tp.Generic[_ArgsT]], # type: ignore[valid-type]\n) -> compat.TypeIs[type[tp.Collection[_ArgsT]]]:\n \"\"\"Test whether this annotation is a collection type and is subscripted.\n\n Examples:\n >>> from typing import Collection, Mapping, NewType\n >>> issubscriptedcollectiontype(Collection)\n False\n >>> issubscriptedcollectiontype(Mapping[str, str])\n True\n >>> issubscriptedcollectiontype(str)\n False\n >>> issubscriptedcollectiontype(NewType(\"Foo\", Collection[int]))\n True\n \"\"\"\n return iscollectiontype(obj) and issubscriptedgeneric(obj)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issubscriptedgeneric","title":"issubscriptedgeneric","text":"issubscriptedgeneric(t: Any) -> bool\n
Test whether the given type is a typing generic.
Examples:
>>> from typing import Tuple, Generic, TypeVar\n
>>>\n>>> isgeneric(Tuple)\nTrue\n>>> isgeneric(tuple)\nFalse\n>>> T = TypeVar(\"T\")\n>>> class MyGeneric(Generic[T]): ...\n>>> isgeneric(MyGeneric[int])\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef issubscriptedgeneric(t: tp.Any) -> bool:\n \"\"\"Test whether the given type is a typing generic.\n\n Examples:\n >>> from typing import Tuple, Generic, TypeVar\n\n >>>\n >>> isgeneric(Tuple)\n True\n >>> isgeneric(tuple)\n False\n >>> T = TypeVar(\"T\")\n >>> class MyGeneric(Generic[T]): ...\n >>> isgeneric(MyGeneric[int])\n True\n \"\"\"\n strobj = str(t)\n og = tp.get_origin(t) or t\n is_generic = isgeneric(og) or isgeneric(t)\n is_subscripted = \"[\" in strobj\n return is_generic and is_subscripted\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istexttype","title":"istexttype","text":"istexttype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]\n
Test whether the given type is a subclass of text or bytes.
Examples:
>>> class MyStr(str): ...\n...\n>>> istexttype(MyStr)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istexttype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:\n \"\"\"Test whether the given type is a subclass of text or bytes.\n\n Examples:\n >>> class MyStr(str): ...\n ...\n >>> istexttype(MyStr)\n True\n \"\"\"\n return _safe_issubclass(t, (str, bytes, bytearray, memoryview))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istimedeltatype","title":"istimedeltatype","text":"istimedeltatype(obj: type) -> TypeIs[type[timedelta]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> istimedeltatype(datetime.timedelta)\nTrue\n>>> istimedeltatype(NewType(\"Foo\", datetime.timedelta))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istimedeltatype(obj: type) -> compat.TypeIs[type[datetime.timedelta]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> istimedeltatype(datetime.timedelta)\n True\n >>> istimedeltatype(NewType(\"Foo\", datetime.timedelta))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.timedelta)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istimetype","title":"istimetype","text":"istimetype(obj: type) -> TypeIs[type[time]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> istimetype(datetime.time)\nTrue\n>>> istimetype(NewType(\"Foo\", datetime.time))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istimetype(obj: type) -> compat.TypeIs[type[datetime.time]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> istimetype(datetime.time)\n True\n >>> istimetype(NewType(\"Foo\", datetime.time))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.time)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istupletype","title":"istupletype","text":"istupletype(obj: Callable[..., Any] | type[Any]) -> TypeIs[type[tuple]]\n
Tests whether the given type is a subclass of tuple
.
Examples:
>>> from typing import NamedTuple, Tuple\n>>> class MyTup(NamedTuple):\n... field: int\n...\n>>> istupletype(tuple)\nTrue\n>>> istupletype(Tuple[str])\nTrue\n>>> istupletype(MyTup)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istupletype(\n obj: tp.Callable[..., tp.Any] | type[tp.Any],\n) -> compat.TypeIs[type[tuple]]:\n \"\"\"Tests whether the given type is a subclass of [`tuple`][].\n\n Examples:\n >>> from typing import NamedTuple, Tuple\n >>> class MyTup(NamedTuple):\n ... field: int\n ...\n >>> istupletype(tuple)\n True\n >>> istupletype(Tuple[str])\n True\n >>> istupletype(MyTup)\n True\n \"\"\"\n obj = origin(obj)\n return obj is tuple or issubclass(obj, tuple) # type: ignore[arg-type]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istypealiastype","title":"istypealiastype","text":"istypealiastype(t: Any) -> TypeIs[TypeAliasType]\n
Detect if the given object is a typing.TypeAliasType
.
Examples:
>>> type IntList = list[int]\n>>> istypealiastype(IntList)\nTrue\n>>> IntList = compat.TypeAliasType(\"IntList\", list[int])\n>>> istypealiastype(IntList)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istypealiastype(t: tp.Any) -> compat.TypeIs[compat.TypeAliasType]:\n \"\"\"Detect if the given object is a [`typing.TypeAliasType`][].\n\n Examples:\n >>> type IntList = list[int]\n >>> istypealiastype(IntList)\n True\n >>> IntList = compat.TypeAliasType(\"IntList\", list[int])\n >>> istypealiastype(IntList)\n True\n\n \"\"\"\n return isinstance(t, compat.TypeAliasType)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istypeddict","title":"istypeddict","text":"istypeddict(obj: Any) -> bool\n
Check whether an object is a typing.TypedDict
.
Examples:
>>> from typing import TypedDict\n>>>\n>>> class FooMap(TypedDict):\n... bar: str\n...\n>>> istypeddict(FooMap)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istypeddict(obj: tp.Any) -> bool:\n \"\"\"Check whether an object is a [`typing.TypedDict`][].\n\n Examples:\n >>> from typing import TypedDict\n >>>\n >>> class FooMap(TypedDict):\n ... bar: str\n ...\n >>> istypeddict(FooMap)\n True\n \"\"\"\n return (\n inspect.isclass(obj)\n and dict in {*inspect.getmro(obj)}\n and hasattr(obj, \"__total__\")\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istypedtuple","title":"istypedtuple","text":"istypedtuple(obj: type) -> TypeIs[type[NamedTuple]]\n
Check whether an object is a \"typed\" tuple (typing.NamedTuple
).
Examples:
>>> from typing import NamedTuple\n>>>\n>>> class FooTup(NamedTuple):\n... bar: str\n...\n>>> istypedtuple(FooTup)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istypedtuple(obj: type) -> compat.TypeIs[type[tp.NamedTuple]]:\n \"\"\"Check whether an object is a \"typed\" tuple ([`typing.NamedTuple`][]).\n\n Examples:\n >>> from typing import NamedTuple\n >>>\n >>> class FooTup(NamedTuple):\n ... bar: str\n ...\n >>> istypedtuple(FooTup)\n True\n \"\"\"\n return (\n inspect.isclass(obj)\n and issubclass(obj, tuple)\n and bool(getattr(obj, \"__annotations__\", False))\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isunresolvable","title":"isunresolvable","text":"isunresolvable(t: Any) -> bool\n
Test whether the given type is unresolvable.
Examples:
>>> import typing\n>>> isunresolvable(int)\nFalse\n>>> isunresolvable(typ.Any)\nTrue\n>>> isunresolvable(...)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isunresolvable(t: tp.Any) -> bool:\n \"\"\"Test whether the given type is unresolvable.\n\n Examples:\n >>> import typing\n >>> isunresolvable(int)\n False\n >>> isunresolvable(typ.Any)\n True\n >>> isunresolvable(...)\n True\n \"\"\"\n return t in _UNRESOLVABLE\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isuuidtype","title":"isuuidtype","text":"isuuidtype(obj: type) -> TypeIs[type[UUID]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import uuid\n>>> from typing import NewType\n>>> isuuidtype(uuid.UUID)\nTrue\n>>> class MyUUID(uuid.UUID): ...\n...\n>>> isuuidtype(MyUUID)\nTrue\n>>> isuuidtype(NewType(\"Foo\", uuid.UUID))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isuuidtype(obj: type) -> compat.TypeIs[type[uuid.UUID]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import uuid\n >>> from typing import NewType\n >>> isuuidtype(uuid.UUID)\n True\n >>> class MyUUID(uuid.UUID): ...\n ...\n >>> isuuidtype(MyUUID)\n True\n >>> isuuidtype(NewType(\"Foo\", uuid.UUID))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), uuid.UUID)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.name","title":"name","text":"name(obj: Union[type, ForwardRef, Callable]) -> str\n
Safely retrieve the name of either a standard object or a type annotation.
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, Any, TypeVar\n>>> T = TypeVar(\"T\")\n>>> name(Dict)\n'Dict'\n>>> name(Dict[str, str])\n'Dict'\n>>> name(Any)\n'Any'\n>>> name(dict)\n'dict'\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef name(obj: tp.Union[type, refs.ForwardRef, tp.Callable]) -> str:\n \"\"\"Safely retrieve the name of either a standard object or a type annotation.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, Any, TypeVar\n >>> T = TypeVar(\"T\")\n >>> name(Dict)\n 'Dict'\n >>> name(Dict[str, str])\n 'Dict'\n >>> name(Any)\n 'Any'\n >>> name(dict)\n 'dict'\n \"\"\"\n strobj = qualname(obj)\n return strobj.rsplit(\".\")[-1]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.normalize_typevar","title":"normalize_typevar","text":"normalize_typevar(tvar: TypeVar) -> type[Any]\n
Reduce a TypeVar to a simple type.
Note TypeVar normalization follows this strategy:
-> If the TypeVar is bound\n-----> return the bound type\n-> Else If the TypeVar has constraints\n-----> return a Union of the constraints\n-> Else\n-----> return Any\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef normalize_typevar(tvar: tp.TypeVar) -> type[tp.Any]:\n \"\"\"Reduce a TypeVar to a simple type.\n\n Note:\n TypeVar normalization follows this strategy:\n\n -> If the TypeVar is bound\n -----> return the bound type\n -> Else If the TypeVar has constraints\n -----> return a Union of the constraints\n -> Else\n -----> return Any\n \"\"\"\n if tvar.__bound__:\n return tvar.__bound__\n elif tvar.__constraints__:\n return tp.Union[tvar.__constraints__] # type: ignore[return-value]\n return tp.Any # type: ignore[return-value]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.origin","title":"origin","text":"origin(annotation: Any) -> Any\n
Get the highest-order 'origin'-type for a given type definition.
Tip For the purposes of this library, if we can resolve to a builtin type, we will.
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, Mapping, NewType, Optional\n>>> origin(Dict)\n<class 'dict'>\n>>> origin(Mapping)\n<class 'dict'>\n>>> Registry = NewType('Registry', Dict)\n>>> origin(Registry)\n<class 'dict'>\n>>> class Foo: ...\n...\n>>> origin(Foo)\n<class 'typelib.Foo'>\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef origin(annotation: tp.Any) -> tp.Any:\n \"\"\"Get the highest-order 'origin'-type for a given type definition.\n\n Tip:\n For the purposes of this library, if we can resolve to a builtin type, we will.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, Mapping, NewType, Optional\n >>> origin(Dict)\n <class 'dict'>\n >>> origin(Mapping)\n <class 'dict'>\n >>> Registry = NewType('Registry', Dict)\n >>> origin(Registry)\n <class 'dict'>\n >>> class Foo: ...\n ...\n >>> origin(Foo)\n <class 'typelib.Foo'>\n \"\"\"\n # Resolve custom NewTypes.\n actual = resolve_supertype(annotation)\n\n # Unwrap optional/classvar\n if isclassvartype(actual):\n a = args(actual)\n actual = a[0] if a else actual\n\n if istypealiastype(actual):\n actual = actual.__value__\n\n actual = tp.get_origin(actual) or actual\n\n # provide defaults for generics\n if not isbuiltintype(actual):\n actual = _check_generics(actual)\n\n if iscallable(actual):\n actual = tp.Callable\n\n return actual\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.qualname","title":"qualname","text":"qualname(obj: Union[type, ForwardRef, Callable]) -> str\n
Safely retrieve the qualname of either a standard object or a type annotation.
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, Any, TypeVar\n>>> T = TypeVar(\"T\")\n>>> qualname(Dict)\n'typing.Dict'\n>>> qualname(Dict[str, str])\n'typing.Dict'\n>>> qualname(Any)\n'typing.Any'\n>>> qualname(dict)\n'dict'\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef qualname(obj: tp.Union[type, refs.ForwardRef, tp.Callable]) -> str:\n \"\"\"Safely retrieve the qualname of either a standard object or a type annotation.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, Any, TypeVar\n >>> T = TypeVar(\"T\")\n >>> qualname(Dict)\n 'typing.Dict'\n >>> qualname(Dict[str, str])\n 'typing.Dict'\n >>> qualname(Any)\n 'typing.Any'\n >>> qualname(dict)\n 'dict'\n \"\"\"\n strobj = str(obj)\n if isinstance(obj, refs.ForwardRef):\n strobj = str(obj.__forward_arg__) # type: ignore[union-attr]\n is_generic = isgeneric(strobj)\n # We got a typing thing.\n if is_generic:\n # If this is a subscripted generic we should clean that up.\n return strobj.split(\"[\", maxsplit=1)[0]\n # Easy-ish path, use name magix\n qname = getattr(obj, \"__qualname__\", None)\n nm = getattr(obj, \"__name__\", None)\n if qname is not None:\n return qname.replace(\"<locals>.\", \"\")\n if nm is not None: # pragma: no cover\n return nm\n return strobj\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.resolve_supertype","title":"resolve_supertype","text":"resolve_supertype(annotation: type[Any] | FunctionType) -> Any\n
Get the highest-order supertype for a NewType.
Examples:
>>> from typelib.py import inspection\n>>> from typing import NewType\n>>> UserID = NewType(\"UserID\", int)\n>>> AdminID = NewType(\"AdminID\", UserID)\n>>> resolve_supertype(AdminID)\n<class 'int'>\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef resolve_supertype(annotation: type[tp.Any] | types.FunctionType) -> tp.Any:\n \"\"\"Get the highest-order supertype for a NewType.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import NewType\n >>> UserID = NewType(\"UserID\", int)\n >>> AdminID = NewType(\"AdminID\", UserID)\n >>> resolve_supertype(AdminID)\n <class 'int'>\n \"\"\"\n while hasattr(annotation, \"__supertype__\"):\n annotation = annotation.__supertype__ # type: ignore[union-attr]\n return annotation\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.safe_get_params","title":"safe_get_params","text":"safe_get_params(obj: type) -> Mapping[str, Parameter]\n
Try to extract the parameters of the given object.
Return an empty mapping if we encounter an error.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef safe_get_params(obj: type) -> tp.Mapping[str, inspect.Parameter]:\n \"\"\"Try to extract the parameters of the given object.\n\n Return an empty mapping if we encounter an error.\n \"\"\"\n params: tp.Mapping[str, inspect.Parameter]\n try:\n if ismappingtype(obj) and not istypeddict(obj):\n return {}\n params = cached_signature(obj).parameters\n except (ValueError, TypeError): # pragma: nocover\n params = {}\n return params\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.should_unwrap","title":"should_unwrap","text":"should_unwrap(obj: type) -> bool\n
Test whether we should use the args attr for resolving the type.
This is useful for determining what type to use at run-time for coercion.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef should_unwrap(obj: type) -> bool:\n \"\"\"Test whether we should use the __args__ attr for resolving the type.\n\n This is useful for determining what type to use at run-time for coercion.\n \"\"\"\n return (not isliteral(obj)) and any(x(obj) for x in _UNWRAPPABLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.signature","title":"signature","text":"signature(obj: Callable[..., Any] | type[Any]) -> Signature\n
Get the signature of a type or callable.
Also supports TypedDict subclasses
Source code in src/typelib/py/inspection.py
def signature(obj: tp.Callable[..., tp.Any] | type[tp.Any]) -> inspect.Signature:\n \"\"\"Get the signature of a type or callable.\n\n Also supports TypedDict subclasses\n \"\"\"\n if inspect.isclass(obj) or isgeneric(obj):\n if istypeddict(obj):\n return typed_dict_signature(obj)\n if istupletype(obj) and not isnamedtuple(obj):\n return tuple_signature(obj)\n return inspect.signature(obj)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.simple_attributes","title":"simple_attributes","text":"simple_attributes(t: type) -> Tuple[str, ...]\n
Extract all public, static data-attributes for a given type.
Source code in src/typelib/py/inspection.py
def simple_attributes(t: type) -> tp.Tuple[str, ...]:\n \"\"\"Extract all public, static data-attributes for a given type.\"\"\"\n # If slots are defined, this is the best way to locate static attributes.\n if hasattr(t, \"__slots__\") and t.__slots__:\n return (\n *(\n f\n for f in t.__slots__\n if not f.startswith(\"_\")\n # JIC - check if this is something fancy.\n and not isinstance(getattr(t, f, ...), _DYNAMIC_ATTRIBUTES)\n ),\n )\n # Otherwise we have to guess. This is inherently faulty, as attributes aren't\n # always defined on a class before instantiation. The alternative is reverse\n # engineering the constructor... yikes.\n return (\n *(\n x\n for x, y in inspect.getmembers(t, predicate=issimpleattribute)\n if not x.startswith(\"_\") and not isinstance(y, _DYNAMIC_ATTRIBUTES)\n ),\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.tuple_signature","title":"tuple_signature","text":"tuple_signature(t: type[TupleT]) -> Signature\n
A little faker for getting the \"signature\" of a tuple
.
Note At runtime, tuples are just tuples, but we can make use of their type hints to define a predictable signature.
Source code in src/typelib/py/inspection.py
def tuple_signature(t: type[compat.TupleT]) -> inspect.Signature:\n \"\"\"A little faker for getting the \"signature\" of a [`tuple`][].\n\n Note:\n At runtime, tuples are just tuples, but we can make use of their type hints to\n define a predictable signature.\n \"\"\"\n a = args(t)\n if not a or a[-1] is ...:\n argt = tp.Any if not a else a[0]\n param = inspect.Parameter(\n name=\"args\", kind=inspect.Parameter.VAR_POSITIONAL, annotation=argt\n )\n sig = inspect.Signature(parameters=(param,))\n return sig\n kind = inspect.Parameter.POSITIONAL_ONLY\n params = tuple(\n inspect.Parameter(name=f\"arg{str(i)}\", kind=kind, annotation=at)\n for i, at in enumerate(a)\n )\n sig = inspect.Signature(parameters=params)\n return sig\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.typed_dict_signature","title":"typed_dict_signature","text":"typed_dict_signature(obj: Callable) -> Signature\n
A little faker for getting the \"signature\" of a typing.TypedDict
.
Note Technically, these are dicts at runtime, but we are enforcing a static shape, so we should be able to declare a matching signature for it.
Source code in src/typelib/py/inspection.py
def typed_dict_signature(obj: tp.Callable) -> inspect.Signature:\n \"\"\"A little faker for getting the \"signature\" of a [`typing.TypedDict`][].\n\n Note:\n Technically, these are dicts at runtime, but we are enforcing a static shape,\n so we should be able to declare a matching signature for it.\n \"\"\"\n hints = cached_type_hints(obj)\n total = getattr(obj, \"__total__\", True)\n default = inspect.Parameter.empty if total else ...\n return inspect.Signature(\n parameters=tuple(\n inspect.Parameter(\n name=x,\n kind=inspect.Parameter.KEYWORD_ONLY,\n annotation=y,\n default=getattr(obj, x, default),\n )\n for x, y in hints.items()\n )\n )\n
"},{"location":"reference/typelib/py/refs/","title":"Refs","text":""},{"location":"reference/typelib/py/refs/#typelib.py.refs","title":"refs","text":"Utilities for working with typing.ForwardRef
.
This module allows the developer to create and evaluate typing.ForwardRef
instances with additional logic to support forwards compatibility.
Typical Usage
>>> from typelib.py import refs\n>>> ref = refs.forwardref(\"str\")\n>>> cls = refs.evaluate(ref)\n>>> cls is str\nTrue\n
"},{"location":"reference/typelib/py/refs/#typelib.py.refs.forwardref","title":"forwardref","text":"forwardref(ref: str | type, *, is_argument: bool = False, module: Any | None = None, is_class: bool = True) -> ForwardRef\n
Create a typing.ForwardRef
instance from a ref
string.
This wrapper function will attempt to determine the module name ahead of instantiation if not provided. This is important when resolving the reference to an actual type.
Parameters:
-
ref
(str | type
) \u2013 The type reference string.
-
is_argument
(bool
, default: False
) \u2013 Whether the reference string was an argument to a function (default False).
-
module
(Any | None
, default: None
) \u2013 The python module in which the reference string is defined (optional)
-
is_class
(bool
, default: True
) \u2013 Whether the reference string is a class (default True).
Source code in src/typelib/py/refs.py
def forwardref(\n ref: str | type,\n *,\n is_argument: bool = False,\n module: typing.Any | None = None,\n is_class: bool = True,\n) -> ForwardRef:\n \"\"\"Create a [`typing.ForwardRef`][] instance from a `ref` string.\n\n This wrapper function will attempt to determine the module name ahead of instantiation\n if not provided. This is important when resolving the reference to an actual type.\n\n Args:\n ref: The type reference string.\n is_argument: Whether the reference string was an argument to a function (default False).\n module: The python module in which the reference string is defined (optional)\n is_class: Whether the reference string is a class (default True).\n \"\"\"\n if not isinstance(ref, str):\n name = inspection.qualname(ref)\n module = module or getattr(ref, \"__module__\", None)\n else:\n name = typing.cast(str, ref)\n\n module = _resolve_module_name(ref, module)\n if module is not None:\n name = name.replace(f\"{module}.\", \"\")\n\n return ForwardRef(\n name,\n is_argument=is_argument,\n module=module,\n is_class=is_class,\n )\n
"},{"location":"reference/typelib/unmarshals/","title":"Index","text":""},{"location":"reference/typelib/unmarshals/#typelib.unmarshals","title":"unmarshals","text":"Support for unmarshalling unstructured data into Python data structures.
Notes \"Unmarshalling\" refers to the process of taking a \"primitive\" form of data, such as a basic dictionary or JSON string, and coercing it into a higher-order structured data type.
Tip You may use this package directly, but we encourage you to work with the higher-level API provided by the typelib
module.
Typical Usage
>>> import dataclasses\n>>> import decimal\n>>> from typelib import unmarshals\n>>>\n>>> @dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\n... class Struct:\n... key: str\n... number: decimal.Decimal\n...\n>>>\n>>> data = {\"key\": \"some-key\", \"number\": \"3.14\"}\n>>> unmarshals.unmarshal(Struct, data)\nStruct(key='some-key', number=decimal.Decimal('3.14'))\n>>> unmarshaller = unmarshals.unmarshaller(Struct)\n>>> unmarshaller(data)\nStruct(key='some-key', number=decimal.Decimal('3.14'))\n
See Also unmarshals
unmarshaller
typelib.codec
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.AbstractUnmarshaller","title":"AbstractUnmarshaller","text":"AbstractUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for unmarshallers.
Unmarshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic for decoding and converting primtive Python objects or JSON-endcoded data into their target type.
Unmarshallers support contextual deserialization, which enables the unmarshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.AbstractUnmarshaller.__call__","title":"__call__ abstractmethod
","text":"__call__(val: Any) -> T\n
Unmarshall a Python object into its target type.
Not implemented for the abstract base class.
Source code in src/typelib/unmarshals/routines.py
@abc.abstractmethod\ndef __call__(self, val: tp.Any) -> T:\n \"\"\"Unmarshall a Python object into its target type.\n\n Not implemented for the abstract base class.\n \"\"\"\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.BytesUnmarshaller","title":"BytesUnmarshaller","text":"BytesUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[BytesT]
, Generic[BytesT]
Unmarshaller that encodes an input to bytes.
Note We will format a member of the datetime
module into ISO format before converting to bytes.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.CastUnmarshaller","title":"CastUnmarshaller","text":"CastUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[T]
Unmarshaller that converts an input to an instance of T
with a direct cast.
Note Before casting to the bound type, we will attempt to decode the value into a real Python object.
See Also Parameters:
-
t
(type[T]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context (unused).
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context (unused).\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.caster: tp.Callable[[tp.Any], T] = self.origin # type: ignore[assignment]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.CastUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> T\n
Unmarshal a value into the bound T
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> T:\n \"\"\"Unmarshal a value into the bound `T` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Try to load the string, if this is JSON or a literal expression.\n decoded = serdes.load(val)\n # Short-circuit cast if we have the type we want.\n if isinstance(decoded, self.t):\n return decoded\n # Cast the decoded value to the type.\n return self.caster(decoded)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateTimeUnmarshaller","title":"DateTimeUnmarshaller","text":"DateTimeUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[datetime]
, Generic[DateTimeT]
Unmarshaller that converts an input to a datetime.datetime
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.datetime
instance, return it. - If we are a
float
or int
instance, treat it as a unix timestamp, at UTC. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into either a
datetime.date
instance, a datetime.time
instance or a datetime.datetime
. - If the parsed result is a
datetime.time
instance, then merge the parsed time with today, at the timezone specified in the time instance. - If the parsed result is a
datetime.date
instance, create a datetime.datetime
instance at midnight of the indicated date, UTC.
TL;DR There are many ways to represent a datetime object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateTimeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> datetime\n
Unmarshal a value into the bound DateTimeT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> datetime.datetime:\n \"\"\"Unmarshal a value into the bound `DateTimeT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, self.t):\n return val\n\n # Numbers can be treated as time since epoch.\n if isinstance(val, (int, float)):\n val = datetime.datetime.fromtimestamp(val, tz=datetime.timezone.utc)\n # Always decode bytes.\n decoded = serdes.decode(val)\n # Parse strings.\n dt: datetime.datetime | datetime.date | datetime.time = (\n serdes.dateparse(decoded, self.t) if isinstance(decoded, str) else decoded\n )\n # If we have a time object, default to today.\n if isinstance(dt, datetime.time):\n return self.t.now(tz=dt.tzinfo).replace(\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n tzinfo=dt.tzinfo,\n )\n # Exact class matching.\n if dt.__class__ is self.t:\n return dt # type: ignore[return-value]\n # Subclass check for datetimes.\n if isinstance(dt, datetime.datetime):\n return self.t(\n year=dt.year,\n month=dt.month,\n day=dt.day,\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n tzinfo=dt.tzinfo,\n fold=dt.fold,\n )\n # Implicit: we have a date object.\n return self.t(\n year=dt.year, month=dt.month, day=dt.day, tzinfo=datetime.timezone.utc\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateUnmarshaller","title":"DateUnmarshaller","text":"DateUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[DateT]
, Generic[DateT]
Unmarshaller that converts an input to a datetime.date
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.date
instance, return it. - If we are a
float
or int
instance, treat it as a unix timestamp, at UTC. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into either a
datetime.date
- If the parsed result is a
datetime.time
instance, then return the result of datetime.datetime.now
, at UTC, as a datetime.date
.
TL;DR There are many ways to represent a date object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> DateT\n
Unmarshal a value into the bound DateT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> DateT:\n \"\"\"Unmarshal a value into the bound `DateT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, self.t) and not isinstance(val, datetime.datetime):\n return val\n\n # Numbers can be treated as time since epoch.\n if isinstance(val, (int, float)):\n val = datetime.datetime.fromtimestamp(val, tz=datetime.timezone.utc)\n # Always decode bytes.\n decoded = serdes.decode(val)\n # Parse strings.\n date: datetime.date | datetime.time = (\n serdes.dateparse(decoded, self.t) if isinstance(decoded, str) else decoded\n )\n # Time-only construct is treated as today.\n if isinstance(date, datetime.time):\n date = datetime.datetime.now(tz=datetime.timezone.utc).today()\n # Exact class matching - the parser returns subclasses.\n if date.__class__ is self.t:\n return date # type: ignore[return-value]\n # Reconstruct as the exact type.\n return self.t(year=date.year, month=date.month, day=date.day)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DelayedUnmarshaller","title":"DelayedUnmarshaller","text":"DelayedUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[T]
Delayed proxy for a given type's unmarshaller, used when we encounter a typing.ForwardRef
.
Notes This allows us to delay the resolution of the given type reference until call-time, enabling support for cyclic and recursive types.
Source code in src/typelib/unmarshals/api.py
def __init__(\n self, t: type[T], context: routines.ContextT, *, var: str | None = None\n):\n super().__init__(t, context, var=var)\n self._resolved: routines.AbstractUnmarshaller[T] | None = None\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DelayedUnmarshaller.resolved","title":"resolved property
","text":"resolved: AbstractUnmarshaller[T]\n
The resolved unmarshaller.
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.FixedTupleUnmarshaller","title":"FixedTupleUnmarshaller","text":"FixedTupleUnmarshaller(t: type[TupleT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[TupleT]
Unmarshaller for a \"fixed\" tuple (e.g., tuple[int, str, float]
).
Note Python supports two distinct uses for tuples, unlike in other languages:
- Tuples with a fixed number of members.
- Tuples of variable length (an immutable sequence).
\"Fixed\" tuples may have a distinct type for each member, while variable-length tuples may only have a single type (or union of types) for all members.
Variable-length tuples are handled by our generic iterable unmarshaller.
For \"fixed\" tuples, the algorithm is:
- Attempt to decode the input into a real Python object.
- zip the stack of member unmarshallers and the values in the decoded object.
- Unmarshal each value using the associated unmarshaller for that position.
- Pass the unmarshalling iterator in to the type's constructor.
Tip If the input has more members than the type definition allows, those members will be dropped by nature of our unmarshalling algorithm.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[TupleT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the value unmarshaller stack.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(\n self, t: type[compat.TupleT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the value unmarshaller stack.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n self.ordered_routines = [self.context[vt] for vt in self.stack]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.FixedTupleUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> TupleT\n
Unmarshal a value into the bound tuple
structure.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> compat.TupleT:\n \"\"\"Unmarshal a value into the bound [`tuple`][] structure.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n decoded = serdes.load(val)\n return self.origin(\n routine(v)\n for routine, v in zip(self.ordered_routines, serdes.itervalues(decoded))\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.LiteralUnmarshaller","title":"LiteralUnmarshaller","text":"LiteralUnmarshaller(t: type[LiteralT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[LiteralT]
, Generic[LiteralT]
Unmarshaller that will enforce an input conform to a defined typing.Literal
.
Note We will attempt to decode the value into a real Python object if the input fails initial membership evaluation.
See Also Parameters:
-
t
(type[LiteralT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context (unused).
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[LiteralT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context (unused).\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.values = inspection.args(t)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NoOpUnmarshaller","title":"NoOpUnmarshaller","text":"NoOpUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[T]
Unmarshaller that does nothing.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NoneTypeUnmarshaller","title":"NoneTypeUnmarshaller","text":"NoneTypeUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[None]
Unmarshaller for null values.
Note We will attempt to decode any string/bytes input before evaluating for None
.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NoneTypeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> None\n
Unmarshal the given input into a None
value.
Parameters:
-
val
(Any
) \u2013 The value to unmarshal.
Raises:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> None:\n \"\"\"Unmarshal the given input into a `None` value.\n\n Args:\n val: The value to unmarshal.\n\n Raises:\n ValueError: If `val` is not `None` after decoding.\n \"\"\"\n decoded = serdes.decode(val)\n if decoded is not None:\n raise ValueError(f\"{val!r} is not of {types.NoneType!r}\")\n return None\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NumberUnmarshaller","title":"NumberUnmarshaller","text":"NumberUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[NumberT]
, Generic[NumberT]
Unmarshaller that converts an input to a number.
Note Number unmarshalling follows a best-effort strategy. We may extend type resolution to support more advanced type unmarshalling strategies in the future.
As of now: 1. Attempt to decode any bytes/string input into a real Python value. 2. If the input is a member of the datetime
module, convert it to a number. 3. If the input is a mapping, unpack it into the number constructor. 4. If the input is an iterable, unpack it into the number constructor. 5. Otherwise, call the number constructor with the input.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NumberUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> NumberT\n
Unmarshall a value into the bound Number type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> NumberT:\n \"\"\"Unmarshall a value into the bound Number type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.decode(val)\n if isinstance(decoded, self.t):\n return decoded\n # Represent date/time objects as time since unix epoch.\n if isinstance(val, (datetime.date, datetime.time, datetime.timedelta)):\n decoded = serdes.unixtime(val)\n # Treat containers as constructor args.\n if inspection.ismappingtype(decoded.__class__):\n return self.t(**decoded)\n if inspection.isiterabletype(decoded.__class__) and not inspection.istexttype(\n decoded.__class__\n ):\n return self.t(*decoded)\n # Simple cast for non-containers.\n return self.t(decoded) # type: ignore[call-arg]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.PatternUnmarshaller","title":"PatternUnmarshaller","text":"PatternUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[PatternT]
, Generic[PatternT]
Unmarshaller that converts an input to a re.Pattern
.
Note You can't instantiate a re.Pattern
directly, so we don't have a good method for handling patterns from a different library out-of-the-box. We simply call re.compile()
on the decoded input.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.StringUnmarshaller","title":"StringUnmarshaller","text":"StringUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[StringT]
, Generic[StringT]
Unmarshaller that converts an input to a string.
Note We will format a member of the datetime
module into ISO format.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.StructuredTypeUnmarshaller","title":"StructuredTypeUnmarshaller","text":"StructuredTypeUnmarshaller(t: type[_ST], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[_ST]
Unmarshaller for a \"structured\" (user-defined) type.
Note This unmarshaller supports the unmarshalling of any mapping or structured type into the targeted structured type. There are limitations.
The algorithm is:
- Attempt to decode the input into a real Python object.
- Using a mapping of the structured types \"field\" to the field-type's unmarshaller, iterate over the field->value pairs of the input, skipping fields in the input which are not present in the field mapping.
- Store each unmarshalled value in a keyword-argument mapping.
- Unpack the keyword argument mapping into the bound type's constructor.
Tip While we don't currently support arbitrary collections, we may add this functionality at a later date. Doing so requires more advanced introspection and parameter-binding that would lead to a significant loss in performance if not done carefully.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[_ST]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the value field-to-unmarshaller mapping.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[_ST], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the value field-to-unmarshaller mapping.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.fields_by_var = self._fields_by_var()\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.StructuredTypeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> _ST\n
Unmarshal a value into the bound type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> _ST:\n \"\"\"Unmarshal a value into the bound type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n decoded = serdes.load(val)\n fields = self.fields_by_var\n kwargs = {f: fields[f](v) for f, v in serdes.iteritems(decoded) if f in fields}\n return self.t(**kwargs)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIterableUnmarshaller","title":"SubscriptedIterableUnmarshaller","text":"SubscriptedIterableUnmarshaller(t: type[IterableT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[IterableT]
, Generic[IterableT]
Unmarshaller for a subscripted iterable type.
Note This unmarshaller handles standard simple iterable types. We leverage our own generic itervalues
to allow for translating other collections or structured objects into the target iterable.
The algorithm is as follows:
- We attempt to decode the input into a real Python object.
- We iterate over values in the decoded input.
- We call the value-type's unmarshaller on the
value
members. - We pass the unmarshalling iterator in to the type's constructor.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[IterableT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshaller.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(\n self, t: type[IterableT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshaller.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t=t, context=context, var=var)\n # supporting tuple[str, ...]\n (value_t, *_) = inspection.args(t)\n self.values = context[value_t]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIterableUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> IterableT\n
Unmarshal a value into the bound IterableT
.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> IterableT:\n \"\"\"Unmarshal a value into the bound `IterableT`.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.load(val)\n values = self.values\n return self.origin((values(v) for v in serdes.itervalues(decoded))) # type: ignore[call-arg]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIteratorUnmarshaller","title":"SubscriptedIteratorUnmarshaller","text":"SubscriptedIteratorUnmarshaller(t: type[IteratorT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[IteratorT]
, Generic[IteratorT]
Unmarshaller for a subscripted iterator type.
Note This unmarshaller handles standard simple iterable types. We leverage our own generic itervalues
to allow for translating other collections or structured objects into the target iterator.
The algorithm is as follows:
- We attempt to decode the input into a real Python object.
- We iterate over values in the decoded input.
- We call the value-type's unmarshaller on the
value
members. - We return a new, unmarshalling iterator.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[IteratorT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshaller.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(\n self, t: type[IteratorT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshaller.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n (value_t,) = inspection.args(t)\n self.values = context[value_t]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIteratorUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> IteratorT\n
Unmarshal a value into the bound IteratorT
.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> IteratorT:\n \"\"\"Unmarshal a value into the bound `IteratorT`.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.load(val)\n values = self.values\n it: IteratorT = (values(v) for v in serdes.itervalues(decoded)) # type: ignore[assignment]\n return it\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedMappingUnmarshaller","title":"SubscriptedMappingUnmarshaller","text":"SubscriptedMappingUnmarshaller(t: type[MappingT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[MappingT]
, Generic[MappingT]
Unmarshaller for a subscripted mapping type.
Note This unmarshaller handles standard key->value mappings. We leverage our own generic iteritems
to allow for translating other collections or structured objects into the target mapping.
The algorithm is as follows:
- We attempt to decode the input into a real Python object.
- We iterate over key->value pairs.
- We call the key-type's unmarshaller on the
key
members. - We call the value-type's unmarshaller on the
value
members. - We pass the unmarshalling iterator in to the type's constructor.
See Also typelib.serdes.load
typelib.serdes.iteritems
Parameters:
-
t
(type[MappingT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[MappingT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n key_t, value_t = inspection.args(t)\n self.keys = context[key_t]\n self.values = context[value_t]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedMappingUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> MappingT\n
Unmarshal a value into the bound MappingT
.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> MappingT:\n \"\"\"Unmarshal a value into the bound `MappingT`.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.load(val)\n keys = self.keys\n values = self.values\n return self.origin( # type: ignore[call-arg]\n ((keys(k), values(v)) for k, v in serdes.iteritems(decoded))\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeDeltaUnmarshaller","title":"TimeDeltaUnmarshaller","text":"TimeDeltaUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[TimeDeltaT]
, Generic[TimeDeltaT]
Unmarshaller that converts an input to a datetime.timedelta
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.timedelta
instance, return it. - If we are a
float
or int
instance, treat it as total seconds for a delta. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into a
datetime.timedelta
instance. - If the parsed result is not exactly the bound
TimeDeltaT
type, convert it.
TL;DR There are many ways to represent a time object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeDeltaUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> TimeDeltaT\n
Unmarshal a value into the bound TimeDeltaT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> TimeDeltaT:\n \"\"\"Unmarshal a value into the bound `TimeDeltaT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, (int, float)):\n return self.t(seconds=int(val))\n\n decoded = serdes.decode(val)\n td: datetime.timedelta = (\n serdes.dateparse(decoded, t=datetime.timedelta)\n if isinstance(decoded, str)\n else decoded\n )\n\n if td.__class__ is self.t:\n return td # type: ignore[return-value]\n\n return self.t(seconds=td.total_seconds())\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeUnmarshaller","title":"TimeUnmarshaller","text":"TimeUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[TimeT]
, Generic[TimeT]
Unmarshaller that converts an input to adatetime.time
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.time
instance, return it. - If we are a
float
or int
instance, treat it as a unix timestamp, at UTC. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into either a
datetime.date
instance, a datetime.time
instance or a datetime.datetime
. - If the parsed result is a
datetime.datetime
instance, then extract the time portion, preserving the associated timezone. - If the parsed result is a
datetime.date
instance, create a time instance at midnight, UTC.
TL;DR There are many ways to represent a time object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> TimeT\n
Unmarshal a value into the bound TimeT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> TimeT:\n \"\"\"Unmarshal a value into the bound `TimeT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, self.t):\n return val\n\n decoded = serdes.decode(val)\n if isinstance(decoded, (int, float)):\n decoded = (\n datetime.datetime.fromtimestamp(val, tz=datetime.timezone.utc)\n .time()\n # datetime.time() strips tzinfo...\n .replace(tzinfo=datetime.timezone.utc)\n )\n dt: datetime.datetime | datetime.date | datetime.time = (\n serdes.dateparse(decoded, self.t) if isinstance(decoded, str) else decoded\n )\n\n if isinstance(dt, datetime.datetime):\n # datetime.time() strips tzinfo...\n dt = dt.time().replace(tzinfo=dt.tzinfo)\n elif isinstance(dt, datetime.date):\n dt = self.t(tzinfo=datetime.timezone.utc)\n\n if dt.__class__ is self.t:\n return dt # type: ignore[return-value]\n\n return self.t(\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n tzinfo=dt.tzinfo,\n fold=dt.fold,\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UUIDUnmarshaller","title":"UUIDUnmarshaller","text":"UUIDUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[UUIDT]
, Generic[UUIDT]
Unmarshaller that converts an input to a uuid.UUID
(or subclasses).
Note The resolution algorithm is intentionally simple:
- Attempt to decode any bytes/string input into a real Python object.
- If the value is an integer, pass it into the constructor via the
int=
param. - Otherwise, pass into the constructor directly.
Tip While the uuid.UUID
constructor supports many different keyword inputs for different types of UUID formats/encodings, we don't have a great method for detecting the correct input. We have moved with the assumption that the two most common formats are a standard string encoding, or an integer encoding.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UUIDUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> UUIDT\n
Unmarshal a value into the bound UUIDT
type.
Parameters:
See Also Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> UUIDT:\n \"\"\"Unmarshal a value into the bound `UUIDT` type.\n\n Args:\n val: The input value to unmarshal.\n\n See Also:\n - [`typelib.serdes.load`][]\n \"\"\"\n decoded = serdes.load(val)\n if isinstance(decoded, int):\n return self.t(int=decoded)\n if isinstance(decoded, self.t):\n return decoded\n return self.t(decoded) # type: ignore[arg-type]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UnionUnmarshaller","title":"UnionUnmarshaller","text":"UnionUnmarshaller(t: type[UnionT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[UnionT]
, Generic[UnionT]
Unmarshaller that will convert an input to one of the types defined in a typing.Union
.
Note Union deserialization is messy and violates a static type-checking mechanism - for static type-checkers, str | int
is equivalent to int | str
. This breaks down during unmarshalling for the simple fact that casting something to str
will always succeed, so we would never actually unmarshal the input it an int
, even if that is the \"correct\" result.
Our algorithm is intentionally simple:
- We iterate through each union member from top to bottom and call the resolved unmarshaller, returning the result.
- If any of
(ValueError, TypeError, SyntaxError)
, try again with the next unmarshaller. - If all unmarshallers fail, then we have an invalid input, raise an error.
TL;DR In order to ensure correctness, you should treat your union members as a stack, sorted from most-strict initialization to least-strict.
Parameters:
-
t
(type[UnionT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[UnionT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n if inspection.isoptionaltype(t):\n self.stack = (self.stack[-1], *self.stack[:-1])\n\n self.ordered_routines = [self.context[typ] for typ in self.stack]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UnionUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> UnionT\n
Unmarshal a value into the bound UnionT
.
Parameters:
Raises:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> UnionT:\n \"\"\"Unmarshal a value into the bound `UnionT`.\n\n Args:\n val: The input value to unmarshal.\n\n Raises:\n ValueError: If `val` cannot be unmarshalled into any member type.\n \"\"\"\n for routine in self.ordered_routines:\n with contextlib.suppress(\n ValueError, TypeError, SyntaxError, AttributeError\n ):\n unmarshalled = routine(val)\n return unmarshalled\n\n raise ValueError(f\"{val!r} is not one of types {self.stack!r}\")\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.unmarshal","title":"unmarshal","text":"unmarshal(t: type[T] | ForwardRef | str, value: Any) -> T\n
Unmarshal value
into typ
.
Parameters:
Source code in src/typelib/unmarshals/api.py
def unmarshal(t: type[T] | refs.ForwardRef | str, value: tp.Any) -> T:\n \"\"\"Unmarshal `value` into `typ`.\n\n Args:\n t: The type annotation or reference to unmarshal into.\n value: The value to unmarshal.\n \"\"\"\n routine = unmarshaller(t)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.unmarshaller","title":"unmarshaller","text":"unmarshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractUnmarshaller[T]\n
Get an un-marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate an unmarshaller for. May be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/unmarshals/api.py
@compat.cache\ndef unmarshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractUnmarshaller[T]:\n \"\"\"Get an un-marshaller routine for a given type.\n\n Args:\n t: The type annotation to generate an unmarshaller for.\n May be a type, type alias, [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractUnmarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpUnmarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to typelib
","text":""},{"location":"#pythons-typing-toolkit","title":"Python's Typing Toolkit","text":"typelib
provides a sensible, non-invasive, production-ready toolkit for leveraging Python type annotations at runtime.
"},{"location":"#quickstart","title":"Quickstart","text":""},{"location":"#installation","title":"Installation","text":"poetry add 'typelib[json]'\n
"},{"location":"#bring-your-own-models","title":"Bring Your Own Models","text":"We don't care how your data model is implemented - you can use dataclasses
, TypedDict
, NamedTuple
, a plain collection, a custom class, or any other modeling library. As long as your type is valid at runtime, we'll support it.
"},{"location":"#the-how-and-the-where","title":"The How and the Where","text":""},{"location":"#how-the-high-level-api","title":"How: The High-Level API","text":"We have a simple high-level API which should handle most production use-cases:
from __future__ import annotations\n\nimport dataclasses\nimport datetime\nimport decimal\n\n\nimport typelib\n\n@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\nclass BusinessModel:\n op: str\n value: decimal.Decimal\n id: int | None = None\n created_at: datetime.datetime | None = None\n\n\ncodec = typelib.codec(BusinessModel)\ninstance = codec.decode(b'{\"op\":\"add\",\"value\":\"1.0\"}')\nprint(instance)\n#> BusinessModel(op='add', value=decimal.Decimal('1.0'), id=None, created_at=None)\nencoded = codec.encode(instance)\nprint(encoded)\n#> b'{\"op\":\"add\",\"value\":\"1.0\",\"id\":null,\"created_at\":null}'\n
Tip
Looking for more? Check out our API Reference for the high-level API.
"},{"location":"#where-at-the-edges-of-your-code","title":"Where: At the Edges of Your Code","text":"You can integrate this library at the \"edges\" of your code - e.g., at the integration points between your application and your client or you application and your data-store:
from __future__ import annotations\n\nimport dataclasses\nimport datetime\nimport decimal\nimport operator\nimport random\n\nimport typelib\n\n\nclass ClientRPC:\n def __init__(self):\n self.codec = typelib.codec(BusinessModel)\n\n def call(self, inp: bytes) -> bytes:\n model = self.receive(inp)\n done = self.op(model)\n return self.send(done)\n\n @staticmethod\n def op(model: BusinessModel) -> BusinessModel:\n op = getattr(operator, model.op)\n return dataclasses.replace(\n model,\n value=op(model.value, model.value),\n id=random.getrandbits(64),\n created_at=datetime.datetime.now(tz=datetime.UTC)\n )\n\n def send(self, model: BusinessModel) -> bytes:\n return self.codec.encode(model)\n\n def receive(self, data: bytes) -> BusinessModel:\n return self.codec.decode(data)\n\n\n@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\nclass BusinessModel:\n op: str\n value: decimal.Decimal\n id: int | None = None\n created_at: datetime.datetime | None = None\n
"},{"location":"#where-between-layers-in-your-code","title":"Where: Between Layers in Your Code","text":"You can integrate this library to ease the translation of one type to another:
from __future__ import annotations\n\nimport dataclasses\nimport datetime\nimport decimal\nimport typing as t\n\n\nimport typelib\n\n@dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\nclass BusinessModel:\n op: str\n value: decimal.Decimal\n id: int | None = None\n created_at: datetime.datetime | None = None\n\n\nclass ClientRepr(t.TypedDict):\n op: str\n value: str\n id: str | None\n created_at: datetime.datetime | None\n\n\nbusiness_codec = typelib.codec(BusinessModel)\nclient_codec = typelib.codec(ClientRepr)\n# Initialize your business model directly from your input.\ninstance = business_codec.decode(\n b'{\"op\":\"add\",\"value\":\"1.0\",\"id\":\"10\",\"created_at\":\"1970-01-01T00:00:00+0000}'\n)\nprint(instance)\n#> BusinessModel(op='add', value=Decimal('1.0'), id=10, created_at=datetime.datetime(1970, 1, 1, 0, 0, fold=1, tzinfo=Timezone('UTC')))\n# Encode your business model into the format defined by your ClientRepr.\nencoded = client_codec.encode(instance)\nprint(encoded)\n#> b'{\"op\":\"add\",\"value\":\"1.0\",\"id\":\"10\",\"created_at\":\"1970-01-01T00:00:00+00:00\"}'\n
Tip
There's no need to initialize your ClientRepr instance to leverage its codec, as long as:
- The instance you pass in has the same overlap of required fields.
- The values in the overlapping fields can be translated to the target type.
"},{"location":"#why-typelib","title":"Why typelib
","text":"typelib
provides a simple, non-invasive API to make everyday data wrangling in your production applications easy and reliable.
"},{"location":"#we-do","title":"We DO","text":" - Provide an API for marshalling and unmarshalling data based upon type annotations.
- Provide an API for integrating our marshalling with over-the-wire serialization and deserialization.
- Provide fine-grained, high-performance, runtime introspection of Python types.
- Provide future-proofing to allow for emerging type annotation syntax.
"},{"location":"#we-dont","title":"We DON'T","text":" - Require you to inherit from a custom base class.
- Require you to use custom class decorators.
- Rely upon generated code.
"},{"location":"#how-it-works","title":"How It Works","text":"typelib
's implementation is unique among runtime type analyzers - we use an iterative, graph-based resolver to build a predictable, static ordering of the types represented by an annotation. We have implemented our type-resolution algorithm in isolation from our logic for marshalling and unmarshalling as a simple iterative loop, making the logic simple to reason about.
Tip
Read a detailed discussion here.
"},{"location":"changelog/","title":"Changelog","text":"All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
"},{"location":"changelog/#v016-2024-10-30","title":"v0.1.6 - 2024-10-30","text":"Compare with v0.1.5
"},{"location":"changelog/#bug-fixes","title":"Bug Fixes","text":" - Unwrap
TypeAliasType
for all nested types in a type graph (c837838 by Sean Stewart).
"},{"location":"changelog/#v015-2024-10-30","title":"v0.1.5 - 2024-10-30","text":"Compare with v0.1.4
"},{"location":"changelog/#bug-fixes_1","title":"Bug Fixes","text":" - extract the real
__value__
from a TypeAliasType
when calling origin(...)
(94218a4 by Sean Stewart).
"},{"location":"changelog/#v014-2024-10-26","title":"v0.1.4 - 2024-10-26","text":"Compare with v0.1.2
"},{"location":"changelog/#bug-fixes_2","title":"Bug Fixes","text":" - remove use of
graphlib.TypeNode
in type context (e4742c0 by Sean Stewart). - correct handling optional types (79e431a by Sean Stewart).
"},{"location":"changelog/#v012-2024-10-16","title":"v0.1.2 - 2024-10-16","text":"Compare with v0.1.1
"},{"location":"changelog/#bug-fixes_3","title":"Bug Fixes","text":" - handle case where a resolved type reference can't match up to the original hint (a5ddf68 by Sean Stewart).
- inspect types when resolving field marshallers for structured types (78d4896 by Sean Stewart).
"},{"location":"changelog/#v011-2024-10-16","title":"v0.1.1 - 2024-10-16","text":"Compare with v0.1.0
"},{"location":"changelog/#features","title":"Features","text":" - support
enum.Enum
subtypes (d2a699a by Sean Stewart).
"},{"location":"changelog/#bug-fixes_4","title":"Bug Fixes","text":" - allow optional and union types to be marks as \"stdlib\" (bf4ad13 by Sean Stewart).
"},{"location":"changelog/#v010-2024-09-05","title":"v0.1.0 - 2024-09-05","text":"Compare with first commit
"},{"location":"changelog/#features_1","title":"Features","text":" - implement the top-level API (611f590 by Sean Stewart).
- rename some inspections and rework imports (3a5946e by Sean Stewart).
- re-organize utility modules (5019468 by Sean Stewart).
- codec interface (6996275 by Sean Stewart).
- type-enforce signature binding (a56418b by Sean Stewart).
- add high-level API for creating marshal/unmarshal protocols (2fa5345 by Sean Stewart).
- Implement marshallers. (ed159ef by Sean Stewart).
- Support TypeAliasType (e235f43 by Sean Stewart).
- Support for cyclic types. (4422413 by Sean Stewart).
- Defining the unmarshal API. (8117e0c by Sean Stewart).
- Better generics interface. (0f96785 by Sean Stewart).
- Initial pass of complex types for unmarshalling. (82b566c by Sean Stewart).
- Unmarshallers for initial primitive types. (1c6aa1c by Sean Stewart).
- Core utilities and graph resolver, with test coverage. (108faa1 by Sean Stewart).
"},{"location":"changelog/#bug-fixes_5","title":"Bug Fixes","text":" - treat sequences as unique from iterables in iteritems (1f1b0fd by Sean Stewart).
- update param name for type input in dateparse (1779b4e by Sean Stewart).
- weakref bug with slotted Codec (0887083 by Sean Stewart).
- mypy type hinting for non py312 in compat.py (b36c7d6 by Sean Stewart).
- use
datetime.timestamp
when converting date/time to numeric values (ecdc908 by Sean Stewart). - reliable UTC timestamps for date objects. (582686d by Sean Stewart).
- use compat for future types. (2e8aa24 by Sean Stewart).
- Fix type-hints for lower versions of python (7c08c8c by Sean Stewart).
- Fix type hints for marshalled values (f4742e0 by Sean Stewart).
- Enforce utc for tz-naive datetime.date during number conversion (afe79fb by Sean Stewart).
- The factory function can handle strings/forwardrefs. (34dd7dd by Sean Stewart).
- Tweaking root node assignment. (6b1f141 by Sean Stewart).
- Fix passing var names to unmarshaller (38c2002 by Sean Stewart).
"},{"location":"conduct/","title":"Contributor Covenant Code of Conduct","text":""},{"location":"conduct/#our-pledge","title":"Our Pledge","text":"In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
"},{"location":"conduct/#our-standards","title":"Our Standards","text":"Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
"},{"location":"conduct/#our-responsibilities","title":"Our Responsibilities","text":"Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
"},{"location":"conduct/#scope","title":"Scope","text":"This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
"},{"location":"conduct/#enforcement","title":"Enforcement","text":"Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by creating a new issue with the label conduct-review
. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
"},{"location":"conduct/#attribution","title":"Attribution","text":"This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
"},{"location":"graph/","title":"Your Annotation is a Graph","text":"Graph theory is a fundamental aspect of computer programming - nearly every problem can be modeled as a graph and in most cases, doing so can drastically reduce the complexity of the solution. We use graph theory to map your type annotations into a reliable resolution order for building out your marshalling and unmarshalling logic.
Read More
- Wikipedia - Graph Theory
- StackOverflow - Beginners Guide to Graph Theory
"},{"location":"graph/#handling-data-models","title":"Handling Data Models","text":"Below we map out some examples of the type graph for some common annotations:
---\ntitle: \"A Simple Mapping\"\n---\nerDiagram\n \"dict[str, int]\" ||--|{ str : contains\n \"dict[str, int]\" ||--|{ int : contains
Given the above graph, the static order for resolving the type dict[str, int]
would be: (0: str, 1: int, 2: dict[str, int])
.
---\ntitle: \"A Data Class Definition\"\n---\nerDiagram\n Customer ||--|| str : contains\n Customer ||--|| uuid : contains\n Customer ||--|| datetime : contains\n Customer {\n str name\n uuid id\n datetime created_at\n }
Given the above graph, the static order for resolving type Customer
would be: (0: str, 1: uuid, 2: datetime, 3: Customer)
.
Implementers can iterate over the static order, building a localized context for the type definition as we traverse from outer edges to the root node.
Note
As an implementation detail, edge types will be resolved in the order they are declared within the containing type. However, we only guarantee that all edges will be provided before the containing type, the field-order of these edges is not guaranteed.
"},{"location":"graph/#handling-cyclic-data-models","title":"Handling Cyclic Data Models","text":"Graphs may have cycles - if not addressed, this can result in infinite recursion. When we encounter a cyclic or direct recursive type, we wrap the cycle in a typing.ForwardRef
and terminate that branch. This provides another guarantee to implementations which leverage our graph resolver - all forward references are cyclic types and should be delayed.
---\ntitle: \"A Recursive Type Definition\"\n---\nerDiagram\n Node ||--o| Node : contains\n Node {\n parent Node\n }
---\ntitle: \"A Cyclic Type Definition\"\n---\nerDiagram\n ClassA ||--|| ClassB : contains\n ClassB ||--o| ClassA : contains\n ClassA {\n ClassB attr\n }\n ClassB {\n ClassA attr\n }
"},{"location":"graph/#further-reading","title":"Further Reading","text":""},{"location":"reference/typelib/","title":"Index","text":""},{"location":"reference/typelib/#typelib","title":"typelib","text":"The top-level API for typelib.
Typical Usage
>>> import dataclasses\n>>> import typelib\n>>>\n>>> @dataclasses.dataclass\n... class Data:\n... attr: int\n... key: str\n...\n>>>\n>>> typelib.unmarshal(Data, '{\"key\":\"string\",\"attr\":\"1\"}')\nData(attr=1, key='string')\n>>> typelib.marshal(Data(attr=1, key='string'))\n{'attr': 1, 'key': 'string'}\n>>> codec = typelib.codec(Data)\n>>> codec.encode(Data(attr=1, key='string'))\nb'{\"attr\":1,\"key\":\"string\"}'\n>>> codec.decode(b'{\"key\":\"string\",\"attr\":1}')\nData(attr=1, key='string')\n
"},{"location":"reference/typelib/#typelib.AbstractMarshaller","title":"AbstractMarshaller","text":"AbstractMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for marshallers.
Marshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic reducing Python objects into their primitive representations for over-the-wire encoding.
Marshallers support contextual serialization, which enables the marshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/#typelib.AbstractUnmarshaller","title":"AbstractUnmarshaller","text":"AbstractUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for unmarshallers.
Unmarshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic for decoding and converting primtive Python objects or JSON-endcoded data into their target type.
Unmarshallers support contextual deserialization, which enables the unmarshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/#typelib.AbstractUnmarshaller.__call__","title":"__call__ abstractmethod
","text":"__call__(val: Any) -> T\n
Unmarshall a Python object into its target type.
Not implemented for the abstract base class.
Source code in src/typelib/unmarshals/routines.py
@abc.abstractmethod\ndef __call__(self, val: tp.Any) -> T:\n \"\"\"Unmarshall a Python object into its target type.\n\n Not implemented for the abstract base class.\n \"\"\"\n
"},{"location":"reference/typelib/#typelib.Codec","title":"Codec dataclass
","text":"Codec(marshal: AbstractMarshaller[T], unmarshal: AbstractUnmarshaller[T], encoder: EncoderT, decoder: DecoderT)\n
Bases: Generic[T]
A standard wire protocol (codec).
This codec enables you to directly encode and decode your data model into your wire protocol.
"},{"location":"reference/typelib/#typelib.Codec.decoder","title":"decoder instance-attribute
","text":"decoder: DecoderT\n
The decoder used to deserialize a bytes-like object into a Python data structure for marshalling into T
.
"},{"location":"reference/typelib/#typelib.Codec.encoder","title":"encoder instance-attribute
","text":"encoder: EncoderT\n
The encoder used to serialize a marshalled T
into bytes.
"},{"location":"reference/typelib/#typelib.Codec.marshal","title":"marshal instance-attribute
","text":"marshal: AbstractMarshaller[T]\n
The marshaller used to convert an instance of T
to a serializable object.
"},{"location":"reference/typelib/#typelib.Codec.unmarshal","title":"unmarshal instance-attribute
","text":"unmarshal: AbstractUnmarshaller[T]\n
The unmarshaller used to convert a deserialized object into an instance of T
.
"},{"location":"reference/typelib/#typelib.Codec.decode","title":"decode","text":"decode(value: bytes) -> T\n
Decode an instance of T
from bytes.
We will first decode the data from bytes using the decoder
, then unmarshal the data into an instance of T
using unmarshal
.
Parameters:
-
value
(bytes
) \u2013 The bytes to decode.
Source code in src/typelib/codecs.py
def decode(self, value: bytes) -> T:\n \"\"\"Decode an instance of `T` from bytes.\n\n We will first decode the data from bytes using the\n [`decoder`][typelib.Codec.decoder], then unmarshal the data into an\n instance of `T` using [`unmarshal`][typelib.Codec.unmarshal].\n\n Args:\n value: The bytes to decode.\n \"\"\"\n decoded = self.decoder(value)\n unmarshalled = self.unmarshal(decoded)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.Codec.encode","title":"encode","text":"encode(value: T) -> bytes\n
Encode an instance of T
to bytes.
We will first marshal the given instance using the marshal
, then encode the marshalled data into bytes using the encoder
.
Parameters:
-
value
(T
) \u2013 The instance to encode.
Source code in src/typelib/codecs.py
def encode(self, value: T) -> bytes:\n \"\"\"Encode an instance of `T` to bytes.\n\n We will first marshal the given instance using the\n [`marshal`][typelib.Codec.marshal], then encode the marshalled data\n into bytes using the [`encoder`][typelib.Codec.encoder].\n\n Args:\n value: The instance to encode.\n \"\"\"\n marshalled = self.marshal(value)\n encoded = self.encoder(marshalled)\n return encoded\n
"},{"location":"reference/typelib/#typelib.codec","title":"codec","text":"codec(t: type[T], *, marshaller: AbstractMarshaller[T] | None = None, unmarshaller: AbstractUnmarshaller[T] | None = None, encoder: EncoderT = compat.json.dumps, decoder: DecoderT = compat.json.loads, codec_cls: type[CodecT[T]] | None = None) -> CodecT[T]\n
Factory function for creating a Codec
instance.
Note In the simplest case, all that needs be provided is the first parameter (t
). We will generate a marshaller, unmarshaller and build a codec. However, we provide ample means for customization:
- You can pass in a subclass of
Codec
to codec_cls
. - You may supply custom
marshaller
or unmarshaller
callables - we will generate one using the high-level APIs from marshals
and unmarshals
if not supplied. - The
encoder
and decoder
default to JSON, using either stdlib json
or orjson
if available. You may provide custom encoder
and decoder
callables, the only requirement is they ser/des to/from bytes
.
Tip
If you installed the json
extra when you installed this library, then you have installed orjson
.
Parameters:
-
t
(type[T]
) \u2013 The type to create the interchange protocol for.
-
marshaller
(AbstractMarshaller[T] | None
, default: None
) \u2013 The marshaller used to marshal inputs into the associated type. (optional)
-
unmarshaller
(AbstractUnmarshaller[T] | None
, default: None
) \u2013 The unmarshaller used to unmarshal inputs into the associated type. (optional)
-
encoder
(EncoderT
, default: dumps
) \u2013 The encoder for encoding data for over-the-wire (defaults to JSON).
-
decoder
(DecoderT
, default: loads
) \u2013 The decoder for decoding data from over-the-wire (defaults to JSON).
-
codec_cls
(type[CodecT[T]] | None
, default: None
) \u2013 The codec class definition, if overloading (optional).
Source code in src/typelib/codecs.py
@compat.cache\ndef codec(\n t: type[T],\n *,\n marshaller: marshals.AbstractMarshaller[T] | None = None,\n unmarshaller: unmarshals.AbstractUnmarshaller[T] | None = None,\n encoder: EncoderT = compat.json.dumps,\n decoder: DecoderT = compat.json.loads,\n codec_cls: type[CodecT[T]] | None = None,\n) -> CodecT[T]:\n \"\"\"Factory function for creating a [`Codec`][typelib.Codec] instance.\n\n Note:\n In the simplest case, all that needs be provided is the first parameter (`t`). We will\n generate a marshaller, unmarshaller and build a codec. However, we provide ample\n means for customization:\n\n - You can pass in a subclass of [`Codec`][typelib.codecs.Codec] to `codec_cls`.\n - You may supply custom `marshaller` or `unmarshaller` callables - we will generate\n one using the high-level APIs from [`marshals`][typelib.marshals] and\n [`unmarshals`][typelib.unmarshals] if not supplied.\n - The `encoder` and `decoder` default to JSON, using either\n stdlib [`json`][] or [`orjson`](https://github.com/ijl/orjson){.external}\n if available. You may provide custom `encoder` and `decoder` callables, the only\n requirement is they ser/des to/from `bytes`.\n\n /// tip\n If you installed the `json` extra when you installed this library, then you have\n installed [`orjson`](https://github.com/ijl/orjson){.external}.\n ///\n\n Args:\n t: The type to create the interchange protocol for.\n marshaller: The marshaller used to marshal inputs into the associated type. (optional)\n unmarshaller: The unmarshaller used to unmarshal inputs into the associated type. (optional)\n encoder: The encoder for encoding data for over-the-wire (defaults to JSON).\n decoder: The decoder for decoding data from over-the-wire (defaults to JSON).\n codec_cls: The codec class definition, if overloading (optional).\n\n \"\"\"\n marshal = marshaller or marshals.marshaller(t=t)\n unmarshal = unmarshaller or unmarshals.unmarshaller(t=t)\n cls = codec_cls or Codec\n if inspection.isbytestype(t):\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=lambda v: v, # type: ignore[arg-type,return-value]\n decoder=lambda v: v, # type: ignore[arg-type,return-value]\n )\n return cdc\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=encoder,\n decoder=decoder,\n )\n return cdc\n
"},{"location":"reference/typelib/#typelib.decode","title":"decode","text":"decode(t: type[T] | ForwardRef | str, value: bytes, *, decoder: DecoderT = compat.json.loads) -> T\n
Decode a bytes object into an instance of t
.
Parameters:
Source code in src/typelib/api.py
def decode(\n t: type[T] | refs.ForwardRef | str,\n value: bytes,\n *,\n decoder: DecoderT = compat.json.loads,\n) -> T:\n \"\"\"Decode a bytes object into an instance of `t`.\n\n Args:\n t: A type hint for resolving the unmarshaller.\n value: The value to decode.\n decoder: A callable that takes a bytes object and returns a Python value.\n \"\"\"\n decoded = decoder(value)\n unmarshalled = unmarshal(t=t, value=decoded)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.encode","title":"encode","text":"encode(value: T, *, t: type[T] | ForwardRef | str | None = None, encoder: EncoderT = compat.json.dumps) -> bytes\n
Encode a value into a bytes object.
Parameters:
Source code in src/typelib/api.py
def encode(\n value: T,\n *,\n t: type[T] | refs.ForwardRef | str | None = None,\n encoder: EncoderT = compat.json.dumps,\n) -> bytes:\n \"\"\"Encode a value into a bytes object.\n\n Args:\n value: The value to encode.\n t: A type hint for resolving the marshaller.\n encoder: A callable that takes a value and returns a bytes object.\n \"\"\"\n marshalled = marshal(value=value, t=t)\n encoded = encoder(marshalled)\n return encoded\n
"},{"location":"reference/typelib/#typelib.marshal","title":"marshal","text":"marshal(value: Any, *, t: type[T] | ForwardRef | str | None = None) -> MarshalledValueT\n
Marshal value
from typ
into MarshalledValueT
.
Parameters:
-
value
(Any
) \u2013 The value to reduce to a simple, encode-able type.
-
t
(type[T] | ForwardRef | str | None
, default: None
) \u2013 The type to use for building the marshaller (optional). If not provided, we'll default to the type of the input value.
Source code in src/typelib/marshals/api.py
def marshal(\n value: tp.Any, *, t: type[T] | refs.ForwardRef | str | None = None\n) -> serdes.MarshalledValueT:\n \"\"\"Marshal `value` from `typ` into [`MarshalledValueT`][typelib.serdes.MarshalledValueT].\n\n Args:\n value: The value to reduce to a simple, encode-able type.\n t:\n The type to use for building the marshaller (optional).\n If not provided, we'll default to the type of the input value.\n \"\"\"\n typ = value.__class__ if t is None else t\n routine: routines.AbstractMarshaller[T] = marshaller(typ)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.marshaller","title":"marshaller","text":"marshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractMarshaller[T]\n
Get a marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate a marshaller for. Can be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/marshals/api.py
@compat.cache\ndef marshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractMarshaller[T]:\n \"\"\"Get a marshaller routine for a given type.\n\n Args:\n t:\n The type annotation to generate a marshaller for. Can be a type, type alias,\n [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractMarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpMarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"},{"location":"reference/typelib/#typelib.unmarshal","title":"unmarshal","text":"unmarshal(t: type[T] | ForwardRef | str, value: Any) -> T\n
Unmarshal value
into typ
.
Parameters:
Source code in src/typelib/unmarshals/api.py
def unmarshal(t: type[T] | refs.ForwardRef | str, value: tp.Any) -> T:\n \"\"\"Unmarshal `value` into `typ`.\n\n Args:\n t: The type annotation or reference to unmarshal into.\n value: The value to unmarshal.\n \"\"\"\n routine = unmarshaller(t)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/#typelib.unmarshaller","title":"unmarshaller","text":"unmarshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractUnmarshaller[T]\n
Get an un-marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate an unmarshaller for. May be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/unmarshals/api.py
@compat.cache\ndef unmarshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractUnmarshaller[T]:\n \"\"\"Get an un-marshaller routine for a given type.\n\n Args:\n t: The type annotation to generate an unmarshaller for.\n May be a type, type alias, [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractUnmarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpUnmarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"},{"location":"reference/typelib/binding/","title":"Binding","text":""},{"location":"reference/typelib/binding/#typelib.binding","title":"binding","text":"Utilities for automatic unmarshalling of inputs according to a callable object's signature.
Typical Usage
>>> from typelib import binding\n>>>\n>>> def foo(val: int) -> int:\n... return val * 2\n...\n>>> bound = binding.bind(foo)\n>>> bound(\"2\")\n4\n>>> bound.call(\"3\")\n'33'\n
"},{"location":"reference/typelib/binding/#typelib.binding.AbstractBinding","title":"AbstractBinding","text":"AbstractBinding(*, signature: Signature, binding: BindingT, varkwd: AbstractUnmarshaller | None = None, varpos: AbstractUnmarshaller | None = None, startpos: int | None = None)\n
Bases: ABC
, Generic[P]
The abstract base class for all type-enforced bindings.
Note \"Bindings\" are callables which leverage the type annotations in a signature to unmarshal inputs.
We differentiate each subclass based upon the possible combinations of parameter kinds:
- Positional-only arguments
- Keyword-only arguments
- Positional-or-Keyword arguments
- Variable-positional arguments (
*args
) - Variable-keyword arguments (
**kwargs
)
This allows us to micro-optimize the call for each subclass to exactly what is necessary for the that combination, which can lead to a significant speedup in hot loops.
Parameters:
-
signature
(Signature
) \u2013 The signature for the binding.
-
binding
(BindingT
) \u2013 A mapping of parameter names and positions to unmarshallers. This accounts for positional, keyword, or positional-or-keyword arguments.
-
varkwd
(AbstractUnmarshaller | None
, default: None
) \u2013 The unmarshaller for var-keyword arguments (**kwargs
).
-
varpos
(AbstractUnmarshaller | None
, default: None
) \u2013 The unmarshaller for var-positional arguments (*args
).
-
startpos
(int | None
, default: None
) \u2013 The start position of var-positional arguments (*args
). This accounts for the fact that var-positional comes after positional-only.
Source code in src/typelib/binding.py
def __init__(\n self,\n *,\n signature: inspect.Signature,\n binding: BindingT,\n varkwd: unmarshals.AbstractUnmarshaller | None = None,\n varpos: unmarshals.AbstractUnmarshaller | None = None,\n startpos: int | None = None,\n):\n \"\"\"Constructor.\n\n Args:\n signature: The signature for the binding.\n binding: A mapping of parameter names and positions to unmarshallers.\n This accounts for positional, keyword, or positional-or-keyword arguments.\n varkwd: The unmarshaller for var-keyword arguments (`**kwargs`).\n varpos: The unmarshaller for var-positional arguments (`*args`).\n startpos: The start position of var-positional arguments (`*args`).\n This accounts for the fact that var-positional comes after positional-only.\n \"\"\"\n self.signature = signature\n self.binding = binding\n self.varkwd = varkwd\n self.varpos = varpos\n self.startpos = startpos\n
"},{"location":"reference/typelib/binding/#typelib.binding.AbstractBinding.__call__","title":"__call__ abstractmethod
","text":"__call__(args: tuple[Any], kwargs: dict[str, Any]) -> tuple[args, kwargs]\n
Inspect the given args
and kwargs
and unmarshal them.
Parameters:
Source code in src/typelib/binding.py
@abc.abstractmethod\ndef __call__(\n self, args: tuple[tp.Any], kwargs: dict[str, tp.Any]\n) -> tuple[P.args, P.kwargs]:\n \"\"\"Inspect the given `args` and `kwargs` and unmarshal them.\n\n Args:\n args: The positional arguments.\n kwargs: The keyword arguments.\n \"\"\"\n
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine","title":"BoundRoutine dataclass
","text":"BoundRoutine(call: Callable[P, R], binding: AbstractBinding[P])\n
Bases: Generic[P, R]
A type-enforced, bound routine for a callable object.
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine.binding","title":"binding instance-attribute
","text":"binding: AbstractBinding[P]\n
The parameter->type binding.
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine.call","title":"call instance-attribute
","text":"call: Callable[P, R]\n
The callable object.
"},{"location":"reference/typelib/binding/#typelib.binding.BoundRoutine.__call__","title":"__call__","text":"__call__(*args: Any, **kwargs: Any) -> R\n
Binding an input to the parameters of call
,
then call the callable and return the result.
Source code in src/typelib/binding.py
def __call__(self, *args: tp.Any, **kwargs: tp.Any) -> R:\n \"\"\"Binding an input to the parameters of `call`,\n\n then call the callable and return the result.\"\"\"\n bargs, bkwargs = self.binding(args=args, kwargs=kwargs)\n return self.call(*bargs, **bkwargs)\n
"},{"location":"reference/typelib/binding/#typelib.binding.bind","title":"bind","text":"bind(obj: Callable[P, R]) -> BoundRoutine[P, R]\n
Create a type-enforced, bound routine for a callable object.
Note In contrast to typelib.binding.wrap
, this function creates a new, type-enforced BoundRoutine
instance. Rather than masquerading as the given obj
, we encapsulate it in the routine instance, which is more obvious and provides developers with the ability to side-step type enforcement when it is deemed unnecessary, which should be most of the time if your code is strongly typed and statically analyzed.
TL;DR This function returns an object that walks like your duck and quacks like your duck, but doesn't look like your duck.
Parameters:
Source code in src/typelib/binding.py
def bind(obj: tp.Callable[P, R]) -> BoundRoutine[P, R]:\n \"\"\"Create a type-enforced, bound routine for a callable object.\n\n Note:\n In contrast to [`typelib.binding.wrap`][], this function creates a new,\n type-enforced [`BoundRoutine`][typelib.binding.BoundRoutine] instance. Rather than\n masquerading as the given `obj`, we encapsulate it in the routine\n instance, which is more obvious and provides developers with the ability to\n side-step type enforcement when it is deemed unnecessary, which should be\n most of the time if your code is strongly typed and statically analyzed.\n\n Tip: TL;DR\n This function returns an object that walks like your duck and quacks like your duck,\n but doesn't look like your duck.\n\n Args:\n obj: The callable object to bind.\n \"\"\"\n binding: AbstractBinding[P] = _get_binding(obj)\n routine: BoundRoutine[P, R] = BoundRoutine(\n call=obj,\n binding=binding,\n )\n return routine\n
"},{"location":"reference/typelib/binding/#typelib.binding.wrap","title":"wrap","text":"wrap(obj: Callable[P, R]) -> Callable[..., R]\n
Wrap a callable object for runtime type coercion of inputs.
Note If a class is given, we will attempt to wrap the init method.
Warning This is a useful feature. It is also very surprising. By wrapping a callable in this decorator, we end up with implicit behavior that's not obvious to the caller or a fellow developer.
You're encouraged to prefer typelib.binding.bind
for similar functionality, less the implicit nature, especially when a class is given.
TL;DR This function returns an object walks, quacks, and tries to look like your duck.
Parameters:
Source code in src/typelib/binding.py
def wrap(obj: tp.Callable[P, R]) -> tp.Callable[..., R]:\n \"\"\"Wrap a callable object for runtime type coercion of inputs.\n\n Note:\n If a class is given, we will attempt to wrap the init method.\n\n Warning:\n This is a useful feature. It is also very *surprising*. By wrapping a callable\n in this decorator, we end up with *implicit* behavior that's not obvious to the\n caller or a fellow developer.\n\n You're encouraged to prefer [`typelib.binding.bind`][] for similar\n functionality, less the implicit nature, especially when a class is given.\n\n Tip: TL;DR\n This function returns an object walks, quacks, and tries to look like your duck.\n\n Args:\n obj: The callable object to wrap.\n Maybe be a function, a callable class instance, or a class.\n \"\"\"\n\n binding: AbstractBinding[P] = _get_binding(obj)\n\n if inspect.isclass(obj):\n obj.__init__ = wrap(obj.__init__)\n return obj\n\n @functools.wraps(obj) # type: ignore[arg-type]\n def binding_wrapper(*args: tp.Any, __binding=binding, **kwargs: tp.Any) -> R:\n bargs, bkwargs = __binding(args, kwargs)\n return obj(*bargs, **bkwargs)\n\n return binding_wrapper\n
"},{"location":"reference/typelib/codecs/","title":"Codecs","text":""},{"location":"reference/typelib/codecs/#typelib.codecs","title":"codecs","text":"Interfaces for managing type-enforced wire protocols (codecs).
"},{"location":"reference/typelib/codecs/#typelib.codecs.CodecT","title":"CodecT module-attribute
","text":"CodecT = TypeAliasType('CodecT', Codec, type_params=(T))\n
Generic type alias with an upper bound of Codec
.
"},{"location":"reference/typelib/codecs/#typelib.codecs.DecoderT","title":"DecoderT module-attribute
","text":"DecoderT: TypeAlias = Callable[[bytes], MarshalledValueT]\n
Protocol for a wire deserializer.
"},{"location":"reference/typelib/codecs/#typelib.codecs.EncoderT","title":"EncoderT module-attribute
","text":"EncoderT: TypeAlias = Callable[[MarshalledValueT], bytes]\n
Protocol for a wire serializer.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec","title":"Codec dataclass
","text":"Codec(marshal: AbstractMarshaller[T], unmarshal: AbstractUnmarshaller[T], encoder: EncoderT, decoder: DecoderT)\n
Bases: Generic[T]
A standard wire protocol (codec).
This codec enables you to directly encode and decode your data model into your wire protocol.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.decoder","title":"decoder instance-attribute
","text":"decoder: DecoderT\n
The decoder used to deserialize a bytes-like object into a Python data structure for marshalling into T
.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.encoder","title":"encoder instance-attribute
","text":"encoder: EncoderT\n
The encoder used to serialize a marshalled T
into bytes.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.marshal","title":"marshal instance-attribute
","text":"marshal: AbstractMarshaller[T]\n
The marshaller used to convert an instance of T
to a serializable object.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.unmarshal","title":"unmarshal instance-attribute
","text":"unmarshal: AbstractUnmarshaller[T]\n
The unmarshaller used to convert a deserialized object into an instance of T
.
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.decode","title":"decode","text":"decode(value: bytes) -> T\n
Decode an instance of T
from bytes.
We will first decode the data from bytes using the decoder
, then unmarshal the data into an instance of T
using unmarshal
.
Parameters:
-
value
(bytes
) \u2013 The bytes to decode.
Source code in src/typelib/codecs.py
def decode(self, value: bytes) -> T:\n \"\"\"Decode an instance of `T` from bytes.\n\n We will first decode the data from bytes using the\n [`decoder`][typelib.Codec.decoder], then unmarshal the data into an\n instance of `T` using [`unmarshal`][typelib.Codec.unmarshal].\n\n Args:\n value: The bytes to decode.\n \"\"\"\n decoded = self.decoder(value)\n unmarshalled = self.unmarshal(decoded)\n return unmarshalled\n
"},{"location":"reference/typelib/codecs/#typelib.codecs.Codec.encode","title":"encode","text":"encode(value: T) -> bytes\n
Encode an instance of T
to bytes.
We will first marshal the given instance using the marshal
, then encode the marshalled data into bytes using the encoder
.
Parameters:
-
value
(T
) \u2013 The instance to encode.
Source code in src/typelib/codecs.py
def encode(self, value: T) -> bytes:\n \"\"\"Encode an instance of `T` to bytes.\n\n We will first marshal the given instance using the\n [`marshal`][typelib.Codec.marshal], then encode the marshalled data\n into bytes using the [`encoder`][typelib.Codec.encoder].\n\n Args:\n value: The instance to encode.\n \"\"\"\n marshalled = self.marshal(value)\n encoded = self.encoder(marshalled)\n return encoded\n
"},{"location":"reference/typelib/codecs/#typelib.codecs.codec","title":"codec","text":"codec(t: type[T], *, marshaller: AbstractMarshaller[T] | None = None, unmarshaller: AbstractUnmarshaller[T] | None = None, encoder: EncoderT = compat.json.dumps, decoder: DecoderT = compat.json.loads, codec_cls: type[CodecT[T]] | None = None) -> CodecT[T]\n
Factory function for creating a Codec
instance.
Note In the simplest case, all that needs be provided is the first parameter (t
). We will generate a marshaller, unmarshaller and build a codec. However, we provide ample means for customization:
- You can pass in a subclass of
Codec
to codec_cls
. - You may supply custom
marshaller
or unmarshaller
callables - we will generate one using the high-level APIs from marshals
and unmarshals
if not supplied. - The
encoder
and decoder
default to JSON, using either stdlib json
or orjson
if available. You may provide custom encoder
and decoder
callables, the only requirement is they ser/des to/from bytes
.
Tip
If you installed the json
extra when you installed this library, then you have installed orjson
.
Parameters:
-
t
(type[T]
) \u2013 The type to create the interchange protocol for.
-
marshaller
(AbstractMarshaller[T] | None
, default: None
) \u2013 The marshaller used to marshal inputs into the associated type. (optional)
-
unmarshaller
(AbstractUnmarshaller[T] | None
, default: None
) \u2013 The unmarshaller used to unmarshal inputs into the associated type. (optional)
-
encoder
(EncoderT
, default: dumps
) \u2013 The encoder for encoding data for over-the-wire (defaults to JSON).
-
decoder
(DecoderT
, default: loads
) \u2013 The decoder for decoding data from over-the-wire (defaults to JSON).
-
codec_cls
(type[CodecT[T]] | None
, default: None
) \u2013 The codec class definition, if overloading (optional).
Source code in src/typelib/codecs.py
@compat.cache\ndef codec(\n t: type[T],\n *,\n marshaller: marshals.AbstractMarshaller[T] | None = None,\n unmarshaller: unmarshals.AbstractUnmarshaller[T] | None = None,\n encoder: EncoderT = compat.json.dumps,\n decoder: DecoderT = compat.json.loads,\n codec_cls: type[CodecT[T]] | None = None,\n) -> CodecT[T]:\n \"\"\"Factory function for creating a [`Codec`][typelib.Codec] instance.\n\n Note:\n In the simplest case, all that needs be provided is the first parameter (`t`). We will\n generate a marshaller, unmarshaller and build a codec. However, we provide ample\n means for customization:\n\n - You can pass in a subclass of [`Codec`][typelib.codecs.Codec] to `codec_cls`.\n - You may supply custom `marshaller` or `unmarshaller` callables - we will generate\n one using the high-level APIs from [`marshals`][typelib.marshals] and\n [`unmarshals`][typelib.unmarshals] if not supplied.\n - The `encoder` and `decoder` default to JSON, using either\n stdlib [`json`][] or [`orjson`](https://github.com/ijl/orjson){.external}\n if available. You may provide custom `encoder` and `decoder` callables, the only\n requirement is they ser/des to/from `bytes`.\n\n /// tip\n If you installed the `json` extra when you installed this library, then you have\n installed [`orjson`](https://github.com/ijl/orjson){.external}.\n ///\n\n Args:\n t: The type to create the interchange protocol for.\n marshaller: The marshaller used to marshal inputs into the associated type. (optional)\n unmarshaller: The unmarshaller used to unmarshal inputs into the associated type. (optional)\n encoder: The encoder for encoding data for over-the-wire (defaults to JSON).\n decoder: The decoder for decoding data from over-the-wire (defaults to JSON).\n codec_cls: The codec class definition, if overloading (optional).\n\n \"\"\"\n marshal = marshaller or marshals.marshaller(t=t)\n unmarshal = unmarshaller or unmarshals.unmarshaller(t=t)\n cls = codec_cls or Codec\n if inspection.isbytestype(t):\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=lambda v: v, # type: ignore[arg-type,return-value]\n decoder=lambda v: v, # type: ignore[arg-type,return-value]\n )\n return cdc\n cdc = cls(\n marshal=marshal,\n unmarshal=unmarshal,\n encoder=encoder,\n decoder=decoder,\n )\n return cdc\n
"},{"location":"reference/typelib/constants/","title":"Constants","text":""},{"location":"reference/typelib/constants/#typelib.constants","title":"constants","text":"Constants used throughout the library.
"},{"location":"reference/typelib/constants/#typelib.constants.empty","title":"empty","text":"A singleton for signalling no input.
"},{"location":"reference/typelib/ctx/","title":"Ctx","text":""},{"location":"reference/typelib/ctx/#typelib.ctx","title":"ctx","text":"A simple hashmap for working with types in a contextual manner.
"},{"location":"reference/typelib/ctx/#typelib.ctx.TypeContext","title":"TypeContext","text":" Bases: dict[KeyT, ValueT]
, Generic[ValueT]
A key-value mapping which can map between forward references and real types.
"},{"location":"reference/typelib/ctx/#typelib.ctx.TypeContext.__missing__","title":"__missing__","text":"__missing__(key: type | ForwardRef)\n
Hook to handle missing type references.
Allows for sharing lookup results between forward references and real types.
Parameters:
-
key
(type | ForwardRef
) \u2013 The type or reference.
Source code in src/typelib/ctx.py
def __missing__(self, key: type | refs.ForwardRef):\n \"\"\"Hook to handle missing type references.\n\n Allows for sharing lookup results between forward references and real types.\n\n Args:\n key: The type or reference.\n \"\"\"\n # If we missed a ForwardRef, we've already tried this, bail out.\n if isinstance(key, refs.ForwardRef):\n raise KeyError(key)\n\n ref = refs.forwardref(key)\n return self[ref]\n
"},{"location":"reference/typelib/graph/","title":"Graph","text":""},{"location":"reference/typelib/graph/#typelib.graph","title":"graph","text":"Utilities for working with types as graphs.
Typical Usage
>>> import dataclasses\n>>> from typelib import graph\n>>> graph.static_order(dict[str, str])\n[TypeNode(type=<class 'str'>, var=None, cyclic=False), TypeNode(type=dict[str, str], var=None, cyclic=False)]\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> graph.static_order(Class)\n[TypeNode(type=<class 'str'>, var='attr', cyclic=False), TypeNode(type=<class '__main__.Class'>, var=None, cyclic=False)]\n
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode","title":"TypeNode dataclass
","text":"TypeNode(type: Any, var: str | None = None, cyclic: bool = False)\n
A \"node\" in a type graph.
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode.cyclic","title":"cyclic class-attribute
instance-attribute
","text":"cyclic: bool = field(default=False, hash=False, compare=False)\n
Whether this type annotation is cyclic.
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode.type","title":"type instance-attribute
","text":"type: Any\n
The type annotation for this node.
"},{"location":"reference/typelib/graph/#typelib.graph.TypeNode.var","title":"var class-attribute
instance-attribute
","text":"var: str | None = None\n
The variable or parameter name associated to the type annotation for this node.
"},{"location":"reference/typelib/graph/#typelib.graph.get_type_graph","title":"get_type_graph","text":"get_type_graph(t: type) -> TopologicalSorter[TypeNode]\n
Get a directed graph of the type(s) this annotation represents,
Parameters:
-
t
(type
) \u2013 A type annotation.
Returns:
Note A key aspect of building a directed graph of a given type is pre-emptive detection and termination of cycles in the graph. If we detect a cycle, we will wrap the type in a typing.ForwardRef
and mark the TypeNode
instance as cyclic=True
.
Consumers of the graph can \"delay\" the resolution of a forward reference until the graph's static_order()
has been exhausted, at which point they have enough type information to resolve into the real type. (At least one layer down).
Resolution of cyclic/recursive types is always (necessarily) lazy and should only resolve one level deep on each attempt, otherwise we will find ourselves stuck in a closed loop which never terminates (infinite recursion).
Source code in src/typelib/graph.py
def get_type_graph(t: type) -> graphlib.TopologicalSorter[TypeNode]:\n \"\"\"Get a directed graph of the type(s) this annotation represents,\n\n Args:\n t: A type annotation.\n\n Returns:\n [`graphlib.TopologicalSorter`][]\n\n Note:\n A key aspect of building a directed graph of a given type is pre-emptive\n detection and termination of cycles in the graph. If we detect a cycle, we\n will wrap the type in a [`typing.ForwardRef`][] and mark the\n [`TypeNode`][typelib.graph.TypeNode] instance as `cyclic=True`.\n\n Consumers of the graph can \"delay\" the resolution of a forward reference until\n the graph's `static_order()` has been exhausted, at which point they have\n enough type information to resolve into the real type. (At least one layer down).\n\n Resolution of cyclic/recursive types is always (necessarily) lazy and should only\n resolve one level deep on each attempt, otherwise we will find ourselves stuck\n in a closed loop which never terminates (infinite recursion).\n \"\"\"\n if inspection.istypealiastype(t):\n t = t.__value__\n\n graph: graphlib.TopologicalSorter = graphlib.TopologicalSorter()\n root = TypeNode(t)\n stack = collections.deque([root])\n visited = {root.type}\n while stack:\n parent = stack.popleft()\n if inspection.isliteral(parent.type):\n graph.add(parent)\n continue\n\n predecessors = []\n for var, child in _level(parent.type):\n # If no type was provided, there's no reason to do further processing.\n if child in (constants.empty, typing.Any):\n continue\n if inspection.istypealiastype(child):\n child = child.__value__\n\n # Only subscripted generics or non-stdlib types can be cyclic.\n # i.e., we may get `str` or `datetime` any number of times,\n # that's not cyclic, so we can just add it to the graph.\n is_visited = child in visited\n is_subscripted = inspection.issubscriptedgeneric(child)\n is_stdlib = inspection.isstdlibtype(child)\n can_be_cyclic = is_subscripted or is_stdlib is False\n # We detected a cyclic type,\n # wrap in a ForwardRef and don't add it to the stack\n # This will terminate this edge to prevent infinite cycles.\n if is_visited and can_be_cyclic:\n qualname = inspection.qualname(child)\n *rest, refname = qualname.split(\".\", maxsplit=1)\n is_argument = var is not None\n module = \".\".join(rest) or getattr(child, \"__module__\", None)\n if module in (None, \"__main__\") and rest:\n module = rest[0]\n is_class = inspect.isclass(child)\n ref = refs.forwardref(\n refname, is_argument=is_argument, module=module, is_class=is_class\n )\n node = TypeNode(ref, var=var, cyclic=True)\n # Otherwise, add the type to the stack and track that it's been seen.\n else:\n node = TypeNode(type=child, var=var)\n visited.add(node.type)\n stack.append(node)\n # Flag the type as a \"predecessor\" of the parent type.\n # This lets us resolve child types first when we iterate over the graph.\n predecessors.append(node)\n # Add the parent type and its predecessors to the graph.\n graph.add(parent, *predecessors)\n\n return graph\n
"},{"location":"reference/typelib/graph/#typelib.graph.itertypes","title":"itertypes","text":"itertypes(t: type | str | ForwardRef | TypeAliasType) -> Iterable[TypeNode]\n
Iterate over the type-graph represented by t
from edges to root.
Parameters:
-
t
(type | str | ForwardRef | TypeAliasType
) \u2013 The \"root\" type.
Yields:
-
Iterable[TypeNode]
\u2013 TypeNode
Note We will build a graph of types with the given type t
as the root node, then iterate from the outermost leaves back to the root using BFS.
This is computationally expensive, so you are encouraged to use static_order
instead of itertypes
.
Source code in src/typelib/graph.py
def itertypes(\n t: type | str | refs.ForwardRef | compat.TypeAliasType,\n) -> typing.Iterable[TypeNode]:\n \"\"\"Iterate over the type-graph represented by `t` from edges to root.\n\n Args:\n t: The \"root\" type.\n\n Yields:\n [`TypeNode`][typelib.graph.TypeNode]\n\n Note:\n We will build a graph of types with the given type `t` as the root node,\n then iterate from the outermost leaves back to the root using BFS.\n\n This is computationally expensive, so you are encouraged to use\n [`static_order`][typelib.graph.static_order] instead of\n [`itertypes`][typelib.graph.itertypes].\n \"\"\"\n if inspection.istypealiastype(t):\n t = t.__value__\n if isinstance(t, (str, refs.ForwardRef)): # pragma: no cover\n ref = refs.forwardref(t) if isinstance(t, str) else t\n t = refs.evaluate(ref)\n\n graph = get_type_graph(t) # type: ignore[arg-type]\n yield from graph.static_order()\n
"},{"location":"reference/typelib/graph/#typelib.graph.static_order","title":"static_order","text":"static_order(t: type | str | ForwardRef | TypeAliasType) -> Sequence[TypeNode]\n
Get an ordered iterable of types which resolve into the root type provided.
Parameters:
Note The order of types is guaranteed to rank from edges to root. If there are multiple edges, the order of those edges is not guaranteed.
This function is memoized to avoid the cost of re-computing a type annotation multiple times at runtime, which would be wasted effort, as types don't change at runtime.
To avoid memoization, you can make use of itertypes
.
Source code in src/typelib/graph.py
@compat.cache\ndef static_order(\n t: type | str | refs.ForwardRef | compat.TypeAliasType,\n) -> typing.Sequence[TypeNode]:\n \"\"\"Get an ordered iterable of types which resolve into the root type provided.\n\n Args:\n t: The type to extract an ordered stack from.\n\n Note:\n The order of types is guaranteed to rank from edges to root. If there are\n multiple edges, the order of those edges is not guaranteed.\n\n This function is memoized to avoid the cost of re-computing a type annotation\n multiple times at runtime, which would be wasted effort, as types don't change\n at runtime.\n\n To avoid memoization, you can make use of [`itertypes`][typelib.graph.itertypes].\n \"\"\"\n # We want to leverage the cache if possible, hence the recursive call.\n # Shouldn't actually recurse more than once or twice.\n if inspection.istypealiastype(t):\n return static_order(t.__value__)\n if isinstance(t, (str, refs.ForwardRef)):\n ref = refs.forwardref(t) if isinstance(t, str) else t\n t = refs.evaluate(ref)\n return static_order(t)\n\n return [*itertypes(t)]\n
"},{"location":"reference/typelib/serdes/","title":"Serdes","text":""},{"location":"reference/typelib/serdes/#typelib.serdes","title":"serdes","text":"Utilities for type translation, serialization, and deserialization.
Typical Usage
>>> from typelib import serdes\n>>>\n>>> serdes.load(\"1\")\n1\n
>>> import datetime\n>>> from typelib import serdes\n>>>\n>>> serdes.unixtime(datetime.datetime(2020, 1, 1))\n1577854800.0\n>>> serdes.isoformat(datetime.timedelta(hours=1))\n'PT1H'\n
>>> import dataclasses\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> dict(serdes.iteritems(instance))\n{'attr': 'value'}\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.MarshalledValueT","title":"MarshalledValueT module-attribute
","text":"MarshalledValueT: TypeAlias = 'PythonPrimitiveT | dict[PythonPrimitiveT, MarshalledValueT] | list[MarshalledValueT]'\n
Type alias for a Python value which is ready for over-the-wire serialization.
"},{"location":"reference/typelib/serdes/#typelib.serdes.PythonPrimitiveT","title":"PythonPrimitiveT module-attribute
","text":"PythonPrimitiveT: TypeAlias = 'bool | int | float | str | None'\n
Type alias for serializable, non-container Python types.
"},{"location":"reference/typelib/serdes/#typelib.serdes.PythonValueT","title":"PythonValueT module-attribute
","text":"PythonValueT: TypeAlias = 'PythonPrimitiveT | dict[PythonPrimitiveT, PythonValueT] | list[PythonValueT] | tuple[PythonValueT, ...] | set[PythonValueT]'\n
Type alias for any Python builtin type.
"},{"location":"reference/typelib/serdes/#typelib.serdes.dateparse","title":"dateparse","text":"dateparse(val: str, t: type[DateTimeT]) -> DateTimeT\n
Parse a date string into a datetime object.
Examples:
>>> import datetime\n>>> from typelib import serdes\n>>> serdes.dateparse(\"1970-01-01\",t=datetime.datetime)\nDateTime(1970, 1, 1, 0, 0, 0, tzinfo=Timezone('UTC'))\n
Parameters:
Returns:
Raises:
Source code in src/typelib/serdes.py
@compat.lru_cache(maxsize=100_000)\ndef dateparse(val: str, t: type[DateTimeT]) -> DateTimeT:\n \"\"\"Parse a date string into a datetime object.\n\n Examples:\n >>> import datetime\n >>> from typelib import serdes\n >>> serdes.dateparse(\"1970-01-01\",t=datetime.datetime)\n DateTime(1970, 1, 1, 0, 0, 0, tzinfo=Timezone('UTC'))\n\n Args:\n val: The date string to parse.\n t: The target datetime type.\n\n Returns:\n The parsed datetime object.\n\n Raises:\n ValueError:\n If `val` is not a date string or does not resolve to an instance of\n the target datetime type.\n \"\"\"\n try:\n # When `exact=False`, the only two possibilities are DateTime and Duration.\n parsed: pendulum.DateTime | pendulum.Duration = pendulum.parse(val) # type: ignore[assignment]\n normalized = _nomalize_dt(val=val, parsed=parsed, td=t)\n return normalized\n except ValueError:\n if val.isdigit() or val.isdecimal():\n return _normalize_number(numval=float(val), td=t)\n raise\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.decode","title":"decode","text":"decode(val: Any, *, encoding: str = constants.DEFAULT_ENCODING) -> Any\n
Decode a bytes-like object into a str.
Note If a non-bytes-like object is passed, it will be returned unchanged.
Examples:
>>> from typelib import serdes\n>>> serdes.decode(b\"abc\")\n'abc'\n>>> serdes.decode(memoryview(b\"abc\"))\n'abc'\n
Parameters:
Source code in src/typelib/serdes.py
def decode(val: t.Any, *, encoding: str = constants.DEFAULT_ENCODING) -> t.Any:\n \"\"\"Decode a bytes-like object into a str.\n\n Note:\n If a non-bytes-like object is passed, it will be returned unchanged.\n\n Examples:\n >>> from typelib import serdes\n >>> serdes.decode(b\"abc\")\n 'abc'\n >>> serdes.decode(memoryview(b\"abc\"))\n 'abc'\n\n Args:\n val: The object to be decoded.\n encoding: The encoding to use when decoding the object (defaults \"utf8\").\n \"\"\"\n val = val.tobytes() if isinstance(val, memoryview) else val\n if isinstance(val, (bytes, bytearray)):\n decoded = val.decode(encoding)\n return decoded\n return val\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.get_items_iter","title":"get_items_iter","text":"get_items_iter(tp: type) -> Callable[[Any], Iterable[tuple[Any, Any]]]\n
Given a type, return a callable which will produce an iterator over (field, value) pairs.
Examples:
>>> import dataclasses\n>>> from typelib import serdes\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> iteritems = get_items_iter(Class)\n>>> next(iteritems(instance))\n('attr', 'value')\n
Parameters:
Source code in src/typelib/serdes.py
@compat.cache\ndef get_items_iter(tp: type) -> t.Callable[[t.Any], t.Iterable[tuple[t.Any, t.Any]]]:\n \"\"\"Given a type, return a callable which will produce an iterator over (field, value) pairs.\n\n Examples:\n >>> import dataclasses\n >>> from typelib import serdes\n >>>\n >>> @dataclasses.dataclass\n ... class Class:\n ... attr: str\n ...\n >>> instance = Class(attr=\"value\")\n >>> iteritems = get_items_iter(Class)\n >>> next(iteritems(instance))\n ('attr', 'value')\n\n Args:\n tp: The type to create an iterator for.\n \"\"\"\n ismapping, isnamedtuple, isiterable = (\n inspection.ismappingtype(tp),\n inspection.isnamedtuple(tp),\n inspection.isiterabletype(tp),\n )\n if ismapping:\n return _itemscaller\n if isnamedtuple:\n return _namedtupleitems\n if isiterable:\n return enumerate\n return _make_fields_iterator(tp)\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.isoformat","title":"isoformat","text":"isoformat(dt: date | time | timedelta) -> str\n
Format any date/time object into an ISO-8601 string.
Note While the standard library includes isoformat()
methods for datetime.date
, datetime.time
, & datetime.datetime
, they do not include a method for serializing datetime.timedelta
, even though durations are included in the ISO 8601 specification. This function fills that gap.
Examples:
>>> import datetime\n>>> from typelib import serdes\n>>> serdes.isoformat(datetime.date(1970, 1, 1))\n'1970-01-01'\n>>> serdes.isoformat(datetime.time())\n'00:00:00'\n>>> serdes.isoformat(datetime.datetime(1970, 1, 1))\n'1970-01-01T00:00:00'\n>>> serdes.isoformat(datetime.timedelta(hours=1))\n'PT1H'\n
Source code in src/typelib/serdes.py
@compat.lru_cache(maxsize=100_000)\ndef isoformat(dt: datetime.date | datetime.time | datetime.timedelta) -> str:\n \"\"\"Format any date/time object into an ISO-8601 string.\n\n Note:\n While the standard library includes `isoformat()` methods for\n [`datetime.date`][], [`datetime.time`][], &\n [`datetime.datetime`][], they do not include a method for serializing\n [`datetime.timedelta`][], even though durations are included in the\n ISO 8601 specification. This function fills that gap.\n\n Examples:\n >>> import datetime\n >>> from typelib import serdes\n >>> serdes.isoformat(datetime.date(1970, 1, 1))\n '1970-01-01'\n >>> serdes.isoformat(datetime.time())\n '00:00:00'\n >>> serdes.isoformat(datetime.datetime(1970, 1, 1))\n '1970-01-01T00:00:00'\n >>> serdes.isoformat(datetime.timedelta(hours=1))\n 'PT1H'\n \"\"\"\n if isinstance(dt, (datetime.date, datetime.time)):\n return dt.isoformat()\n dur: pendulum.Duration = (\n dt\n if isinstance(dt, pendulum.Duration)\n else pendulum.duration(\n days=dt.days,\n seconds=dt.seconds,\n microseconds=dt.microseconds,\n )\n )\n datepart = \"\".join(\n f\"{p}{s}\"\n for p, s in ((dur.years, \"Y\"), (dur.months, \"M\"), (dur.remaining_days, \"D\"))\n if p\n )\n timepart = \"\".join(\n f\"{p}{s}\"\n for p, s in (\n (dur.hours, \"H\"),\n (dur.minutes, \"M\"),\n (\n f\"{dur.remaining_seconds}.{dur.microseconds:06}\"\n if dur.microseconds\n else dur.remaining_seconds,\n \"S\",\n ),\n )\n if p\n )\n period = f\"P{datepart}T{timepart}\"\n return period\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.iteritems","title":"iteritems","text":"iteritems(val: Any) -> Iterable[tuple[Any, Any]]\n
Iterate over (field, value) pairs for any object.
Examples:
>>> import dataclasses\n>>> from typelib import serdes\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> [*serdes.iteritems(instance)]\n[('attr', 'value')]\n>>> [*serdes.iteritems(\"string\")]\n[(0, 's'), (1, 't'), (2, 'r'), (3, 'i'), (4, 'n'), (5, 'g')]\n>>> [*serdes.iteritems(serdes.iteritems(instance))]\n[('attr', 'value')]\n
Note If the given item is detected to be an iterable of pairs (e.g., [('a', 1), ('b', 2)]
), we will iterate directly over that.
Otherwise, we will create an iterator over (field, value) pairs with the following strategy:
- For mappings ->
((key, value), ...)
- For structured objects (user-defined classes) ->
((field, value), ...)
- For all other iterables ->
((index, value), ...)
.
Parameters:
Source code in src/typelib/serdes.py
def iteritems(val: t.Any) -> t.Iterable[tuple[t.Any, t.Any]]:\n \"\"\"Iterate over (field, value) pairs for any object.\n\n Examples:\n >>> import dataclasses\n >>> from typelib import serdes\n >>>\n >>> @dataclasses.dataclass\n ... class Class:\n ... attr: str\n ...\n >>> instance = Class(attr=\"value\")\n >>> [*serdes.iteritems(instance)]\n [('attr', 'value')]\n >>> [*serdes.iteritems(\"string\")]\n [(0, 's'), (1, 't'), (2, 'r'), (3, 'i'), (4, 'n'), (5, 'g')]\n >>> [*serdes.iteritems(serdes.iteritems(instance))]\n [('attr', 'value')]\n\n Note:\n If the given item is detected to be an iterable of pairs (e.g., `[('a', 1), ('b', 2)]`),\n we will iterate directly over that.\n\n Otherwise, we will create an iterator over (field, value) pairs with the following\n strategy:\n\n - For mappings -> `((key, value), ...)`\n - For structured objects (user-defined classes) -> `((field, value), ...)`\n - For all other iterables -> `((index, value), ...)`.\n\n Args:\n val: The object to iterate over.\n \"\"\"\n # If the given value is an iterable, we will create a `peekable` so we can inspect it.\n # In that case, we want to continue using the peekable, since the act of peeking\n # is destructive to the original iterable in the event it is an iterator.\n is_pairs, it = _is_iterable_of_pairs(val)\n if is_pairs:\n return iter(it)\n\n iterate = get_items_iter(val.__class__)\n return iterate(it)\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.itervalues","title":"itervalues","text":"itervalues(val: Any) -> Iterator[Any]\n
Iterate over the contained values for any object.
Examples:
>>> import dataclasses\n>>> from typelib import serdes\n>>>\n>>> @dataclasses.dataclass\n... class Class:\n... attr: str\n...\n>>> instance = Class(attr=\"value\")\n>>> [*serdes.itervalues(instance)]\n['value']\n
Parameters:
Source code in src/typelib/serdes.py
def itervalues(val: t.Any) -> t.Iterator[t.Any]:\n \"\"\"Iterate over the contained values for any object.\n\n Examples:\n >>> import dataclasses\n >>> from typelib import serdes\n >>>\n >>> @dataclasses.dataclass\n ... class Class:\n ... attr: str\n ...\n >>> instance = Class(attr=\"value\")\n >>> [*serdes.itervalues(instance)]\n ['value']\n\n Args:\n val: The object to iterate over.\n \"\"\"\n iterate = get_items_iter(val.__class__)\n return (v for k, v in iterate(val))\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.load","title":"load","text":"load(val: _T) -> PythonValueT | _T\n
Attempt to decode val
if it is a text-like object.
Otherwise, return val
unchanged.
Examples:
>>> from typelib import serdes\n>>> serdes.load(1)\n1\n>>> serdes.load(\"1\")\n1\n>>> serdes.load(\"1,2\")\n(1, 2)\n>>> serdes.load(b'{\"a\": 1, \"b\": 2}')\n{'a': 1, 'b': 2}\n
Parameters:
-
val
(_T
) \u2013 The value to decode.
Source code in src/typelib/serdes.py
def load(val: _T) -> PythonValueT | _T:\n \"\"\"Attempt to decode `val` if it is a text-like object.\n\n Otherwise, return `val` unchanged.\n\n Examples:\n >>> from typelib import serdes\n >>> serdes.load(1)\n 1\n >>> serdes.load(\"1\")\n 1\n >>> serdes.load(\"1,2\")\n (1, 2)\n >>> serdes.load(b'{\"a\": 1, \"b\": 2}')\n {'a': 1, 'b': 2}\n\n Args:\n val: The value to decode.\n \"\"\"\n return strload(val) if inspection.istexttype(val.__class__) else val # type: ignore[arg-type]\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.strload","title":"strload","text":"strload(val: str | bytes | bytearray | memoryview) -> PythonValueT\n
Attempt to decode a string-like input into a Python value.
Examples:
>>> from typelib import serdes\n>>> serdes.strload(\"1\")\n1\n>>> serdes.strload(\"1,2\")\n(1, 2)\n>>> serdes.strload(b'{\"a\": 1, \"b\": 2}')\n{'a': 1, 'b': 2}\n
Tip This function is memoized and only safe for text-type inputs.
See Also Parameters:
Source code in src/typelib/serdes.py
@compat.lru_cache(maxsize=100_000)\ndef strload(val: str | bytes | bytearray | memoryview) -> PythonValueT:\n \"\"\"Attempt to decode a string-like input into a Python value.\n\n Examples:\n >>> from typelib import serdes\n >>> serdes.strload(\"1\")\n 1\n >>> serdes.strload(\"1,2\")\n (1, 2)\n >>> serdes.strload(b'{\"a\": 1, \"b\": 2}')\n {'a': 1, 'b': 2}\n\n\n Tip:\n This function is memoized and only safe for text-type inputs.\n\n See Also:\n - [`load`][typelib.serdes.load]\n\n Args:\n val: The string-like input to be decoded.\n \"\"\"\n with contextlib.suppress(ValueError):\n return compat.json.loads(val)\n\n decoded = decode(val)\n with contextlib.suppress(ValueError, TypeError, SyntaxError):\n return ast.literal_eval(decoded)\n\n return decoded\n
"},{"location":"reference/typelib/serdes/#typelib.serdes.unixtime","title":"unixtime","text":"unixtime(dt: date | time | timedelta) -> float\n
Convert a date/time object to a unix timestamp.
Note Time is messy. Here is how we've decided to make this work:
datetime.datetime
instances will preserve the current tzinfo (even if naive). datetime.time
instances will default to today, preserving the tzinfo (even if naive). datetime.date
instances will assume UTC. datetime.timedelta
instances be reflected as total seconds since epoch (January 1, 1970).
If you find yourself in a situation where this does not work for you, your best bet is to stop using tz-naive date/time objects. It's always best to keep your time explicit!
Parameters:
Examples:
>>> import datetime\n>>> from typelib import serdes\n>>>\n>>> serdes.unixtime(datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc))\n0.0\n>>> serdes.unixtime(datetime.date(1970, 1, 1))\n0.0\n
Source code in src/typelib/serdes.py
def unixtime(dt: datetime.date | datetime.time | datetime.timedelta) -> float:\n \"\"\"Convert a date/time object to a unix timestamp.\n\n Note:\n Time is messy. Here is how we've decided to make this work:\n\n - `datetime.datetime` instances will preserve the current tzinfo (even if naive).\n - `datetime.time` instances will default to today, preserving the tzinfo (even if naive).\n - `datetime.date` instances will assume UTC.\n - `datetime.timedelta` instances be reflected as total seconds since epoch (January 1, 1970).\n\n If you find yourself in a situation where this does not work for you, your best\n bet is to stop using tz-naive date/time objects. *It's always best to keep your time\n explicit!*\n\n Args:\n dt: The object to be converted.\n\n Examples:\n >>> import datetime\n >>> from typelib import serdes\n >>>\n >>> serdes.unixtime(datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc))\n 0.0\n >>> serdes.unixtime(datetime.date(1970, 1, 1))\n 0.0\n \"\"\"\n if isinstance(dt, datetime.timedelta):\n return dt.total_seconds()\n\n if isinstance(dt, datetime.time):\n dt = datetime.datetime.now(tz=dt.tzinfo).replace(\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n )\n if isinstance(dt, datetime.date) and not isinstance(dt, datetime.datetime):\n dt = datetime.datetime(\n year=dt.year,\n month=dt.month,\n day=dt.day,\n tzinfo=datetime.timezone.utc,\n )\n\n return dt.timestamp()\n
"},{"location":"reference/typelib/marshals/","title":"Index","text":""},{"location":"reference/typelib/marshals/#typelib.marshals","title":"marshals","text":"Support for marshalling Python data structures into primitive equivalents.
Notes \"Marshalling\" your data structure prepares it to be serialized into binary and sent over the wire, but does not serialize it. We keep these stages isolated to ensure maximum flexibility and simplicity.
We ensure that your marshalled data is compatible with Python's built-in json
module. This provides maximum compatibility with most serialization protocols by limiting the output to simple Python builtin types:
bool
int
float
str
None
list
dict
Tip You may use this package directly, but we encourage you to work with our high-level API provided by the top-level typelib
module.
Typical Usage
>>> import dataclasses\n>>> import decimal\n>>> from typelib import marshals\n>>>\n>>> @dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\n... class Struct:\n... key: str\n... number: decimal.Decimal\n...\n>>>\n>>> data = Struct(key=\"some-key\", number=decimal.Decimal(\"1.0\"))\n>>> marshals.marshal(data)\n{'key': 'some-key', 'number': '1.0'}\n>>> marshaller = marshals.marshaller(Struct)\n>>> marshaller(data)\n{'key': 'some-key', 'number': '1.0'}\n
See Also marshal
marshaller
typelib.codec
"},{"location":"reference/typelib/marshals/#typelib.marshals.AbstractMarshaller","title":"AbstractMarshaller","text":"AbstractMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for marshallers.
Marshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic reducing Python objects into their primitive representations for over-the-wire encoding.
Marshallers support contextual serialization, which enables the marshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.DelayedMarshaller","title":"DelayedMarshaller","text":"DelayedMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[T]
Delayed proxy for a given type's marshaller, used when we encounter a typing.ForwardRef
.
Notes This allows us to delay the resolution of the given type reference until call-time, enabling support for cyclic and recursive types.
Source code in src/typelib/marshals/api.py
def __init__(\n self, t: type[T], context: routines.ContextT, *, var: str | None = None\n):\n super().__init__(t, context, var=var)\n self._resolved: routines.AbstractMarshaller[T] | None = None\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.DelayedMarshaller.resolved","title":"resolved property
","text":"resolved: AbstractMarshaller[T]\n
The resolved marshaller.
"},{"location":"reference/typelib/marshals/#typelib.marshals.EnumMarshaller","title":"EnumMarshaller","text":"EnumMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[EnumT]
, Generic[EnumT]
A marshaller that converts an enum.Enum
instance to its assigned value.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.EnumMarshaller.__call__","title":"__call__","text":"__call__(val: EnumT) -> MarshalledValueT\n
Marshal an enum.Enum
instance into a serdes.MarshalledValueT
.
Parameters:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: EnumT) -> serdes.MarshalledValueT:\n \"\"\"Marshal an [`enum.Enum`][] instance into a [`serdes.MarshalledValueT`][].\n\n Args:\n val: The enum instance to marshal.\n \"\"\"\n return val.value\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.FixedTupleMarshaller","title":"FixedTupleMarshaller","text":"FixedTupleMarshaller(t: type[TupleT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[TupleT]
A marshaller for dumping a \"fixed\" tuple to a simple list
.
Values are marshalled according to the value-type in the order they are defined.
See Also Parameters:
-
t
(type[TupleT]
) \u2013 The type to unmarshal from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(\n self, t: type[compat.TupleT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n self.ordered_routines = [self.context[vt] for vt in self.stack]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.FixedTupleMarshaller.__call__","title":"__call__","text":"__call__(val: TupleT) -> MarshalledIterableT\n
Marshal a tuple into a simple list
.
Parameters:
-
val
(TupleT
) \u2013 The tuple to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: compat.TupleT) -> MarshalledIterableT:\n \"\"\"Marshal a tuple into a simple [`list`][].\n\n Args:\n val: The tuple to marshal.\n \"\"\"\n return [\n routine(v)\n for routine, v in zip(self.ordered_routines, serdes.itervalues(val))\n ]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.IterableMarshaller","title":"IterableMarshaller","text":"IterableMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[IterableT]
, Generic[IterableT]
A marshaller for dumping any iterable into a simple list
.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.IterableMarshaller.__call__","title":"__call__","text":"__call__(val: IterableT) -> MarshalledIterableT\n
Marshal an iterable into a simple list
.
Parameters:
-
val
(IterableT
) \u2013 The iterable to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: IterableT) -> MarshalledIterableT:\n \"\"\"Marshal an iterable into a simple [`list`][].\n\n Args:\n val: The iterable to marshal.\n \"\"\"\n return [*val]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.LiteralMarshaller","title":"LiteralMarshaller","text":"LiteralMarshaller(t: type[LiteralT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[LiteralT]
, Generic[LiteralT]
A marshaller that enforces the given value be one of the values in the defined typing.Literal
Parameters:
-
t
(type[LiteralT]
) \u2013 The Literal type to enforce.
-
context
(ContextT
) \u2013 Nested type context (unused).
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[LiteralT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The Literal type to enforce.\n context: Nested type context (unused).\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.values = inspection.args(t)\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.LiteralMarshaller.__call__","title":"__call__","text":"__call__(val: LiteralT) -> MarshalledValueT\n
Enforce the given value is a member of the bound Literal
type.
Parameters:
-
val
(LiteralT
) \u2013 The value to enforce.
Raises:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: LiteralT) -> serdes.MarshalledValueT:\n \"\"\"Enforce the given value is a member of the bound `Literal` type.\n\n Args:\n val: The value to enforce.\n\n Raises:\n ValueError: If `val` is not a member of the bound `Literal` type.\n \"\"\"\n if val in self.values:\n return val # type: ignore[return-value]\n\n raise ValueError(f\"{val!r} is not one of {self.values!r}\")\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.MappingMarshaller","title":"MappingMarshaller","text":"MappingMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[MappingT]
, Generic[MappingT]
A marshaller for dumping any mapping into a simple dict
.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.MappingMarshaller.__call__","title":"__call__","text":"__call__(val: MappingT) -> MarshalledMappingT\n
Marshal a mapping into a simple dict
.
Parameters:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: MappingT) -> MarshalledMappingT:\n \"\"\"Marshal a mapping into a simple [`dict`][].\n\n Args:\n val: The mapping object to marshal.\n \"\"\"\n return {**val}\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.PatternMarshaller","title":"PatternMarshaller","text":"PatternMarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[PatternT]
A marshaller that converts a re.Pattern
to a string.
Parameters:
-
t
(type[T]
) \u2013 The root type of this marshaller.
-
context
(ContextT
) \u2013 The complete type context for this marshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct a marshaller instance.\n\n Args:\n t: The root type of this marshaller.\n context: The complete type context for this marshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.PatternMarshaller.__call__","title":"__call__","text":"__call__(val: PatternT) -> str\n
Marshal a compiled regex pattern into a string.
Parameters:
-
val
(PatternT
) \u2013 The pattern to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: PatternT) -> str:\n \"\"\"Marshal a compiled regex pattern into a string.\n\n Args:\n val: The pattern to marshal.\n \"\"\"\n return val.pattern\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.StructuredTypeMarshaller","title":"StructuredTypeMarshaller","text":"StructuredTypeMarshaller(t: type[_ST], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[_ST]
A marshaller for dumping a structured (user-defined) type to a simple dict
.
See Also StructuredTypeUnmarshaller
Parameters:
-
t
(type[_ST]
) \u2013 The type to unmarshals from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[_ST], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshals from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.fields_by_var = self._fields_by_var()\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.StructuredTypeMarshaller.__call__","title":"__call__","text":"__call__(val: _ST) -> MarshalledMappingT\n
Marshal a structured type into a simple dict
.
Parameters:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: _ST) -> MarshalledMappingT:\n \"\"\"Marshal a structured type into a simple [`dict`][].\n\n Args:\n val: The structured type to marshal.\n \"\"\"\n fields = self.fields_by_var\n return {f: fields[f](v) for f, v in serdes.iteritems(val) if f in fields}\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.SubscriptedIterableMarshaller","title":"SubscriptedIterableMarshaller","text":"SubscriptedIterableMarshaller(t: type[IterableT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[IterableT]
, Generic[IterableT]
A marshaller for dumping a subscripted iterable into a simple list
.
Values are marshalled according to the defined value-type.
See Also SubscriptedIterableUnmarshaller
Parameters:
-
t
(type[IterableT]
) \u2013 The type to unmarshals from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(\n self, t: type[IterableT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshals from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t=t, context=context, var=var)\n # supporting tuple[str, ...]\n (value_t, *_) = inspection.args(t)\n self.values = context[value_t]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.SubscriptedIterableMarshaller.__call__","title":"__call__","text":"__call__(val: IterableT) -> MarshalledIterableT\n
Marshal an iterable into a simple list
.
Parameters:
-
val
(IterableT
) \u2013 The iterable to marshal.
Source code in src/typelib/marshals/routines.py
def __call__(self, val: IterableT) -> MarshalledIterableT:\n \"\"\"Marshal an iterable into a simple [`list`][].\n\n Args:\n val: The iterable to marshal.\n \"\"\"\n # Always decode bytes.\n values = self.values\n return [values(v) for v in serdes.itervalues(val)]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.SubscriptedMappingMarshaller","title":"SubscriptedMappingMarshaller","text":"SubscriptedMappingMarshaller(t: type[MappingT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[MappingT]
, Generic[MappingT]
A marshaller for dumping a subscripted mapping into a simple dict
.
Keys are marshalled according to the defined key-type, values according to the defined value-type.
See Also SubscriptedMappingUnmarshaller
Parameters:
-
t
(type[MappingT]
) \u2013 The type to unmarshals from.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[MappingT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshals from.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n key_t, value_t = inspection.args(t)\n self.keys = context[key_t]\n self.values = context[value_t]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.UnionMarshaller","title":"UnionMarshaller","text":"UnionMarshaller(t: type[UnionT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractMarshaller[UnionT]
, Generic[UnionT]
A marshaller for dumping a given value via one of the types in the defined bound union.
See Also Parameters:
-
t
(type[UnionT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member marshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/marshals/routines.py
def __init__(self, t: type[UnionT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member marshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n self.nullable = inspection.isoptionaltype(t)\n self.ordered_routines = [self.context[typ] for typ in self.stack]\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.UnionMarshaller.__call__","title":"__call__","text":"__call__(val: UnionT) -> MarshalledValueT\n
Marshal a value into the bound UnionT
.
Parameters:
Raises:
Source code in src/typelib/marshals/routines.py
def __call__(self, val: UnionT) -> serdes.MarshalledValueT:\n \"\"\"Marshal a value into the bound `UnionT`.\n\n Args:\n val: The input value to unmarshal.\n\n Raises:\n ValueError: If `val` cannot be marshalled via any member type.\n \"\"\"\n if self.nullable and val is None:\n return val\n\n for routine in self.ordered_routines:\n with contextlib.suppress(\n ValueError, TypeError, SyntaxError, AttributeError\n ):\n unmarshalled = routine(val)\n return unmarshalled\n\n raise ValueError(f\"{val!r} is not one of types {self.stack!r}\")\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.marshal","title":"marshal","text":"marshal(value: Any, *, t: type[T] | ForwardRef | str | None = None) -> MarshalledValueT\n
Marshal value
from typ
into MarshalledValueT
.
Parameters:
-
value
(Any
) \u2013 The value to reduce to a simple, encode-able type.
-
t
(type[T] | ForwardRef | str | None
, default: None
) \u2013 The type to use for building the marshaller (optional). If not provided, we'll default to the type of the input value.
Source code in src/typelib/marshals/api.py
def marshal(\n value: tp.Any, *, t: type[T] | refs.ForwardRef | str | None = None\n) -> serdes.MarshalledValueT:\n \"\"\"Marshal `value` from `typ` into [`MarshalledValueT`][typelib.serdes.MarshalledValueT].\n\n Args:\n value: The value to reduce to a simple, encode-able type.\n t:\n The type to use for building the marshaller (optional).\n If not provided, we'll default to the type of the input value.\n \"\"\"\n typ = value.__class__ if t is None else t\n routine: routines.AbstractMarshaller[T] = marshaller(typ)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/marshals/#typelib.marshals.marshaller","title":"marshaller","text":"marshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractMarshaller[T]\n
Get a marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate a marshaller for. Can be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/marshals/api.py
@compat.cache\ndef marshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractMarshaller[T]:\n \"\"\"Get a marshaller routine for a given type.\n\n Args:\n t:\n The type annotation to generate a marshaller for. Can be a type, type alias,\n [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractMarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpMarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"},{"location":"reference/typelib/py/","title":"Index","text":""},{"location":"reference/typelib/py/#typelib.py","title":"py","text":"Components for Python compatibility, introspection, and reflection.
Notes The functionality here enables us to support multiple versions of Python to maintain forwards- and backwards-compatibility. This enables developers to leverage the bleeding edge of Python typing as new features and innovations are introduced, without having to upgrade to a new version of the language.
Think of this package as a polyfill for Python typing.
"},{"location":"reference/typelib/py/classes/","title":"Classes","text":""},{"location":"reference/typelib/py/classes/#typelib.py.classes","title":"classes","text":"Vendored class decorators for dataclasses.
Notes This module is unnecessary for Python versions >= 3.10.
Typical Usage >>> import dataclasses\n>>> from typelib.py import classes\n>>>\n>>> @classes.slotted\n>>> @dataclasses.dataclass\n>>> class Slotted:\n... attr: str\n...\n>>> Slotted.__slots__\n('attr',)\n
"},{"location":"reference/typelib/py/classes/#typelib.py.classes.slotted","title":"slotted","text":"slotted(_cls: _ClsT | None = None, *, dict: bool = False, weakref: bool = True) -> Callable[[_ClsT], _ClsT] | _ClsT\n
Decorator to create a \"slotted\" version of the provided class.
Parameters:
Warning This function returns new class object as it's not possible to add __slots__
after class creation.
See Also Source code in src/typelib/py/classes.py
def slotted( # noqa: C901\n _cls: _ClsT | None = None,\n *,\n dict: bool = False,\n weakref: bool = True,\n) -> Callable[[_ClsT], _ClsT] | _ClsT:\n \"\"\"Decorator to create a \"slotted\" version of the provided class.\n\n Args:\n _cls: The class to decorate.\n dict: Whether to add a slot for `__dict__`.\n weakref: Whether to add a slot for `__weakref__`.\n\n Warning:\n This function returns new class object as it's not possible to add `__slots__`\n after class creation.\n\n See Also:\n - [dataslots](https://github.com/starhel/dataslots/blob/master/src/dataslots/__init__.py)\n \"\"\"\n\n def _slots_setstate(self, state):\n for param_dict in filter(None, state):\n for slot, value in param_dict.items():\n object.__setattr__(self, slot, value)\n\n def wrap(cls):\n key = repr(cls)\n if key in _stack: # pragma: no cover\n raise TypeError(\n f\"{cls!r} uses a custom metaclass {cls.__class__!r} \"\n \"which is not compatible with automatic slots. \"\n \"See Issue !typical#104 on GitHub for more information.\"\n ) from None\n\n _stack.add(key)\n\n if (\n sys.version_info >= (3, 10) and constants.PKG_NAME not in cls.__module__\n ): # pragma: no cover\n warnings.warn(\n f\"You are using Python {sys.version}. \"\n \"Python 3.10 introduced native support for slotted dataclasses. \"\n \"This is the preferred method for adding slots.\",\n stacklevel=2,\n )\n\n cls_dict = {**cls.__dict__}\n # Create only missing slots\n inherited_slots = set().union(*(getattr(c, \"__slots__\", ()) for c in cls.mro()))\n\n field_names = {f.name: ... for f in dataclasses.fields(cls) if f.name}\n if dict:\n field_names[\"__dict__\"] = ...\n if weakref:\n field_names[\"__weakref__\"] = ...\n cls_dict[\"__slots__\"] = (*(f for f in field_names if f not in inherited_slots),)\n\n # Erase filed names from class __dict__\n for f in field_names:\n cls_dict.pop(f, None)\n\n # Erase __dict__ and __weakref__\n cls_dict.pop(\"__dict__\", None)\n cls_dict.pop(\"__weakref__\", None)\n\n # Pickle fix for frozen dataclass as mentioned in https://bugs.python.org/issue36424\n # Use only if __getstate__ and __setstate__ are not declared and frozen=True\n if (\n all(param not in cls_dict for param in [\"__getstate__\", \"__setstate__\"])\n and cls.__dataclass_params__.frozen\n ):\n cls_dict[\"__setstate__\"] = _slots_setstate\n\n # Prepare new class with slots\n new_cls = cls.__class__(cls.__name__, cls.__bases__, cls_dict)\n new_cls.__qualname__ = cls.__qualname__\n new_cls.__module__ = cls.__module__\n\n _stack.clear()\n return new_cls\n\n return wrap if _cls is None else wrap(_cls)\n
"},{"location":"reference/typelib/py/compat/","title":"Compat","text":""},{"location":"reference/typelib/py/compat/#typelib.py.compat","title":"compat","text":""},{"location":"reference/typelib/py/contrib/","title":"Contrib","text":""},{"location":"reference/typelib/py/contrib/#typelib.py.contrib","title":"contrib","text":""},{"location":"reference/typelib/py/frames/","title":"Frames","text":""},{"location":"reference/typelib/py/frames/#typelib.py.frames","title":"frames","text":"Utilities for working with stack traces and frames.
Typical Usage
>>> import inspect\n>>> from typelib.py import frames\n>>> var = 1\n>>> frames.extract(\"var\")\n1\n>>> current_frame = inspect.currentframe()\n>>> frames.getcaller() == current_frame\nTrue\n
"},{"location":"reference/typelib/py/frames/#typelib.py.frames.extract","title":"extract","text":"extract(name: str, *, frame: FrameType = None) -> Any | None\n
Extract name
from the stacktrace of frame
.
If frame
is not provided, this function will use the current frame.
Parameters:
Source code in src/typelib/py/frames.py
def extract(name: str, *, frame: types.FrameType = None) -> Any | None:\n \"\"\"Extract `name` from the stacktrace of `frame`.\n\n If `frame` is not provided, this function will use the current frame.\n\n Args:\n name: The name of the object to extract from the stacktrace.\n frame: The [`types.FrameType`][] instance to start from (optional).\n \"\"\"\n frame = frame or inspect.currentframe()\n seen: set[types.FrameType] = set()\n add = seen.add\n while frame and frame not in seen:\n if name in frame.f_globals:\n return frame.f_globals[name]\n if name in frame.f_locals:\n return frame.f_locals[name]\n add(frame)\n frame = frame.f_back\n\n return None\n
"},{"location":"reference/typelib/py/frames/#typelib.py.frames.getcaller","title":"getcaller","text":"getcaller(frame: FrameType = None) -> FrameType\n
Get the caller of the current scope, excluding this library.
If frame
is not provided, this function will use the current frame.
Parameters:
Source code in src/typelib/py/frames.py
def getcaller(frame: types.FrameType = None) -> types.FrameType:\n \"\"\"Get the caller of the current scope, excluding this library.\n\n If `frame` is not provided, this function will use the current frame.\n\n Args:\n frame: The [`types.FrameType`][] instance to start from (optional).\n \"\"\"\n\n frame = frame or inspect.currentframe()\n while frame.f_back:\n frame = frame.f_back\n module = inspect.getmodule(frame)\n if module and module.__name__.startswith(PKG_NAME):\n continue\n\n code = frame.f_code\n if getattr(code, \"co_qualname\", \"\").startswith(PKG_NAME):\n continue\n if PKG_NAME in code.co_filename:\n continue\n return frame\n\n return frame\n
"},{"location":"reference/typelib/py/future/","title":"Future","text":""},{"location":"reference/typelib/py/future/#typelib.py.future","title":"future","text":"Utilities for maintaining runtime compatibility with emerging type annotation operations.
Notes This module's functionality is unnecessary for Python versions >= 3.10
Typical Usage
>>> from typelib.py import future\n>>> future.transform_annotation(\"str | int\")\n'typing.Union[str, int]'\n>>> future.transform_annotation(\"dict[str, int]\")\n'typing.Dict[str, int]'\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation","title":"TransformAnnotation","text":"TransformAnnotation(union: str = 'typing.Union')\n
Bases: NodeTransformer
A ast.NodeTransformer
that transforms typing.Union
.
Source code in src/typelib/py/future.py
def __init__(self, union: str = \"typing.Union\") -> None:\n self.union = union\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_BinOp","title":"visit_BinOp","text":"visit_BinOp(node: BinOp)\n
Transform a ast.BinOp
to typing.Union
.
Source code in src/typelib/py/future.py
def visit_BinOp(self, node: ast.BinOp):\n \"\"\"Transform a [`ast.BinOp`][] to [`typing.Union`][].\"\"\"\n # Ignore anything but a bitwise OR `|`\n if not isinstance(node.op, ast.BitOr):\n return node\n # Build a stack of args to the bitor\n args = collections.deque([node.right])\n left = node.left\n while isinstance(left, ast.BinOp):\n args.appendleft(left.right)\n left = left.left\n args.appendleft(left)\n # Visit each node in the stack\n elts = [self.visit(n) for n in args]\n # Write the old-style `Union`.\n union = ast.Subscript(\n value=ast.Name(id=self.union, ctx=ast.Load()),\n slice=ast.Index(value=ast.Tuple(elts=elts, ctx=ast.Load())), # type: ignore[call-arg,arg-type]\n ctx=ast.Load(),\n )\n ast.copy_location(union, node)\n ast.fix_missing_locations(union)\n return union\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_Name","title":"visit_Name","text":"visit_Name(node: Name)\n
Transform a builtin ast.Name
to the typing
equivalent.
Source code in src/typelib/py/future.py
def visit_Name(self, node: ast.Name):\n \"\"\"Transform a builtin [`ast.Name`][] to the `typing` equivalent.\"\"\"\n # Re-write new-style builtin generics as old-style typing generics\n if node.id not in _GENERICS:\n return node\n\n new = ast.Name(id=_GENERICS[node.id], ctx=ast.Load())\n ast.copy_location(new, node)\n return new\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_Subscript","title":"visit_Subscript","text":"visit_Subscript(node: Subscript)\n
Transform all subscripts within a ast.Subscript
.
Source code in src/typelib/py/future.py
def visit_Subscript(self, node: ast.Subscript):\n \"\"\"Transform all subscripts within a [`ast.Subscript`][].\"\"\"\n # Scan all subscripts to we transform nested new-style types.\n transformed = self.visit(node.slice)\n new = ast.Subscript(\n value=self.visit(node.value),\n slice=transformed,\n ctx=node.ctx,\n )\n ast.copy_location(new, node)\n ast.fix_missing_locations(new)\n return new\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.TransformAnnotation.visit_Tuple","title":"visit_Tuple","text":"visit_Tuple(node: Tuple)\n
Transform all values within a ast.Tuple
.
Source code in src/typelib/py/future.py
def visit_Tuple(self, node: ast.Tuple):\n \"\"\"Transform all values within a [`ast.Tuple`][].\"\"\"\n # Scan all tuples to ensure we transform nested new-style types.\n transformed = [self.visit(n) for n in node.elts]\n new = ast.Tuple(elts=transformed, ctx=node.ctx)\n ast.copy_location(new, node)\n ast.fix_missing_locations(new)\n return new\n
"},{"location":"reference/typelib/py/future/#typelib.py.future.transform","title":"transform cached
","text":"transform(annotation: str, *, union: str = 'typing.Union') -> str\n
Transform a modern annotations into their typing
equivalent:
types.UnionType
into a typing.Union
(str | int
-> typing.Union[str, int]
) - builtin generics into typing generics (
dict[str, int]
-> typing.Dict[str, int]
)
Parameters:
-
annotation
(str
) \u2013 The annotation to transform, as a string.
-
union
(str
, default: 'typing.Union'
) \u2013 The name of the Union type to subscript (defaults \"typing.Union\"
).
Note While this transformation requires your expression be valid Python syntax, it doesn't make sure the type annotation is valid.
Source code in src/typelib/py/future.py
@functools.cache\ndef transform(annotation: str, *, union: str = \"typing.Union\") -> str:\n \"\"\"Transform a modern annotations into their [`typing`][] equivalent:\n\n - [`types.UnionType`][] into a [`typing.Union`][] (`str | int` -> `typing.Union[str, int]`)\n - builtin generics into typing generics (`dict[str, int]` -> `typing.Dict[str, int]`)\n\n Args:\n annotation: The annotation to transform, as a string.\n union: The name of the Union type to subscript (defaults `\"typing.Union\"`).\n\n Note:\n While this transformation requires your expression be valid Python syntax, it\n doesn't make sure the type annotation is valid.\n \"\"\"\n parsed = ast.parse(annotation, mode=\"eval\")\n transformed = TransformAnnotation(union=union).generic_visit(parsed)\n unparsed = ast.unparse(transformed).strip()\n return unparsed\n
"},{"location":"reference/typelib/py/inspection/","title":"Inspection","text":""},{"location":"reference/typelib/py/inspection/#typelib.py.inspection","title":"inspection","text":"High-performance, Fine-grained runtime type inspections.
Typical Usage
>>> from typelib.py import inspection\n>>> inspection.ismappingtype(dict)\nTrue\n>>> inspection.isfixedtupletype(tuple[int, str])\nTrue\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.args","title":"args","text":"args(annotation: Any) -> Tuple[Any, ...]\n
Get the args supplied to an annotation, normalizing typing.TypeVar
.
Note TypeVar normalization follows this strategy:
-> If the TypeVar is bound\n-----> return the bound type\n-> Else If the TypeVar has constraints\n-----> return a Union of the constraints\n-> Else\n-----> return Any\n
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, TypeVar, Any\n>>> T = TypeVar(\"T\")\n>>> args(Dict)\n()\n>>> args(Dict[str, int])\n(<class 'str'>, <class 'int'>)\n>>> args(Dict[str, T])\n(<class 'str'>, typing.Any)\n
Source code in src/typelib/py/inspection.py
def args(annotation: tp.Any) -> tp.Tuple[tp.Any, ...]:\n \"\"\"Get the args supplied to an annotation, normalizing [`typing.TypeVar`][].\n\n Note:\n TypeVar normalization follows this strategy:\n\n -> If the TypeVar is bound\n -----> return the bound type\n -> Else If the TypeVar has constraints\n -----> return a Union of the constraints\n -> Else\n -----> return Any\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, TypeVar, Any\n >>> T = TypeVar(\"T\")\n >>> args(Dict)\n ()\n >>> args(Dict[str, int])\n (<class 'str'>, <class 'int'>)\n >>> args(Dict[str, T])\n (<class 'str'>, typing.Any)\n \"\"\"\n a = tp.get_args(annotation)\n if not a:\n a = getattr(annotation, \"__args__\", a)\n\n return (*_normalize_typevars(*a),)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.get_type_hints","title":"get_type_hints","text":"get_type_hints(obj: Union[type, Callable], exhaustive: bool = True) -> dict[str, type[Any]]\n
Wrapper for typing.get_type_hints
.
If typing.get_type_hints
raises ([NameError][], [TypeError][])
, we will default to an empty dict.
Parameters:
Source code in src/typelib/py/inspection.py
def get_type_hints(\n obj: tp.Union[type, tp.Callable], exhaustive: bool = True\n) -> dict[str, type[tp.Any]]:\n \"\"\"Wrapper for [`typing.get_type_hints`][].\n\n If [`typing.get_type_hints`][] raises `([NameError][], [TypeError][])`, we will\n default to an empty dict.\n\n Args:\n obj: The object to inspect.\n exhaustive:\n Whether to pull type hints from the signature of the object if\n none can be found via [`typing.get_type_hints`][]. (defaults True)\n \"\"\"\n try:\n hints = tp.get_type_hints(obj)\n except (NameError, TypeError):\n hints = {}\n # KW_ONLY is a special sentinel to denote kw-only params in a dataclass.\n # We don't want to do anything with this hint/field. It's not real.\n hints = {f: t for f, t in hints.items() if t is not compat.KW_ONLY}\n if not hints and exhaustive:\n hints = _hints_from_signature(obj)\n return hints\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isabstract","title":"isabstract","text":"isabstract(o) -> TypeIs[ABC]\n
Test whether the given object is an abstract type.
Examples:
>>> import abc\n>>> import numbers\n
>>>\n>>> isabstract(numbers.Number)\nTrue\n>>>\n>>> class MyABC(abc.ABC): ...\n...\n>>> isabstract(MyABC)\nTrue\n
Source code in src/typelib/py/inspection.py
def isabstract(o) -> compat.TypeIs[abc.ABC]:\n \"\"\"Test whether the given object is an abstract type.\n\n Examples:\n >>> import abc\n >>> import numbers\n\n >>>\n >>> isabstract(numbers.Number)\n True\n >>>\n >>> class MyABC(abc.ABC): ...\n ...\n >>> isabstract(MyABC)\n True\n\n \"\"\"\n return inspect.isabstract(o) or o in _ABCS\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbuiltininstance","title":"isbuiltininstance","text":"isbuiltininstance(o: Any) -> TypeIs[BuiltIntypeT]\n
Test whether an object is an instance of a builtin type.
Examples:
>>> isbuiltininstance(\"\")\nTrue\n
Source code in src/typelib/py/inspection.py
def isbuiltininstance(o: tp.Any) -> compat.TypeIs[BuiltIntypeT]:\n \"\"\"Test whether an object is an instance of a builtin type.\n\n Examples:\n >>> isbuiltininstance(\"\")\n True\n \"\"\"\n return builtins.isinstance(o, BUILTIN_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbuiltinsubtype","title":"isbuiltinsubtype","text":"isbuiltinsubtype(t: type) -> TypeIs[type[BuiltIntypeT]]\n
Check whether the provided type is a subclass of a builtin-type.
Examples:
>>> from typing import NewType, Mapping\n>>> class SuperStr(str): ...\n...\n>>> isbuiltinsubtype(SuperStr)\nTrue\n>>> isbuiltinsubtype(NewType(\"MyStr\", SuperStr))\nTrue\n>>> class Foo: ...\n...\n>>> isbuiltintype(Foo)\nFalse\n>>> isbuiltintype(Mapping)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isbuiltinsubtype(t: type) -> compat.TypeIs[type[BuiltIntypeT]]:\n \"\"\"Check whether the provided type is a subclass of a builtin-type.\n\n Examples:\n >>> from typing import NewType, Mapping\n >>> class SuperStr(str): ...\n ...\n >>> isbuiltinsubtype(SuperStr)\n True\n >>> isbuiltinsubtype(NewType(\"MyStr\", SuperStr))\n True\n >>> class Foo: ...\n ...\n >>> isbuiltintype(Foo)\n False\n >>> isbuiltintype(Mapping)\n False\n \"\"\"\n return issubclass(resolve_supertype(t), BUILTIN_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbuiltintype","title":"isbuiltintype","text":"isbuiltintype(obj: type | FunctionType) -> TypeIs[type[BuiltIntypeT]]\n
Check whether the provided object is a builtin-type.
Note Python stdlib and Python documentation have no \"definitive list\" of builtin-types, despite the fact that they are well-known. The closest we have is https://docs.python.org/3.7/library/functions.html, which clumps the builtin-types with builtin-functions. Despite clumping these types with functions in the documentation, these types eval as False when compared to types.BuiltinFunctionType
, which is meant to be an alias for the builtin-functions listed in the documentation.
All this to say, here we are with a custom check to determine whether a type is a builtin.
Examples:
>>> from typing import NewType, Mapping\n>>> isbuiltintype(str)\nTrue\n>>> isbuiltintype(NewType(\"MyStr\", str))\nTrue\n>>> class Foo: ...\n...\n>>> isbuiltintype(Foo)\nFalse\n>>> isbuiltintype(Mapping)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isbuiltintype(\n obj: type | types.FunctionType,\n) -> compat.TypeIs[type[BuiltIntypeT]]:\n \"\"\"Check whether the provided object is a builtin-type.\n\n Note:\n Python stdlib and Python documentation have no \"definitive list\" of\n builtin-**types**, despite the fact that they are well-known. The closest we have\n is https://docs.python.org/3.7/library/functions.html, which clumps the\n builtin-types with builtin-functions. Despite clumping these types with functions\n in the documentation, these types eval as False when compared to\n [`types.BuiltinFunctionType`][], which is meant to be an alias for the\n builtin-functions listed in the documentation.\n\n All this to say, here we are with a custom check to determine whether a type is a\n builtin.\n\n Examples:\n >>> from typing import NewType, Mapping\n >>> isbuiltintype(str)\n True\n >>> isbuiltintype(NewType(\"MyStr\", str))\n True\n >>> class Foo: ...\n ...\n >>> isbuiltintype(Foo)\n False\n >>> isbuiltintype(Mapping)\n False\n \"\"\"\n return (\n resolve_supertype(obj) in BUILTIN_TYPES\n or resolve_supertype(type(obj)) in BUILTIN_TYPES\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isbytestype","title":"isbytestype","text":"isbytestype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]\n
Test whether the given type is a subclass of text or bytes.
Examples:
>>> class MyStr(str): ...\n...\n>>> istexttype(MyStr)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isbytestype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:\n \"\"\"Test whether the given type is a subclass of text or bytes.\n\n Examples:\n >>> class MyStr(str): ...\n ...\n >>> istexttype(MyStr)\n True\n \"\"\"\n return _safe_issubclass(t, (bytes, bytearray, memoryview))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.iscallable","title":"iscallable","text":"iscallable(t: Any) -> TypeIs[Callable]\n
Test whether the given type is a callable.
Examples:
>>> import typing\n>>> import collections.abc\n>>> iscallable(lambda: None)\nTrue\n>>> iscallable(typing.Callable)\nTrue\n>>> iscallable(collections.abc.Callable)\nTrue\n>>> iscallable(1)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache # type: ignore[arg-type]\ndef iscallable(t: tp.Any) -> compat.TypeIs[tp.Callable]:\n \"\"\"Test whether the given type is a callable.\n\n Examples:\n >>> import typing\n >>> import collections.abc\n >>> iscallable(lambda: None)\n True\n >>> iscallable(typing.Callable)\n True\n >>> iscallable(collections.abc.Callable)\n True\n >>> iscallable(1)\n False\n \"\"\"\n return inspect.isroutine(t) or t is tp.Callable or _safe_issubclass(t, abc_Callable) # type: ignore[arg-type]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isclassvartype","title":"isclassvartype","text":"isclassvartype(obj: type) -> bool\n
Test whether an annotation is a ClassVar annotation.
Examples:
>>> from typing import ClassVar, NewType\n>>> isclassvartype(ClassVar[str])\nTrue\n>>> isclassvartype(NewType(\"Foo\", ClassVar[str]))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isclassvartype(obj: type) -> bool:\n \"\"\"Test whether an annotation is a ClassVar annotation.\n\n Examples:\n >>> from typing import ClassVar, NewType\n >>> isclassvartype(ClassVar[str])\n True\n >>> isclassvartype(NewType(\"Foo\", ClassVar[str]))\n True\n \"\"\"\n obj = resolve_supertype(obj)\n return getattr(obj, \"__origin__\", obj) is tp.ClassVar\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.iscollectiontype","title":"iscollectiontype","text":"iscollectiontype(obj: type) -> TypeIs[type[Collection]]\n
Test whether this annotation is a subclass of typing.Collection
.
Includes builtins.
Examples:
>>> from typing import Collection, Mapping, NewType\n>>> iscollectiontype(Collection)\nTrue\n>>> iscollectiontype(Mapping[str, str])\nTrue\n>>> iscollectiontype(str)\nTrue\n>>> iscollectiontype(list)\nTrue\n>>> iscollectiontype(NewType(\"Foo\", dict))\nTrue\n>>> iscollectiontype(int)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef iscollectiontype(obj: type) -> compat.TypeIs[type[tp.Collection]]:\n \"\"\"Test whether this annotation is a subclass of [`typing.Collection`][].\n\n Includes builtins.\n\n Examples:\n >>> from typing import Collection, Mapping, NewType\n >>> iscollectiontype(Collection)\n True\n >>> iscollectiontype(Mapping[str, str])\n True\n >>> iscollectiontype(str)\n True\n >>> iscollectiontype(list)\n True\n >>> iscollectiontype(NewType(\"Foo\", dict))\n True\n >>> iscollectiontype(int)\n False\n \"\"\"\n obj = origin(obj)\n return obj in _COLLECTIONS or builtins.issubclass(obj, tp.Collection)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdatetimetype","title":"isdatetimetype","text":"isdatetimetype(obj: type) -> TypeIs[type[Union[datetime, date]]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> isdatetype(datetime.datetime)\nTrue\n>>> isdatetype(datetime.date)\nTrue\n>>> isdatetype(NewType(\"Foo\", datetime.datetime))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isdatetimetype(\n obj: type,\n) -> compat.TypeIs[type[tp.Union[datetime.datetime, datetime.date]]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> isdatetype(datetime.datetime)\n True\n >>> isdatetype(datetime.date)\n True\n >>> isdatetype(NewType(\"Foo\", datetime.datetime))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.datetime)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdatetype","title":"isdatetype","text":"isdatetype(obj: type) -> TypeIs[type[Union[datetime, date]]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> isdatetype(datetime.datetime)\nTrue\n>>> isdatetype(datetime.date)\nTrue\n>>> isdatetype(NewType(\"Foo\", datetime.datetime))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isdatetype(\n obj: type,\n) -> compat.TypeIs[type[tp.Union[datetime.datetime, datetime.date]]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> isdatetype(datetime.datetime)\n True\n >>> isdatetype(datetime.date)\n True\n >>> isdatetype(NewType(\"Foo\", datetime.datetime))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.date)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdecimaltype","title":"isdecimaltype","text":"isdecimaltype(obj: type) -> TypeIs[type[Decimal]]\n
Test whether this annotation is a Decimal object.
Examples:
>>> import decimal\n>>> from typing import NewType\n>>> isdecimaltype(decimal.Decimal)\nTrue\n>>> isdecimaltype(NewType(\"Foo\", decimal.Decimal))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isdecimaltype(obj: type) -> compat.TypeIs[type[decimal.Decimal]]:\n \"\"\"Test whether this annotation is a Decimal object.\n\n Examples:\n >>> import decimal\n >>> from typing import NewType\n >>> isdecimaltype(decimal.Decimal)\n True\n >>> isdecimaltype(NewType(\"Foo\", decimal.Decimal))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), decimal.Decimal)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isdescriptor","title":"isdescriptor","text":"isdescriptor(obj) -> TypeIs[DescriptorT]\n
Test whether the given object is a types.GetSetDescriptorType
Examples:
>>> class StringDescriptor:\n... __slots__ = (\"value\",)\n...\n... def __init__(self, default: str = \"value\"):\n... self.value = default\n...\n... def __get__(self, instance: Any, value: str) -> str:\n... return self.value\n...\n>>> isdescriptor(StringDescriptor)\nTrue\n
Source code in src/typelib/py/inspection.py
def isdescriptor(obj) -> compat.TypeIs[DescriptorT]:\n \"\"\"Test whether the given object is a [`types.GetSetDescriptorType`][]\n\n Examples:\n >>> class StringDescriptor:\n ... __slots__ = (\"value\",)\n ...\n ... def __init__(self, default: str = \"value\"):\n ... self.value = default\n ...\n ... def __get__(self, instance: Any, value: str) -> str:\n ... return self.value\n ...\n >>> isdescriptor(StringDescriptor)\n True\n \"\"\"\n intersection = {*dir(obj)} & _DESCRIPTOR_METHODS\n return bool(intersection)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isenumtype","title":"isenumtype","text":"isenumtype(obj: type) -> TypeIs[type[Enum]]\n
Test whether this annotation is a subclass of enum.Enum
Examples:
>>> import enum\n>>>\n>>> class FooNum(enum.Enum): ...\n...\n>>> isenumtype(FooNum)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isenumtype(obj: type) -> compat.TypeIs[type[enum.Enum]]:\n \"\"\"Test whether this annotation is a subclass of [`enum.Enum`][]\n\n Examples:\n >>> import enum\n >>>\n >>> class FooNum(enum.Enum): ...\n ...\n >>> isenumtype(FooNum)\n True\n \"\"\"\n return _safe_issubclass(obj, enum.Enum)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfinal","title":"isfinal","text":"isfinal(obj: type) -> bool\n
Test whether an annotation is typing.Final
.
Examples:
>>> from typing import NewType\n>>> from typelib.py.compat import Final\n>>> isfinal(Final[str])\nTrue\n>>> isfinal(NewType(\"Foo\", Final[str]))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfinal(obj: type) -> bool:\n \"\"\"Test whether an annotation is [`typing.Final`][].\n\n Examples:\n >>> from typing import NewType\n >>> from typelib.py.compat import Final\n >>> isfinal(Final[str])\n True\n >>> isfinal(NewType(\"Foo\", Final[str]))\n True\n \"\"\"\n return origin(obj) is compat.Final\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfixedtupletype","title":"isfixedtupletype","text":"isfixedtupletype(obj: type) -> TypeIs[type[tuple]]\n
Check whether an object is a \"fixed\" tuple, e.g., tuple[int, int]
.
Examples:
>>> from typing import Tuple\n>>>\n>>>\n>>> isfixedtupletype(Tuple[str, int])\nTrue\n>>> isfixedtupletype(Tuple[str, ...])\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfixedtupletype(obj: type) -> compat.TypeIs[type[tuple]]:\n \"\"\"Check whether an object is a \"fixed\" tuple, e.g., `tuple[int, int]`.\n\n Examples:\n >>> from typing import Tuple\n >>>\n >>>\n >>> isfixedtupletype(Tuple[str, int])\n True\n >>> isfixedtupletype(Tuple[str, ...])\n False\n \"\"\"\n a = args(obj)\n origin = tp.get_origin(obj)\n if not a or a[-1] is ...:\n return False\n return _safe_issubclass(origin, tuple)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfloattype","title":"isfloattype","text":"isfloattype(t: type[Any]) -> TypeIs[type[float]]\n
Test whether t
is a subclass of the numbers.Number
protocol.
Examples:
>>> import decimal\n
>>> isnumbertype(int)\nFalse\n>>> isnumbertype(float)\nTrue\n>>> isnumbertype(decimal.Decimal)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfloattype(t: type[tp.Any]) -> compat.TypeIs[type[float]]:\n \"\"\"Test whether `t` is a subclass of the [`numbers.Number`][] protocol.\n\n Examples:\n >>> import decimal\n\n >>> isnumbertype(int)\n False\n >>> isnumbertype(float)\n True\n >>> isnumbertype(decimal.Decimal)\n False\n \"\"\"\n return _safe_issubclass(t, float)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isforwardref","title":"isforwardref","text":"isforwardref(obj: Any) -> TypeIs[ForwardRef]\n
Tests whether the given object is a typing.ForwardRef
.
Source code in src/typelib/py/inspection.py
def isforwardref(obj: tp.Any) -> compat.TypeIs[refs.ForwardRef]:\n \"\"\"Tests whether the given object is a [`typing.ForwardRef`][].\"\"\"\n return obj.__class__ is refs.ForwardRef\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfractiontype","title":"isfractiontype","text":"isfractiontype(obj: type) -> TypeIs[type[Fraction]]\n
Test whether this annotation is a Decimal object.
Examples:
>>> import fractions\n>>> from typing import NewType\n>>> isdecimaltype(fractions.Fraction)\nTrue\n>>> isdecimaltype(NewType(\"Foo\", fractions.Fraction))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfractiontype(obj: type) -> compat.TypeIs[type[fractions.Fraction]]:\n \"\"\"Test whether this annotation is a Decimal object.\n\n Examples:\n >>> import fractions\n >>> from typing import NewType\n >>> isdecimaltype(fractions.Fraction)\n True\n >>> isdecimaltype(NewType(\"Foo\", fractions.Fraction))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), fractions.Fraction)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfromdictclass","title":"isfromdictclass","text":"isfromdictclass(obj: type) -> TypeIs[type[_FromDict]]\n
Test whether this annotation is a class with a from_dict()
method.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfromdictclass(obj: type) -> compat.TypeIs[type[_FromDict]]:\n \"\"\"Test whether this annotation is a class with a `from_dict()` method.\"\"\"\n return inspect.isclass(obj) and hasattr(obj, \"from_dict\")\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isfrozendataclass","title":"isfrozendataclass","text":"isfrozendataclass(obj: type) -> TypeIs[type[_FrozenDataclass]]\n
Test whether this is a dataclass and whether it's frozen.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isfrozendataclass(obj: type) -> compat.TypeIs[type[_FrozenDataclass]]:\n \"\"\"Test whether this is a dataclass and whether it's frozen.\"\"\"\n return getattr(getattr(obj, \"__dataclass_params__\", None), \"frozen\", False)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isgeneric","title":"isgeneric","text":"isgeneric(t: Any) -> bool\n
Test whether the given type is a typing generic.
Examples:
>>> from typing import Tuple, Generic, TypeVar\n
>>>\n>>> isgeneric(Tuple)\nTrue\n>>> isgeneric(tuple)\nFalse\n>>> T = TypeVar(\"T\")\n>>> class MyGeneric(Generic[T]): ...\n>>> isgeneric(MyGeneric[int])\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isgeneric(t: tp.Any) -> bool:\n \"\"\"Test whether the given type is a typing generic.\n\n Examples:\n >>> from typing import Tuple, Generic, TypeVar\n\n >>>\n >>> isgeneric(Tuple)\n True\n >>> isgeneric(tuple)\n False\n >>> T = TypeVar(\"T\")\n >>> class MyGeneric(Generic[T]): ...\n >>> isgeneric(MyGeneric[int])\n True\n \"\"\"\n strobj = str(t)\n is_generic = (\n strobj.startswith(\"typing.\")\n or strobj.startswith(\"typing_extensions.\")\n or \"[\" in strobj\n or _safe_issubclass(t, tp.Generic) # type: ignore[arg-type]\n )\n return is_generic\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ishashable","title":"ishashable","text":"ishashable(obj: Any) -> TypeIs[Hashable]\n
Check whether an object is hashable.
An order of magnitude faster than isinstance
with typing.Hashable
Examples:
>>> ishashable(str())\nTrue\n>>> ishashable(frozenset())\nTrue\n>>> ishashable(list())\nFalse\n
Source code in src/typelib/py/inspection.py
def ishashable(obj: tp.Any) -> compat.TypeIs[tp.Hashable]:\n \"\"\"Check whether an object is hashable.\n\n An order of magnitude faster than [`isinstance`][] with\n [`typing.Hashable`][]\n\n Examples:\n >>> ishashable(str())\n True\n >>> ishashable(frozenset())\n True\n >>> ishashable(list())\n False\n \"\"\"\n return __hashgetter(obj) is not None\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isintegertype","title":"isintegertype","text":"isintegertype(t: type[Any]) -> TypeIs[type[int]]\n
Test whether t
is a subclass of the numbers.Number
protocol.
Examples:
>>> import decimal\n
>>> isnumbertype(int)\nTrue\n>>> isnumbertype(float)\nFalse\n>>> isnumbertype(decimal.Decimal)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isintegertype(t: type[tp.Any]) -> compat.TypeIs[type[int]]:\n \"\"\"Test whether `t` is a subclass of the [`numbers.Number`][] protocol.\n\n Examples:\n >>> import decimal\n\n >>> isnumbertype(int)\n True\n >>> isnumbertype(float)\n False\n >>> isnumbertype(decimal.Decimal)\n False\n \"\"\"\n return _safe_issubclass(t, int)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isiterabletype","title":"isiterabletype","text":"isiterabletype(obj: type) -> TypeIs[type[Iterable]]\n
Test whether the given type is iterable.
Examples:
>>> from typing import Sequence, Collection\n>>> isiterabletype(Sequence[str])\nTrue\n>>> isiterabletype(Collection)\nTrue\n>>> isiterabletype(str)\nTrue\n>>> isiterabletype(tuple)\nTrue\n>>> isiterabletype(int)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isiterabletype(obj: type) -> compat.TypeIs[type[tp.Iterable]]:\n \"\"\"Test whether the given type is iterable.\n\n Examples:\n >>> from typing import Sequence, Collection\n >>> isiterabletype(Sequence[str])\n True\n >>> isiterabletype(Collection)\n True\n >>> isiterabletype(str)\n True\n >>> isiterabletype(tuple)\n True\n >>> isiterabletype(int)\n False\n \"\"\"\n obj = origin(obj)\n return builtins.issubclass(obj, tp.Iterable)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isiteratortype","title":"isiteratortype","text":"isiteratortype(obj: type) -> TypeIs[type[Iterator]]\n
Check whether the given object is a subclass of an Iterator.
Examples:
>>> def mygen(): yield 1\n...\n>>> isiteratortype(mygen().__class__)\nTrue\n>>> isiteratortype(iter([]).__class__)\nTrue\n>>> isiteratortype(mygen)\nFalse\n>>> isiteratortype(list)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isiteratortype(obj: type) -> compat.TypeIs[type[tp.Iterator]]:\n \"\"\"Check whether the given object is a subclass of an Iterator.\n\n Examples:\n >>> def mygen(): yield 1\n ...\n >>> isiteratortype(mygen().__class__)\n True\n >>> isiteratortype(iter([]).__class__)\n True\n >>> isiteratortype(mygen)\n False\n >>> isiteratortype(list)\n False\n \"\"\"\n obj = origin(obj)\n return builtins.issubclass(obj, tp.Iterator)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isliteral","title":"isliteral","text":"isliteral(obj) -> bool\n
Test whether an annotation is typing.Literal
.
Examples:
>>>\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isliteral(obj) -> bool:\n \"\"\"Test whether an annotation is [`typing.Literal`][].\n\n Examples:\n >>>\n \"\"\"\n return origin(obj) is tp.Literal or (\n obj.__class__ is refs.ForwardRef and obj.__forward_arg__.startswith(\"Literal\")\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ismappingtype","title":"ismappingtype","text":"ismappingtype(obj: type) -> TypeIs[type[Mapping]]\n
Test whether this annotation is a subtype of typing.Mapping
.
Examples:
>>> from typing import Mapping, Dict, DefaultDict, NewType\n>>> ismappingtype(Mapping)\nTrue\n>>> ismappingtype(Dict[str, str])\nTrue\n>>> ismappingtype(DefaultDict)\nTrue\n>>> ismappingtype(dict)\nTrue\n>>> class MyDict(dict): ...\n...\n>>> ismappingtype(MyDict)\nTrue\n>>> class MyMapping(Mapping): ...\n...\n>>> ismappingtype(MyMapping)\nTrue\n>>> ismappingtype(NewType(\"Foo\", dict))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef ismappingtype(obj: type) -> compat.TypeIs[type[tp.Mapping]]:\n \"\"\"Test whether this annotation is a subtype of [`typing.Mapping`][].\n\n Examples:\n >>> from typing import Mapping, Dict, DefaultDict, NewType\n >>> ismappingtype(Mapping)\n True\n >>> ismappingtype(Dict[str, str])\n True\n >>> ismappingtype(DefaultDict)\n True\n >>> ismappingtype(dict)\n True\n >>> class MyDict(dict): ...\n ...\n >>> ismappingtype(MyDict)\n True\n >>> class MyMapping(Mapping): ...\n ...\n >>> ismappingtype(MyMapping)\n True\n >>> ismappingtype(NewType(\"Foo\", dict))\n True\n \"\"\"\n obj = origin(obj)\n return builtins.issubclass(obj, _MAPPING_TYPES) or builtins.issubclass(\n obj, tp.Mapping\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isnamedtuple","title":"isnamedtuple","text":"isnamedtuple(obj: type) -> TypeIs[type[NamedTuple]]\n
Check whether an object is a \"named\" tuple (collections.namedtuple
).
Examples:
>>> from collections import namedtuple\n>>>\n>>> FooTup = namedtuple(\"FooTup\", [\"bar\"])\n>>> isnamedtuple(FooTup)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isnamedtuple(obj: type) -> compat.TypeIs[type[tp.NamedTuple]]:\n \"\"\"Check whether an object is a \"named\" tuple ([`collections.namedtuple`][]).\n\n Examples:\n >>> from collections import namedtuple\n >>>\n >>> FooTup = namedtuple(\"FooTup\", [\"bar\"])\n >>> isnamedtuple(FooTup)\n True\n \"\"\"\n return inspect.isclass(obj) and issubclass(obj, tuple) and hasattr(obj, \"_fields\")\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isnonetype","title":"isnonetype","text":"isnonetype(t: Any) -> TypeIs[None]\n
Detect if the given type is a types.NoneType
.
Examples:
>>> isnonetype(None)\nTrue\n>>> isnonetype(type(None))\nTrue\n>>> isnonetype(1)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isnonetype(t: tp.Any) -> compat.TypeIs[None]:\n \"\"\"Detect if the given type is a [`types.NoneType`][].\n\n Examples:\n >>> isnonetype(None)\n True\n >>> isnonetype(type(None))\n True\n >>> isnonetype(1)\n False\n \"\"\"\n return t in (None, type(None))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isnumbertype","title":"isnumbertype","text":"isnumbertype(t: type[Any]) -> TypeIs[type[Number]]\n
Test whether t
is a subclass of the numbers.Number
protocol.
Examples:
>>> import decimal\n
>>> isnumbertype(int)\nTrue\n>>> isnumbertype(float)\nTrue\n>>> isnumbertype(decimal.Decimal)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isnumbertype(t: type[tp.Any]) -> compat.TypeIs[type[numbers.Number]]:\n \"\"\"Test whether `t` is a subclass of the [`numbers.Number`][] protocol.\n\n Examples:\n >>> import decimal\n\n >>> isnumbertype(int)\n True\n >>> isnumbertype(float)\n True\n >>> isnumbertype(decimal.Decimal)\n True\n \"\"\"\n return _safe_issubclass(t, numbers.Number)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isoptionaltype","title":"isoptionaltype","text":"isoptionaltype(obj: type[_OT]) -> TypeIs[type[Optional[_OT]]]\n
Test whether an annotation is typing.Optional
, or can be treated as.
typing.Optional
is an alias for typing.Union[<T>, None]
, so both are \"optional\".
Examples:
>>> from typing import Optional, Union, Dict, Literal\n>>> isoptionaltype(Optional[str])\nTrue\n>>> isoptionaltype(Union[str, None])\nTrue\n>>> isoptionaltype(Literal[\"\", None])\nTrue\n>>> isoptionaltype(Dict[str, None])\n
False
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isoptionaltype(obj: type[_OT]) -> compat.TypeIs[type[tp.Optional[_OT]]]:\n \"\"\"Test whether an annotation is [`typing.Optional`][], or can be treated as.\n\n [`typing.Optional`][] is an alias for `typing.Union[<T>, None]`, so both are\n \"optional\".\n\n Examples:\n >>> from typing import Optional, Union, Dict, Literal\n >>> isoptionaltype(Optional[str])\n True\n >>> isoptionaltype(Union[str, None])\n True\n >>> isoptionaltype(Literal[\"\", None])\n True\n >>> isoptionaltype(Dict[str, None])\n False\n \"\"\"\n args = getattr(obj, \"__args__\", ())\n tname = name(origin(obj))\n nullarg = next((a for a in args if a in (type(None), None)), ...)\n isoptional = tname == \"Optional\" or (\n nullarg is not ... and tname in (\"Union\", \"Uniontype\", \"Literal\")\n )\n return isoptional\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ispathtype","title":"ispathtype","text":"ispathtype(t: Any) -> TypeIs[Path]\n
Detect if the given type is a pathlib.Path
.
Examples:
>>> import pathlib\n>>> ispathtype(pathlib.Path.cwd())\nTrue\n>>> ispathtype(\".\")\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef ispathtype(t: tp.Any) -> compat.TypeIs[pathlib.Path]:\n \"\"\"Detect if the given type is a [`pathlib.Path`][].\n\n Examples:\n >>> import pathlib\n >>> ispathtype(pathlib.Path.cwd())\n True\n >>> ispathtype(\".\")\n False\n \"\"\"\n return _safe_issubclass(t, pathlib.PurePath)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.ispatterntype","title":"ispatterntype","text":"ispatterntype(t: Any) -> TypeIs[Pattern]\n
Detect if the given type is a re.Pattern
.
Examples:
>>> import re\n>>> ispatterntype(re.compile(r\"^[a-z]+$\"))\nTrue\n>>> ispatterntype(r\"^[a-z]+$\")\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef ispatterntype(t: tp.Any) -> compat.TypeIs[re.Pattern]:\n \"\"\"Detect if the given type is a [`re.Pattern`][].\n\n Examples:\n >>> import re\n >>> ispatterntype(re.compile(r\"^[a-z]+$\"))\n True\n >>> ispatterntype(r\"^[a-z]+$\")\n False\n \"\"\"\n return _safe_issubclass(t, re.Pattern)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isproperty","title":"isproperty","text":"isproperty(obj) -> TypeIs[DynamicClassAttribute]\n
Test whether the given object is an instance of [property
] or functools.cached_property
.
Examples:
>>> import functools\n
>>> class Foo:\n... @property\n... def prop(self) -> int:\n... return 1\n...\n... @functools.cached_property\n... def cached(self) -> str:\n... return \"foo\"\n...\n>>> isproperty(Foo.prop)\nTrue\n>>> isproperty(Foo.cached)\nTrue\n
Source code in src/typelib/py/inspection.py
def isproperty(obj) -> compat.TypeIs[types.DynamicClassAttribute]:\n \"\"\"Test whether the given object is an instance of [`property`] or [`functools.cached_property`][].\n\n Examples:\n >>> import functools\n\n >>> class Foo:\n ... @property\n ... def prop(self) -> int:\n ... return 1\n ...\n ... @functools.cached_property\n ... def cached(self) -> str:\n ... return \"foo\"\n ...\n >>> isproperty(Foo.prop)\n True\n >>> isproperty(Foo.cached)\n True\n \"\"\"\n\n return builtins.issubclass(obj.__class__, (property, functools.cached_property))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issequencetype","title":"issequencetype","text":"issequencetype(obj: type) -> TypeIs[type[Collection]]\n
Test whether this annotation is a subclass of typing.Collection
.
Includes builtins.
Examples:
>>> from typing import Collection, Mapping, NewType, Sequence\n>>> issequencetype(Sequence)\nTrue\n>>> issequencetype(Mapping[str, str])\nTrue\n>>> issequencetype(str)\nTrue\n>>> issequencetype(list)\nTrue\n>>> issequencetype(NewType(\"Foo\", dict))\nTrue\n>>> issequencetype(int)\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef issequencetype(obj: type) -> compat.TypeIs[type[tp.Collection]]:\n \"\"\"Test whether this annotation is a subclass of [`typing.Collection`][].\n\n Includes builtins.\n\n Examples:\n >>> from typing import Collection, Mapping, NewType, Sequence\n >>> issequencetype(Sequence)\n True\n >>> issequencetype(Mapping[str, str])\n True\n >>> issequencetype(str)\n True\n >>> issequencetype(list)\n True\n >>> issequencetype(NewType(\"Foo\", dict))\n True\n >>> issequencetype(int)\n False\n \"\"\"\n obj = origin(obj)\n return obj in _COLLECTIONS or builtins.issubclass(obj, tp.Sequence)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issimpleattribute","title":"issimpleattribute","text":"issimpleattribute(v) -> bool\n
Test whether the given object is a static value
(e.g., not a function, class, or descriptor).
Examples:
>>> class MyOperator:\n... type = str\n...\n... def operate(self, v) -> type:\n... return self.type(v)\n...\n... @property\n... def default(self) -> type:\n... return self.type()\n...\n>>> issimpleattribute(MyOperator.type)\nFalse\n>>> issimpleattribute(MyOperator.operate)\nFalse\n>>> issimpleattribute(MyOperator.default)\nFalse\n
Source code in src/typelib/py/inspection.py
def issimpleattribute(v) -> bool:\n \"\"\"Test whether the given object is a static value\n\n (e.g., not a function, class, or descriptor).\n\n Examples:\n >>> class MyOperator:\n ... type = str\n ...\n ... def operate(self, v) -> type:\n ... return self.type(v)\n ...\n ... @property\n ... def default(self) -> type:\n ... return self.type()\n ...\n >>> issimpleattribute(MyOperator.type)\n False\n >>> issimpleattribute(MyOperator.operate)\n False\n >>> issimpleattribute(MyOperator.default)\n False\n \"\"\"\n return not any(c(v) for c in _ATTR_CHECKS)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstdlibinstance","title":"isstdlibinstance","text":"isstdlibinstance(o: Any) -> TypeIs[STDLibtypeT]\n
Test whether an object is an instance of a type in the standard-lib.
Source code in src/typelib/py/inspection.py
def isstdlibinstance(o: tp.Any) -> compat.TypeIs[STDLibtypeT]:\n \"\"\"Test whether an object is an instance of a type in the standard-lib.\"\"\"\n return builtins.isinstance(o, STDLIB_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstdlibsubtype","title":"isstdlibsubtype","text":"isstdlibsubtype(t: type) -> TypeIs[type[STDLibtypeT]]\n
Test whether the given type is a subclass of a standard-lib type.
Examples:
>>> import datetime\n
>>> class MyDate(datetime.date): ...\n...\n>>> isstdlibsubtype(MyDate)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isstdlibsubtype(t: type) -> compat.TypeIs[type[STDLibtypeT]]:\n \"\"\"Test whether the given type is a subclass of a standard-lib type.\n\n Examples:\n >>> import datetime\n\n >>> class MyDate(datetime.date): ...\n ...\n >>> isstdlibsubtype(MyDate)\n True\n \"\"\"\n return _safe_issubclass(resolve_supertype(t), STDLIB_TYPES_TUPLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstringtype","title":"isstringtype","text":"isstringtype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]\n
Test whether the given type is a subclass of text or bytes.
Examples:
>>> class MyStr(str): ...\n...\n>>> istexttype(MyStr)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isstringtype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:\n \"\"\"Test whether the given type is a subclass of text or bytes.\n\n Examples:\n >>> class MyStr(str): ...\n ...\n >>> istexttype(MyStr)\n True\n \"\"\"\n return _safe_issubclass(t, str)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isstructuredtype","title":"isstructuredtype","text":"isstructuredtype(t: type[Any]) -> bool\n
Test whether the given type has a fixed set of fields.
Examples:
>>> import dataclasses\n>>> from typing import Tuple, NamedTuple, TypedDict, Union, Literal, Collection\n
>>>\n>>> isstructuredtype(Tuple[str, int])\nTrue\n>>> isstructuredtype(class MyDict(TypedDict): ...)\nTrue\n>>> isstructuredtype(class MyTup(NamedTuple): ...)\nTrue\n>>> isstructuredtype(class MyClass: ...)\nTrue\n>>> isstructuredtype(Union[str, int])\nFalse\n>>> isstructuredtype(Literal[1, 2])\nFalse\n>>> isstructuredtype(tuple)\nFalse\n>>> isstructuredtype(Collection[str])\nFalse\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isstructuredtype(t: type[tp.Any]) -> bool:\n \"\"\"Test whether the given type has a fixed set of fields.\n\n Examples:\n >>> import dataclasses\n >>> from typing import Tuple, NamedTuple, TypedDict, Union, Literal, Collection\n\n >>>\n >>> isstructuredtype(Tuple[str, int])\n True\n >>> isstructuredtype(class MyDict(TypedDict): ...)\n True\n >>> isstructuredtype(class MyTup(NamedTuple): ...)\n True\n >>> isstructuredtype(class MyClass: ...)\n True\n >>> isstructuredtype(Union[str, int])\n False\n >>> isstructuredtype(Literal[1, 2])\n False\n >>> isstructuredtype(tuple)\n False\n >>> isstructuredtype(Collection[str])\n False\n \"\"\"\n return (\n isfixedtupletype(t)\n or isnamedtuple(t)\n or istypeddict(t)\n or (not isstdlibsubtype(origin(t)) and not isuniontype(t) and not isliteral(t))\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issubscriptedcollectiontype","title":"issubscriptedcollectiontype","text":"issubscriptedcollectiontype(obj: type[Generic[_ArgsT]]) -> TypeIs[type[Collection[_ArgsT]]]\n
Test whether this annotation is a collection type and is subscripted.
Examples:
>>> from typing import Collection, Mapping, NewType\n>>> issubscriptedcollectiontype(Collection)\nFalse\n>>> issubscriptedcollectiontype(Mapping[str, str])\nTrue\n>>> issubscriptedcollectiontype(str)\nFalse\n>>> issubscriptedcollectiontype(NewType(\"Foo\", Collection[int]))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef issubscriptedcollectiontype(\n obj: type[tp.Generic[_ArgsT]], # type: ignore[valid-type]\n) -> compat.TypeIs[type[tp.Collection[_ArgsT]]]:\n \"\"\"Test whether this annotation is a collection type and is subscripted.\n\n Examples:\n >>> from typing import Collection, Mapping, NewType\n >>> issubscriptedcollectiontype(Collection)\n False\n >>> issubscriptedcollectiontype(Mapping[str, str])\n True\n >>> issubscriptedcollectiontype(str)\n False\n >>> issubscriptedcollectiontype(NewType(\"Foo\", Collection[int]))\n True\n \"\"\"\n return iscollectiontype(obj) and issubscriptedgeneric(obj)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.issubscriptedgeneric","title":"issubscriptedgeneric","text":"issubscriptedgeneric(t: Any) -> bool\n
Test whether the given type is a typing generic.
Examples:
>>> from typing import Tuple, Generic, TypeVar\n
>>>\n>>> isgeneric(Tuple)\nTrue\n>>> isgeneric(tuple)\nFalse\n>>> T = TypeVar(\"T\")\n>>> class MyGeneric(Generic[T]): ...\n>>> isgeneric(MyGeneric[int])\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef issubscriptedgeneric(t: tp.Any) -> bool:\n \"\"\"Test whether the given type is a typing generic.\n\n Examples:\n >>> from typing import Tuple, Generic, TypeVar\n\n >>>\n >>> isgeneric(Tuple)\n True\n >>> isgeneric(tuple)\n False\n >>> T = TypeVar(\"T\")\n >>> class MyGeneric(Generic[T]): ...\n >>> isgeneric(MyGeneric[int])\n True\n \"\"\"\n strobj = str(t)\n og = tp.get_origin(t) or t\n is_generic = isgeneric(og) or isgeneric(t)\n is_subscripted = \"[\" in strobj\n return is_generic and is_subscripted\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istexttype","title":"istexttype","text":"istexttype(t: type[Any]) -> TypeIs[type[str | bytes | bytearray]]\n
Test whether the given type is a subclass of text or bytes.
Examples:
>>> class MyStr(str): ...\n...\n>>> istexttype(MyStr)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istexttype(t: type[tp.Any]) -> compat.TypeIs[type[str | bytes | bytearray]]:\n \"\"\"Test whether the given type is a subclass of text or bytes.\n\n Examples:\n >>> class MyStr(str): ...\n ...\n >>> istexttype(MyStr)\n True\n \"\"\"\n return _safe_issubclass(t, (str, bytes, bytearray, memoryview))\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istimedeltatype","title":"istimedeltatype","text":"istimedeltatype(obj: type) -> TypeIs[type[timedelta]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> istimedeltatype(datetime.timedelta)\nTrue\n>>> istimedeltatype(NewType(\"Foo\", datetime.timedelta))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istimedeltatype(obj: type) -> compat.TypeIs[type[datetime.timedelta]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> istimedeltatype(datetime.timedelta)\n True\n >>> istimedeltatype(NewType(\"Foo\", datetime.timedelta))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.timedelta)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istimetype","title":"istimetype","text":"istimetype(obj: type) -> TypeIs[type[time]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import datetime\n>>> from typing import NewType\n>>> istimetype(datetime.time)\nTrue\n>>> istimetype(NewType(\"Foo\", datetime.time))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istimetype(obj: type) -> compat.TypeIs[type[datetime.time]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import datetime\n >>> from typing import NewType\n >>> istimetype(datetime.time)\n True\n >>> istimetype(NewType(\"Foo\", datetime.time))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), datetime.time)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istupletype","title":"istupletype","text":"istupletype(obj: Callable[..., Any] | type[Any]) -> TypeIs[type[tuple]]\n
Tests whether the given type is a subclass of tuple
.
Examples:
>>> from typing import NamedTuple, Tuple\n>>> class MyTup(NamedTuple):\n... field: int\n...\n>>> istupletype(tuple)\nTrue\n>>> istupletype(Tuple[str])\nTrue\n>>> istupletype(MyTup)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istupletype(\n obj: tp.Callable[..., tp.Any] | type[tp.Any],\n) -> compat.TypeIs[type[tuple]]:\n \"\"\"Tests whether the given type is a subclass of [`tuple`][].\n\n Examples:\n >>> from typing import NamedTuple, Tuple\n >>> class MyTup(NamedTuple):\n ... field: int\n ...\n >>> istupletype(tuple)\n True\n >>> istupletype(Tuple[str])\n True\n >>> istupletype(MyTup)\n True\n \"\"\"\n obj = origin(obj)\n return obj is tuple or issubclass(obj, tuple) # type: ignore[arg-type]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istypealiastype","title":"istypealiastype","text":"istypealiastype(t: Any) -> TypeIs[TypeAliasType]\n
Detect if the given object is a typing.TypeAliasType
.
Examples:
>>> type IntList = list[int]\n>>> istypealiastype(IntList)\nTrue\n>>> IntList = compat.TypeAliasType(\"IntList\", list[int])\n>>> istypealiastype(IntList)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istypealiastype(t: tp.Any) -> compat.TypeIs[compat.TypeAliasType]:\n \"\"\"Detect if the given object is a [`typing.TypeAliasType`][].\n\n Examples:\n >>> type IntList = list[int]\n >>> istypealiastype(IntList)\n True\n >>> IntList = compat.TypeAliasType(\"IntList\", list[int])\n >>> istypealiastype(IntList)\n True\n\n \"\"\"\n return isinstance(t, compat.TypeAliasType)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istypeddict","title":"istypeddict","text":"istypeddict(obj: Any) -> bool\n
Check whether an object is a typing.TypedDict
.
Examples:
>>> from typing import TypedDict\n>>>\n>>> class FooMap(TypedDict):\n... bar: str\n...\n>>> istypeddict(FooMap)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istypeddict(obj: tp.Any) -> bool:\n \"\"\"Check whether an object is a [`typing.TypedDict`][].\n\n Examples:\n >>> from typing import TypedDict\n >>>\n >>> class FooMap(TypedDict):\n ... bar: str\n ...\n >>> istypeddict(FooMap)\n True\n \"\"\"\n return (\n inspect.isclass(obj)\n and dict in {*inspect.getmro(obj)}\n and hasattr(obj, \"__total__\")\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.istypedtuple","title":"istypedtuple","text":"istypedtuple(obj: type) -> TypeIs[type[NamedTuple]]\n
Check whether an object is a \"typed\" tuple (typing.NamedTuple
).
Examples:
>>> from typing import NamedTuple\n>>>\n>>> class FooTup(NamedTuple):\n... bar: str\n...\n>>> istypedtuple(FooTup)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef istypedtuple(obj: type) -> compat.TypeIs[type[tp.NamedTuple]]:\n \"\"\"Check whether an object is a \"typed\" tuple ([`typing.NamedTuple`][]).\n\n Examples:\n >>> from typing import NamedTuple\n >>>\n >>> class FooTup(NamedTuple):\n ... bar: str\n ...\n >>> istypedtuple(FooTup)\n True\n \"\"\"\n return (\n inspect.isclass(obj)\n and issubclass(obj, tuple)\n and bool(getattr(obj, \"__annotations__\", False))\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isunresolvable","title":"isunresolvable","text":"isunresolvable(t: Any) -> bool\n
Test whether the given type is unresolvable.
Examples:
>>> import typing\n>>> isunresolvable(int)\nFalse\n>>> isunresolvable(typ.Any)\nTrue\n>>> isunresolvable(...)\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isunresolvable(t: tp.Any) -> bool:\n \"\"\"Test whether the given type is unresolvable.\n\n Examples:\n >>> import typing\n >>> isunresolvable(int)\n False\n >>> isunresolvable(typ.Any)\n True\n >>> isunresolvable(...)\n True\n \"\"\"\n return t in _UNRESOLVABLE\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.isuuidtype","title":"isuuidtype","text":"isuuidtype(obj: type) -> TypeIs[type[UUID]]\n
Test whether this annotation is a a date/datetime object.
Examples:
>>> import uuid\n>>> from typing import NewType\n>>> isuuidtype(uuid.UUID)\nTrue\n>>> class MyUUID(uuid.UUID): ...\n...\n>>> isuuidtype(MyUUID)\nTrue\n>>> isuuidtype(NewType(\"Foo\", uuid.UUID))\nTrue\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef isuuidtype(obj: type) -> compat.TypeIs[type[uuid.UUID]]:\n \"\"\"Test whether this annotation is a a date/datetime object.\n\n Examples:\n >>> import uuid\n >>> from typing import NewType\n >>> isuuidtype(uuid.UUID)\n True\n >>> class MyUUID(uuid.UUID): ...\n ...\n >>> isuuidtype(MyUUID)\n True\n >>> isuuidtype(NewType(\"Foo\", uuid.UUID))\n True\n \"\"\"\n return builtins.issubclass(origin(obj), uuid.UUID)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.name","title":"name","text":"name(obj: Union[type, ForwardRef, Callable]) -> str\n
Safely retrieve the name of either a standard object or a type annotation.
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, Any, TypeVar\n>>> T = TypeVar(\"T\")\n>>> name(Dict)\n'Dict'\n>>> name(Dict[str, str])\n'Dict'\n>>> name(Any)\n'Any'\n>>> name(dict)\n'dict'\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef name(obj: tp.Union[type, refs.ForwardRef, tp.Callable]) -> str:\n \"\"\"Safely retrieve the name of either a standard object or a type annotation.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, Any, TypeVar\n >>> T = TypeVar(\"T\")\n >>> name(Dict)\n 'Dict'\n >>> name(Dict[str, str])\n 'Dict'\n >>> name(Any)\n 'Any'\n >>> name(dict)\n 'dict'\n \"\"\"\n strobj = qualname(obj)\n return strobj.rsplit(\".\")[-1]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.normalize_typevar","title":"normalize_typevar","text":"normalize_typevar(tvar: TypeVar) -> type[Any]\n
Reduce a TypeVar to a simple type.
Note TypeVar normalization follows this strategy:
-> If the TypeVar is bound\n-----> return the bound type\n-> Else If the TypeVar has constraints\n-----> return a Union of the constraints\n-> Else\n-----> return Any\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef normalize_typevar(tvar: tp.TypeVar) -> type[tp.Any]:\n \"\"\"Reduce a TypeVar to a simple type.\n\n Note:\n TypeVar normalization follows this strategy:\n\n -> If the TypeVar is bound\n -----> return the bound type\n -> Else If the TypeVar has constraints\n -----> return a Union of the constraints\n -> Else\n -----> return Any\n \"\"\"\n if tvar.__bound__:\n return tvar.__bound__\n elif tvar.__constraints__:\n return tp.Union[tvar.__constraints__] # type: ignore[return-value]\n return tp.Any # type: ignore[return-value]\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.origin","title":"origin","text":"origin(annotation: Any) -> Any\n
Get the highest-order 'origin'-type for a given type definition.
Tip For the purposes of this library, if we can resolve to a builtin type, we will.
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, Mapping, NewType, Optional\n>>> origin(Dict)\n<class 'dict'>\n>>> origin(Mapping)\n<class 'dict'>\n>>> Registry = NewType('Registry', Dict)\n>>> origin(Registry)\n<class 'dict'>\n>>> class Foo: ...\n...\n>>> origin(Foo)\n<class 'typelib.Foo'>\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef origin(annotation: tp.Any) -> tp.Any:\n \"\"\"Get the highest-order 'origin'-type for a given type definition.\n\n Tip:\n For the purposes of this library, if we can resolve to a builtin type, we will.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, Mapping, NewType, Optional\n >>> origin(Dict)\n <class 'dict'>\n >>> origin(Mapping)\n <class 'dict'>\n >>> Registry = NewType('Registry', Dict)\n >>> origin(Registry)\n <class 'dict'>\n >>> class Foo: ...\n ...\n >>> origin(Foo)\n <class 'typelib.Foo'>\n \"\"\"\n # Resolve custom NewTypes.\n actual = resolve_supertype(annotation)\n\n # Unwrap optional/classvar\n if isclassvartype(actual):\n a = args(actual)\n actual = a[0] if a else actual\n\n if istypealiastype(actual):\n actual = actual.__value__\n\n actual = tp.get_origin(actual) or actual\n\n # provide defaults for generics\n if not isbuiltintype(actual):\n actual = _check_generics(actual)\n\n if iscallable(actual):\n actual = tp.Callable\n\n return actual\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.qualname","title":"qualname","text":"qualname(obj: Union[type, ForwardRef, Callable]) -> str\n
Safely retrieve the qualname of either a standard object or a type annotation.
Examples:
>>> from typelib.py import inspection\n>>> from typing import Dict, Any, TypeVar\n>>> T = TypeVar(\"T\")\n>>> qualname(Dict)\n'typing.Dict'\n>>> qualname(Dict[str, str])\n'typing.Dict'\n>>> qualname(Any)\n'typing.Any'\n>>> qualname(dict)\n'dict'\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef qualname(obj: tp.Union[type, refs.ForwardRef, tp.Callable]) -> str:\n \"\"\"Safely retrieve the qualname of either a standard object or a type annotation.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import Dict, Any, TypeVar\n >>> T = TypeVar(\"T\")\n >>> qualname(Dict)\n 'typing.Dict'\n >>> qualname(Dict[str, str])\n 'typing.Dict'\n >>> qualname(Any)\n 'typing.Any'\n >>> qualname(dict)\n 'dict'\n \"\"\"\n strobj = str(obj)\n if isinstance(obj, refs.ForwardRef):\n strobj = str(obj.__forward_arg__) # type: ignore[union-attr]\n is_generic = isgeneric(strobj)\n # We got a typing thing.\n if is_generic:\n # If this is a subscripted generic we should clean that up.\n return strobj.split(\"[\", maxsplit=1)[0]\n # Easy-ish path, use name magix\n qname = getattr(obj, \"__qualname__\", None)\n nm = getattr(obj, \"__name__\", None)\n if qname is not None:\n return qname.replace(\"<locals>.\", \"\")\n if nm is not None: # pragma: no cover\n return nm\n return strobj\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.resolve_supertype","title":"resolve_supertype","text":"resolve_supertype(annotation: type[Any] | FunctionType) -> Any\n
Get the highest-order supertype for a NewType.
Examples:
>>> from typelib.py import inspection\n>>> from typing import NewType\n>>> UserID = NewType(\"UserID\", int)\n>>> AdminID = NewType(\"AdminID\", UserID)\n>>> resolve_supertype(AdminID)\n<class 'int'>\n
Source code in src/typelib/py/inspection.py
@compat.cache\ndef resolve_supertype(annotation: type[tp.Any] | types.FunctionType) -> tp.Any:\n \"\"\"Get the highest-order supertype for a NewType.\n\n Examples:\n >>> from typelib.py import inspection\n >>> from typing import NewType\n >>> UserID = NewType(\"UserID\", int)\n >>> AdminID = NewType(\"AdminID\", UserID)\n >>> resolve_supertype(AdminID)\n <class 'int'>\n \"\"\"\n while hasattr(annotation, \"__supertype__\"):\n annotation = annotation.__supertype__ # type: ignore[union-attr]\n return annotation\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.safe_get_params","title":"safe_get_params","text":"safe_get_params(obj: type) -> Mapping[str, Parameter]\n
Try to extract the parameters of the given object.
Return an empty mapping if we encounter an error.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef safe_get_params(obj: type) -> tp.Mapping[str, inspect.Parameter]:\n \"\"\"Try to extract the parameters of the given object.\n\n Return an empty mapping if we encounter an error.\n \"\"\"\n params: tp.Mapping[str, inspect.Parameter]\n try:\n if ismappingtype(obj) and not istypeddict(obj):\n return {}\n params = cached_signature(obj).parameters\n except (ValueError, TypeError): # pragma: nocover\n params = {}\n return params\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.should_unwrap","title":"should_unwrap","text":"should_unwrap(obj: type) -> bool\n
Test whether we should use the args attr for resolving the type.
This is useful for determining what type to use at run-time for coercion.
Source code in src/typelib/py/inspection.py
@compat.cache\ndef should_unwrap(obj: type) -> bool:\n \"\"\"Test whether we should use the __args__ attr for resolving the type.\n\n This is useful for determining what type to use at run-time for coercion.\n \"\"\"\n return (not isliteral(obj)) and any(x(obj) for x in _UNWRAPPABLE)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.signature","title":"signature","text":"signature(obj: Callable[..., Any] | type[Any]) -> Signature\n
Get the signature of a type or callable.
Also supports TypedDict subclasses
Source code in src/typelib/py/inspection.py
def signature(obj: tp.Callable[..., tp.Any] | type[tp.Any]) -> inspect.Signature:\n \"\"\"Get the signature of a type or callable.\n\n Also supports TypedDict subclasses\n \"\"\"\n if inspect.isclass(obj) or isgeneric(obj):\n if istypeddict(obj):\n return typed_dict_signature(obj)\n if istupletype(obj) and not isnamedtuple(obj):\n return tuple_signature(obj)\n return inspect.signature(obj)\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.simple_attributes","title":"simple_attributes","text":"simple_attributes(t: type) -> Tuple[str, ...]\n
Extract all public, static data-attributes for a given type.
Source code in src/typelib/py/inspection.py
def simple_attributes(t: type) -> tp.Tuple[str, ...]:\n \"\"\"Extract all public, static data-attributes for a given type.\"\"\"\n # If slots are defined, this is the best way to locate static attributes.\n if hasattr(t, \"__slots__\") and t.__slots__:\n return (\n *(\n f\n for f in t.__slots__\n if not f.startswith(\"_\")\n # JIC - check if this is something fancy.\n and not isinstance(getattr(t, f, ...), _DYNAMIC_ATTRIBUTES)\n ),\n )\n # Otherwise we have to guess. This is inherently faulty, as attributes aren't\n # always defined on a class before instantiation. The alternative is reverse\n # engineering the constructor... yikes.\n return (\n *(\n x\n for x, y in inspect.getmembers(t, predicate=issimpleattribute)\n if not x.startswith(\"_\") and not isinstance(y, _DYNAMIC_ATTRIBUTES)\n ),\n )\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.tuple_signature","title":"tuple_signature","text":"tuple_signature(t: type[TupleT]) -> Signature\n
A little faker for getting the \"signature\" of a tuple
.
Note At runtime, tuples are just tuples, but we can make use of their type hints to define a predictable signature.
Source code in src/typelib/py/inspection.py
def tuple_signature(t: type[compat.TupleT]) -> inspect.Signature:\n \"\"\"A little faker for getting the \"signature\" of a [`tuple`][].\n\n Note:\n At runtime, tuples are just tuples, but we can make use of their type hints to\n define a predictable signature.\n \"\"\"\n a = args(t)\n if not a or a[-1] is ...:\n argt = tp.Any if not a else a[0]\n param = inspect.Parameter(\n name=\"args\", kind=inspect.Parameter.VAR_POSITIONAL, annotation=argt\n )\n sig = inspect.Signature(parameters=(param,))\n return sig\n kind = inspect.Parameter.POSITIONAL_ONLY\n params = tuple(\n inspect.Parameter(name=f\"arg{str(i)}\", kind=kind, annotation=at)\n for i, at in enumerate(a)\n )\n sig = inspect.Signature(parameters=params)\n return sig\n
"},{"location":"reference/typelib/py/inspection/#typelib.py.inspection.typed_dict_signature","title":"typed_dict_signature","text":"typed_dict_signature(obj: Callable) -> Signature\n
A little faker for getting the \"signature\" of a typing.TypedDict
.
Note Technically, these are dicts at runtime, but we are enforcing a static shape, so we should be able to declare a matching signature for it.
Source code in src/typelib/py/inspection.py
def typed_dict_signature(obj: tp.Callable) -> inspect.Signature:\n \"\"\"A little faker for getting the \"signature\" of a [`typing.TypedDict`][].\n\n Note:\n Technically, these are dicts at runtime, but we are enforcing a static shape,\n so we should be able to declare a matching signature for it.\n \"\"\"\n hints = cached_type_hints(obj)\n total = getattr(obj, \"__total__\", True)\n default = inspect.Parameter.empty if total else ...\n return inspect.Signature(\n parameters=tuple(\n inspect.Parameter(\n name=x,\n kind=inspect.Parameter.KEYWORD_ONLY,\n annotation=y,\n default=getattr(obj, x, default),\n )\n for x, y in hints.items()\n )\n )\n
"},{"location":"reference/typelib/py/refs/","title":"Refs","text":""},{"location":"reference/typelib/py/refs/#typelib.py.refs","title":"refs","text":"Utilities for working with typing.ForwardRef
.
This module allows the developer to create and evaluate typing.ForwardRef
instances with additional logic to support forwards compatibility.
Typical Usage
>>> from typelib.py import refs\n>>> ref = refs.forwardref(\"str\")\n>>> cls = refs.evaluate(ref)\n>>> cls is str\nTrue\n
"},{"location":"reference/typelib/py/refs/#typelib.py.refs.forwardref","title":"forwardref","text":"forwardref(ref: str | type, *, is_argument: bool = False, module: Any | None = None, is_class: bool = True) -> ForwardRef\n
Create a typing.ForwardRef
instance from a ref
string.
This wrapper function will attempt to determine the module name ahead of instantiation if not provided. This is important when resolving the reference to an actual type.
Parameters:
-
ref
(str | type
) \u2013 The type reference string.
-
is_argument
(bool
, default: False
) \u2013 Whether the reference string was an argument to a function (default False).
-
module
(Any | None
, default: None
) \u2013 The python module in which the reference string is defined (optional)
-
is_class
(bool
, default: True
) \u2013 Whether the reference string is a class (default True).
Source code in src/typelib/py/refs.py
def forwardref(\n ref: str | type,\n *,\n is_argument: bool = False,\n module: typing.Any | None = None,\n is_class: bool = True,\n) -> ForwardRef:\n \"\"\"Create a [`typing.ForwardRef`][] instance from a `ref` string.\n\n This wrapper function will attempt to determine the module name ahead of instantiation\n if not provided. This is important when resolving the reference to an actual type.\n\n Args:\n ref: The type reference string.\n is_argument: Whether the reference string was an argument to a function (default False).\n module: The python module in which the reference string is defined (optional)\n is_class: Whether the reference string is a class (default True).\n \"\"\"\n if not isinstance(ref, str):\n name = inspection.qualname(ref)\n module = module or getattr(ref, \"__module__\", None)\n else:\n name = typing.cast(str, ref)\n\n module = _resolve_module_name(ref, module)\n if module is not None:\n name = name.replace(f\"{module}.\", \"\")\n\n return ForwardRef(\n name,\n is_argument=is_argument,\n module=module,\n is_class=is_class,\n )\n
"},{"location":"reference/typelib/unmarshals/","title":"Index","text":""},{"location":"reference/typelib/unmarshals/#typelib.unmarshals","title":"unmarshals","text":"Support for unmarshalling unstructured data into Python data structures.
Notes \"Unmarshalling\" refers to the process of taking a \"primitive\" form of data, such as a basic dictionary or JSON string, and coercing it into a higher-order structured data type.
Tip You may use this package directly, but we encourage you to work with the higher-level API provided by the typelib
module.
Typical Usage
>>> import dataclasses\n>>> import decimal\n>>> from typelib import unmarshals\n>>>\n>>> @dataclasses.dataclass(slots=True, weakref_slot=True, kw_only=True)\n... class Struct:\n... key: str\n... number: decimal.Decimal\n...\n>>>\n>>> data = {\"key\": \"some-key\", \"number\": \"3.14\"}\n>>> unmarshals.unmarshal(Struct, data)\nStruct(key='some-key', number=decimal.Decimal('3.14'))\n>>> unmarshaller = unmarshals.unmarshaller(Struct)\n>>> unmarshaller(data)\nStruct(key='some-key', number=decimal.Decimal('3.14'))\n
See Also unmarshals
unmarshaller
typelib.codec
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.AbstractUnmarshaller","title":"AbstractUnmarshaller","text":"AbstractUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: ABC
, Generic[T]
Abstract base class defining the common interface for unmarshallers.
Unmarshallers are custom callables which maintain type-specific information. They use this information to provide robust, performant logic for decoding and converting primtive Python objects or JSON-endcoded data into their target type.
Unmarshallers support contextual deserialization, which enables the unmarshalling of nested types.
Attributes:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
origin
(type[T]
) \u2013 If t
is a generic, this will be an actionable runtime type related to t
, otherwise it is the same as t
.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
) \u2013 If this unmarshaller is used in a nested context, this will reference the field/parameter/index at which this unmarshaller should be used.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.AbstractUnmarshaller.__call__","title":"__call__ abstractmethod
","text":"__call__(val: Any) -> T\n
Unmarshall a Python object into its target type.
Not implemented for the abstract base class.
Source code in src/typelib/unmarshals/routines.py
@abc.abstractmethod\ndef __call__(self, val: tp.Any) -> T:\n \"\"\"Unmarshall a Python object into its target type.\n\n Not implemented for the abstract base class.\n \"\"\"\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.BytesUnmarshaller","title":"BytesUnmarshaller","text":"BytesUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[BytesT]
, Generic[BytesT]
Unmarshaller that encodes an input to bytes.
Note We will format a member of the datetime
module into ISO format before converting to bytes.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.CastUnmarshaller","title":"CastUnmarshaller","text":"CastUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[T]
Unmarshaller that converts an input to an instance of T
with a direct cast.
Note Before casting to the bound type, we will attempt to decode the value into a real Python object.
See Also Parameters:
-
t
(type[T]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context (unused).
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context (unused).\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.caster: tp.Callable[[tp.Any], T] = self.origin # type: ignore[assignment]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.CastUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> T\n
Unmarshal a value into the bound T
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> T:\n \"\"\"Unmarshal a value into the bound `T` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Try to load the string, if this is JSON or a literal expression.\n decoded = serdes.load(val)\n # Short-circuit cast if we have the type we want.\n if isinstance(decoded, self.t):\n return decoded\n # Cast the decoded value to the type.\n return self.caster(decoded)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateTimeUnmarshaller","title":"DateTimeUnmarshaller","text":"DateTimeUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[datetime]
, Generic[DateTimeT]
Unmarshaller that converts an input to a datetime.datetime
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.datetime
instance, return it. - If we are a
float
or int
instance, treat it as a unix timestamp, at UTC. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into either a
datetime.date
instance, a datetime.time
instance or a datetime.datetime
. - If the parsed result is a
datetime.time
instance, then merge the parsed time with today, at the timezone specified in the time instance. - If the parsed result is a
datetime.date
instance, create a datetime.datetime
instance at midnight of the indicated date, UTC.
TL;DR There are many ways to represent a datetime object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateTimeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> datetime\n
Unmarshal a value into the bound DateTimeT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> datetime.datetime:\n \"\"\"Unmarshal a value into the bound `DateTimeT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, self.t):\n return val\n\n # Numbers can be treated as time since epoch.\n if isinstance(val, (int, float)):\n val = datetime.datetime.fromtimestamp(val, tz=datetime.timezone.utc)\n # Always decode bytes.\n decoded = serdes.decode(val)\n # Parse strings.\n dt: datetime.datetime | datetime.date | datetime.time = (\n serdes.dateparse(decoded, self.t) if isinstance(decoded, str) else decoded\n )\n # If we have a time object, default to today.\n if isinstance(dt, datetime.time):\n return self.t.now(tz=dt.tzinfo).replace(\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n tzinfo=dt.tzinfo,\n )\n # Exact class matching.\n if dt.__class__ is self.t:\n return dt # type: ignore[return-value]\n # Subclass check for datetimes.\n if isinstance(dt, datetime.datetime):\n return self.t(\n year=dt.year,\n month=dt.month,\n day=dt.day,\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n tzinfo=dt.tzinfo,\n fold=dt.fold,\n )\n # Implicit: we have a date object.\n return self.t(\n year=dt.year, month=dt.month, day=dt.day, tzinfo=datetime.timezone.utc\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateUnmarshaller","title":"DateUnmarshaller","text":"DateUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[DateT]
, Generic[DateT]
Unmarshaller that converts an input to a datetime.date
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.date
instance, return it. - If we are a
float
or int
instance, treat it as a unix timestamp, at UTC. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into either a
datetime.date
- If the parsed result is a
datetime.time
instance, then return the result of datetime.datetime.now
, at UTC, as a datetime.date
.
TL;DR There are many ways to represent a date object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DateUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> DateT\n
Unmarshal a value into the bound DateT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> DateT:\n \"\"\"Unmarshal a value into the bound `DateT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, self.t) and not isinstance(val, datetime.datetime):\n return val\n\n # Numbers can be treated as time since epoch.\n if isinstance(val, (int, float)):\n val = datetime.datetime.fromtimestamp(val, tz=datetime.timezone.utc)\n # Always decode bytes.\n decoded = serdes.decode(val)\n # Parse strings.\n date: datetime.date | datetime.time = (\n serdes.dateparse(decoded, self.t) if isinstance(decoded, str) else decoded\n )\n # Time-only construct is treated as today.\n if isinstance(date, datetime.time):\n date = datetime.datetime.now(tz=datetime.timezone.utc).today()\n # Exact class matching - the parser returns subclasses.\n if date.__class__ is self.t:\n return date # type: ignore[return-value]\n # Reconstruct as the exact type.\n return self.t(year=date.year, month=date.month, day=date.day)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DelayedUnmarshaller","title":"DelayedUnmarshaller","text":"DelayedUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[T]
Delayed proxy for a given type's unmarshaller, used when we encounter a typing.ForwardRef
.
Notes This allows us to delay the resolution of the given type reference until call-time, enabling support for cyclic and recursive types.
Source code in src/typelib/unmarshals/api.py
def __init__(\n self, t: type[T], context: routines.ContextT, *, var: str | None = None\n):\n super().__init__(t, context, var=var)\n self._resolved: routines.AbstractUnmarshaller[T] | None = None\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.DelayedUnmarshaller.resolved","title":"resolved property
","text":"resolved: AbstractUnmarshaller[T]\n
The resolved unmarshaller.
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.FixedTupleUnmarshaller","title":"FixedTupleUnmarshaller","text":"FixedTupleUnmarshaller(t: type[TupleT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[TupleT]
Unmarshaller for a \"fixed\" tuple (e.g., tuple[int, str, float]
).
Note Python supports two distinct uses for tuples, unlike in other languages:
- Tuples with a fixed number of members.
- Tuples of variable length (an immutable sequence).
\"Fixed\" tuples may have a distinct type for each member, while variable-length tuples may only have a single type (or union of types) for all members.
Variable-length tuples are handled by our generic iterable unmarshaller.
For \"fixed\" tuples, the algorithm is:
- Attempt to decode the input into a real Python object.
- zip the stack of member unmarshallers and the values in the decoded object.
- Unmarshal each value using the associated unmarshaller for that position.
- Pass the unmarshalling iterator in to the type's constructor.
Tip If the input has more members than the type definition allows, those members will be dropped by nature of our unmarshalling algorithm.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[TupleT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the value unmarshaller stack.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(\n self, t: type[compat.TupleT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the value unmarshaller stack.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n self.ordered_routines = [self.context[vt] for vt in self.stack]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.FixedTupleUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> TupleT\n
Unmarshal a value into the bound tuple
structure.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> compat.TupleT:\n \"\"\"Unmarshal a value into the bound [`tuple`][] structure.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n decoded = serdes.load(val)\n return self.origin(\n routine(v)\n for routine, v in zip(self.ordered_routines, serdes.itervalues(decoded))\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.LiteralUnmarshaller","title":"LiteralUnmarshaller","text":"LiteralUnmarshaller(t: type[LiteralT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[LiteralT]
, Generic[LiteralT]
Unmarshaller that will enforce an input conform to a defined typing.Literal
.
Note We will attempt to decode the value into a real Python object if the input fails initial membership evaluation.
See Also Parameters:
-
t
(type[LiteralT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context (unused).
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[LiteralT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context (unused).\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.values = inspection.args(t)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NoOpUnmarshaller","title":"NoOpUnmarshaller","text":"NoOpUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[T]
Unmarshaller that does nothing.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NoneTypeUnmarshaller","title":"NoneTypeUnmarshaller","text":"NoneTypeUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[None]
Unmarshaller for null values.
Note We will attempt to decode any string/bytes input before evaluating for None
.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NoneTypeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> None\n
Unmarshal the given input into a None
value.
Parameters:
-
val
(Any
) \u2013 The value to unmarshal.
Raises:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> None:\n \"\"\"Unmarshal the given input into a `None` value.\n\n Args:\n val: The value to unmarshal.\n\n Raises:\n ValueError: If `val` is not `None` after decoding.\n \"\"\"\n decoded = serdes.decode(val)\n if decoded is not None:\n raise ValueError(f\"{val!r} is not of {types.NoneType!r}\")\n return None\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NumberUnmarshaller","title":"NumberUnmarshaller","text":"NumberUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[NumberT]
, Generic[NumberT]
Unmarshaller that converts an input to a number.
Note Number unmarshalling follows a best-effort strategy. We may extend type resolution to support more advanced type unmarshalling strategies in the future.
As of now: 1. Attempt to decode any bytes/string input into a real Python value. 2. If the input is a member of the datetime
module, convert it to a number. 3. If the input is a mapping, unpack it into the number constructor. 4. If the input is an iterable, unpack it into the number constructor. 5. Otherwise, call the number constructor with the input.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.NumberUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> NumberT\n
Unmarshall a value into the bound Number type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> NumberT:\n \"\"\"Unmarshall a value into the bound Number type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.decode(val)\n if isinstance(decoded, self.t):\n return decoded\n # Represent date/time objects as time since unix epoch.\n if isinstance(val, (datetime.date, datetime.time, datetime.timedelta)):\n decoded = serdes.unixtime(val)\n # Treat containers as constructor args.\n if inspection.ismappingtype(decoded.__class__):\n return self.t(**decoded)\n if inspection.isiterabletype(decoded.__class__) and not inspection.istexttype(\n decoded.__class__\n ):\n return self.t(*decoded)\n # Simple cast for non-containers.\n return self.t(decoded) # type: ignore[call-arg]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.PatternUnmarshaller","title":"PatternUnmarshaller","text":"PatternUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[PatternT]
, Generic[PatternT]
Unmarshaller that converts an input to a re.Pattern
.
Note You can't instantiate a re.Pattern
directly, so we don't have a good method for handling patterns from a different library out-of-the-box. We simply call re.compile()
on the decoded input.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.StringUnmarshaller","title":"StringUnmarshaller","text":"StringUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[StringT]
, Generic[StringT]
Unmarshaller that converts an input to a string.
Note We will format a member of the datetime
module into ISO format.
See Also Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.StructuredTypeUnmarshaller","title":"StructuredTypeUnmarshaller","text":"StructuredTypeUnmarshaller(t: type[_ST], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[_ST]
Unmarshaller for a \"structured\" (user-defined) type.
Note This unmarshaller supports the unmarshalling of any mapping or structured type into the targeted structured type. There are limitations.
The algorithm is:
- Attempt to decode the input into a real Python object.
- Using a mapping of the structured types \"field\" to the field-type's unmarshaller, iterate over the field->value pairs of the input, skipping fields in the input which are not present in the field mapping.
- Store each unmarshalled value in a keyword-argument mapping.
- Unpack the keyword argument mapping into the bound type's constructor.
Tip While we don't currently support arbitrary collections, we may add this functionality at a later date. Doing so requires more advanced introspection and parameter-binding that would lead to a significant loss in performance if not done carefully.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[_ST]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the value field-to-unmarshaller mapping.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[_ST], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the value field-to-unmarshaller mapping.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.fields_by_var = self._fields_by_var()\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.StructuredTypeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> _ST\n
Unmarshal a value into the bound type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> _ST:\n \"\"\"Unmarshal a value into the bound type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n decoded = serdes.load(val)\n fields = self.fields_by_var\n kwargs = {f: fields[f](v) for f, v in serdes.iteritems(decoded) if f in fields}\n return self.t(**kwargs)\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIterableUnmarshaller","title":"SubscriptedIterableUnmarshaller","text":"SubscriptedIterableUnmarshaller(t: type[IterableT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[IterableT]
, Generic[IterableT]
Unmarshaller for a subscripted iterable type.
Note This unmarshaller handles standard simple iterable types. We leverage our own generic itervalues
to allow for translating other collections or structured objects into the target iterable.
The algorithm is as follows:
- We attempt to decode the input into a real Python object.
- We iterate over values in the decoded input.
- We call the value-type's unmarshaller on the
value
members. - We pass the unmarshalling iterator in to the type's constructor.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[IterableT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshaller.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(\n self, t: type[IterableT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshaller.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t=t, context=context, var=var)\n # supporting tuple[str, ...]\n (value_t, *_) = inspection.args(t)\n self.values = context[value_t]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIterableUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> IterableT\n
Unmarshal a value into the bound IterableT
.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> IterableT:\n \"\"\"Unmarshal a value into the bound `IterableT`.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.load(val)\n values = self.values\n return self.origin((values(v) for v in serdes.itervalues(decoded))) # type: ignore[call-arg]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIteratorUnmarshaller","title":"SubscriptedIteratorUnmarshaller","text":"SubscriptedIteratorUnmarshaller(t: type[IteratorT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[IteratorT]
, Generic[IteratorT]
Unmarshaller for a subscripted iterator type.
Note This unmarshaller handles standard simple iterable types. We leverage our own generic itervalues
to allow for translating other collections or structured objects into the target iterator.
The algorithm is as follows:
- We attempt to decode the input into a real Python object.
- We iterate over values in the decoded input.
- We call the value-type's unmarshaller on the
value
members. - We return a new, unmarshalling iterator.
See Also typelib.serdes.load
typelib.serdes.itervalues
Parameters:
-
t
(type[IteratorT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshaller.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(\n self, t: type[IteratorT], context: ContextT, *, var: str | None = None\n):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshaller.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n (value_t,) = inspection.args(t)\n self.values = context[value_t]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedIteratorUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> IteratorT\n
Unmarshal a value into the bound IteratorT
.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> IteratorT:\n \"\"\"Unmarshal a value into the bound `IteratorT`.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.load(val)\n values = self.values\n it: IteratorT = (values(v) for v in serdes.itervalues(decoded)) # type: ignore[assignment]\n return it\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedMappingUnmarshaller","title":"SubscriptedMappingUnmarshaller","text":"SubscriptedMappingUnmarshaller(t: type[MappingT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[MappingT]
, Generic[MappingT]
Unmarshaller for a subscripted mapping type.
Note This unmarshaller handles standard key->value mappings. We leverage our own generic iteritems
to allow for translating other collections or structured objects into the target mapping.
The algorithm is as follows:
- We attempt to decode the input into a real Python object.
- We iterate over key->value pairs.
- We call the key-type's unmarshaller on the
key
members. - We call the value-type's unmarshaller on the
value
members. - We pass the unmarshalling iterator in to the type's constructor.
See Also typelib.serdes.load
typelib.serdes.iteritems
Parameters:
-
t
(type[MappingT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[MappingT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n key_t, value_t = inspection.args(t)\n self.keys = context[key_t]\n self.values = context[value_t]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.SubscriptedMappingUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> MappingT\n
Unmarshal a value into the bound MappingT
.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> MappingT:\n \"\"\"Unmarshal a value into the bound `MappingT`.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n # Always decode bytes.\n decoded = serdes.load(val)\n keys = self.keys\n values = self.values\n return self.origin( # type: ignore[call-arg]\n ((keys(k), values(v)) for k, v in serdes.iteritems(decoded))\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeDeltaUnmarshaller","title":"TimeDeltaUnmarshaller","text":"TimeDeltaUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[TimeDeltaT]
, Generic[TimeDeltaT]
Unmarshaller that converts an input to a datetime.timedelta
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.timedelta
instance, return it. - If we are a
float
or int
instance, treat it as total seconds for a delta. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into a
datetime.timedelta
instance. - If the parsed result is not exactly the bound
TimeDeltaT
type, convert it.
TL;DR There are many ways to represent a time object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeDeltaUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> TimeDeltaT\n
Unmarshal a value into the bound TimeDeltaT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> TimeDeltaT:\n \"\"\"Unmarshal a value into the bound `TimeDeltaT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, (int, float)):\n return self.t(seconds=int(val))\n\n decoded = serdes.decode(val)\n td: datetime.timedelta = (\n serdes.dateparse(decoded, t=datetime.timedelta)\n if isinstance(decoded, str)\n else decoded\n )\n\n if td.__class__ is self.t:\n return td # type: ignore[return-value]\n\n return self.t(seconds=td.total_seconds())\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeUnmarshaller","title":"TimeUnmarshaller","text":"TimeUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[TimeT]
, Generic[TimeT]
Unmarshaller that converts an input to adatetime.time
(or subclasses).
Notes This class tries to handle the 90% case:
- If we are already a
datetime.time
instance, return it. - If we are a
float
or int
instance, treat it as a unix timestamp, at UTC. - Attempt to decode any bytes/string input into a real Python value.
- If we have a string value, parse it into either a
datetime.date
instance, a datetime.time
instance or a datetime.datetime
. - If the parsed result is a
datetime.datetime
instance, then extract the time portion, preserving the associated timezone. - If the parsed result is a
datetime.date
instance, create a time instance at midnight, UTC.
TL;DR There are many ways to represent a time object over-the-wire. Your most fool-proof method is to rely upon ISO 8601 or RFC 3339.
See Also typelib.serdes.decode
typelib.serdes.dateparse
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.TimeUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> TimeT\n
Unmarshal a value into the bound TimeT
type.
Parameters:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> TimeT:\n \"\"\"Unmarshal a value into the bound `TimeT` type.\n\n Args:\n val: The input value to unmarshal.\n \"\"\"\n if isinstance(val, self.t):\n return val\n\n decoded = serdes.decode(val)\n if isinstance(decoded, (int, float)):\n decoded = (\n datetime.datetime.fromtimestamp(val, tz=datetime.timezone.utc)\n .time()\n # datetime.time() strips tzinfo...\n .replace(tzinfo=datetime.timezone.utc)\n )\n dt: datetime.datetime | datetime.date | datetime.time = (\n serdes.dateparse(decoded, self.t) if isinstance(decoded, str) else decoded\n )\n\n if isinstance(dt, datetime.datetime):\n # datetime.time() strips tzinfo...\n dt = dt.time().replace(tzinfo=dt.tzinfo)\n elif isinstance(dt, datetime.date):\n dt = self.t(tzinfo=datetime.timezone.utc)\n\n if dt.__class__ is self.t:\n return dt # type: ignore[return-value]\n\n return self.t(\n hour=dt.hour,\n minute=dt.minute,\n second=dt.second,\n microsecond=dt.microsecond,\n tzinfo=dt.tzinfo,\n fold=dt.fold,\n )\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UUIDUnmarshaller","title":"UUIDUnmarshaller","text":"UUIDUnmarshaller(t: type[T], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[UUIDT]
, Generic[UUIDT]
Unmarshaller that converts an input to a uuid.UUID
(or subclasses).
Note The resolution algorithm is intentionally simple:
- Attempt to decode any bytes/string input into a real Python object.
- If the value is an integer, pass it into the constructor via the
int=
param. - Otherwise, pass into the constructor directly.
Tip While the uuid.UUID
constructor supports many different keyword inputs for different types of UUID formats/encodings, we don't have a great method for detecting the correct input. We have moved with the assumption that the two most common formats are a standard string encoding, or an integer encoding.
Parameters:
-
t
(type[T]
) \u2013 The root type of this unmarshaller.
-
context
(ContextT
) \u2013 The complete type context for this unmarshaller.
-
var
(str | None
, default: None
) \u2013 The associated field or parameter name for this unmarshaller (optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[T], context: ContextT, *, var: str | None = None):\n \"\"\"Construct an unmarshaller instance.\n\n Args:\n t: The root type of this unmarshaller.\n context: The complete type context for this unmarshaller.\n var: The associated field or parameter name for this unmarshaller (optional).\n \"\"\"\n self.t = t\n self.origin = inspection.origin(self.t)\n self.context = context\n self.var = var\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UUIDUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> UUIDT\n
Unmarshal a value into the bound UUIDT
type.
Parameters:
See Also Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> UUIDT:\n \"\"\"Unmarshal a value into the bound `UUIDT` type.\n\n Args:\n val: The input value to unmarshal.\n\n See Also:\n - [`typelib.serdes.load`][]\n \"\"\"\n decoded = serdes.load(val)\n if isinstance(decoded, int):\n return self.t(int=decoded)\n if isinstance(decoded, self.t):\n return decoded\n return self.t(decoded) # type: ignore[arg-type]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UnionUnmarshaller","title":"UnionUnmarshaller","text":"UnionUnmarshaller(t: type[UnionT], context: ContextT, *, var: str | None = None)\n
Bases: AbstractUnmarshaller[UnionT]
, Generic[UnionT]
Unmarshaller that will convert an input to one of the types defined in a typing.Union
.
Note Union deserialization is messy and violates a static type-checking mechanism - for static type-checkers, str | int
is equivalent to int | str
. This breaks down during unmarshalling for the simple fact that casting something to str
will always succeed, so we would never actually unmarshal the input it an int
, even if that is the \"correct\" result.
Our algorithm is intentionally simple:
- We iterate through each union member from top to bottom and call the resolved unmarshaller, returning the result.
- If any of
(ValueError, TypeError, SyntaxError)
, try again with the next unmarshaller. - If all unmarshallers fail, then we have an invalid input, raise an error.
TL;DR In order to ensure correctness, you should treat your union members as a stack, sorted from most-strict initialization to least-strict.
Parameters:
-
t
(type[UnionT]
) \u2013 The type to unmarshal into.
-
context
(ContextT
) \u2013 Any nested type context. Used to resolve the member unmarshallers.
-
var
(str | None
, default: None
) \u2013 A variable name for the indicated type annotation (unused, optional).
Source code in src/typelib/unmarshals/routines.py
def __init__(self, t: type[UnionT], context: ContextT, *, var: str | None = None):\n \"\"\"Constructor.\n\n Args:\n t: The type to unmarshal into.\n context: Any nested type context. Used to resolve the member unmarshallers.\n var: A variable name for the indicated type annotation (unused, optional).\n \"\"\"\n super().__init__(t, context, var=var)\n self.stack = inspection.args(t)\n if inspection.isoptionaltype(t):\n self.stack = (self.stack[-1], *self.stack[:-1])\n\n self.ordered_routines = [self.context[typ] for typ in self.stack]\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.UnionUnmarshaller.__call__","title":"__call__","text":"__call__(val: Any) -> UnionT\n
Unmarshal a value into the bound UnionT
.
Parameters:
Raises:
Source code in src/typelib/unmarshals/routines.py
def __call__(self, val: tp.Any) -> UnionT:\n \"\"\"Unmarshal a value into the bound `UnionT`.\n\n Args:\n val: The input value to unmarshal.\n\n Raises:\n ValueError: If `val` cannot be unmarshalled into any member type.\n \"\"\"\n for routine in self.ordered_routines:\n with contextlib.suppress(\n ValueError, TypeError, SyntaxError, AttributeError\n ):\n unmarshalled = routine(val)\n return unmarshalled\n\n raise ValueError(f\"{val!r} is not one of types {self.stack!r}\")\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.unmarshal","title":"unmarshal","text":"unmarshal(t: type[T] | ForwardRef | str, value: Any) -> T\n
Unmarshal value
into typ
.
Parameters:
Source code in src/typelib/unmarshals/api.py
def unmarshal(t: type[T] | refs.ForwardRef | str, value: tp.Any) -> T:\n \"\"\"Unmarshal `value` into `typ`.\n\n Args:\n t: The type annotation or reference to unmarshal into.\n value: The value to unmarshal.\n \"\"\"\n routine = unmarshaller(t)\n unmarshalled = routine(value)\n return unmarshalled\n
"},{"location":"reference/typelib/unmarshals/#typelib.unmarshals.unmarshaller","title":"unmarshaller","text":"unmarshaller(t: type[T] | ForwardRef | TypeAliasType | str) -> AbstractUnmarshaller[T]\n
Get an un-marshaller routine for a given type.
Parameters:
-
t
(type[T] | ForwardRef | TypeAliasType | str
) \u2013 The type annotation to generate an unmarshaller for. May be a type, type alias, typing.ForwardRef
, or string reference.
Source code in src/typelib/unmarshals/api.py
@compat.cache\ndef unmarshaller(\n t: type[T] | refs.ForwardRef | compat.TypeAliasType | str,\n) -> routines.AbstractUnmarshaller[T]:\n \"\"\"Get an un-marshaller routine for a given type.\n\n Args:\n t: The type annotation to generate an unmarshaller for.\n May be a type, type alias, [`typing.ForwardRef`][], or string reference.\n \"\"\"\n nodes = graph.static_order(t)\n context: ctx.TypeContext[routines.AbstractUnmarshaller] = ctx.TypeContext()\n if not nodes:\n return routines.NoOpUnmarshaller(t=t, context=context, var=None) # type: ignore[arg-type]\n\n # \"root\" type will always be the final node in the sequence.\n root = nodes[-1]\n for node in nodes:\n context[node.type] = _get_unmarshaller(node, context=context)\n\n return context[root.type]\n
"}]}
\ No newline at end of file