polyforce


Namepolyforce JSON
Version 0.3.0 PyPI version JSON
download
home_page
SummaryEnforce annotations in your python code
upload_time2023-10-16 16:27:26
maintainer
docs_urlNone
author
requires_python>=3.8
license
keywords polyforce
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Polyforce

<p align="center">
  <a href="https://polyforce.tarsild.io"><img src="https://res.cloudinary.com/tarsild/image/upload/v1696959172/packages/polyforce/logo_pyynl9.png" alt='Polyforce'></a>
</p>

<p align="center">
    <em>🔥 Enforce static typing in your codebase at runtime 🔥</em>
</p>

<p align="center">
<a href="https://github.com/tarsil/polyforce/workflows/Test%20Suite/badge.svg?event=push&branch=main" target="_blank">
    <img src="https://github.com/tarsil/polyforce/workflows/Test%20Suite/badge.svg?event=push&branch=main" alt="Test Suite">
</a>

<a href="https://pypi.org/project/polyforce" target="_blank">
    <img src="https://img.shields.io/pypi/v/polyforce?color=%2334D058&label=pypi%20package" alt="Package version">
</a>

<a href="https://pypi.org/project/polyforce" target="_blank">
    <img src="https://img.shields.io/pypi/pyversions/polyforce.svg?color=%2334D058" alt="Supported Python versions">
</a>
</p>

---

**Documentation**: [https://polyforce.tarsild.io][polyforce] 📚

**Source Code**: [https://github.com/tarsil/polyforce](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][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.

* [Via model](./model.md)
* [Via decorator](./decorator.md)

## Installation

```shell
$ 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

```python
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:

```python
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

```python
class MyClass:

    def my_function(self, name: str):
        return name
```

And then this will be also valid.

```python
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)

### Polyforce

Now, let us use the same examples used before but using **Polyforce** and see what happens?

#### Simple function

```python hl_lines="1"
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.

```python
my_function("Polyforce") # Throws an exception
```

The correct way would be:

```python hl_lines="1 5"
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?

```python
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.

```python hl_lines="1 4"
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:

```python hl_lines="1 4"
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`.

```python
from polyforce import PolyModel
```

And to use the decorator you simply can:

```python
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.

[polyforce]: https://polyforce.tarsild.io
[mypy]: https://mypy.readthedocs.io/en/stable/

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "polyforce",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "polyforce",
    "author": "",
    "author_email": "Tiago Silva <tiago.arasilva@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/96/ef/b7f229ca6e917d80506f29ecec3fb3261fd9f9e4a230fecddd2b4fbcc0e0/polyforce-0.3.0.tar.gz",
    "platform": null,
    "description": "# Polyforce\n\n<p align=\"center\">\n  <a href=\"https://polyforce.tarsild.io\"><img src=\"https://res.cloudinary.com/tarsild/image/upload/v1696959172/packages/polyforce/logo_pyynl9.png\" alt='Polyforce'></a>\n</p>\n\n<p align=\"center\">\n    <em>\ud83d\udd25 Enforce static typing in your codebase at runtime \ud83d\udd25</em>\n</p>\n\n<p align=\"center\">\n<a href=\"https://github.com/tarsil/polyforce/workflows/Test%20Suite/badge.svg?event=push&branch=main\" target=\"_blank\">\n    <img src=\"https://github.com/tarsil/polyforce/workflows/Test%20Suite/badge.svg?event=push&branch=main\" alt=\"Test Suite\">\n</a>\n\n<a href=\"https://pypi.org/project/polyforce\" target=\"_blank\">\n    <img src=\"https://img.shields.io/pypi/v/polyforce?color=%2334D058&label=pypi%20package\" alt=\"Package version\">\n</a>\n\n<a href=\"https://pypi.org/project/polyforce\" target=\"_blank\">\n    <img src=\"https://img.shields.io/pypi/pyversions/polyforce.svg?color=%2334D058\" alt=\"Supported Python versions\">\n</a>\n</p>\n\n---\n\n**Documentation**: [https://polyforce.tarsild.io][polyforce] \ud83d\udcda\n\n**Source Code**: [https://github.com/tarsil/polyforce](https://github.com/tarsil/polyforce)\n\n---\n\n## Motivation\n\nDuring software development we face issues where we don't know what do pass as specific parameters\nor even return of the functions itself.\n\nTools like [mypy][mypy] for example, allow you to run static checking in your code and therefore\nallowing to type your codebase properly but **it does not enforce it when running**.\n\nFor those coming from hevily static typed languages like **Java**, **.net** and many others, Python\ncan be overwhelming and sometimes confusing because of its versatility.\n\n**Polyforce** was created to make sure you:\n\n* Don't forget to type your functions and variables.\n* Validates the typing in **runtime**.\n* Don't forget thr return annotations.\n\nWouldn't be cool to have something like this:\n\n> What if my function that expects a parameter of type string, if something else is passed could\nsimply fail, as intended?\n\nThis is where **Polyforce enters**.\n\n## The library\n\nPolyforce was designed to enforce the static typing **everywhere** in your code base. From functions\nto parameters.\n\nIt was also designed to make sure the typing is enforced at runtime.\n\nIn other words, if you declare a type `string` and decide to pass an `integer`, it will blow throw\nand intended error.\n\nThe library offers two ways of implementing the solution.\n\n* [Via model](./model.md)\n* [Via decorator](./decorator.md)\n\n## Installation\n\n```shell\n$ pip install polyforce\n```\n\n## How to use it\n\nLet us see some scenarios where the conventional python is applied and then where **Polyforce**\ncan make the whole difference for you.\n\n### Conventional Python\n\nLet us start with a simple python function.\n\n#### Simple function\n\n```python\ndef my_function(name: str):\n    return name\n```\n\nIn the normal python world, this wouldn't make any difference, and let us be honest, if you don't care\nabout mypy or any related tools, this will work without any issues.\n\nThis will also allow this to run without any errors:\n\n```python\nmy_function(\"Polyfactory\") # returns \"Polyfactory\"\nmy_function(1) # returns 1\nmy_function(2.0) # returns 2.0\n```\n\nThe example above is 100% valid for that specific function and all values passed will be returned\nequaly valid and the reson for this is because Python **does not enforce the static typing** so\nthe `str` declared for the parameter `name` **is merely visual**.\n\n#### With objects\n\n```python\nclass MyClass:\n\n    def my_function(self, name: str):\n        return name\n```\n\nAnd then this will be also valid.\n\n```python\nmy_class = MyClass()\n\nmy_class.my_function(\"Polyfactory\") # returns \"Polyfactory\"\nmy_class.my_function(1) # returns 1\nmy_class.my_function(2.0) # returns 2.0\n```\n\nI believe you understand the gist of what is being referred here. So, what if there was a solution\nwhere we actually enforce the typing at runtime? Throw some errors when something is missing from\nthe typing and also when the wrong type is being sent into a function?\n\nEnters [Polyforce](#polyforce)\n\n### Polyforce\n\nNow, let us use the same examples used before but using **Polyforce** and see what happens?\n\n#### Simple function\n\n```python hl_lines=\"1\"\nfrom polyforce import polycheck\n\n\n@polycheck()\ndef my_function(name: str):\n    return name\n```\n\nThe example above it will throw a `ReturnSignatureMissing` or a `MissingAnnotation`\nbecause the **missing return annotation** of the function or a parameter annotation respectively.\n\n```python\nmy_function(\"Polyforce\") # Throws an exception\n```\n\nThe correct way would be:\n\n```python hl_lines=\"1 5\"\nfrom polyforce import polycheck\n\n\n@polycheck()\ndef my_function(name: str) -> str:\n    return name\n```\n\nSo what if now you pass a value that is not of type string?\n\n```python\nmy_function(1) # Throws an exception\n```\n\nThis will also throw a `TypeError` exception because you are trying to pass a type `int` into a\ndeclared type `str`.\n\n#### With objects\n\nThe same level of validations are applied within class objects too.\n\n```python hl_lines=\"1 4\"\nfrom polyforce import PolyModel\n\n\nclass MyClass(PolyModel):\n\n    def __init__(self, name, age: int):\n        ...\n\n    def my_function(self, name: str):\n        return name\n```\n\nThe example above it will throw a `ReturnSignatureMissing` and a `MissingAnnotation`\nbecause the **missing return annotation for both __init__ and the function** as well as the missing\ntypes for the parameters in both.\n\nThe correct way would be:\n\n```python hl_lines=\"1 4\"\nfrom polyforce import PolyModel\n\n\nclass MyClass(PolyModel):\n\n    def __init__(self, name: str, age: int) -> None:\n        ...\n\n    def my_function(self, name: str) -> str:\n        return name\n```\n\n## The Polyforce\n\nAs you can see, utilising the library is very simple and very easy, in fact, it was never so easy to\nenforce statuc typing in python.\n\nFor classes, you simply need to import the `PolyModel`.\n\n```python\nfrom polyforce import PolyModel\n```\n\nAnd to use the decorator you simply can:\n\n```python\nfrom polyforce import polycheck\n```\n\n## PolyModel vs polycheck\n\nWhen using `PolyModel`, there is no need to apply the `polycheck` decorator. The `PolyModel` is\nsmart enough to apply the same level of validations as the `polycheck`.\n\nWhen using the `PolyModel` you can use normal python as you would normally do and that means\n`classmethod`, `staticmethod` and normal functions.\n\nThis like this, the `polycheck` is used for all the functions that are not inside a class.\n\n## Limitations\n\nFor now, **Polyforce** is not looking at **native magic methods** (usually start and end with double underscore).\nIn the future it is planned to understand those on a class level.\n\n[polyforce]: https://polyforce.tarsild.io\n[mypy]: https://mypy.readthedocs.io/en/stable/\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Enforce annotations in your python code",
    "version": "0.3.0",
    "project_urls": {
        "Changelog": "https://polyforce.tarsild.io/release-notes/",
        "Documentation": "https://polyforce.tarsild.io",
        "Funding": "https://github.com/sponsors/tarsil",
        "Homepage": "https://github.com/tarsil/polyforce",
        "Source": "https://github.com/tarsil/polyforce"
    },
    "split_keywords": [
        "polyforce"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "cb1ef991a6e5db142d6e177aef0304d1c8a8d478e27a649d47e6a2979b1a0b6b",
                "md5": "9e2d3d3b295b7b1ea15abbf13d55c4bf",
                "sha256": "7d8c0ba2ed41f8840b5ec30fae29eed00952fdfe8947ae71a333f345823deb14"
            },
            "downloads": -1,
            "filename": "polyforce-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9e2d3d3b295b7b1ea15abbf13d55c4bf",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 22842,
            "upload_time": "2023-10-16T16:27:24",
            "upload_time_iso_8601": "2023-10-16T16:27:24.945127Z",
            "url": "https://files.pythonhosted.org/packages/cb/1e/f991a6e5db142d6e177aef0304d1c8a8d478e27a649d47e6a2979b1a0b6b/polyforce-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "96efb7f229ca6e917d80506f29ecec3fb3261fd9f9e4a230fecddd2b4fbcc0e0",
                "md5": "3c978f911f309353d8b8861cf3eb95a5",
                "sha256": "fc2bc7b7a950dd8cbf783976ea5f9d4a15c83e86f552157950637da543252584"
            },
            "downloads": -1,
            "filename": "polyforce-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "3c978f911f309353d8b8861cf3eb95a5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 16651,
            "upload_time": "2023-10-16T16:27:26",
            "upload_time_iso_8601": "2023-10-16T16:27:26.666851Z",
            "url": "https://files.pythonhosted.org/packages/96/ef/b7f229ca6e917d80506f29ecec3fb3261fd9f9e4a230fecddd2b4fbcc0e0/polyforce-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-10-16 16:27:26",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "sponsors",
    "github_project": "tarsil",
    "github_not_found": true,
    "lcname": "polyforce"
}
        
Elapsed time: 1.13284s