Skip to main content

Type-Driven Development for Python

Project description

pytyped

pytyped is a Python package whose goal is to enable as much type-driven development as possible in Python. We believe in using types to automate mundane and repetitive tasks. Currently, given a type, JSON decoders/encoders and metric extractors can be automatically extracted for that type.

Installation

You can install pytyped from PyPI:

pip install pytyped

pytyped is checked on Python 3.6+.

Why pytyped?

To our knowledge, pytyped is the only Python package that supports type-based automation for all typing constructs including even recursive types that, up to this day, is not even fully supported by Python itself yet. Additionally, pytyped is designed to be extensible. That is, despite the fact that JSON encoding/decoding provided by pytyped is a very nice feature, it's only one the use cases of pytyped. In the past, we have successfully used pytyped in different scenarios such as automating report generation based on data types as well as automating DynamoDB interactions based on the type of the data that is being stored/retrieved.

In order to see how you can automate your own workflow on top of pytyped, see how JSON decoding/encoding and metric extractions use pytyped.macros to achieve type-driven meta-programming.

Currently, pytyped supports the following type classes:

  • Basic types such as int, bool, date, datetime, str, and Decimal.
  • Simple usual combinators such as List[T] and Dict[A, B].
  • Named product types such as NamedTuples or dataclasses.
  • Anonymous product types such as Tuple[T1, T2, ...].
  • Anonymous union types such as Optional[T], Union[T1, T2, ...], etc.
  • Named union types such as class hierarchies.
  • Generic types and type variables.
  • Recursive types.

Using pytyped to extract JSON decoders/encoders

First, define your type. For example, in the following we want to define an account that can either be a personal account or a business account with personal account having one owner and possibly a co-owner while a business account has the company name as the owner and a list of persons that can represent the company.

from dataclasses import dataclass
from datetime import datetime
from typing import List
from typing import Optional

@dataclass
class Person:
    first_name: str
    last_name: str


@dataclass
class Account:
    created_at: datetime


@dataclass
class PersonalAccount(Account):
    owner: Person
    co_owner: Optional[Person]


@dataclass
class BusinessAccount(Account):
    owner: str
    representatives: List[Person]

Second, use an instance of AutoJsonDecoder and AutoJsonEncoder to extract JSON decoders and encoders as below:

from pytyped.json.decoder import AutoJsonDecoder
from pytyped.json.decoder import JsonDecoder
from pytyped.json.encoder import AutoJsonEncoder
from pytyped.json.encoder import JsonEncoder

_auto_json_decoder = AutoJsonDecoder()
_auto_json_encoder = AutoJsonEncoder()

account_decoder: JsonDecoder[Account] = _auto_json_decoder.extract(Account)
account_encoder: JsonEncoder[Account] = _auto_json_encoder.extract(Account)

Third, define some instances of the Account class:

personal_account = PersonalAccount(
    created_at = datetime.now(),
    owner = Person(first_name = "John", last_name = "Doe"),
    co_owner = None
)

business_account = BusinessAccount(
    created_at = datetime.now(),
    owner = "Doe Ltd.",
    representatives = [Person(first_name = "John", last_name = "Doe"), Person(first_name = "Jane", last_name = "Doe")]
) 

Finally, use account_encoder and account_decoder to convert data in your instances to/from JSON as below:

>>> json = account_encoder.write(personal_account)
>>> json
{'created_at': '2020-08-24T20:00:18.205347', 'owner': {'first_name': 'John', 'last_name': 'Doe'}, 'co_owner': None, 'Account': 'PersonalAccount'}
>>> account_decoder.read(json)
PersonalAccount(created_at=datetime.datetime(2020, 8, 24, 20, 0, 18, 205347), owner=Person(first_name='John', last_name='Doe'), co_owner=None)


>>> json = account_encoder.write(business_account)
>>> json
{'created_at': '2020-08-24T20:00:40.057088', 'owner': 'Doe Ltd.', 'representatives': [{'first_name': 'John', 'last_name': 'Doe'}, {'first_name': 'Jane', 'last_name': 'Doe'}], 'Account': 'BusinessAccount'}
>>> account_decoder.read(json)
BusinessAccount(created_at=datetime.datetime(2020, 8, 24, 20, 0, 40, 57088), owner='Doe Ltd.', representatives=[Person(first_name='John', last_name='Doe'), Person(first_name='Jane', last_name='Doe')])

To illustrate the types of validation that JSON decoders enable for you, consider the following example invalid JSONs:

>>> account_decoder.read({'created_at': '2020-08-24T20:00:40.057088', 'owner': 'Doe Ltd.', 'representatives': [{'first_name': 'John', 'last_name': 'Doe'}, {'first_name': 'Jane', 'last_name': 'Doe'}], 'Account': 'NewAccount'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "~/Repos/pytyped/pytyped/json/decoder.py", line 91, in read
    raise JsDecodeException(t_or_error)
pytyped.json.decoder.JsDecodeException: Found 1 errors while validating JSON: [
  Error when decoding JSON: /Account: Unknown tag value NewAccount (possible values are: PersonalAccount, BusinessAccount).]


>>> account_decoder.read({'created_at': '2020-08-24T20:00:40.057088', 'owner': 'Doe Ltd.', 'representatives': [{'first_name': 'John', 'last_name': 'Doe'}, {'first_name': 'Jane', 'last': 'Doe'}], 'Account': 'BusinessAccount'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/shahab/Repos/pytyped/pytyped/json/decoder.py", line 91, in read
    raise JsDecodeException(t_or_error)
pytyped.json.decoder.JsDecodeException: Found 1 errors while validating JSON: [
  Error when decoding JSON: /representatives[1]/last_name: Non-optional field was not found]

Using pytyped to extract metrics

Similar to extracting JSON decoders/encoders except that pytyped.metrics.exporter.AutoMetricExporter is used. Further explanation is WIP.

Defining New Type Automations

You can follow the examples of JSON decoders, JSON encoders, and metric exporters to define new type-based automations. You just need to extend pytyped.macros.extractor.Extractor and implement the abstrtact methods there. Further explanation is WIP.

Issues

Please report any issues to the GitHub repository for this package.

Contributors

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

pytyped-0.1.0.tar.gz (23.3 kB view details)

Uploaded Source

Built Distribution

pytyped-0.1.0-py3-none-any.whl (38.8 kB view details)

Uploaded Python 3

File details

Details for the file pytyped-0.1.0.tar.gz.

File metadata

  • Download URL: pytyped-0.1.0.tar.gz
  • Upload date:
  • Size: 23.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.5.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.61.1 CPython/3.6.9

File hashes

Hashes for pytyped-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e97421d214b09d420b754ce892704493e6bdbb5c156fde873535947f0b8e29e3
MD5 4443c0c52d1e2f263944d4826dddff77
BLAKE2b-256 04c1529f8cbffe638d2504c62c6ee041244a6d7e286f0cf714b338f74fab1d6f

See more details on using hashes here.

File details

Details for the file pytyped-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pytyped-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 38.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.5.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.61.1 CPython/3.6.9

File hashes

Hashes for pytyped-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 81c12d5e4fd1a54fa45c5e752583f23c9265521b6246adc7d2d75be163d9c288
MD5 883c945cb58ddad268fe5de7ef6b8517
BLAKE2b-256 8846a5df8a726b89838d3d3757c6431762616ac1a1e84e9161ec197dab26a17f

See more details on using hashes here.

Supported by

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