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
300+ Python Interview Questions

300+ Python Interview Questions

Pradeep Kumar 1.1k Jan 02, 2023
level2-data-annotation_cv-level2-cv-15 created by GitHub Classroom

[AI Tech 3기 Level2 P Stage] 글자 검출 대회 팀원 소개 김규리_T3016 박정현_T3094 석진혁_T3109 손정균_T3111 이현진_T3174 임종현_T3182 Overview OCR (Optimal Character Recognition) 기술

6 Jun 10, 2022
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
Modified fork of CPython's ast module that parses `# type:` comments

Typed AST typed_ast is a Python 3 package that provides a Python 2.7 and Python 3 parser similar to the standard ast library. Unlike ast up to Python

Python 217 Dec 06, 2022
Quick tutorial on orchest.io that shows how to build multiple deep learning models on your data with a single line of code using python

Deep AutoViML Pipeline for orchest.io Quickstart Build Deep Learning models with a single line of code: deep_autoviml Deep AutoViML helps you build te

Ram Seshadri 6 Oct 02, 2022
Build documentation in multiple repos into one site.

mkdocs-multirepo-plugin Build documentation in multiple repos into one site. Setup Install plugin using pip: pip install git+https://github.com/jdoiro

Joseph Doiron 47 Dec 28, 2022
Deduplicating archiver with compression and authenticated encryption.

More screencasts: installation, advanced usage What is BorgBackup? BorgBackup (short: Borg) is a deduplicating backup program. Optionally, it supports

BorgBackup 9k Jan 09, 2023
Generate modern Python clients from OpenAPI

openapi-python-client Generate modern Python clients from OpenAPI 3.x documents. This generator does not support OpenAPI 2.x FKA Swagger. If you need

555 Jan 02, 2023
Members: Thomas Longuevergne Program: Network Security Course: 1DV501 Date of submission: 2021-11-02

Mini-project report Members: Thomas Longuevergne Program: Network Security Course: 1DV501 Date of submission: 2021-11-02 Introduction This project was

1 Nov 08, 2021
Python Advanced --- numpy, decorators, networking

Python Advanced --- numpy, decorators, networking (and more?) Hello everyone 👋 This is the project repo for the "Python Advanced - ..." introductory

Andreas Poehlmann 2 Nov 05, 2021
Python 3 wrapper for the Vultr API v2.0

Vultr Python Python wrapper for the Vultr API. https://www.vultr.com https://www.vultr.com/api This is currently a WIP and not complete, but has some

CSSNR 6 Apr 28, 2022
An introduction to hikari, complete with different examples for different command handlers.

An intro to hikari This repo provides some simple examples to get you started with hikari. Contained in this repo are bots designed with both the hika

Ethan Henderson 18 Nov 29, 2022
Comprehensive Python Cheatsheet

Comprehensive Python Cheatsheet Download text file, Buy PDF, Fork me on GitHub or Check out FAQ. Contents 1. Collections: List, Dictionary, Set, Tuple

Jefferson 1 Jan 23, 2022
Read write method - Read files in various types of formats

一个关于所有格式文件读取的方法 1。 问题描述: 各种各样的文件格式,读写操作非常的麻烦,能够有一种方法,可以整合所有格式的文件,方便用户进行读取和写入。 2

2 Jan 26, 2022
In this Github repository I will share my freqtrade files with you. I want to help people with this repository who don't know Freqtrade so much yet.

My Freqtrade stuff In this Github repository I will share my freqtrade files with you. I want to help people with this repository who don't know Freqt

Simon Kebekus 104 Dec 31, 2022
Python bindings to OpenSlide

OpenSlide Python OpenSlide Python is a Python interface to the OpenSlide library. OpenSlide is a C library that provides a simple interface for readin

OpenSlide 297 Dec 21, 2022
A simple flask application to collect annotations for the Turing Change Point Dataset, a benchmark dataset for change point detection algorithms

AnnotateChange Welcome to the repository of the "AnnotateChange" application. This application was created to collect annotations of time series data

The Alan Turing Institute 16 Jul 21, 2022
Lightweight, configurable Sphinx theme. Now the Sphinx default!

What is Alabaster? Alabaster is a visually (c)lean, responsive, configurable theme for the Sphinx documentation system. It is Python 2+3 compatible. I

Jeff Forcier 670 Dec 19, 2022
DocumentPy is a Python application that runs in a command-line interface environment, made for creating HTML documents.

DocumentPy DocumentPy is a Python application that runs in a command-line interface environment, made for creating HTML documents. Usage DocumentPy, a

Lotus 0 Jul 15, 2021
A collection of online resources to help you on your Tech journey.

Everything Tech Resources & Projects About The Project Coming from an engineering background and looking to up skill yourself on a new field can be di

Mohamed A 396 Dec 31, 2022