netaddr-pydantic


Namenetaddr-pydantic JSON
Version 0.2.0 PyPI version JSON
download
home_pagehttps://github.com/Anvil/netaddr-pydantic/
SummaryNone
upload_time2025-07-23 16:37:54
maintainerNone
docs_urlNone
authorDamien Nadé
requires_python>=3.11
licenseLGPL-2.1-or-later
keywords pydantic netaddr
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # netaddr-pydantic

[![PyPI version](https://img.shields.io/pypi/v/netaddr-pydantic?logo=pypi&style=plastic)](https://pypi.python.org/pypi/netaddr-pydantic/)
[![Supported Python Version](https://img.shields.io/pypi/pyversions/netaddr-pydantic?logo=python&style=plastic)](https://pypi.python.org/pypi/netaddr-pydantic/)
[![License](https://img.shields.io/pypi/l/netaddr-pydantic?color=green&logo=GNU&style=plastic)](https://github.com/Anvil/netaddr-pydantic/blob/main/LICENSE)

[![Pylint Static Quality Github Action](https://github.com/Anvil/netaddr-pydantic/actions/workflows/pylint.yml/badge.svg)](https://github.com/Anvil/netaddr-pydantic/actions/workflows/pylint.yml)
[![Mypy Static Quality Github Action](https://github.com/Anvil/netaddr-pydantic/actions/workflows/mypy.yml/badge.svg)](https://github.com/Anvil/netaddr-pydantic/actions/workflows/mypy.yml)
[![Pylint Static Quality Github Action](https://github.com/Anvil/netaddr-pydantic/actions/workflows/python-app.yml/badge.svg)](https://github.com/Anvil/netaddr-pydantic/actions/workflows/python-app.yml)

Use [Netaddr](https://pypi.org/project/netaddr/) objects in [Pydantic](https://docs.pydantic.dev/latest/) Models


## Rational

### Origin of the issue.

The [ipaddress](https://docs.python.org/3/library/ipaddress.html) module supports Iternet Protocol addresses and networks but lacks support for funny objects such as ranges, IP sets, globbing and so on.

The `Pydantic` framework provides out-of-the-box support for IPv4/IPv6 addresses and networks through the `ipaddress` module and this allows you to easily validate or serialize data from/to interfaces.

```python
import pydantic

class Model(pydantic.BaseModel):
    address: pydantic.IPvAnyAddress


m = Model(address="1.2.3.4")
print(type(m.address))
print(m.model_dump_json())
```

This produces:

```
<class 'ipaddress.IPv4Address'>
{"address":"1.2.3.4"}
```


Unfortunately, once the data is parsed, `ipaddress` objects cannot be inserted as-is to a `netaddr.IPSet` for example.

Alternatively, you would want to switch to `netaddr`-typed fields with like this:


```python
import pydantic
import netaddr

class Model(pydantic.BaseModel):
    address: netaddr.IPAddress
```

Unfortunately, `pydantic` cannot compile such thing:

```
pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'netaddr.ip.IPAddress'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.

If you got this error by calling handler(<some type>) within `__get_pydantic_core_schema__` then you likely need to call `handler.generate_schema(<some type>)` since we do not call `__get_pydantic_core_schema__` on `<some type>` otherwise to avoid infinite recursion.

For further information visit https://errors.pydantic.dev/2.11/u/schema-for-unknown-type
```

This is due to the lack of `pydantic` metadata in `netaddr` classes. Mainly, `pydantic` needs to know how to validate (from basic types, such as strings and integers) and how to to serialize the objects (return the basic types).


### Should you use netaddr-pydantic?


A way to fix this issue is to write your own validators and serializers and if you're lazy enough (no judgement here :]), this is just what `netaddr-pydantic` is bringing on the table.

This code:

```python
import pydantic
import netaddr_pydantic

class Model(pydantic.BaseModel):
    address: netaddr_pydantic.IPAddress

m = Model(address="1.2.3.4")
print(type(m.address))
print(m.model_dump_json())
```

Naturally produces the following:

```
<class 'netaddr.ip.IPAddress'>
{"address":"1.2.3.4"}
```

Basically, `netaddr-pydantic` just defines `Annotated` types with `pydantic` functional validators and serializers. For instance, `IPAddress` and `IPNetwork` are just defined this way:

```python
from typing import Annotated
from pydantic import PlainValidator, PlainSerializer
import netaddr

IPAddress = Annotated[
    netaddr.IPAddress, PlainValidator(netaddr.IPAddress), PlainSerializer(str)
]

IPNetwork = Annotated[
    netaddr.IPNetwork, PlainValidator(netaddr.IPNetwork), PlainSerializer(str)
]
```

And by all means, if *this* is all you need, do not bother with `netaddr-pydantic`. But if you need to use `IPRange`s and/or `IPSet`s as well then maybe you should use `netaddr-pydantic`, because, while `IPrange` and `IPSet` validators and serializers are just a very little bit more complex, I dont feel they are worth repeating.

Plus this is what *I* need in my own production environment, these days, so it will be maintained for a while.

## Still there? OK, then let's see.

The `netaddr-pydantic` annotations are only really useful in a `pydantic` context. You can use plain `netaddr` in other places.

### Supported objects and conversions


| Object Types | Can be obtained from | Serialized as | Comment |
| :----------- | :------------------: | :-----------: | :------ |
| IPAddress    | `str`, `int` | `str` |  | 
| IPNetwork    | `str`, 2-items `tuple` or `list` | a CIDR `str` | |
| IPRange      | `"<start-ip>-<end-ip>"` `str` or 2-items `list` or `tuple` | a `"<start-ip>-<end-ip>"` `str` | 
| IPSet        | `list`, `tuple` or `set` of `str`s or `int`s | `list` of `str`s. The `IPSet.iter_cidrs` is used to compute the items of the list. | If you do not want to work with `IPSet`, use `list[IPAddress \| IPNetwork]`, or similar. |
| IPGlob | `str` | `str` | `netaddr` implementation seems to be limited to IPv4 


The validation relies mostly on `netaddr` objects constructors. There's currently no bijection possible from the validated format to the serialized format. I do not intend to implement it at this time.

### Additionnal features

That may not be much, but an `IPAny` type is available. `pydantic` should produce the most acccurate object depending of the source object. An `IPAny` field will produce

* an `IPSet` if provided type is `list`, `tuple` or `set` 
* an `IPNetwork` if value is a CIDR `str`
* an `IPRange` if value is an `str` containing a `-` character
* an `IPGlob` if value is an `str` containing a `*` char.
* an `IPAddress` in other cases.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Anvil/netaddr-pydantic/",
    "name": "netaddr-pydantic",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "pydantic, netaddr",
    "author": "Damien Nad\u00e9",
    "author_email": "anvil.github+netaddr-pydantic@livna.org",
    "download_url": "https://files.pythonhosted.org/packages/bd/5d/3030a7c3a3abc1d28647336fff3a0d8d93dbec12bafa957b029ffad71382/netaddr_pydantic-0.2.0.tar.gz",
    "platform": null,
    "description": "# netaddr-pydantic\n\n[![PyPI version](https://img.shields.io/pypi/v/netaddr-pydantic?logo=pypi&style=plastic)](https://pypi.python.org/pypi/netaddr-pydantic/)\n[![Supported Python Version](https://img.shields.io/pypi/pyversions/netaddr-pydantic?logo=python&style=plastic)](https://pypi.python.org/pypi/netaddr-pydantic/)\n[![License](https://img.shields.io/pypi/l/netaddr-pydantic?color=green&logo=GNU&style=plastic)](https://github.com/Anvil/netaddr-pydantic/blob/main/LICENSE)\n\n[![Pylint Static Quality Github Action](https://github.com/Anvil/netaddr-pydantic/actions/workflows/pylint.yml/badge.svg)](https://github.com/Anvil/netaddr-pydantic/actions/workflows/pylint.yml)\n[![Mypy Static Quality Github Action](https://github.com/Anvil/netaddr-pydantic/actions/workflows/mypy.yml/badge.svg)](https://github.com/Anvil/netaddr-pydantic/actions/workflows/mypy.yml)\n[![Pylint Static Quality Github Action](https://github.com/Anvil/netaddr-pydantic/actions/workflows/python-app.yml/badge.svg)](https://github.com/Anvil/netaddr-pydantic/actions/workflows/python-app.yml)\n\nUse [Netaddr](https://pypi.org/project/netaddr/) objects in [Pydantic](https://docs.pydantic.dev/latest/) Models\n\n\n## Rational\n\n### Origin of the issue.\n\nThe [ipaddress](https://docs.python.org/3/library/ipaddress.html) module supports Iternet Protocol addresses and networks but lacks support for funny objects such as ranges, IP sets, globbing and so on.\n\nThe `Pydantic` framework provides out-of-the-box support for IPv4/IPv6 addresses and networks through the `ipaddress` module and this allows you to easily validate or serialize data from/to interfaces.\n\n```python\nimport pydantic\n\nclass Model(pydantic.BaseModel):\n    address: pydantic.IPvAnyAddress\n\n\nm = Model(address=\"1.2.3.4\")\nprint(type(m.address))\nprint(m.model_dump_json())\n```\n\nThis produces:\n\n```\n<class 'ipaddress.IPv4Address'>\n{\"address\":\"1.2.3.4\"}\n```\n\n\nUnfortunately, once the data is parsed, `ipaddress` objects cannot be inserted as-is to a `netaddr.IPSet` for example.\n\nAlternatively, you would want to switch to `netaddr`-typed fields with like this:\n\n\n```python\nimport pydantic\nimport netaddr\n\nclass Model(pydantic.BaseModel):\n    address: netaddr.IPAddress\n```\n\nUnfortunately, `pydantic` cannot compile such thing:\n\n```\npydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'netaddr.ip.IPAddress'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.\n\nIf you got this error by calling handler(<some type>) within `__get_pydantic_core_schema__` then you likely need to call `handler.generate_schema(<some type>)` since we do not call `__get_pydantic_core_schema__` on `<some type>` otherwise to avoid infinite recursion.\n\nFor further information visit https://errors.pydantic.dev/2.11/u/schema-for-unknown-type\n```\n\nThis is due to the lack of `pydantic` metadata in `netaddr` classes. Mainly, `pydantic` needs to know how to validate (from basic types, such as strings and integers) and how to to serialize the objects (return the basic types).\n\n\n### Should you use netaddr-pydantic?\n\n\nA way to fix this issue is to write your own validators and serializers and if you're lazy enough (no judgement here :]), this is just what `netaddr-pydantic` is bringing on the table.\n\nThis code:\n\n```python\nimport pydantic\nimport netaddr_pydantic\n\nclass Model(pydantic.BaseModel):\n    address: netaddr_pydantic.IPAddress\n\nm = Model(address=\"1.2.3.4\")\nprint(type(m.address))\nprint(m.model_dump_json())\n```\n\nNaturally produces the following:\n\n```\n<class 'netaddr.ip.IPAddress'>\n{\"address\":\"1.2.3.4\"}\n```\n\nBasically, `netaddr-pydantic` just defines `Annotated` types with `pydantic` functional validators and serializers. For instance, `IPAddress` and `IPNetwork` are just defined this way:\n\n```python\nfrom typing import Annotated\nfrom pydantic import PlainValidator, PlainSerializer\nimport netaddr\n\nIPAddress = Annotated[\n    netaddr.IPAddress, PlainValidator(netaddr.IPAddress), PlainSerializer(str)\n]\n\nIPNetwork = Annotated[\n    netaddr.IPNetwork, PlainValidator(netaddr.IPNetwork), PlainSerializer(str)\n]\n```\n\nAnd by all means, if *this* is all you need, do not bother with `netaddr-pydantic`. But if you need to use `IPRange`s and/or `IPSet`s as well then maybe you should use `netaddr-pydantic`, because, while `IPrange` and `IPSet` validators and serializers are just a very little bit more complex, I dont feel they are worth repeating.\n\nPlus this is what *I* need in my own production environment, these days, so it will be maintained for a while.\n\n## Still there? OK, then let's see.\n\nThe `netaddr-pydantic` annotations are only really useful in a `pydantic` context. You can use plain `netaddr` in other places.\n\n### Supported objects and conversions\n\n\n| Object Types | Can be obtained from | Serialized as | Comment |\n| :----------- | :------------------: | :-----------: | :------ |\n| IPAddress    | `str`, `int` | `str` |  | \n| IPNetwork    | `str`, 2-items `tuple` or `list` | a CIDR `str` | |\n| IPRange      | `\"<start-ip>-<end-ip>\"` `str` or 2-items `list` or `tuple` | a `\"<start-ip>-<end-ip>\"` `str` | \n| IPSet        | `list`, `tuple` or `set` of `str`s or `int`s | `list` of `str`s. The `IPSet.iter_cidrs` is used to compute the items of the list. | If you do not want to work with `IPSet`, use `list[IPAddress \\| IPNetwork]`, or similar. |\n| IPGlob | `str` | `str` | `netaddr` implementation seems to be limited to IPv4 \n\n\nThe validation relies mostly on `netaddr` objects constructors. There's currently no bijection possible from the validated format to the serialized format. I do not intend to implement it at this time.\n\n### Additionnal features\n\nThat may not be much, but an `IPAny` type is available. `pydantic` should produce the most acccurate object depending of the source object. An `IPAny` field will produce\n\n* an `IPSet` if provided type is `list`, `tuple` or `set` \n* an `IPNetwork` if value is a CIDR `str`\n* an `IPRange` if value is an `str` containing a `-` character\n* an `IPGlob` if value is an `str` containing a `*` char.\n* an `IPAddress` in other cases.\n",
    "bugtrack_url": null,
    "license": "LGPL-2.1-or-later",
    "summary": null,
    "version": "0.2.0",
    "project_urls": {
        "Documentation": "https://github.com/Anvil/netaddr-pydantic/README.md",
        "Homepage": "https://github.com/Anvil/netaddr-pydantic/",
        "Issues": "https://github.com/Anvil/netaddr-pydantic/issues",
        "Repository": "https://github.com/Anvil/netaddr-pydantic/"
    },
    "split_keywords": [
        "pydantic",
        " netaddr"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "408490f222fab9ee9c089f552c81d9c44418ed3faeb450313e1b5ac134b063d1",
                "md5": "308c6019a2449854ddb376e08790cdf1",
                "sha256": "268d329f6ae13d93ac186448606b8e979648a17ab7a0f6145e20940251228fab"
            },
            "downloads": -1,
            "filename": "netaddr_pydantic-0.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "308c6019a2449854ddb376e08790cdf1",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 14919,
            "upload_time": "2025-07-23T16:37:53",
            "upload_time_iso_8601": "2025-07-23T16:37:53.575146Z",
            "url": "https://files.pythonhosted.org/packages/40/84/90f222fab9ee9c089f552c81d9c44418ed3faeb450313e1b5ac134b063d1/netaddr_pydantic-0.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "bd5d3030a7c3a3abc1d28647336fff3a0d8d93dbec12bafa957b029ffad71382",
                "md5": "5b55f22a02e3f4d609988ea924f577c9",
                "sha256": "4c3247e3e2684308267e954d113ff0312ca2e8f7fcc9557ba93225aaad1b1fd6"
            },
            "downloads": -1,
            "filename": "netaddr_pydantic-0.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "5b55f22a02e3f4d609988ea924f577c9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 14122,
            "upload_time": "2025-07-23T16:37:54",
            "upload_time_iso_8601": "2025-07-23T16:37:54.889178Z",
            "url": "https://files.pythonhosted.org/packages/bd/5d/3030a7c3a3abc1d28647336fff3a0d8d93dbec12bafa957b029ffad71382/netaddr_pydantic-0.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-23 16:37:54",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Anvil",
    "github_project": "netaddr-pydantic",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "netaddr-pydantic"
}
        
Elapsed time: 0.75692s