Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 95 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,108 @@

<div align="center">

| Project | | Status |
| --------- | :-- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CI/CD | | [![Latest Release](https://github.com/litestar-org/type-lens/actions/workflows/publish.yml/badge.svg)](https://github.com/litestar-org/type-lens/actions/workflows/publish.yml) [![ci](https://github.com/litestar-org/type-lens/actions/workflows/ci.yml/badge.svg)](https://github.com/litestar-org/type-lens/actions/workflows/ci.yml) [![Documentation Building](https://github.com/litestar-org/type-lens/actions/workflows/docs.yml/badge.svg?branch=main)](https://github.com/litestar-org/type-lens/actions/workflows/docs.yml) |
| Quality | | [![Coverage](https://codecov.io/github/litestar-org/type-lens/graph/badge.svg?token=vKez4Pycrc)](https://codecov.io/github/litestar-org/type-lens) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_type-lens&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=litestar-org_type-lens) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_type-lens&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=litestar-org_type-lens) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_type-lens&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=litestar-org_type-lens) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=litestar-org_type-lens&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=litestar-org_type-lens) |
| Package | | [![PyPI - Version](https://img.shields.io/pypi/v/type-lens?labelColor=202235&color=edb641&logo=python&logoColor=edb641)](https://badge.fury.io/py/litestar) ![PyPI - Support Python Versions](https://img.shields.io/pypi/pyversions/type-lens?labelColor=202235&color=edb641&logo=python&logoColor=edb641) ![type-lens PyPI - Downloads](https://img.shields.io/pypi/dm/type-lens?logo=python&label=package%20downloads&labelColor=202235&color=edb641&logoColor=edb641) |
| Community | | [![Reddit](https://img.shields.io/reddit/subreddit-subscribers/litestarapi?label=r%2FLitestar&logo=reddit&labelColor=202235&color=edb641&logoColor=edb641)](https://reddit.com/r/litestarapi) [![Discord](https://img.shields.io/discord/919193495116337154?labelColor=202235&color=edb641&label=chat%20on%20discord&logo=discord&logoColor=edb641)](https://discord.gg/litestar) [![Matrix](https://img.shields.io/badge/chat%20on%20Matrix-bridged-202235?labelColor=202235&color=edb641&logo=matrix&logoColor=edb641)](https://matrix.to/#/#litestar:matrix.org) [![Medium](https://img.shields.io/badge/Medium-202235?labelColor=202235&color=edb641&logo=medium&logoColor=edb641)](https://blog.litestar.dev) [![Twitter](https://img.shields.io/twitter/follow/LitestarAPI?labelColor=202235&color=edb641&logo=twitter&logoColor=edb641&style=flat)](https://twitter.com/LitestarAPI) [![Blog](https://img.shields.io/badge/Blog-litestar.dev-202235?logo=blogger&labelColor=202235&color=edb641&logoColor=edb641)](https://blog.litestar.dev) |
| Meta | | [![Litestar Project](https://img.shields.io/badge/Litestar%20Org-%E2%AD%90%20Advanced%20Alchemy-202235.svg?logo=python&labelColor=202235&color=edb641&logoColor=edb641)](https://github.com/litestar-org/type-lens) [![types - Mypy](https://img.shields.io/badge/types-Mypy-202235.svg?logo=python&labelColor=202235&color=edb641&logoColor=edb641)](https://github.com/python/mypy) [![License - MIT](https://img.shields.io/badge/license-MIT-202235.svg?logo=python&labelColor=202235&color=edb641&logoColor=edb641)](https://spdx.org/licenses/) [![Litestar Sponsors](https://img.shields.io/badge/Sponsor-%E2%9D%A4-%23edb641.svg?&logo=github&logoColor=edb641&labelColor=202235)](https://github.com/sponsors/litestar-org) [![linting - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json&labelColor=202235)](https://github.com/astral-sh/ruff) [![code style - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/format.json&labelColor=202235)](https://github.com/psf/black) |
| Project | | Status |
| --------- | :-- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CI/CD | | [![Latest Release](https://github.com/litestar-org/type-lens/actions/workflows/publish.yml/badge.svg)](https://github.com/litestar-org/type-lens/actions/workflows/publish.yml) [![ci](https://github.com/litestar-org/type-lens/actions/workflows/ci.yml/badge.svg)](https://github.com/litestar-org/type-lens/actions/workflows/ci.yml) [![Documentation Building](https://github.com/litestar-org/type-lens/actions/workflows/docs.yml/badge.svg?branch=main)](https://github.com/litestar-org/type-lens/actions/workflows/docs.yml) |
| Package | | [![PyPI - Version](https://img.shields.io/pypi/v/type-lens?labelColor=202235&color=edb641&logo=python&logoColor=edb641)](https://badge.fury.io/py/litestar) ![PyPI - Support Python Versions](https://img.shields.io/pypi/pyversions/type-lens?labelColor=202235&color=edb641&logo=python&logoColor=edb641) ![type-lens PyPI - Downloads](https://img.shields.io/pypi/dm/type-lens?logo=python&label=package%20downloads&labelColor=202235&color=edb641&logoColor=edb641) |
| Community | | [![Discord](https://img.shields.io/discord/919193495116337154?labelColor=202235&color=edb641&label=chat%20on%20discord&logo=discord&logoColor=edb641)](https://discord.gg/litestar) |

</div>

## About

`type-lens` is a library that allows for utilizing Python types at runtime.
`type-lens` is a library for introspecting Python type annotations at runtime. It aims to provide a unified, ergonomic
API that works consistently across Python versions, smoothing over the many version-specific quirks and behavioral
differences in Python's `typing` module.

## Usage
The library is built around three core classes:

- Install `type-lens` with `pip` or check out [our installation guide][install-guide]
- **`TypeView`**: Represents a type annotation and exposes a rich set of properties for inspection.
- **`ParameterView`**: Represents a parameter and its associated type information, in the context of a signature.
- **`CallableView`**: Represents a whole callable signature, including all its parameters and return type.

### Examples

```python
from typing import Annotated, Optional, Sequence, Union
from type_lens import TypeView, CallableView

# Union detection works regardless of syntax or Python version
TypeView(int | str).is_union # True (3.10+ pipe syntax)
TypeView(Union[int, str]).is_union # True (all versions)

# Optional / None detection
TypeView(Optional[int]).is_optional # True
TypeView(int | None).is_optional # True

# Annotated metadata is extracted automatically
view = TypeView(Annotated[int, "positive"])
view.annotation # <class 'int'>
view.metadata # ('positive',)
view.is_annotated # True

# Collection and mapping classification
TypeView(list[int]).is_collection # True
TypeView(dict[str, int]).is_mapping # True
TypeView(Sequence[str]).is_collection # True
TypeView(str).is_non_string_collection # False

# Traverse generic args as TypeViews
inner = TypeView(dict[str, list[int]]).inner_types
# (TypeView(str), TypeView(list[int]))
inner[1].inner_types
# (TypeView(int),)

# Resolve abstract types to instantiable concrete types
TypeView(Sequence[int]).instantiable_origin # <class 'list'>
TypeView(Mapping[str, int]).instantiable_origin # <class 'dict'>

# Strip Optional to get the underlying type
TypeView(int | None).strip_optional() # TypeView(int)

# Type alias support (3.12 new-style or typing_extensions)
# type Alias = list[int]
TypeView(Alias).is_type_alias # True
TypeView(Alias).strip_type_alias() # TypeView(list[int])

# Inspect callables
def process(items: list[str], limit: int = 10) -> dict[str, int]: ...

view = CallableView.from_callable(process)
view.return_type # TypeView(dict[str, int])
view.parameters[0].name # 'items'
view.parameters[0].type_view # TypeView(list[str])
view.parameters[1].has_default # True
```

### How it compares to related libraries

**vs. `typing-extensions`**

`typing-extensions` backports new typing constructs (`TypeAlias`, `Protocol`, `ParamSpec`, etc.) to older Python versions.
It is largely a compatibility shim which let users *write* new-style annotations on older Python, but not provide a runtime
introspection API. `type-lens` uses `typing-extensions` internally and is fully complementary to it.

**vs. `typing_inspect`**

`typing_inspect` provides standalone functions (`is_optional_type()`, `get_args()`, `get_parameters()`, etc.) for inspecting
individual types. It appears to be designed to provide some missing unit functionality, but does not attempt to abstract
over the type system itself, nor attempt to normalize behavior across python versions. `type-lens` is designed to be a complete,
version-normalized introspection layer rather than a collection of utilities.

### Python version-specific quirks

Python's typing system has accumulated numerous version-specific inconsistencies. For example:

- Union syntax (``int | str``) (Python >= 3.10)
- `Annotated` + `Optional` bug [#90353](https://github.com/python/cpython/issues/90353) (Python < 3.11)
- `collections.abc` types not generic (Python < 3.10)
- `get_type_hints` forward-reference evaluation (Python < 3.10)

`type-lens` tries to provide an interface that is able to consistently model the type system at runtime, where a naive
implementation might work differently across different python version.

## Installation

```shell
pip install type-lens
Expand Down
13 changes: 13 additions & 0 deletions docs/PYPI_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
<img src="https://raw.githubusercontent.com/litestar-org/branding/473f54621e55cde9acbb6fcab7fc03036173eb3d/assets/Branding%20-%20PNG%20-%20Transparent/Logo%20-%20Banner%20-%20Inline%20-%20Light.png" alt="Litestar Logo - Light" width="100%" height="auto" />
</p>

`type-lens` is a library for introspecting Python type annotations at runtime. It aims to provide a unified, ergonomic
API that works consistently across Python versions, smoothing over the many version-specific quirks and behavioral
differences in Python's `typing` module.

The library is built around three core classes:

- **`TypeView`**: Represents a type annotation and exposes a rich set of properties for inspection.
- **`ParameterView`**: Represents a parameter and its associated type information, in the context of a signature.
- **`CallableView`**: Represents a whole callable signature, including all its parameters and return type.

See the [documentation](https://docs.type-lens.litestar.dev) and
[GitHub repository](https://github.com/litestar-org/type-lens) for full details.

[litestar-org]: https://github.com/litestar-org
[contributing]: https://docs.type-lens.litestar.dev/latest/contribution-guide.html
[discord]: https://discord.gg/litestar
Expand Down
35 changes: 25 additions & 10 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,14 @@

# -- General configuration ---------------------------------------------------
extensions = [
"sphinx.ext.autodoc",
"autoapi.extension",
"sphinx.ext.napoleon",
"sphinx.ext.autosectionlabel",
"sphinx.ext.githubpages",
"sphinx.ext.viewcode",
"sphinx.ext.intersphinx",
"sphinx_copybutton",
"sphinx.ext.todo",
"sphinx.ext.viewcode",
"sphinx_click",
"sphinx_toolbox.collapse",
"sphinx_design",
"sphinx_togglebutton",
Expand All @@ -48,14 +46,23 @@
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
}
PY_CLASS = "py:class"

Check failure on line 49 in docs/conf.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "py:class" 5 times.

See more on https://sonarcloud.io/project/issues?id=litestar-org_type-lens&issues=AZ395pQkStG2890E5pm5&open=AZ395pQkStG2890E5pm5&pullRequest=53
PY_RE = r"py:.*"
PY_METH = "py:meth"
PY_ATTR = "py:attr"
PY_OBJ = "py:obj"

nitpicky = True
nitpick_ignore = []
nitpick_ignore = [
# autoapi generates :py:class: refs for these, but they are not class objects.
# TypeVar and Ellipsis (...) are data objects; TypeAlias and re-exported types
# are documented at the root type_lens namespace, not their submodule paths.
# These cannot be resolved without patching autoapi templates.
("py:class", "T"),
("py:class", "Ellipsis"),
("py:class", "type_lens.types.empty.EmptyType"),
("py:class", "type_lens.type_view.TypeView"),
]
nitpick_ignore_regex = []

napoleon_google_docstring = True
Expand All @@ -65,12 +72,20 @@
napoleon_use_admonition_for_references = False
napoleon_attr_annotations = True

autoclass_content = "class"
autodoc_class_signature = "separated"
autodoc_default_options = {"special-members": "__init__", "show-inheritance": True, "members": True}
autodoc_member_order = "bysource"
autodoc_typehints_format = "short"
autodoc_type_aliases = {"FilterTypes": "FilterTypes"}
autoapi_dirs = ["../type_lens"]
autoapi_root = "reference"
autoapi_options = [
"members",
"undoc-members",
"show-inheritance",
"show-module-summary",
"special-members",
"imported-members",
]
autoapi_own_page_level = "class"
autoapi_python_class_content = "both"
autoapi_generate_api_docs = False
autoapi_add_toctree_entry = False

autosectionlabel_prefix_document = True
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
Expand Down
Loading
Loading