About

Here you will find some important changelogs for the library typed. For the full list of tags, see here.

  1. v0.1.9: extendable models and exact models

  2. v0.1.13: base type factories as functors

  3. v0.1.15: type safety for variables

  4. v0.3.0: compatibility with pydantic and dataclasses

  5. v0.4.0: inclusion of new model types

  6. v0.4.1: optional without default value

  7. v0.4.2: refactoring function types

  8. v0.4.3: introduction of @optional decorator

  9. v0.4.4: introduction of model attributes

  10. v0.4.5: optional and mandatory models

  11. v0.5.0: refactor of factories

  12. v0.5.1: introduce __null__ in factories

  13. v0.5.2: review the basic types

  14. v0.5.3: symmetrization of basic factories

  15. v0.6.0: reorganize structure and imports

  16. v0.7.0: introduction of dependent types

v0.1.9: extendable models and exact models

The possibility of creating a Model or an ExactModel that inherits all components from previously constructed models has been introduced.

 1from typed import *
 2from typed.models import Model
 3
 4Model1 = Model(
 5    arg1=Str,
 6    arg2=List(Dict(Int)),
 7    # ...
 8)
 9
10Model2 = Model(
11    argA=Float,
12    argB=Union(Null(Dict(Str)), Str)
13    # ....
14)
15
16Model3 = Model(
17    __extends__=[Model1, Model2],
18    other_arg=SomeType,
19    # ....
20)

Fully compatible with typed functions.

v0.1.13: base type factories as functors

In typed, there are various type factories, which operate between types to construct new types.

Among these factories are the “base factories,” which essentially correspond to implementations as concrete types of annotations from the typing library, along with a few more factories.

Example.

  1. from typing: Union, List, Tuple, Dict, Set, …

  2. additionalib: Prod, UProd, Null, …

Types have a semantics given by a certain class of sets. Among the types, we have typed functions, so we can think of the category TYPE, where objects are types and morphisms are typed functions.

In this sense, type factories operate at the level of TYPE objects, and it would be natural to try to extend them to functors F: TYPE -> TYPE.

In version typed v0.1.13, this was done for the base type factories listed above.

This means, for example, that we can now take a function: f: SomeType -> OtherType and apply the List, Tuple, etc. operations to it:

List(f): List(f.domain) -> List(f.codomain).

Furthermore, such a construction is done in a way that compositions are respected:

List(g*f) == List(g)*List(f)

Naturally, operations at the function level can be concatenated (just as they were at the type level).

This brings typed closer to providing not only a “typed approach” to Python, but also a truly functional approach.

v0.1.15: type safety for variables

Previously, typed was limited to providing factories (and models) to create new types and to check types passed as type hints to typed functions calls.

Now, it’s also possible to use typed to check values assigned to variables.

Instead of:

1my_var = some_value

use:

1my_var = typed(ExpectedType)(some_value)

At runtime, the interpreter will check if the variable my_var has a value whose type is ExpectedType.

v0.3.0: compatibility with pydantic and dataclasses

It is now possible to transform any class into a model, exact model, or conditional model.

In version typed v0.3.0, the decorators @model, @exact, and @conditional were included. These decorators can be applied to any class, collecting its attributes and passing them as arguments to the Model, Exact, and Conditional type factories, respectively.

This, in particular, allows you to transform other types of model constructs (such as pydantic models and dataclasses) into models in the typed sense.

This also enables better integration with LSPs (type factories are dynamic by nature, while classes are static).

v0.4.0: inclusion of new model types

The typed models system has been completely refactored. It is now possible to create custom model factories from the ModelFactory metaclass.

Additionally, new model factories have been introduced:

  1. Ordered: Validates data with respect to the ordering of entries.

  2. Rigid: Validates data with respect to the exactness and ordering of entries.

The Conditional model factory has been removed. It is now possible to pass a __conditions__ variable to each of the model factories, which defines additional conditions for validation.

v0.4.1: optional without default value

In typed, you can create models by applying the @model decorator to a class. If you want an entry to be optional, you simply use the Optional type factory, passing the type with a default value:

1from typed import SomeType, OtherType
2from typed.models import model, Optional
3
4@model
5class MyModelib:
6    x: SomeType
7    y: Optional(OtherType, some_value)

The vast majority of typed’s builtin types (and also those constructed by its type factories) are nullable, meaning that a “null object” is defined for them, which can be accessed via the null function.

In the new update, if a value is not passed along with a type in the Optional function, it attempts to take that value as the null object of the type in question:

1Optional(SomeType) = Optional(SomeType, null(SomeType))

Naturally, if the type in question is not nullable, an error is returned.

v0.4.2: refactoring function types

One of typed premisses is:

If a type X is a subtype of a type Y, then every instance of X should be an instance of Y.

This is not a natural behavior of Python, which completely distinguishes elements of a class from the elements of any of its extensions.

Mathematically, this is not the expected behavior, something we have “fixed” within typed’s builtin types and type factories.

To exhibit such behavior, classes need to be constructed from specific metaclasses, in which we specify (within the __instancecheck__ method) the necessary conditions to validate instantiation.

I noticed that some function types were not exhibiting the desired behavior. They were then refactored, and now there is a natural chain of function types, in which one is not only a subtype of the other but also preserves instantiation:

  • Callable: general callable entities

  • Builtin: builtin functions

  • Lambda: lambda functions

  • Function (or FuncType): user defined functions

  • CompFuncType: composable functions

  • HintedDomFuncType: user defined functions that have type hint in domain

  • HintedCodFuncType: user defined functions that have type hint in codomain

  • HintedFuncType: user defined functions that have type hint in both domain and codomain

  • TypedDomFuncType: user defined functions with type hints in domain, which is checked at runtime

  • TypedCodFuncType: user defined functions with type hints in codomain, which is checked at runtime

  • TypedFuncType: user defined functions with type hints in domain and codomain, which are checked at runtime

  • BoolFuncType: user defined typed functions whose codomain is Bool

v0.4.3: introduction of @optional decorator

In some cases, we need to build models where all inputs are optional. In the new version of typed, a decorator to facilitate the construction of this type of modelib:

1from typed.models import optional
2
3@optional
4class MyModelib:
5    some_var: SomeType
6    other_var: OtherType = some_value
7    ...

The snippet above is equivalent to the following:

1from typed.models import model, Optional
2
3@model
4class MyModelib:
5    some_var: Optional(Maybe(SomeType), None)
6    other_var: Optional(OtherType, some_value)
7    ...

Here, Maybe is the type factory that receives a type and returns Union(SomeType, None). This means that, by default, @optional assumes that if a default value is not provided, then None is taken as the default.

Instead of passing None as the default, you might want to pass the null object of the type (if it is nullable). To do this, simply use the nullable variable:

1from typed.models import optional
2
3@optional(nullable=True)
4class MyModelib:
5    some_var: SomeType
6    other_var: OtherType = some_value
7    ...

In this case, the snippet above is equivalent to:

1from typed.models import model, Optional
2
3@model
4class MyModelib:
5    some_var: Optional(SomeType, null(SomeType))
6    other_var: Optional(OtherType, some_value)
7    ...

v0.4.4: introduction of model attributes

In typed we have several types of models:

  1. Models (the standard type, where the instance must contain at least the defined attributes)

  2. Exact Models (where the instance must contain exactly the defined attributes)

  3. Ordered Models (where the instance must contain at least the defined attributes, but in the order they were defined)

  4. Rigid Models (where the instance must contain exactly the defined attributes, and in the same order they were defined)

There is a type whose instances are models of each of the above classes: MODEL, EXACT, ORDERED, and RIGID.

Now, such types come with attributes that allow identifying if a given model belongs to a specific class:

  1. MyModel.is_model

  2. MyModel.is_exact

  3. MyModel.is_ordered

  4. MyModel.is_rigid

Attributes that return optional and mandatory attributes have also been added:

  1. MyModel.attrs: returns all attributes, with name, type, default value, and whether it is optional or not.

  2. MyModel.optional_attrs: returns only the optional attributes.

  3. MyModel.mandatory_attrs: returns only the mandatory attributes.

v0.4.5: optional and mandatory models

In v0.4.3 it was introduced the @optional decorator, from which one can to quickly create optional models, which are models whose all attributes are optional.

In this new version, a type OPTIONAL of all optional models was created. Also, the @optional decorator was extended to be applied directory in already existing models, turning them into optional models.

Analogously, it was introduced the type MANDATORY of all mandatory models, i.e, models with none optional arguments. A decorator @mandatory was created, allowed to turn any model into a mandatory model.

If applied in already existing models, both @optional and @mandatory preserves the underlying model kind, i.e, exact models are mapped into exact models, and so on.

v0.5.0: refactor of factories

In v0.5.0 all factories were reviewed:

  1. error messages are now more descriptive

  2. unification of function types with function factories

  3. BoolFunc has now the name Condition

So, for example:

  1. CompType is now Composable which can be applied to both a function or to a pair of integers

  2. TypedFuncType and TypedFunc are now just Typed, which can be applied to functions, types or pair of integers:

  3. and so on

1from typed import Typed, SomeType, OtherType
2from some.where import some_function
3
4Typed # the same as the old TypedFuncType
5Typed(SomeType, cod=OtherType) # the same as the old TypedFunc(SomeType, cod=OtherType)
6Typed(some_function) # the same as the old TypedFunc(some_function)
7...

v0.5.1: introduce __null__ in factories

In typed we have nullable types. These are the types for which the null function is defined. In v0.5.1 the null function was revisited to look for two cases:

  1. predefined builtin nullable types

  2. types with a __null__ defined.

Also, all factories were reviewed to include a __null__ attribute to the type they construct.

v0.5.2: review the basic types

In typed, a type is suppose to be constructed from an {lib:inner metatype}. Previously we have applied this definition to all constructed types, meaning that they are not Python builtin types. So, for example, previously:

typed type

definition

Str

str

Bool

bool

Int

int

Float

float

TYPE

type

Nill

type(None)

table 1

In version v0.5.2 these types are now typed types, so that are constructed from {lib:inner metatypes}.

v0.5.3: symmetrization of basic factories

Some factories are supposed to be symmetric in the sense that they should not depend on the ordering of their arguments. Examples include:

  1. Union

  2. Inter

  3. Tuple

  4. List

  5. Set

  6. Dict

Previously such factories were not symmetric. In version v0.5.3 they were made symmetric.

v0.6.0: reorganize structure and imports

Previously the code entries were centered in a main.py file which imported everything from the feature files using a from typed.mods... import *. The main advantage of this dynamic approach is being stable under changes in the feature files, as the addition or deletion of new types, factories, models, and so on.

However, it has some disadvantages:

  1. it requires a more complex dependencies resolution

  2. it exposes to the user an API with internal functions

In v0.6.0 the code were revisited and the imports were made static. Now we have three main modules:

module

description

typed.types

expose all types

typed.factories

expose all factories

typed.models

expose all models

table 2: new API modules

The use of these modules provide a better LSP experience to the user. The basic decorators @typed and @factory should be imported directly from typed. Alternatively, it can still import anything directly from typed. Indeed, the main entry point __init__.py of typed imports everything from the above modules:

1from typed.mods.decorators import typed, factory
2from typed.types     import *
3from typed.factories import *
4from typed.models    import *

v0.7.0: introduction of dependent types

Recall that a dependent type is some type which depends on the value of its underlying term. They make the type system much more expressive and are fundamental to ensure genuine type safety.

In typed version v0.7.0 an implementation of dependent types was introduced. They are type factories with a .is_dependent_type boolean attribute set to True. They can be passed as annotations to typed functions. In this case, the arguments of the dependent type should be parameters of the typed function. In this sense, the type of the parameter with annotation given by the dependent type depends on the value of the parameters from which the dependent type depends on.

Dependent types are defined through the decorator @dependent and constitute a type Dependent, which is a subtype of Factory, which in turn is a subtype of Typed.

1from typed import dependent, TYPE, SomeType, OtherType, ..., ReturnType
2
3@dependent
4def DepType(x: SomeType, ...) -> TYPE:
5    ...
6
7@typed
8def some_function(x: SomeType, y: DepType, ...) -> ReturnType:
9    ...