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
Read write method - Read files in various types of formats

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

null 2 Jan 26, 2022
Template repo to quickly make a tested and documented GitHub action in Python with Poetry

Python + Poetry GitHub Action Template Getting started from the template Rename the src/action_python_poetry package. Globally replace instances of ac

Kevin Duff 89 Dec 25, 2022
This is a tool to make easier brawl stars modding using csv manipulation

Brawler Maker : Modding Tool for Brawl Stars This is a tool to make easier brawl stars modding using csv manipulation if you want to support me, just

null 6 Nov 16, 2022
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
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 7, 2022
A comprehensive and FREE Online Python Development tutorial going step-by-step into the world of Python.

FREE Reverse Engineering Self-Study Course HERE Fundamental Python The book and code repo for the FREE Fundamental Python book by Kevin Thomas. FREE B

Kevin Thomas 7 Mar 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 5, 2023
Python Eacc is a minimalist but flexible Lexer/Parser tool in Python.

Python Eacc is a parsing tool it implements a flexible lexer and a straightforward approach to analyze documents.

Iury de oliveira gomes figueiredo 60 Nov 16, 2022
Repository for learning Python (Python Tutorial)

Repository for learning Python (Python Tutorial) Languages and Tools ?? Overview ?? Repository for learning Python (Python Tutorial) Languages and Too

Swiftman 2 Aug 22, 2022
advance python series: Data Classes, OOPs, python

Working With Pydantic - Built-in Data Process ========================== Normal way to process data (reading json file): the normal princiople, it's f

Phung Hưng Binh 1 Nov 8, 2021
A simple USI Shogi Engine written in python using python-shogi.

Revengeshogi My attempt at creating a USI Shogi Engine in python using python-shogi. Current State of Engine Currently only generating random moves us

null 1 Jan 6, 2022
Python-slp - Side Ledger Protocol With Python

Side Ledger Protocol Run python-slp node First install Mongo DB and run the mong

Solar 3 Mar 2, 2022
Python-samples - This project is to help someone need some practices when learning python language

Python-samples - This project is to help someone need some practices when learning python language

Gui Chen 0 Feb 14, 2022
Valentine-with-Python - A Python program generates an animation of a heart with cool texts of your loved one

Valentine with Python Valentines with Python is a mini fun project I have coded.

Niraj Tiwari 4 Dec 31, 2022
🏆 A ranked list of awesome python developer tools and libraries. Updated weekly.

Best-of Python Developer Tools ?? A ranked list of awesome python developer tools and libraries. Updated weekly. This curated list contains 250 awesom

Machine Learning Tooling 646 Jan 7, 2023
API spec validator and OpenAPI document generator for Python web frameworks.

API spec validator and OpenAPI document generator for Python web frameworks.

1001001 249 Dec 22, 2022
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 1, 2021
Coursera learning course Python the basics. Programming exercises and tasks

HSE_Python_the_basics Welcome to BAsics programming Python! You’re joining thousands of learners currently enrolled in the course. I'm excited to have

PavelRyzhkov 0 Jan 5, 2022
A collection and example code of every topic you need to know about in the basics of Python.

The Python Beginners Guide: Master The Python Basics Tonight This guide is a collection of every topic you need to know about in the basics of Python.

Ahmed Baari 1 Dec 19, 2021