About
Here you will find some important changelogs for the library typed. For the full list of tags, see here.
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.
from
typing
:Union
,List
,Tuple
,Dict
,Set
, …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:
Ordered: Validates data with respect to the ordering of entries.
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 typeY
, then every instance ofX
should be an instance ofY
.
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 entitiesBuiltin
: builtin functionsLambda
: lambda functionsFunction
(orFuncType
): user defined functionsCompFuncType
: composable functionsHintedDomFuncType
: user defined functions that have type hint in domainHintedCodFuncType
: user defined functions that have type hint in codomainHintedFuncType
: user defined functions that have type hint in both domain and codomainTypedDomFuncType
: user defined functions with type hints in domain, which is checked at runtimeTypedCodFuncType
: user defined functions with type hints in codomain, which is checked at runtimeTypedFuncType
: user defined functions with type hints in domain and codomain, which are checked at runtimeBoolFuncType
: user defined typed functions whose codomain isBool
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:
Models (the standard type, where the instance must contain at least the defined attributes)
Exact Models (where the instance must contain exactly the defined attributes)
Ordered Models (where the instance must contain at least the defined attributes, but in the order they were defined)
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:
MyModel.is_model
MyModel.is_exact
MyModel.is_ordered
MyModel.is_rigid
Attributes that return optional and mandatory attributes have also been added:
MyModel.attrs
: returns all attributes, with name, type, default value, and whether it is optional or not.MyModel.optional_attrs
: returns only the optional attributes.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:
error messages are now more descriptive
unification of function types with function factories
BoolFunc
has now the nameCondition
So, for example:
CompType
is nowComposable
which can be applied to both a function or to a pair of integersTypedFuncType
andTypedFunc
are now justTyped
, which can be applied to functions, types or pair of integers: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:
predefined builtin nullable types
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) |
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:
Union
Inter
Tuple
List
Set
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:
it requires a more complex dependencies resolution
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 |
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 ...