Skip to content

Commit

Permalink
Document msgspec.inspect
Browse files Browse the repository at this point in the history
  • Loading branch information
jcrist committed Jan 5, 2023
1 parent 816c9b7 commit 73935ad
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ JSON Schema

.. autofunction:: schema_components


.. _inspect-api:

Inspect
-------

Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ is required.
:caption: Advanced

extending.rst
inspect.rst
perf-tips.rst

.. toctree::
Expand Down
150 changes: 150 additions & 0 deletions docs/source/inspect.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
Inspecting Types
----------------

.. currentmodule:: msgspec.inspect

.. warning::

This module is experimental. While we don't expect any breaking changes, we
also don't promise not to break things between releases while this interface
stabilizes.

``msgspec`` provides type-introspection support, which can be used to build
tooling on top of msgspec-compatible types. Possible use cases include:

- Generating OpenAPI_ specifications from msgspec-compatible types (note that
the builtin :doc:`jsonschema` support may be a better starting point for
this).
- Generating example instances of types for testing or documentation purposes
- Integration with hypothesis_ for testing

The main function here is `msgspec.inspect.type_info` for converting a type
annotation into a corresponding `msgspec.inspect.Type` object. There's also
`msgspec.inspect.multi_type_info` which converts an iterable of annotations;
this function is more efficient than calling `type_info` in a loop.

.. code-block:: python
>>> import msgspec
>>> msgspec.inspect.type_info(bool)
BoolType()
>>> msgspec.inspect.type_info(int)
IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None)
>>> msgspec.inspect.type_info(list[int]) # nested types are traversed
ListType(
item_type=IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
min_length=None,
max_length=None
)
>>> msgspec.inspect.multi_type_info([bool, int]) # inspect multiple types
(BoolType(), IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None))
Types with :doc:`constraints` will include the constraint information as well:

.. code-block:: python
>>> from typing import Annotated
>>> from msgspec import Meta
>>> PositiveInt = Annotated[int, Meta(gt=0)]
>>> msgspec.inspect.type_info(PositiveInt)
IntType(gt=0, ge=None, lt=None, le=None, multiple_of=None)
Compound types like :doc:`structs` are also supported:

.. code-block:: python
>>> class User(msgspec.Struct):
... name: str
... groups: list[str] = []
... email: str | None = None
>>> msgspec.inspect.type_info(User)
StructType(
cls=User,
fields=(
Field(
name='name',
encode_name='name',
type=StrType(min_length=None, max_length=None, pattern=None),
required=True,
default=UNSET,
default_factory=UNSET
),
Field(
name='groups',
encode_name='groups',
type=ListType(
item_type=StrType(min_length=None, max_length=None, pattern=None),
min_length=None,
max_length=None
),
required=False,
default=[],
default_factory=UNSET
),
Field(
name='email',
encode_name='email',
type=UnionType(
types=(
StrType(min_length=None, max_length=None, pattern=None),
NoneType()
)
),
required=False,
default=None,
default_factory=UNSET
)
),
tag_field=None,
tag=None,
array_like=False,
forbid_unknown_fields=False
)
Types with additional metadata like ``extra_json_schema`` or ``title`` will be
wrapped in a `msgspec.inspect.Metadata` object. Note that all JSON schema
specific fields are merged into a single ``extra_json_schema`` dict.

.. code-block:: python
>>> UnixName = Annotated[
... str,
... Meta(
... min_length=1,
... max_length=32,
... pattern="^[a-z_][a-z0-9_-]*$",
... description="A valid UNIX username"
... )
... ]
>>> msgspec.inspect.type_info(UnixName)
Metadata(
type=StrType(
min_length=1,
max_length=32,
pattern='^[a-z_][a-z0-9_-]*$'
),
extra_json_schema={'description': 'A valid UNIX username'}
)
Every type supported by ``msgspec`` has a corresponding `msgspec.inspect.Type`
subclass. See the :ref:`API docs <inspect-api>` for a complete list of types.

For an example of using these functions, you might find our builtin
:doc:`jsonschema` generator implementation useful - the code for this can be
found `here
<https://github.com/jcrist/msgspec/blob/main/msgspec/_json_schema.py>`__. In
particular, take a look at the large if-else statement in ``_to_schema``.


.. _OpenAPI: https://www.openapis.org/
.. _hypothesis: https://hypothesis.readthedocs.io/en/latest/
28 changes: 28 additions & 0 deletions msgspec/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,19 @@ def multi_type_info(
Returns
-------
tuple[Type, ...]
Examples
--------
>>> msgspec.inspect.type_info([int, float, list[str]])
(
IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
FloatType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
ListType(
item_type=StrType(min_length=None, max_length=None, pattern=None),
min_length=None,
max_length=None
)
)
"""
return _Translator(types, protocol).run()

Expand All @@ -579,6 +592,21 @@ def type_info(type: Any, *, protocol: Literal[None, "msgpack", "json"] = None) -
Returns
-------
Type
Examples
--------
>>> msgspec.inspect.type_info(bool)
BoolType()
>>> msgspec.inspect.type_info(int)
IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None)
>>> msgspec.inspect.type_info(list[int])
ListType(
item_type=IntType(gt=None, ge=None, lt=None, le=None, multiple_of=None),
min_length=None,
max_length=None
)
"""
return multi_type_info([type], protocol=protocol)[0]

Expand Down

0 comments on commit 73935ad

Please sign in to comment.