runtypecheck


Nameruntypecheck JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryRuntime type checking decorator for Python functions and classes
upload_time2025-08-17 15:39:34
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseNone
keywords typecheck type-checking runtime decorator validation typing annotations
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <div align="center">

# runtypecheck

Fast, pragmatic runtime type checking for Python 3.10+: a configurable `@typecheck` decorator (and class wrapper) featuring structural `Protocol` validation, constrained & bound `TypeVar` handling, iterable sampling, lazy iterator inspection, async support, custom validators, and a weak-reference LRU cache.

</div>

---

## Why?

Static type checkers (mypy, pyright, pyre) are invaluable before runtime. This library adds inexpensive runtime assurance at the *boundaries* where static analysis may be weak: plugin entry points, notebook experiments, dynamically constructed data, tests, or external inputs. Design goals:

* Pragmatic: unknown / future typing forms are accepted by default (configurable fallback policy).
* Fast: caches resolved hints & origins, samples containers, and short‑circuits early.
* Focused: only one decorator + a small config surface; no metaclass trickery.
* Extensible: drop-in custom validators with a tiny decorator.

---
## Quick Start

```python
from typecheck import typecheck, TypeCheckError

@typecheck()
def greet(name: str, times: int = 1) -> str:
    return ' '.join([f'Hello {name}!'] * times)

print(greet('Alice', 2))          # OK
try:
    greet('Bob', 'x')              # type: ignore
except TypeCheckError as e:
    print('Caught:', e)
```

Apply to a class to wrap all public methods (plus `__init__` / `__call__`).

```python
@typecheck()
class Calc:
    def add(self, a: int, b: int) -> int: return a + b

Calc().add(1, 2)
```

---
## Feature Matrix

| Category | Support Summary |
|----------|-----------------|
| Primitives / builtins | Standard `isinstance` semantics |
| Container generics | `list`, `tuple` (fixed & variadic), `set`, `frozenset`, `dict`, `deque` + ABCs (`Sequence`, `Mapping`, `Iterable`, `Iterator`) |
| Collections sampling | Validates up to N elements (configurable) unless deep mode |
| Unions & Optional | Full branch validation with aggregated mismatch context |
| `Literal[...]` | Membership check |
| `Callable` | Light structural check: positional arity & simple annotation compatibility |
| `Type[Cls]` | Class identity / subclass acceptance |
| `Final` / `ClassVar` | Inner type validated |
| Structural `Protocol` | Attributes & method signature compatibility (positional params & annotations + return) |
| `TypeVar` | Constraint / bound enforcement + per-call consistent binding |
| `Annotated[T, ...]` | Treated as `T` (metadata ignored) |
| `TypedDict` | Required keys + per-key value validation; extra keys allowed (PEP 589 semantics) |
| `NewType` | Validated against its underlying supertype |
| `Never` | Always error if a runtime value is supplied |
| `NoReturn` | Accepted for parameter context (enforced on returns elsewhere) |
| `LiteralString` (3.11+) | Treated as `str` (best effort) |
| `TypeGuard[T]` | Ensures runtime bool result |
| Forward refs | Permissive or strict (config) |
| Async functions | Wrapper preserves `async` and validates awaited result |
| Lazy iterables | Non-length iterables sampled via `itertools.tee` |
| Deep vs sample | `deep=True` overrides sampling; otherwise first *N* elements |
| Custom validators | `@register_validator(cls)` mapping exact type -> predicate |
| Runtime disable | `TYPECHECK_DISABLED=1` env var skips decoration logic |

Unsupported / unrecognized constructs (e.g., advanced future typing forms) fall back to acceptance unless `config.fallback_policy` is set to `warn` or `error`.

---
## Installation

```bash
pip install runtypecheck
```

Python 3.10–3.13 (tested). Zero runtime dependencies.

---
## Usage Examples

### Collections & Sampling
```python
from typecheck import typecheck, TypeCheckError

@typecheck(sample=3)   # only first 3 elements of large list validated
def head_sum(values: list[int]) -> int:
    return sum(values[:3])

@typecheck(deep=True)  # validate every element
def full_sum(values: list[int]) -> int:
    return sum(values)

head_sum([1,2,3,'x',5])         # OK (sampling hides later mismatch)  # type: ignore
try:
    full_sum([1,2,3,'x',5])     # type: ignore
except TypeCheckError: pass
```

### Protocols & TypeVars
```python
from typing import Protocol, TypeVar
from typecheck import typecheck, TypeCheckError

class SupportsClose(Protocol):
    def close(self) -> None: ...

@typecheck()
def shutdown(r: SupportsClose) -> None: r.close()

T = TypeVar('T', int, str)
@typecheck()
def echo(x: T) -> T: return x
```

### Custom Validator
```python
from typecheck import register_validator, typecheck, TypeCheckError

class PositiveInt(int):
    pass

@register_validator(PositiveInt)
def _validate_positive(v, t): return isinstance(v, int) and v >= 0

@typecheck()
def square(x: PositiveInt) -> int: return x * x
```

### Async
```python
import asyncio
from typecheck import typecheck

@typecheck()
async def fetch(n: int) -> int:
    return n * 2

asyncio.run(fetch(5))
```

### Strict Modes & Method Selection
Wrap only selected methods or ignore specific ones:

```python
from typecheck import typecheck

@typecheck(include=["process", "finalize"], exclude=["finalize"])  # only "process" gets wrapped
class Job:
    def process(self, x: int) -> int: return x
    def finalize(self, x: int) -> int: return x  # excluded
    def helper(self, x: int) -> int: return x  # not in include list

class Service:
    @typecheck(ignore=True)  # marker to skip when class decorated
    def fast_path(self, x: int) -> int: return x
    @typecheck()
    def strict_path(self, x: int) -> int: return x

Service = typecheck()(Service)
```

Parameters:
* `include=[...]`: Only listed methods (plus `__init__` / `__call__`).
* `exclude=[...]`: Remove methods after inclusion filtering.
* Per-method `@typecheck(ignore=True)`: Skip even under class decorator.

```python
from typecheck import typecheck, config

config.strict_mode = True          # missing parameter annotations raise
config.strict_return_mode = True   # missing return annotations raise
```

---
## Configuration (`typecheck.config`)

| Attribute | Default | Effect |
|-----------|---------|--------|
| `sample_size` | 5 | Default element sample for collections / iterables |
| `strict_mode` | False | Enforce all parameters annotated |
| `strict_return_mode` | False | Enforce return annotation presence (independent of `strict_mode`) |
| `deep_checking` | False | If True, decorator defaults to deep validation when `deep` not passed |
| `lazy_iterable_validation` | True | Sample first N elements of single‑pass iterables via `itertools.tee` |
| `fallback_policy` | "silent" | Behavior for unsupported constructs: silent / warn / error |
| `forward_ref_policy` | "permissive" | Unresolved forward refs: permissive accept or strict error |

Per‑call overrides: `@typecheck(sample=10)`, `@typecheck(deep=True)`, `@typecheck(strict=True)`, etc.

Reset to defaults:
```python
from typecheck import config
config.reset()
```

---
## Fallback Policy

If a construct is unrecognized, `_check_type` accepts it by default (policy `silent`). Change behavior:

```python
from typecheck import config
config.set_fallback_policy("warn")   # or "error"
```

`warn` emits a `RuntimeWarning`; `error` raises immediately.

---
## Error Messages

Errors raise `TypeCheckError` with a concise diagnostic:

```
Type mismatch for parameter 'age' in function 'greet': expected int, got str ('twenty-five')
```

Return mismatches use: `Return value type mismatch in function 'func': expected list[int], got dict (...)`.

---
## Performance Notes

* Cached: resolved `get_type_hints` + origin/args via weak LRU caches.
* Sampling: limits deep traversal cost for large structures & streams.
* Iterables: lazy path avoids exhausting one‑shot generators.
* Overhead on simple primitive calls is typically a handful of microseconds (implementation detail; measure in your environment).

Disable entirely with an environment variable:

```bash
TYPECHECK_DISABLED=1 python your_app.py
```

---
## Custom Validators API

```python
from typecheck import register_validator

@register_validator(MyType)
def validate(value, expected_type) -> bool:
    # return True / False or raise TypeCheckError for custom message
    ...
```

Validators run before built‑in generic origin handlers.

---
## Weak LRU Cache Utility

`typecheck.weak_lru.lru_cache(maxsize=..., typed=False)` behaves like `functools.lru_cache` but stores per‑instance caches for methods in a `WeakKeyDictionary` so instances can be GC’d.

```python
from typecheck import weak_lru

@weak_lru.lru_cache(maxsize=256)
def fib(n: int) -> int:
    return n if n < 2 else fib(n-1)+fib(n-2)
```

Use `.cache_info()` / `.cache_clear()` same as stdlib.

---
## Advanced Topics

* Deep vs Sampled: `@typecheck(deep=True)` enforces full traversal; otherwise first `sample_size` (config or decorator arg) elements validated.
* Lazy Iterables: When `lazy_iterable_validation` is True and object lacks `__len__`, the library samples via `itertools.tee` without consuming the original iterator.
* Protocol Enumeration: Methods, properties, classmethods, staticmethods, and annotated attributes all counted as required members.
* TypeVar Binding: A fresh context per function call enforces consistent multi-parameter binding (subtype-compatible reuse accepted).
* TypeGuard: Treated as `bool` sanity gate.

---
## Testing

The project ships with a comprehensive pytest suite (async, protocols, lazy iterables, custom validators, strict returns, weak LRU). Run:

```bash
pytest --cov=src/typecheck --cov-report=term-missing
```

---
## Roadmap (Abridged)

* Finer-grained Callable variance & keyword kind checking
* Optional stricter Protocol variance rules
* Configurable error formatter hook
* Extended TypedDict total / optional key strictness flags
* Richer metadata usage for `Annotated`

---
## Packaging & Type Information

The distribution includes a `py.typed` marker so static type checkers (mypy, pyright) can consume inline type hints.

Supported Python versions: 3.10, 3.11, 3.12, 3.13.

Partially handled (best-effort) constructs: `LiteralString` (treated as `str`), `Annotated` (metadata ignored). Unsupported advanced forms like `ParamSpec`, `Concatenate`, `Unpack`, `Required` / `NotRequired`, `Self` currently fall back per the fallback policy.

---
## License

AGPL-3.0-or-later. See `LICENSE`.

Rationale: AGPL ensures improvements to the runtime validation mechanics remain available to the community even when
used over a network (a common deployment style for frameworks and services). This license choice is intentional and
will not be changed or re-licensed under a more permissive variant.

---
## Cheat Sheet

| Want | Use |
|------|-----|
| Enforce parameter annotations globally | `config.strict_mode = True` |
| Enforce return annotations too | `config.strict_return_mode = True` |
| Disable sampling for a call | `@typecheck(deep=True)` |
| Increase sampling globally | `config.set_sample_size(10)` |
| Custom validator | `@register_validator(MyType)` |
| Skip runtime cost (env) | `TYPECHECK_DISABLED=1` |
| Validate generator lazily | leave `config.lazy_iterable_validation = True` |
| Strict on one function only | `@typecheck(strict=True)` |

---
Happy checking!

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "runtypecheck",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "typecheck, type-checking, runtime, decorator, validation, typing, annotations",
    "author": null,
    "author_email": "Oliver Kleinke <oliver.kleinke@c-01a.de>",
    "download_url": "https://files.pythonhosted.org/packages/07/b2/8b2997c75def226a7858de9e5df09186fa623d4bed73ee7cb70bbaf861d3/runtypecheck-0.1.0.tar.gz",
    "platform": null,
    "description": "<div align=\"center\">\n\n# runtypecheck\n\nFast, pragmatic runtime type checking for Python 3.10+: a configurable `@typecheck` decorator (and class wrapper) featuring structural `Protocol` validation, constrained & bound `TypeVar` handling, iterable sampling, lazy iterator inspection, async support, custom validators, and a weak-reference LRU cache.\n\n</div>\n\n---\n\n## Why?\n\nStatic type checkers (mypy, pyright, pyre) are invaluable before runtime. This library adds inexpensive runtime assurance at the *boundaries* where static analysis may be weak: plugin entry points, notebook experiments, dynamically constructed data, tests, or external inputs. Design goals:\n\n* Pragmatic: unknown / future typing forms are accepted by default (configurable fallback policy).\n* Fast: caches resolved hints & origins, samples containers, and short\u2011circuits early.\n* Focused: only one decorator + a small config surface; no metaclass trickery.\n* Extensible: drop-in custom validators with a tiny decorator.\n\n---\n## Quick Start\n\n```python\nfrom typecheck import typecheck, TypeCheckError\n\n@typecheck()\ndef greet(name: str, times: int = 1) -> str:\n    return ' '.join([f'Hello {name}!'] * times)\n\nprint(greet('Alice', 2))          # OK\ntry:\n    greet('Bob', 'x')              # type: ignore\nexcept TypeCheckError as e:\n    print('Caught:', e)\n```\n\nApply to a class to wrap all public methods (plus `__init__` / `__call__`).\n\n```python\n@typecheck()\nclass Calc:\n    def add(self, a: int, b: int) -> int: return a + b\n\nCalc().add(1, 2)\n```\n\n---\n## Feature Matrix\n\n| Category | Support Summary |\n|----------|-----------------|\n| Primitives / builtins | Standard `isinstance` semantics |\n| Container generics | `list`, `tuple` (fixed & variadic), `set`, `frozenset`, `dict`, `deque` + ABCs (`Sequence`, `Mapping`, `Iterable`, `Iterator`) |\n| Collections sampling | Validates up to N elements (configurable) unless deep mode |\n| Unions & Optional | Full branch validation with aggregated mismatch context |\n| `Literal[...]` | Membership check |\n| `Callable` | Light structural check: positional arity & simple annotation compatibility |\n| `Type[Cls]` | Class identity / subclass acceptance |\n| `Final` / `ClassVar` | Inner type validated |\n| Structural `Protocol` | Attributes & method signature compatibility (positional params & annotations + return) |\n| `TypeVar` | Constraint / bound enforcement + per-call consistent binding |\n| `Annotated[T, ...]` | Treated as `T` (metadata ignored) |\n| `TypedDict` | Required keys + per-key value validation; extra keys allowed (PEP 589 semantics) |\n| `NewType` | Validated against its underlying supertype |\n| `Never` | Always error if a runtime value is supplied |\n| `NoReturn` | Accepted for parameter context (enforced on returns elsewhere) |\n| `LiteralString` (3.11+) | Treated as `str` (best effort) |\n| `TypeGuard[T]` | Ensures runtime bool result |\n| Forward refs | Permissive or strict (config) |\n| Async functions | Wrapper preserves `async` and validates awaited result |\n| Lazy iterables | Non-length iterables sampled via `itertools.tee` |\n| Deep vs sample | `deep=True` overrides sampling; otherwise first *N* elements |\n| Custom validators | `@register_validator(cls)` mapping exact type -> predicate |\n| Runtime disable | `TYPECHECK_DISABLED=1` env var skips decoration logic |\n\nUnsupported / unrecognized constructs (e.g., advanced future typing forms) fall back to acceptance unless `config.fallback_policy` is set to `warn` or `error`.\n\n---\n## Installation\n\n```bash\npip install runtypecheck\n```\n\nPython 3.10\u20133.13 (tested). Zero runtime dependencies.\n\n---\n## Usage Examples\n\n### Collections & Sampling\n```python\nfrom typecheck import typecheck, TypeCheckError\n\n@typecheck(sample=3)   # only first 3 elements of large list validated\ndef head_sum(values: list[int]) -> int:\n    return sum(values[:3])\n\n@typecheck(deep=True)  # validate every element\ndef full_sum(values: list[int]) -> int:\n    return sum(values)\n\nhead_sum([1,2,3,'x',5])         # OK (sampling hides later mismatch)  # type: ignore\ntry:\n    full_sum([1,2,3,'x',5])     # type: ignore\nexcept TypeCheckError: pass\n```\n\n### Protocols & TypeVars\n```python\nfrom typing import Protocol, TypeVar\nfrom typecheck import typecheck, TypeCheckError\n\nclass SupportsClose(Protocol):\n    def close(self) -> None: ...\n\n@typecheck()\ndef shutdown(r: SupportsClose) -> None: r.close()\n\nT = TypeVar('T', int, str)\n@typecheck()\ndef echo(x: T) -> T: return x\n```\n\n### Custom Validator\n```python\nfrom typecheck import register_validator, typecheck, TypeCheckError\n\nclass PositiveInt(int):\n    pass\n\n@register_validator(PositiveInt)\ndef _validate_positive(v, t): return isinstance(v, int) and v >= 0\n\n@typecheck()\ndef square(x: PositiveInt) -> int: return x * x\n```\n\n### Async\n```python\nimport asyncio\nfrom typecheck import typecheck\n\n@typecheck()\nasync def fetch(n: int) -> int:\n    return n * 2\n\nasyncio.run(fetch(5))\n```\n\n### Strict Modes & Method Selection\nWrap only selected methods or ignore specific ones:\n\n```python\nfrom typecheck import typecheck\n\n@typecheck(include=[\"process\", \"finalize\"], exclude=[\"finalize\"])  # only \"process\" gets wrapped\nclass Job:\n    def process(self, x: int) -> int: return x\n    def finalize(self, x: int) -> int: return x  # excluded\n    def helper(self, x: int) -> int: return x  # not in include list\n\nclass Service:\n    @typecheck(ignore=True)  # marker to skip when class decorated\n    def fast_path(self, x: int) -> int: return x\n    @typecheck()\n    def strict_path(self, x: int) -> int: return x\n\nService = typecheck()(Service)\n```\n\nParameters:\n* `include=[...]`: Only listed methods (plus `__init__` / `__call__`).\n* `exclude=[...]`: Remove methods after inclusion filtering.\n* Per-method `@typecheck(ignore=True)`: Skip even under class decorator.\n\n```python\nfrom typecheck import typecheck, config\n\nconfig.strict_mode = True          # missing parameter annotations raise\nconfig.strict_return_mode = True   # missing return annotations raise\n```\n\n---\n## Configuration (`typecheck.config`)\n\n| Attribute | Default | Effect |\n|-----------|---------|--------|\n| `sample_size` | 5 | Default element sample for collections / iterables |\n| `strict_mode` | False | Enforce all parameters annotated |\n| `strict_return_mode` | False | Enforce return annotation presence (independent of `strict_mode`) |\n| `deep_checking` | False | If True, decorator defaults to deep validation when `deep` not passed |\n| `lazy_iterable_validation` | True | Sample first N elements of single\u2011pass iterables via `itertools.tee` |\n| `fallback_policy` | \"silent\" | Behavior for unsupported constructs: silent / warn / error |\n| `forward_ref_policy` | \"permissive\" | Unresolved forward refs: permissive accept or strict error |\n\nPer\u2011call overrides: `@typecheck(sample=10)`, `@typecheck(deep=True)`, `@typecheck(strict=True)`, etc.\n\nReset to defaults:\n```python\nfrom typecheck import config\nconfig.reset()\n```\n\n---\n## Fallback Policy\n\nIf a construct is unrecognized, `_check_type` accepts it by default (policy `silent`). Change behavior:\n\n```python\nfrom typecheck import config\nconfig.set_fallback_policy(\"warn\")   # or \"error\"\n```\n\n`warn` emits a `RuntimeWarning`; `error` raises immediately.\n\n---\n## Error Messages\n\nErrors raise `TypeCheckError` with a concise diagnostic:\n\n```\nType mismatch for parameter 'age' in function 'greet': expected int, got str ('twenty-five')\n```\n\nReturn mismatches use: `Return value type mismatch in function 'func': expected list[int], got dict (...)`.\n\n---\n## Performance Notes\n\n* Cached: resolved `get_type_hints` + origin/args via weak LRU caches.\n* Sampling: limits deep traversal cost for large structures & streams.\n* Iterables: lazy path avoids exhausting one\u2011shot generators.\n* Overhead on simple primitive calls is typically a handful of microseconds (implementation detail; measure in your environment).\n\nDisable entirely with an environment variable:\n\n```bash\nTYPECHECK_DISABLED=1 python your_app.py\n```\n\n---\n## Custom Validators API\n\n```python\nfrom typecheck import register_validator\n\n@register_validator(MyType)\ndef validate(value, expected_type) -> bool:\n    # return True / False or raise TypeCheckError for custom message\n    ...\n```\n\nValidators run before built\u2011in generic origin handlers.\n\n---\n## Weak LRU Cache Utility\n\n`typecheck.weak_lru.lru_cache(maxsize=..., typed=False)` behaves like `functools.lru_cache` but stores per\u2011instance caches for methods in a `WeakKeyDictionary` so instances can be GC\u2019d.\n\n```python\nfrom typecheck import weak_lru\n\n@weak_lru.lru_cache(maxsize=256)\ndef fib(n: int) -> int:\n    return n if n < 2 else fib(n-1)+fib(n-2)\n```\n\nUse `.cache_info()` / `.cache_clear()` same as stdlib.\n\n---\n## Advanced Topics\n\n* Deep vs Sampled: `@typecheck(deep=True)` enforces full traversal; otherwise first `sample_size` (config or decorator arg) elements validated.\n* Lazy Iterables: When `lazy_iterable_validation` is True and object lacks `__len__`, the library samples via `itertools.tee` without consuming the original iterator.\n* Protocol Enumeration: Methods, properties, classmethods, staticmethods, and annotated attributes all counted as required members.\n* TypeVar Binding: A fresh context per function call enforces consistent multi-parameter binding (subtype-compatible reuse accepted).\n* TypeGuard: Treated as `bool` sanity gate.\n\n---\n## Testing\n\nThe project ships with a comprehensive pytest suite (async, protocols, lazy iterables, custom validators, strict returns, weak LRU). Run:\n\n```bash\npytest --cov=src/typecheck --cov-report=term-missing\n```\n\n---\n## Roadmap (Abridged)\n\n* Finer-grained Callable variance & keyword kind checking\n* Optional stricter Protocol variance rules\n* Configurable error formatter hook\n* Extended TypedDict total / optional key strictness flags\n* Richer metadata usage for `Annotated`\n\n---\n## Packaging & Type Information\n\nThe distribution includes a `py.typed` marker so static type checkers (mypy, pyright) can consume inline type hints.\n\nSupported Python versions: 3.10, 3.11, 3.12, 3.13.\n\nPartially handled (best-effort) constructs: `LiteralString` (treated as `str`), `Annotated` (metadata ignored). Unsupported advanced forms like `ParamSpec`, `Concatenate`, `Unpack`, `Required` / `NotRequired`, `Self` currently fall back per the fallback policy.\n\n---\n## License\n\nAGPL-3.0-or-later. See `LICENSE`.\n\nRationale: AGPL ensures improvements to the runtime validation mechanics remain available to the community even when\nused over a network (a common deployment style for frameworks and services). This license choice is intentional and\nwill not be changed or re-licensed under a more permissive variant.\n\n---\n## Cheat Sheet\n\n| Want | Use |\n|------|-----|\n| Enforce parameter annotations globally | `config.strict_mode = True` |\n| Enforce return annotations too | `config.strict_return_mode = True` |\n| Disable sampling for a call | `@typecheck(deep=True)` |\n| Increase sampling globally | `config.set_sample_size(10)` |\n| Custom validator | `@register_validator(MyType)` |\n| Skip runtime cost (env) | `TYPECHECK_DISABLED=1` |\n| Validate generator lazily | leave `config.lazy_iterable_validation = True` |\n| Strict on one function only | `@typecheck(strict=True)` |\n\n---\nHappy checking!\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Runtime type checking decorator for Python functions and classes",
    "version": "0.1.0",
    "project_urls": {
        "Source": "https://github.com/okleinke/runtypecheck/"
    },
    "split_keywords": [
        "typecheck",
        " type-checking",
        " runtime",
        " decorator",
        " validation",
        " typing",
        " annotations"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8426924fa401483b508af61585f77fb748b0c0aaa69d2371e4d16bd0322a96ee",
                "md5": "704500a28374c65db9accf9f5aab8aa6",
                "sha256": "113820fb7c2f1777b08fda8c04af5b53d57b524e0014b6f9cf075704f1d75cff"
            },
            "downloads": -1,
            "filename": "runtypecheck-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "704500a28374c65db9accf9f5aab8aa6",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 36570,
            "upload_time": "2025-08-17T15:39:32",
            "upload_time_iso_8601": "2025-08-17T15:39:32.436584Z",
            "url": "https://files.pythonhosted.org/packages/84/26/924fa401483b508af61585f77fb748b0c0aaa69d2371e4d16bd0322a96ee/runtypecheck-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "07b28b2997c75def226a7858de9e5df09186fa623d4bed73ee7cb70bbaf861d3",
                "md5": "eb05d67749b41c9fd1b048707c8004b6",
                "sha256": "a6ccfc931e9aa127ce7e68728d17a3cbb518d06cf8ef89b51c619bda74d9ff00"
            },
            "downloads": -1,
            "filename": "runtypecheck-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "eb05d67749b41c9fd1b048707c8004b6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 48004,
            "upload_time": "2025-08-17T15:39:34",
            "upload_time_iso_8601": "2025-08-17T15:39:34.005763Z",
            "url": "https://files.pythonhosted.org/packages/07/b2/8b2997c75def226a7858de9e5df09186fa623d4bed73ee7cb70bbaf861d3/runtypecheck-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-17 15:39:34",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "okleinke",
    "github_project": "runtypecheck",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "runtypecheck"
}
        
Elapsed time: 1.25097s