Enforce annotations in your python code
Project description
Polyforce
🔥 Enforce static typing in your codebase at runtime 🔥
Documentation: https://polyforce.tarsild.io 📚
Source Code: https://github.com/tarsil/polyforce
Motivation
During software development we face issues where we don't know what do pass as specific parameters or even return of the functions itself.
Tools like mypy for example, allow you to run static checking in your code and therefore allowing to type your codebase properly but it does not enforce it when running.
For those coming from hevily static typed languages like Java, .net and many others, Python can be overwhelming and sometimes confusing because of its versatility.
Polyforce was created to make sure you:
- Don't forget to type your functions and variables.
- Validates the typing in runtime.
- Don't forget thr return annotations.
Wouldn't be cool to have something like this:
What if my function that expects a parameter of type string, if something else is passed could simply fail, as intended?
This is where Polyforce enters.
The library
Polyforce was designed to enforce the static typing everywhere in your code base. From functions to parameters.
It was also designed to make sure the typing is enforced at runtime.
In other words, if you declare a type string
and decide to pass an integer
, it will blow throw
and intended error.
The library offers two ways of implementing the solution.
Installation
$ pip install polyforce
How to use it
Let us see some scenarios where the conventional python is applied and then where Polyforce can make the whole difference for you.
Conventional Python
Let us start with a simple python function.
Simple function
def my_function(name: str):
return name
In the normal python world, this wouldn't make any difference, and let us be honest, if you don't care about mypy or any related tools, this will work without any issues.
This will also allow this to run without any errors:
my_function("Polyfactory") # returns "Polyfactory"
my_function(1) # returns 1
my_function(2.0) # returns 2.0
The example above is 100% valid for that specific function and all values passed will be returned
equaly valid and the reson for this is because Python does not enforce the static typing so
the str
declared for the parameter name
is merely visual.
With objects
class MyClass:
def my_function(self, name: str):
return name
And then this will be also valid.
my_class = MyClass()
my_class.my_function("Polyfactory") # returns "Polyfactory"
my_class.my_function(1) # returns 1
my_class.my_function(2.0) # returns 2.0
I believe you understand the gist of what is being referred here. So, what if there was a solution where we actually enforce the typing at runtime? Throw some errors when something is missing from the typing and also when the wrong type is being sent into a function?
Enters Polyforce
Polyforce
Now, let us use the same examples used before but using Polyforce and see what happens?
Simple function
from polyforce import polycheck
@polycheck()
def my_function(name: str):
return name
The example above it will throw a ReturnSignatureMissing
or a MissingAnnotation
because the missing return annotation of the function or a parameter annotation respectively.
my_function("Polyforce") # Throws an exception
The correct way would be:
from polyforce import polycheck
@polycheck()
def my_function(name: str) -> str:
return name
So what if now you pass a value that is not of type string?
my_function(1) # Throws an exception
This will also throw a TypeError
exception because you are trying to pass a type int
into a
declared type str
.
With objects
The same level of validations are applied within class objects too.
from polyforce import PolyModel
class MyClass(PolyModel):
def __init__(self, name, age: int):
...
def my_function(self, name: str):
return name
The example above it will throw a ReturnSignatureMissing
and a MissingAnnotation
because the missing return annotation for both init and the function as well as the missing
types for the parameters in both.
The correct way would be:
from polyforce import PolyModel
class MyClass(PolyModel):
def __init__(self, name: str, age: int) -> None:
...
def my_function(self, name: str) -> str:
return name
The Polyforce
As you can see, utilising the library is very simple and very easy, in fact, it was never so easy to enforce statuc typing in python.
For classes, you simply need to import the PolyModel
.
from polyforce import PolyModel
And to use the decorator you simply can:
from polyforce import polycheck
PolyModel vs polycheck
When using PolyModel
, there is no need to apply the polycheck
decorator. The PolyModel
is
smart enough to apply the same level of validations as the polycheck
.
When using the PolyModel
you can use normal python as you would normally do and that means
classmethod
, staticmethod
and normal functions.
This like this, the polycheck
is used for all the functions that are not inside a class.
Limitations
For now, Polyforce is not looking at native magic methods (usually start and end with double underscore). In the future it is planned to understand those on a class level.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for polyforce-0.3.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7d8c0ba2ed41f8840b5ec30fae29eed00952fdfe8947ae71a333f345823deb14 |
|
MD5 | 9e2d3d3b295b7b1ea15abbf13d55c4bf |
|
BLAKE2b-256 | cb1ef991a6e5db142d6e177aef0304d1c8a8d478e27a649d47e6a2979b1a0b6b |