Types that make coding in Python quick and safe.

Overview

Type[T]

PyPI Python Versions Build Status Coverage Status Code Quality

Types that make coding in Python quick and safe.

Type[T] works best with Python 3.6 or later. Prior to 3.6, object types must use comment type hint syntax.

Installation

Install it using pip:

pip install typet

Features

  • An Object base class that eliminates boilerplate code and verifies and coerces types when possible.
  • Validation types that, when instantiated, create an instance of a specific type and verify that they are within the user defined boundaries for the type.

Quick Start: Creating a Person

Import the Type[T] types that you will use.

from typet import Bounded, Object, String
  • Object, for composing complex objects
  • Bound to describe a type that validates its value is of the correct type and within bounds upon instantiation
  • String, which will validate that it is instantiated with a string with a length within the defined bounds.

Create Type Aliases That Describe the Intent of the Type

Age = Bounded[int, 0:150]
Name = String[1:50]
Hobby = String[1:300]

In this example, a Person has an Age, which is an integer between 0 and 150, inclusive; a Name which must be a non-empty string with no more than 50 characters; and finally, a Hobby, which is a non-empty string with no more than 300 characters.

Compose a Person object Using Type Aliases

class Person(Object):
    name: Name
    surname: Name
    age: Age
    hobby: Hobby = None

Assigning a class attribute sets that value as the default value for instances of the Object. In this instance, hobby is assigned a default value of None; by convention, this tells Python that the type is Optional[Hobby], and Type[T] will allow None in addition to strings of the correct length.

Put It All Together

from typet import Bounded, Object, String

Age = Bounded[int, 0:150]
Name = String[1:50]
Hobby = String[1:300]

class Person(Object):
    name: Name
    surname: Name
    age: Age
    hobby: Hobby = None

Person is now a clearly defined and typed object with an intuitive constructor, hash method, comparison operators and bounds checking.

Positional arguments will be in the order of the definition of class attributes, and keyword arguments are also acceptable.

jim = Person('Jim', 'Coder', 23, 'Python')
bob = Person('Robert', 'Coder', hobby='C++', age=51)

Python 2.7 to 3.5

Type[T] supports PEP 484 class comment type hints for defining an Object.

from typing import Optional

from typet import Bounded, Object, String

Age = Bounded[int, 0:150]
Name = String[1:50]
Hobby = String[1:300]

class Person(Object):
    name = None  # type: Name
    surname = None  # type: Name
    age = None  # type: Age
    hobby = None  # type: Optional[Hobby]

Note that, because Python prior to 3.6 cannot annotate an attribute without defining it, by convention, assigning the attribute to None will not imply that it is optional; it must be specified explicitly in the type hint comment.

Object Types

Object

One of the cooler features of Type[T] is the ability to create complex objects with very little code. The following code creates an object that generates properties from the annotated class attributes that will ensure that only values of int or that can be coerced into int can be set. It also generates a full suite of common comparison methods.

from typet import Object

class Point(Object):
    x: int
    y: int

Point objects can be used intuitively because they generate a standard __init__ method that will allow positional and keyword arguments.

p1 = Point(0, 0)      # Point(x=0, y=0)
p2 = Point('2', 2.5)  # Point(x=2, y=2)
p3 = Point(y=5, x=2)  # Point(x=2, y=5)
assert p1 < p2        # True
assert p2 < p1        # AssertionError

A close equivalent traditional class would be much larger, would have to be updated for any new attributes, and wouldn't support more advanced casting, such as to types annotated using the typing module:

class Point(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Point(x={x}, y={y})'.format(x=self.x, y=self.y)

    def __setattr__(self, name, value):
        if name in ('x', 'y'):
            value = int(value)
        super(Point, self).__setattr__(name, value)

    def __eq__(self, other):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

    def __ne__(self, other):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.x, self.y) != (other.x, other.y)

    def __lt__(self, other):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.x, self.y) < (other.x, other.y)

    def __le__(self, other):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.x, self.y) <= (other.x, other.y)

    def __gt__(self, other):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.x, self.y) > (other.x, other.y)

    def __ge__(self, other):
        if other.__class__ is not self.__class__:
            return NotImplemented
        return (self.x, self.y) >= (other.x, other.y)

    def __hash__(self):
        return hash((self.x, self.y))

Attributes can be declared optional either manually, by using typing.Optional or by using the PEP 484 implicit optional of a default value of None.

from typing import Optional

from typet import Object

class Point(Object):
    x: Optional[int]
    y: int = None

p1 = Point()   # Point(x=None, y=None)
p2 = Point(5)  # Point(x=5, y=None)

StrictObject

By default, Object will use cast from typingplus to attempt to coerce any values supplied to attributes to the annotated type. In some cases, it may be preferred to disallow casting and only allow types that are already of the correct type. StrictObject has all of the features of Object, but will not coerce values into the annotated type.

from typet import StrictObject

class Point(StrictObject):
    x: int
    y: int

Point(0, 0)      # Okay
Point('2', 2.5)  # Raises TypeError

StrictObject uses is_instance from typingplus to check types, so it's possible to use types from the typing library for stricter checking.

from typing import List

from typet import StrictObject

class IntegerContainer(StrictObject):
    integers: List[int]

IntegerContainer([0, 1, 2, 3])          # Okay
IntegerContainer(['a', 'b', 'c', 'd'])  # Raises TypeError

Validation Types

Type[T] contains a suite of sliceable classes that will create bounded, or validated, versions of those types that always assert their values are within bounds; however, when an instance of a bounded type is instantiated, the instance will be of the original type.

Bounded

Bounded can be sliced with either two arguments or three. The first argument is the type being bound. The second is a slice containing the upper and lower bounds used for comparison during instantiation.

from typet import Bounded

BoundedInt = Bounded[int, 10:20]

BoundedInt(15)  # Okay
type(x)         # <class 'int'>
BoundedInt(5)   # Raises ValueError

Optionally, a third argument, a function, may be supplied that will be run on the value before the comparison.

from typet import Bounded

LengthBoundedString = Bounded[str, 1:3, len]

LengthBoundedString('ab')    # Okay
LengthBoundedString('')      # Raises ValueError
LengthBoundedString('abcd')  # Raises ValueError

Length

Because len is a common comparison method, there is a shortcut type, Length that takes two arguments and uses len as the comparison method.

from typing import List

from typet import Length

LengthBoundedList = Length[List[int], 1:3]

LengthBoundedList([1, 2])        # Okay
LengthBoundedList([])            # Raises ValueError
LengthBoundedList([1, 2, 3, 4])  # Raises ValueError

String

str and len are commonly used together so a special type, String, has been added to simplify binding strings to specific lengths.

from typet import String

ShortString = String[1:3]

ShortString('ab')    # Okay
ShortString('')      # Raises ValueError
ShortString('abcd')  # Raises ValueError

Note that, on Python 2, String instantiates unicode objects and not str.

Metaclasses and Utilities

Singleton

Singleton will cause a class to allow only one instance.

from typet import Singleton

class Config(metaclass=Singleton):
    pass

c1 = Config()
c2 = Config()
assert c1 is c2  # Okay

Singleton supports an optional __singleton__ method on the class that will allow the instance to update if given new parameters.

from typet import Singleton

class Config(metaclass=Singleton):

    def __init__(self, x):
        self.x = x

    def __singleton__(self, x=None):
        if x:
            self.x = x

c1 = Config(1)
c1.x                   # 1
c2 = Config()          # Okay because __init__ is not called.
c2.x                   # 1
c3 = Config(3)         # Calls __singleton__ if it exists.
c1.x                   # 3
c2.x                   # 3
c3.x                   # 3
assert c1 is c2 is c3  # Okay

@singleton

Additionally, there is a decorator, @singleton that can be used make a class a singleton, even if it already uses another metaclass. This is convenient for creating singleton Objects.

from typet import Object, singleton

@singleton
class Config(Object):
    x: int

c1 = Config(1)
c2 = Config()    # Okay because __init__ is not called.
assert c1 is c2  # Okay

@metaclass

Type[T] contains a class decorator, @metaclass, that will create a derivative metaclass from the given metaclasses and the metaclass used by the decorated class and recreate the class with the derived metaclass.

Most metaclasses are not designed to be used in such a way, so careful testing must be performed when this decorator is to be used. It is primarily intended to ease use of additional metaclasses with Objects.

from typet import metaclass, Object, Singleton

@metaclass(Singleton)
class Config(Object):
    x: int

c1 = Config(1)
c2 = Config()    # Okay because __init__ is not called.
assert c1 is c2  # Okay
Comments
  • The Singleton meta class unexpectedly raises a TypeError when we thought __instance__ existed

    The Singleton meta class unexpectedly raises a TypeError when we thought __instance__ existed

    In [4]: _RcliConfig.call()

    TypeError Traceback (most recent call last) in () ----> 1 _RcliConfig.call()

    ~/rcli/venv/lib/python3.6/site-packages/typet/meta.py in call(cls, args, **kwargs) 78 else: 79 try: ---> 80 cls.instance.singleton(args, **kwargs) # type: ignore 81 except AttributeError: 82 pass

    TypeError: 'NoneType' object is not callable

    The code in question is here:

    https://github.com/contains-io/typet/blob/master/typet/meta.py#L64

    opened by zancas 4
  • Add base classes SingletonObject and StrictSingletonObject

    Add base classes SingletonObject and StrictSingletonObject

    Create a SingletonObject by creating a private metaclass that inherits from Singleton and _ObjectMeta and a metaclass for a StrictSingletonObject that inherits from Singleton and _StrictObjectMeta.

    The proposed use case would be a global settings object:

    from typet import SingletonObject, File
    
    class _Configuration(SingletonObject):
        config: File = '~/.my_config'
    
    settings = _Configuration()
    
    enhancement 
    opened by dangle 1
  • Add support for type comments.

    Add support for type comments.

    Because the annotations are read during the metaclass before the class is created, it may be necessary to add support for reading the type hint comments in the metaclass.

    enhancement 
    opened by dangle 1
  • Register validation types as the sliced type.

    Register validation types as the sliced type.

    It should be possible to run this test:

    from typingplus import is_instance
    from typet import Bounded
    assert is_instance(5, Bounded[int, 0:10])
    

    I believe this causes an issue when using validation types with StrictObject.

    This can be done by setting __instancecheck__ and __subclasscheck__ in BoundedMeta._BoundedSubclass.

    bug 
    opened by dangle 1
  • Break package into multiple modules.

    Break package into multiple modules.

    typet/__init__.py is becoming unwieldy. It should be broken into multiple packages and imported into the package with wildcards.

    Initial proposed packages:

    • typet
    • typet.path
    • typet.validation
    • typet.object
    refactor 
    opened by dangle 0
  • Path validation objects should use pathlib.

    Path validation objects should use pathlib.

    The path validation objects, File, Dir, Path, and ExistingPath should instantiate pathlib objects instead of strings. They should also accept pathlib objects.

    A check to import pathlib2 will need to be added to setup.py.

    bug 
    opened by dangle 0
  • Using the @singleton errors on default metaclass.

    Using the @singleton errors on default metaclass.

    When using @singleton on a class that does not inherit from Object (no other metaclasses defined) throws an exception.

    Traceback (most recent call last):
      File "<input>", line 1, in <module>
        @singleton
      File "/home/dangle/Projects/contains.io/containment/.tox/py36/lib/python3.6/site-packages/typet/meta.py", line 58, in _inner
        class _Meta(base, _Meta):  # pylint: disable=function-redefined
    TypeError: Cannot create a consistent method resolution
    order (MRO) for bases type, Singleton
    
    bug 
    opened by dangle 0
  • Add support for Generics in Object and StrictObject

    Add support for Generics in Object and StrictObject

    Currently, using Generics with Object is a pain. Initial support for casting and type validation exist, but creating the class itself is awkward as it requires creating a metaclass.

    from typet import Object
    from typing import Generic, GenericMeta, TypeVar
    
    T = TypeVar('T')
    
    class Meta(type(Object), GenericMeta): ...
    
    class MyObject(Object, Generic[T], meta=Meta):
        value: T
    
    enhancement 
    opened by dangle 0
  • Non Object attributes violate the expectations of the __hash__ function.

    Non Object attributes violate the expectations of the __hash__ function.

    If I make a proper typet Object, I can still assign to its attributes at run time, subsequent comparison or hash operations on the resulting instance will now return unexpected results. I believe the solution is to add logic to set_attr to prevent non-Object, or StrictObject assignment.

    opened by zancas 1
  • Validation types do not work with mypy.

    Validation types do not work with mypy.

    mypy reports validation objects as invalid type aliases.

    Additionally, if a validation type is used as a class annotation without being aliased, mypy ends analysis with an invalid syntax error.

    See python/mypy#4285.

    blocked 
    opened by dangle 0
Owner
Contains
Contains
The tutorial is a collection of many other resources and my own notes

Why we need CTC? --- looking back on history 1.1. About CRNN 1.2. from Cross Entropy Loss to CTC Loss Details about CTC 2.1. intuition: forward algor

手写AI 7 Sep 19, 2022
A collection of simple python mini projects to enhance your python skills

A collection of simple python mini projects to enhance your python skills

PYTHON WORLD 12.1k Jan 05, 2023
Make posters from Markdown files.

MkPosters Create posters using Markdown. Supports icons, admonitions, and LaTeX mathematics. At the moment it is restricted to the specific layout of

Patrick Kidger 243 Dec 20, 2022
Sphinx Theme Builder

Sphinx Theme Builder Streamline the Sphinx theme development workflow, by building upon existing standardised tools. and provide a: simplified packagi

Pradyun Gedam 23 Dec 26, 2022
A python package to avoid writing and maintaining duplicated python docstrings.

docstring-inheritance is a python package to avoid writing and maintaining duplicated python docstrings.

Antoine Dechaume 15 Dec 07, 2022
More detailed upload statistics for Nicotine+

More Upload Statistics A small plugin for Nicotine+ 3.1+ to create more detailed upload statistics. ⚠ No data previous to enabling this plugin will be

Nick 1 Dec 17, 2021
Tampilan - Change Termux Appearance With Python

Tampilan Gambar usage pkg update && pkg upgrade pkg install git && pkg install f

Creator Lord-Botz 1 Jan 31, 2022
Feature Store for Machine Learning

Overview Feast is an open source feature store for machine learning. Feast is the fastest path to productionizing analytic data for model training and

Feast 3.8k Dec 30, 2022
CoderByte | Practice, Tutorials & Interview Preparation Solutions|

CoderByte | Practice, Tutorials & Interview Preparation Solutions This repository consists of solutions to CoderByte practice, tutorials, and intervie

Eda AYDIN 6 Aug 09, 2022
Bring RGB to life in Neovim

Bring RGB to life in Neovim Change your RGB devices' color depending on Neovim's mode. Fast and asynchronous plugin to live your vim-life to the fulle

Antoine 40 Oct 27, 2022
Always know what to expect from your data.

Great Expectations Always know what to expect from your data. Introduction Great Expectations helps data teams eliminate pipeline debt, through data t

Great Expectations 7.8k Jan 05, 2023
k3heap is a binary min heap implemented with reference

k3heap k3heap is a binary min heap implemented with reference k3heap is a component of pykit3 project: a python3 toolkit set. In this module RefHeap i

pykit3 1 Nov 13, 2021
A simple tutorial to get you started with Discord and it's Python API

Hello there Feel free to fork and star, open issues if there are typos or you have a doubt. I decided to make this post because as a newbie I never fo

Sachit 1 Nov 01, 2021
Sane and flexible OpenAPI 3 schema generation for Django REST framework.

drf-spectacular Sane and flexible OpenAPI 3.0 schema generation for Django REST framework. This project has 3 goals: Extract as much schema informatio

T. Franzel 1.4k Jan 08, 2023
Sphinx-performance - CLI tool to measure the build time of different, free configurable Sphinx-Projects

CLI tool to measure the build time of different, free configurable Sphinx-Projec

useblocks 11 Nov 25, 2022
Tutorial for STARKs with supporting code in python

stark-anatomy STARK tutorial with supporting code in python Outline: introduction overview of STARKs basic tools -- algebra and polynomials FRI low de

121 Jan 03, 2023
An introduction course for Python provided by VetsInTech

Introduction to Python This is an introduction course for Python provided by VetsInTech. For every "boot camp", there usually is a pre-req, but becaus

Vets In Tech 2 Dec 02, 2021
Credit EDA Case Study Using Python

This case study aims to identify patterns which indicate if a client has difficulty paying their installments which may be used for taking actions such as denying the loan, reducing the amount of loa

Purvi Padliya 1 Jan 14, 2022
Testing-crud-login-drf - Creation of an application in django on music albums

testing-crud-login-drf Creation of an application in django on music albums Befo

Juan 1 Jan 11, 2022
An MkDocs plugin that simplifies configuring page titles and their order

MkDocs Awesome Pages Plugin An MkDocs plugin that simplifies configuring page titles and their order The awesome-pages plugin allows you to customize

Lukas Geiter 282 Dec 28, 2022