webuuid


Namewebuuid JSON
Version 0.1.1 PyPI version JSON
download
home_pagehttps://github.com/JGoutin/webuuid
SummaryOptimize UUID for web services
upload_time2023-02-04 02:04:32
maintainer
docs_urlNone
authorJGoutin
requires_python>=3.10,<4.0
licenseBSD-2-Clause
keywords uuid uuid6 uuid7 pydantic fastapi
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ![Tests](https://github.com/JGoutin/webuuid/workflows/tests/badge.svg)
[![codecov](https://codecov.io/gh/JGoutin/webuuid/branch/main/graph/badge.svg?token=mgtUTV7PwM)](https://codecov.io/gh/JGoutin/webuuid)
[![PyPI](https://img.shields.io/pypi/v/webuuid.svg)](https://pypi.org/project/webuuid)

# WebUuid: Optimized UUID primary key for web services

## Why using this library ?

This library as been created to help solve the following question: What is the best
format for ID in a web service and its database ?

The library trie to answer this question using modern UUID and short user-friendly 
string representation.

### Why using UUID instead of auto-incrementing integer with SQL databases ?

Traditionally, auto-incrementing integer are used as Primary keys in databases.
But, UUID are also a good candidate for the following reasons:

Pros:

* **Offline generation:** Auto-incrementing require to call the database first and 
  insert a row to generate the ID value. With UUID, the ID can be generated everywhere,
  and eventually write in the database later if required.
* **Security/Privacy:** UUID is not guessable. If an integer is used as ID and an API 
  allow to query using this ID anyone can easily try to guess ID values to list, count 
  or get some data. This can lead to data leak that can be a security or privacy issue.
* **Uniqueness & collision resistance:** When running a service on a scalable 
  environment (With multiple concurrent servers) or on multiple environments with data 
  that may need to be merged, using auto-incrementing integer will likely lead to 
  collisions (Or complex setup/data reconciliation to avoid it).
  UUID are optimized for almost perfect uniqueness (see below), so there is not such 
  problem like this with them.

Cons:

* **Storage size:** Auto-incrementing integer are smallest in database 
  (4 or 8 bytes instead of 16 bytes).

Fixed cons:

* **SQL Insert performance:** UUID1 to UUID5 are not time sortable, so inserting them in
  a large table lead to a performance penalty.
  Fortunately, with RFC-4122(rev1), there is now UUID6 and UUID7 that are time 
  sortable. This library use UUID7 by default, or UUID8 with sortable timestamp when 
  used with node.
* **Privacy**: UUID1 uses MAC address in its "node" part, this have privacy issue.
  There is no privacy issue with UUID7 that rely on random data instead.

### Why using "short" UUID string format instead of common UUID format ?

The RFC-4122 provides a standard string representation format 
(like `f81d4fae-7dec-11d0-a765-00a0c91e6bf6`) based on hexadecimal representation.

But, there are many other way to encode some bytes in strings, and it is easy to convert
the UUID between all of its formats if required.

#### Popularity and compatibility

RFC-4648 base64 and base32 are very common bytes encoding codecs and are easily 
available in many programming languages and common libraries.

There is some alternate base32 like codecs, but not standardized and not as common,
we'll not use them here.

#### Size 

The RFC-4122 standard UUID string representation is 36 characters length 
(or eventually 32 characters length if keeping only the hexadecimal part)

The equivalent size for RFC-4648 base32 is 26 characters (-27%) and 22 characters (-38%)
for RFC-4648 base64 (without padding in both cases). The storage is more efficient than
hexadecimal. This allows to reduce the traffic in web service requests.

#### User-friendliness

The main advantage of the RFC-4122 format is that it is easy to read the internal
UUID bytes format in it. But, in practices, users don't need to know that the used UUID 
is an UUID7 or 8. So this is not relevant in the use case of a web service.

To be user-friendly an ID should be easy to read, compare and copy by the user 
(Not only with copy/paste).

Base32 is case-insensitive and characters that can be confused are not used in its 
alphabet, this make it more user-friendly than base64. The reduced size also improve 
user-friendliness in comparison with hexadecimal notation. So base32 is a good 
compromise.

### About uniqueness

Since UUID7 as a fixed number of possible values and use a random part, there is still
a very low statistical chance that two generated UUID are identical.
This represents 1/18889465931478580854784 chance to have two identical UUID, 
and that apply only for a period of 1 millisecond. 

This can be managed by:

* EASY SOLUTION: Adding a "unique" clause in database that will raise an error when
  trying to insert the duplicated UUID row.
* MEDIUM SOLUTION: In addition of the previous solution, the application code may 
 handle this case and retry with another ID if a duplicate is detected.
* MISSION CRITICAL SOLUTION: Using UUID8 with node and ensure this node is unique in 
  your application.

## Features:

 * Optimized for database use (Size and performance).
 * Short and user-friendly string representation (With many options).
 * Possibility to use a custom "node" part to help guarantee uniqueness in mission 
   critical application. 
 * Possibility to import a UUID from any 16 bytes length binary in many format and types
  (`bytes`, `int`, `str`, `uuid.UUID`, ...).
 * Possibility to create UUID from a hash to help with deduplication.
 * Included Pydantic support.
 * Fully typped.
 * No external dependencies and pure Python.
 * Easy subclassing.

## Installation

WebUuid is available on PyPI, so it can be installed like any other Python package.

Example with Pip:
```bash
pip install webuuid
```

WebUuid does not require any external dependency and is a pure Python library.

## Usage

### UUID generation

By default, is the class is used without argument, a new UUID7 is generated:

```python
import webuuid


webuuid.Uuid()
```

The class can also be loaded from any 16 bytes length object or its representation as 
source:

```python
import base64
import os
import webuuid


# Using byte-like objects as source
bytes_obj = os.urandom(16)
webuuid.Uuid(bytes_obj)

memoryview_obj = memoryview(bytes_obj)
webuuid.Uuid(memoryview_obj)

bytearray_obj = bytearray(bytes_obj)
webuuid.Uuid(bytearray_obj)

# Using int as source
int_obj = int.from_bytes(bytes_obj, "big")
webuuid.Uuid(int_obj)

# Using base64/base32/hexadecimal str representations
base32_obj = base64.b32encode(bytes_obj).decode()
webuuid.Uuid(base32_obj)

base64_obj = base64.b64encode(bytes_obj).decode()
webuuid.Uuid(base64_obj)

base64url_obj = base64.urlsafe_b64encode(bytes_obj).decode()
webuuid.Uuid(base64url_obj)

hex_obj = bytes_obj.hex()
webuuid.Uuid(hex_obj)

# Using base64/base32 str representations without padding
webuuid.Uuid(base32_obj.rstrip("="))
webuuid.Uuid(base64_obj.rstrip("="))
webuuid.Uuid(base64url_obj.rstrip("="))
```

A standard library `uuid.UUID` can also be used as source:

```python
import uuid
import webuuid


stdlib_uuid = uuid.uuid4()

# Using standard UUID as source
uuid_obj = webuuid.Uuid(stdlib_uuid)
webuuid.Uuid(str(stdlib_uuid))

# Going back to standard library UUID
uuid.UUID(bytes=uuid_obj)
```

#### UUID with custom node

It is also possible to generate a new UUID with a custom "node". This is mainly be 
useful if you do not want relly entirely on randomness to improve the collision 
resistance.
In this case the node need be unique in all your application and may be 
generated/validated against a centralized registry or using a base value that is unique
by design (Example: in a Cloud environment, server/instance ID and process ID).

In the case, the first 64 bits of the UUID are generated normally 
(With 48 bits of timestamp and 10 random bits), the 64 endings bytes (Ignoring version 
and variant fields) are the custom node.

```python
import webuuid
import os


# Using a random value as node
node = urandom(8)

# Generation from node
my_uuid = webuuid.Uuid(node=node)

# The node can be accessed using the following property
my_uuid.node
```

In the case, the generated UUID wil be an UUID8 instead of an UUID7.

#### UUID from hash

It is possible to generate a UUID using the hash of a byte-like input data. This can be 
useful for tables that need deduplication on row IDs.

In this case, like any hash, the UUID is always the same for the same input data.
There is no timestamp in the UUID, so it is not time sortable and can have a negative 
impact on the database performance on INSERT in large tables.

```python
import webuuid
import json


data = json.dumps({"key": "value"}).encode()
my_uuid = webuuid.Uuid.from_hash(data)
```

In the case, the generated UUID will be an UUID8 instead of an UUID7.

The hash function used is `blake2b`.

### UUID class features

The `webuuid.Uuid` class is a subclass of `bytes` and supports all the bytes objects
standards features with the following changes:

* `webuuid.Uuid.decode()` returns an encoded `str` representation using the
  base32 encoding (By default, but can be easily changed) instead of the classical UTF-8
  encoding.
* Using `str()` on `webuuid.Uuid` objects returns the same result as 
  `webuuid.Uuid.decode()`.
* `webuuid.Uuid` can be compared with other `webuuid.Uuid` or byte-like objects but also
  with any `str`, `uuid.UUID` or `int` that can be used as input with `webuuid.Uuid`
  directly.
* `int()` can be used directly  on `webuuid.Uuid` objects. 

In addition to bytes features, the class provides some methods to convert it to various
`str` representations:
* `webuuid.Uuid.base32()`: RFC-4648 Base 32 representation (Without padding by default).
* `webuuid.Uuid.base64()`: RFC-4648 Base 64 representation (Without padding by default).
* `webuuid.Uuid.base64url()`: RFC-4648 Base 64 URL safe representation (Without padding 
  by default).
* `webuuid.Uuid.standard_hex()`: RFC-4122 UUID hexadecimal representation.

#### String representation customization

The `webuuid.Uuid` class use the RFC-4648 Base 32 without padding `str` representation 
by default.

The library also provides the following classes that use a different default 
representation:
* `webuuid.UuidBase64`: RFC-4648 Base 64 without padding.
* `webuuid.UuidBase64Url`: RFC-4648 Base 64 URL safe without padding.

The class can also easily be subclassed to use any representation of your choice as
default output, but also as input:

```python
import webuuid


def custom_encoder(
    value: bytes, encoding: str = "utf-8", errors: str = "strict"
) -> str:
    """Custom encoder.
    
    Args:
        value: UUID bytes input value.
        encoding: See `bytes.decode()` argument.
        errors: See `bytes.decode()` argument.
    """
    # Custom codec implementation


def custom_decoder(value: str) -> bytes:
    """Custom decoder.

    Args:
        value: Input string value.
    """
    # Custom codec implementation


class CustomUuid(webuuid.Uuid):
    """Custom UUID that only support the specified codec."""

    # Set custom codec
    STR_ENCODER = custom_encoder
    FALLBACK_STR_DECODER = custom_decoder

    # Disable other codecs.
    STR_DECODERS = dict()


class CustomCompatibleUuid(webuuid.Uuid):
    """Custom UUID that add the support for an extra codec."""

    # Set custom encoder
    STR_ENCODER = custom_encoder

    # Add customer decoder.
    # In this example the encoder generate a 48 characters length str
    STR_DECODERS = webuuid.Uuid.STR_DECODERS.copy()
    STR_DECODERS[48] = custom_decoder
```

### Usage with other libraries

This part give some tips on how to use WebUuid with some common libraries.

#### JSON serialization/deserialization

In web application it is very common to serialize/deserialize data in JSON. By default,
the standard `json` library (And compatible alternative) only supports base Python 
types, but it is easy to add the `webuuid.Uuid` support to it:

```python
import json
import typing
import webuuid


def json_default_uuid(obj: typing.Any) -> typing.Any:
    """webuuid.uuid JSON serializer.

    Args:
        obj: Object to serialize.
    """
    if isinstance(obj, webuuid.Uuid):
        return str(obj)
    raise TypeError


# Serializing object with UUID
data = {"id": webuuid.Uuid()}
json.loads(data, default=json_default_uuid)
```

Some more high level libraries provides feature to register JSON encoders or `default` 
function for automatic use.

#### Pydantic

[Pydantic](https://github.com/pydantic/pydantic) is a data validation library. 
It is notably used with the [FastAPI](https://fastapi.tiangolo.com/) web framework.

`webuuid.Uuid` as native Pydantic support with validator and schema:

```python
import pydantic
import webuuid


class Model(pydantic.BaseModel):
    """Pydantic model."""
    
    class Config:
        """Config."""

        # Automatically JSON serialize UUID to its string representation
        json_encoders = {webuuid.Uuid: str}

    # UUID field
    id: webuuid.Uuid
    
    # UUID Field generating a default new value if not specified
    value: webuuid.Uuid = pydantic.Field(default_factory=webuuid.Uuid)
```

On custom subclass, the schema can easily be customized by overriding the 
`webuuid.Uuid.FIELD_SCHEMA` class dictionary.

#### Databases libraries

In databases, the UUID can be stored in a 16 bytes length binary column,
like BINARY(16). Depending on the library used and the database engine, the type and the
syntax to use may vary.

##### SQLAlchemy

With [SQLAlchemy](https://www.sqlalchemy.org/), the UUID can be stored using the
`LargeBinary(length=16)` type. SQLAlchemy will use the proper type for the required 
database engine behind the scene.

```python
import sqlalchemy


metadata = sqlalchemy.MetaData()

table = Table(
    "table_name",
    metadata,
    sqlalchemy.Column(
        "uuid",
        sqlalchemy.LargeBinary(length=16),
        primary_key=True,
        index=True
    ),
)
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/JGoutin/webuuid",
    "name": "webuuid",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.10,<4.0",
    "maintainer_email": "",
    "keywords": "uuid,uuid6,uuid7,pydantic,fastapi",
    "author": "JGoutin",
    "author_email": "",
    "download_url": "https://files.pythonhosted.org/packages/48/59/1053234cb092021fbf62a3b36822c64f4854a41cc57c23ecb0e18de97be6/webuuid-0.1.1.tar.gz",
    "platform": null,
    "description": "![Tests](https://github.com/JGoutin/webuuid/workflows/tests/badge.svg)\n[![codecov](https://codecov.io/gh/JGoutin/webuuid/branch/main/graph/badge.svg?token=mgtUTV7PwM)](https://codecov.io/gh/JGoutin/webuuid)\n[![PyPI](https://img.shields.io/pypi/v/webuuid.svg)](https://pypi.org/project/webuuid)\n\n# WebUuid: Optimized UUID primary key for web services\n\n## Why using this library ?\n\nThis library as been created to help solve the following question: What is the best\nformat for ID in a web service and its database ?\n\nThe library trie to answer this question using modern UUID and short user-friendly \nstring representation.\n\n### Why using UUID instead of auto-incrementing integer with SQL databases ?\n\nTraditionally, auto-incrementing integer are used as Primary keys in databases.\nBut, UUID are also a good candidate for the following reasons:\n\nPros:\n\n* **Offline generation:** Auto-incrementing require to call the database first and \n  insert a row to generate the ID value. With UUID, the ID can be generated everywhere,\n  and eventually write in the database later if required.\n* **Security/Privacy:** UUID is not guessable. If an integer is used as ID and an API \n  allow to query using this ID anyone can easily try to guess ID values to list, count \n  or get some data. This can lead to data leak that can be a security or privacy issue.\n* **Uniqueness & collision resistance:** When running a service on a scalable \n  environment (With multiple concurrent servers) or on multiple environments with data \n  that may need to be merged, using auto-incrementing integer will likely lead to \n  collisions (Or complex setup/data reconciliation to avoid it).\n  UUID are optimized for almost perfect uniqueness (see below), so there is not such \n  problem like this with them.\n\nCons:\n\n* **Storage size:** Auto-incrementing integer are smallest in database \n  (4 or 8 bytes instead of 16 bytes).\n\nFixed cons:\n\n* **SQL Insert performance:** UUID1 to UUID5 are not time sortable, so inserting them in\n  a large table lead to a performance penalty.\n  Fortunately, with RFC-4122(rev1), there is now UUID6 and UUID7 that are time \n  sortable. This library use UUID7 by default, or UUID8 with sortable timestamp when \n  used with node.\n* **Privacy**: UUID1 uses MAC address in its \"node\" part, this have privacy issue.\n  There is no privacy issue with UUID7 that rely on random data instead.\n\n### Why using \"short\" UUID string format instead of common UUID format ?\n\nThe RFC-4122 provides a standard string representation format \n(like `f81d4fae-7dec-11d0-a765-00a0c91e6bf6`) based on hexadecimal representation.\n\nBut, there are many other way to encode some bytes in strings, and it is easy to convert\nthe UUID between all of its formats if required.\n\n#### Popularity and compatibility\n\nRFC-4648 base64 and base32 are very common bytes encoding codecs and are easily \navailable in many programming languages and common libraries.\n\nThere is some alternate base32 like codecs, but not standardized and not as common,\nwe'll not use them here.\n\n#### Size \n\nThe RFC-4122 standard UUID string representation is 36 characters length \n(or eventually 32 characters length if keeping only the hexadecimal part)\n\nThe equivalent size for RFC-4648 base32 is 26 characters (-27%) and 22 characters (-38%)\nfor RFC-4648 base64 (without padding in both cases). The storage is more efficient than\nhexadecimal. This allows to reduce the traffic in web service requests.\n\n#### User-friendliness\n\nThe main advantage of the RFC-4122 format is that it is easy to read the internal\nUUID bytes format in it. But, in practices, users don't need to know that the used UUID \nis an UUID7 or 8. So this is not relevant in the use case of a web service.\n\nTo be user-friendly an ID should be easy to read, compare and copy by the user \n(Not only with copy/paste).\n\nBase32 is case-insensitive and characters that can be confused are not used in its \nalphabet, this make it more user-friendly than base64. The reduced size also improve \nuser-friendliness in comparison with hexadecimal notation. So base32 is a good \ncompromise.\n\n### About uniqueness\n\nSince UUID7 as a fixed number of possible values and use a random part, there is still\na very low statistical chance that two generated UUID are identical.\nThis represents 1/18889465931478580854784 chance to have two identical UUID, \nand that apply only for a period of 1 millisecond. \n\nThis can be managed by:\n\n* EASY SOLUTION: Adding a \"unique\" clause in database that will raise an error when\n  trying to insert the duplicated UUID row.\n* MEDIUM SOLUTION: In addition of the previous solution, the application code may \n handle this case and retry with another ID if a duplicate is detected.\n* MISSION CRITICAL SOLUTION: Using UUID8 with node and ensure this node is unique in \n  your application.\n\n## Features:\n\n * Optimized for database use (Size and performance).\n * Short and user-friendly string representation (With many options).\n * Possibility to use a custom \"node\" part to help guarantee uniqueness in mission \n   critical application. \n * Possibility to import a UUID from any 16 bytes length binary in many format and types\n  (`bytes`, `int`, `str`, `uuid.UUID`, ...).\n * Possibility to create UUID from a hash to help with deduplication.\n * Included Pydantic support.\n * Fully typped.\n * No external dependencies and pure Python.\n * Easy subclassing.\n\n## Installation\n\nWebUuid is available on PyPI, so it can be installed like any other Python package.\n\nExample with Pip:\n```bash\npip install webuuid\n```\n\nWebUuid does not require any external dependency and is a pure Python library.\n\n## Usage\n\n### UUID generation\n\nBy default, is the class is used without argument, a new UUID7 is generated:\n\n```python\nimport webuuid\n\n\nwebuuid.Uuid()\n```\n\nThe class can also be loaded from any 16 bytes length object or its representation as \nsource:\n\n```python\nimport base64\nimport os\nimport webuuid\n\n\n# Using byte-like objects as source\nbytes_obj = os.urandom(16)\nwebuuid.Uuid(bytes_obj)\n\nmemoryview_obj = memoryview(bytes_obj)\nwebuuid.Uuid(memoryview_obj)\n\nbytearray_obj = bytearray(bytes_obj)\nwebuuid.Uuid(bytearray_obj)\n\n# Using int as source\nint_obj = int.from_bytes(bytes_obj, \"big\")\nwebuuid.Uuid(int_obj)\n\n# Using base64/base32/hexadecimal str representations\nbase32_obj = base64.b32encode(bytes_obj).decode()\nwebuuid.Uuid(base32_obj)\n\nbase64_obj = base64.b64encode(bytes_obj).decode()\nwebuuid.Uuid(base64_obj)\n\nbase64url_obj = base64.urlsafe_b64encode(bytes_obj).decode()\nwebuuid.Uuid(base64url_obj)\n\nhex_obj = bytes_obj.hex()\nwebuuid.Uuid(hex_obj)\n\n# Using base64/base32 str representations without padding\nwebuuid.Uuid(base32_obj.rstrip(\"=\"))\nwebuuid.Uuid(base64_obj.rstrip(\"=\"))\nwebuuid.Uuid(base64url_obj.rstrip(\"=\"))\n```\n\nA standard library `uuid.UUID` can also be used as source:\n\n```python\nimport uuid\nimport webuuid\n\n\nstdlib_uuid = uuid.uuid4()\n\n# Using standard UUID as source\nuuid_obj = webuuid.Uuid(stdlib_uuid)\nwebuuid.Uuid(str(stdlib_uuid))\n\n# Going back to standard library UUID\nuuid.UUID(bytes=uuid_obj)\n```\n\n#### UUID with custom node\n\nIt is also possible to generate a new UUID with a custom \"node\". This is mainly be \nuseful if you do not want relly entirely on randomness to improve the collision \nresistance.\nIn this case the node need be unique in all your application and may be \ngenerated/validated against a centralized registry or using a base value that is unique\nby design (Example: in a Cloud environment, server/instance ID and process ID).\n\nIn the case, the first 64 bits of the UUID are generated normally \n(With 48 bits of timestamp and 10 random bits), the 64 endings bytes (Ignoring version \nand variant fields) are the custom node.\n\n```python\nimport webuuid\nimport os\n\n\n# Using a random value as node\nnode = urandom(8)\n\n# Generation from node\nmy_uuid = webuuid.Uuid(node=node)\n\n# The node can be accessed using the following property\nmy_uuid.node\n```\n\nIn the case, the generated UUID wil be an UUID8 instead of an UUID7.\n\n#### UUID from hash\n\nIt is possible to generate a UUID using the hash of a byte-like input data. This can be \nuseful for tables that need deduplication on row IDs.\n\nIn this case, like any hash, the UUID is always the same for the same input data.\nThere is no timestamp in the UUID, so it is not time sortable and can have a negative \nimpact on the database performance on INSERT in large tables.\n\n```python\nimport webuuid\nimport json\n\n\ndata = json.dumps({\"key\": \"value\"}).encode()\nmy_uuid = webuuid.Uuid.from_hash(data)\n```\n\nIn the case, the generated UUID will be an UUID8 instead of an UUID7.\n\nThe hash function used is `blake2b`.\n\n### UUID class features\n\nThe `webuuid.Uuid` class is a subclass of `bytes` and supports all the bytes objects\nstandards features with the following changes:\n\n* `webuuid.Uuid.decode()` returns an encoded `str` representation using the\n  base32 encoding (By default, but can be easily changed) instead of the classical UTF-8\n  encoding.\n* Using `str()` on `webuuid.Uuid` objects returns the same result as \n  `webuuid.Uuid.decode()`.\n* `webuuid.Uuid` can be compared with other `webuuid.Uuid` or byte-like objects but also\n  with any `str`, `uuid.UUID` or `int` that can be used as input with `webuuid.Uuid`\n  directly.\n* `int()` can be used directly  on `webuuid.Uuid` objects. \n\nIn addition to bytes features, the class provides some methods to convert it to various\n`str` representations:\n* `webuuid.Uuid.base32()`: RFC-4648 Base 32 representation (Without padding by default).\n* `webuuid.Uuid.base64()`: RFC-4648 Base 64 representation (Without padding by default).\n* `webuuid.Uuid.base64url()`: RFC-4648 Base 64 URL safe representation (Without padding \n  by default).\n* `webuuid.Uuid.standard_hex()`: RFC-4122 UUID hexadecimal representation.\n\n#### String representation customization\n\nThe `webuuid.Uuid` class use the RFC-4648 Base 32 without padding `str` representation \nby default.\n\nThe library also provides the following classes that use a different default \nrepresentation:\n* `webuuid.UuidBase64`: RFC-4648 Base 64 without padding.\n* `webuuid.UuidBase64Url`: RFC-4648 Base 64 URL safe without padding.\n\nThe class can also easily be subclassed to use any representation of your choice as\ndefault output, but also as input:\n\n```python\nimport webuuid\n\n\ndef custom_encoder(\n    value: bytes, encoding: str = \"utf-8\", errors: str = \"strict\"\n) -> str:\n    \"\"\"Custom encoder.\n    \n    Args:\n        value: UUID bytes input value.\n        encoding: See `bytes.decode()` argument.\n        errors: See `bytes.decode()` argument.\n    \"\"\"\n    # Custom codec implementation\n\n\ndef custom_decoder(value: str) -> bytes:\n    \"\"\"Custom decoder.\n\n    Args:\n        value: Input string value.\n    \"\"\"\n    # Custom codec implementation\n\n\nclass CustomUuid(webuuid.Uuid):\n    \"\"\"Custom UUID that only support the specified codec.\"\"\"\n\n    # Set custom codec\n    STR_ENCODER = custom_encoder\n    FALLBACK_STR_DECODER = custom_decoder\n\n    # Disable other codecs.\n    STR_DECODERS = dict()\n\n\nclass CustomCompatibleUuid(webuuid.Uuid):\n    \"\"\"Custom UUID that add the support for an extra codec.\"\"\"\n\n    # Set custom encoder\n    STR_ENCODER = custom_encoder\n\n    # Add customer decoder.\n    # In this example the encoder generate a 48 characters length str\n    STR_DECODERS = webuuid.Uuid.STR_DECODERS.copy()\n    STR_DECODERS[48] = custom_decoder\n```\n\n### Usage with other libraries\n\nThis part give some tips on how to use WebUuid with some common libraries.\n\n#### JSON serialization/deserialization\n\nIn web application it is very common to serialize/deserialize data in JSON. By default,\nthe standard `json` library (And compatible alternative) only supports base Python \ntypes, but it is easy to add the `webuuid.Uuid` support to it:\n\n```python\nimport json\nimport typing\nimport webuuid\n\n\ndef json_default_uuid(obj: typing.Any) -> typing.Any:\n    \"\"\"webuuid.uuid JSON serializer.\n\n    Args:\n        obj: Object to serialize.\n    \"\"\"\n    if isinstance(obj, webuuid.Uuid):\n        return str(obj)\n    raise TypeError\n\n\n# Serializing object with UUID\ndata = {\"id\": webuuid.Uuid()}\njson.loads(data, default=json_default_uuid)\n```\n\nSome more high level libraries provides feature to register JSON encoders or `default` \nfunction for automatic use.\n\n#### Pydantic\n\n[Pydantic](https://github.com/pydantic/pydantic) is a data validation library. \nIt is notably used with the [FastAPI](https://fastapi.tiangolo.com/) web framework.\n\n`webuuid.Uuid` as native Pydantic support with validator and schema:\n\n```python\nimport pydantic\nimport webuuid\n\n\nclass Model(pydantic.BaseModel):\n    \"\"\"Pydantic model.\"\"\"\n    \n    class Config:\n        \"\"\"Config.\"\"\"\n\n        # Automatically JSON serialize UUID to its string representation\n        json_encoders = {webuuid.Uuid: str}\n\n    # UUID field\n    id: webuuid.Uuid\n    \n    # UUID Field generating a default new value if not specified\n    value: webuuid.Uuid = pydantic.Field(default_factory=webuuid.Uuid)\n```\n\nOn custom subclass, the schema can easily be customized by overriding the \n`webuuid.Uuid.FIELD_SCHEMA` class dictionary.\n\n#### Databases libraries\n\nIn databases, the UUID can be stored in a 16 bytes length binary column,\nlike BINARY(16). Depending on the library used and the database engine, the type and the\nsyntax to use may vary.\n\n##### SQLAlchemy\n\nWith [SQLAlchemy](https://www.sqlalchemy.org/), the UUID can be stored using the\n`LargeBinary(length=16)` type. SQLAlchemy will use the proper type for the required \ndatabase engine behind the scene.\n\n```python\nimport sqlalchemy\n\n\nmetadata = sqlalchemy.MetaData()\n\ntable = Table(\n    \"table_name\",\n    metadata,\n    sqlalchemy.Column(\n        \"uuid\",\n        sqlalchemy.LargeBinary(length=16),\n        primary_key=True,\n        index=True\n    ),\n)\n```\n",
    "bugtrack_url": null,
    "license": "BSD-2-Clause",
    "summary": "Optimize UUID for web services",
    "version": "0.1.1",
    "split_keywords": [
        "uuid",
        "uuid6",
        "uuid7",
        "pydantic",
        "fastapi"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "68d0601ea9b052dba3bf3c8336c6dc2706ca554c68aaa186cdb7c48daf8aef1a",
                "md5": "b78fd3377a6744e07b46a8914d5ad96b",
                "sha256": "3d4e2daf6ff343b4a963ee3f874274d512fdd261c79a424f24d39ea3b651ad58"
            },
            "downloads": -1,
            "filename": "webuuid-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "b78fd3377a6744e07b46a8914d5ad96b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10,<4.0",
            "size": 10644,
            "upload_time": "2023-02-04T02:04:31",
            "upload_time_iso_8601": "2023-02-04T02:04:31.315773Z",
            "url": "https://files.pythonhosted.org/packages/68/d0/601ea9b052dba3bf3c8336c6dc2706ca554c68aaa186cdb7c48daf8aef1a/webuuid-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "48591053234cb092021fbf62a3b36822c64f4854a41cc57c23ecb0e18de97be6",
                "md5": "8b2fda0e52a58ab45278594731d41536",
                "sha256": "f03703fdda184348a9cf21013b04a32c5976590bb6f8576897e5b3a01bf488ef"
            },
            "downloads": -1,
            "filename": "webuuid-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "8b2fda0e52a58ab45278594731d41536",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10,<4.0",
            "size": 16096,
            "upload_time": "2023-02-04T02:04:32",
            "upload_time_iso_8601": "2023-02-04T02:04:32.843910Z",
            "url": "https://files.pythonhosted.org/packages/48/59/1053234cb092021fbf62a3b36822c64f4854a41cc57c23ecb0e18de97be6/webuuid-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-02-04 02:04:32",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "JGoutin",
    "github_project": "webuuid",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "webuuid"
}
        
Elapsed time: 0.17639s