Skip to main content

Type dispatch and validation for run-time Python

Project description

Runtype

Runtype is a collection of run-time type utilities for Python.

It is:

:runner: Fast! (benchmarks coming soon)

:brain: Smart! Supports typing, constraints, auto-casting, and much more.

:gear: Very customizable. You can use dataclass and dispatch with your own typesystem!


Modules

  • :star: dataclass - Adds run-time type validation to the built-in dataclass.

    • Improves dataclass ergonomics.
    • Supports automatic value casting, Pydantic-style. (Optional, off by default)
    • Supports types with constraints. (e.g. String(max_length=10))
  • :star: dispatch - Provides fast multiple-dispatch for functions and methods, via a decorator.

    • Inspired by Julia.
  • base_types - Provides a set of classes to implement your own type-system.

    • Used by runtype itself, to emulate the Python type-system.
  • isa - Provides a smarter alternative to isinstance and issubclass.

Docs

Read the docs here: https://runtype.readthedocs.io/ (A little outdated. Will fix soon)

Install

$ pip install runtype

No dependencies.

Requires Python 3.6 or up.

Build Status codecov

Multiple Dispatch

Multiple-dispatch is a state-of-the-art technique for structuring code, that complements object-oriented programming.

Unlike in OOP, where the type of the "object" (or: first argument) is always what determines the dispatch, in multiple-dispatch all the arguments decide together, according the idea of specificity: The more specific classes (i.e. subclasses) get picked before the more abstract ones (i.e. superclasses).

That means that when you need to define a logical operation that applies to several types, you can first solve the most abstract case, and then slowly add special handling for more specific types as required. If you ever found yourself writing several "isinstance" in a row, you could probably use multiple-dispatch to write better code!

Runtype's dispatcher is fast, and will never make an arbitrary choice: in ambiguous situations it will always throw an error.

As a side-effect, it also provides type-validation to functions. Trying to dispatch with types that don't match, will result in a dispatch-error.

Type-Safe Dataclasses

The ability to annotate dataclasses with types has spurred the creation of many great static type-validation tools (such as mypy). Unfortunately, they can't always predict what types your dataclasses will receive.

The trouble with storing the wrong data, is that it can just sit there for a while, and by time you get the error, it's hard to track which component or thread put it there.

Runtype provides a dataclass drop-in replacement to Python's native dataclass, that validates the types in runtime, and makes sure you'll see the error the moment something goes wrong, and in the right context.

While Runtype's validation can add a small runtime overhead, it's relatively light. And because it's a drop-in replacement, you can always just switch the import back once you're done debugging.

Examples

Isa & Subclass

from typing import Dict
from runtype import isa, issubclass

isa({'a': 1}, Dict[str, int])      # True
isa({'a': 'b'}, Dict[str, int])    #  False

issubclass(Dict[str, int], Mapping[str, int])   # True
issubclass(Dict[str, int], Mapping[int, str])   # False

Multiple Dispatch

from runtype import Dispatch
dp = Dispatch()

@dp
def append(a: list, b):
    return a + [b]

@dp
def append(a: tuple, b):
    return a + (b,)

@dp
def append(a: str, b: str):
    return a + b


print( append([1, 2, 3], 4) )        # prints [1, 2, 3, 4]
print( append((1, 2, 3), 4) )        # prints (1, 2, 3, 4)
print( append('foo', 'bar') )        # prints foobar
print( append('foo', 4)     )        # raises DispatchError, no signature for (str, int)

Dataclasses

Basic usage:

>>> from runtype import dataclass

>>> @dataclass
>>> class Point:
...     x: int
...     y: int

>>> p = Point(2, 3)
>>> p
Point(x=2, y=3)
>>> dict(p)          # Maintains order
{'x': 2, 'y': 3}

>>> p.replace(x=30)  # New instance
Point(x=30, y=3)

>>> Point(10, 3.5)   # Actively validates types
Traceback (most recent call last):
    ...
TypeError: [Point] Attribute 'y' expected value of type <class 'int'>, instead got 3.5

Using advanced types:

>>> from typing import Optional, Callable
>>> from runtype import dataclass

>>> @dataclass
>>> class Animal:
...     name: str
...     make_sound: Optional[Callable] = None

>>> Animal("Ant")
Animal(name='Ant', make_sound=None)

>>> Animal("Cat", lambda: print("meow"))
Animal(name='Cat', make_sound=<function <lambda> at ...>)

>>> Animal("Dog", "woof")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ...
[Animal] Attribute 'make_sound' expected value of type typing.Union[typing.Callable, NoneType], instead got 'woof'

Performance

Type verification in classes introduces a slight run-time overhead. When running in production, it's recommended to use the -O switch for Python. It will skip all asserts, and also skip type verification on classes by default (use the check_types option to adjust it manually).

Multiple-dispatch caches call-signatures by default (disable at your own risk!), and should add a minimal overhead after the initial resolution. Dispatch is only 5 to 8 times slower than adding two numbers (see: examples/benchmark_dispatch), which is negligible.

Runtype is not recommended for use in functions that are called often in time-critical code (or classes that are created often).

License

Runtype uses the MIT license.

Donate

If you like Runtype and want to show your appreciation, you can do so at my patreon page, or ko-fi page.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

runtype-0.2.1.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

runtype-0.2.1-py3-none-any.whl (19.1 kB view details)

Uploaded Python 3

File details

Details for the file runtype-0.2.1.tar.gz.

File metadata

  • Download URL: runtype-0.2.1.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.7 CPython/3.8.2 Windows/10

File hashes

Hashes for runtype-0.2.1.tar.gz
Algorithm Hash digest
SHA256 5b0d05be090b3a12b832d066d3a4d99091e8c5c0a3af25b4e320b1bd0481b2a9
MD5 4fcf91519d40413b9eda51a584f6687a
BLAKE2b-256 9e2ab05db20765d50fe1e57d443752be9548cd4617987bc3fac5c386f6de4b98

See more details on using hashes here.

File details

Details for the file runtype-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: runtype-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 19.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.1.7 CPython/3.8.2 Windows/10

File hashes

Hashes for runtype-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2a8a1adf419694ecfd85a816b2482c937a3974b6ba51e69fa3952fc06a249c31
MD5 07087e3cf995d2043f83868244c72fbf
BLAKE2b-256 c01c7c7841196667e46573a06f23685464f2aedfebf88fdbd506db3dacc980f0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page