From fa5201083f3418236ed67d2a2a22e4bd2e791d7d Mon Sep 17 00:00:00 2001 From: Harry Mander Date: Tue, 17 Jun 2025 10:43:55 +1200 Subject: [PATCH 1/7] Make _DataclassStructInternal public --- dataclasses_struct/__init__.py | 2 ++ dataclasses_struct/dataclass.py | 22 +++++++++++----------- dataclasses_struct/ext/mypy_plugin.py | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/dataclasses_struct/__init__.py b/dataclasses_struct/__init__.py index 63a361f..3fdb1c4 100644 --- a/dataclasses_struct/__init__.py +++ b/dataclasses_struct/__init__.py @@ -3,6 +3,7 @@ __version__ = metadata.version(__package__) from .dataclass import ( + DataclassStructInternal, DataclassStructProtocol, dataclass_struct, get_struct_size, @@ -67,6 +68,7 @@ "BoolField", "Char", "CharField", + "DataclassStructInternal", "DataclassStructProtocol", "FloatingPointField", "Int", diff --git a/dataclasses_struct/dataclass.py b/dataclasses_struct/dataclass.py index b966a14..0e9da7c 100644 --- a/dataclasses_struct/dataclass.py +++ b/dataclasses_struct/dataclass.py @@ -82,7 +82,7 @@ class _FieldInfo: init: bool -class _DataclassStructInternal(Generic[T]): +class DataclassStructInternal(Generic[T]): struct: Struct cls: type[T] _fields: list[_FieldInfo] @@ -126,16 +126,16 @@ def _flatten_attr(attrs: list[Any], attr: object) -> None: attrs.extend(attr.__dataclass_struct__._flattened_attrs(attr)) elif isinstance(attr, list): for sub_attr in attr: - _DataclassStructInternal._flatten_attr(attrs, sub_attr) + DataclassStructInternal._flatten_attr(attrs, sub_attr) else: attrs.append(attr) - def pack(self, obj: T) -> bytes: + def _pack(self, obj: T) -> bytes: return self.struct.pack(*self._flattened_attrs(obj)) def _arg_generator(self, args: Iterator) -> Generator: for field in self._fields: - yield from _DataclassStructInternal._generate_args_recursively( + yield from DataclassStructInternal._generate_args_recursively( args, field.field, field.type_ ) @@ -151,7 +151,7 @@ def _generate_args_recursively( items: list = [] for _ in range(field.n): items.extend( - _DataclassStructInternal._generate_args_recursively( + DataclassStructInternal._generate_args_recursively( args, field.item_field, field.item_type ) ) @@ -177,12 +177,12 @@ def _init_from_args(self, args: Iterator) -> T: setattr(obj, name, arg) return obj - def unpack(self, data: bytes) -> T: + def _unpack(self, data: bytes) -> T: return self._init_from_args(iter(self.struct.unpack(data))) class DataclassStructProtocol(Protocol): - __dataclass_struct__: _DataclassStructInternal + __dataclass_struct__: DataclassStructInternal @classmethod def from_packed(cls: type[T], data: bytes) -> T: ... @@ -213,7 +213,7 @@ def is_dataclass_struct( return ( dataclasses.is_dataclass(obj) and hasattr(obj, "__dataclass_struct__") - and isinstance(obj.__dataclass_struct__, _DataclassStructInternal) + and isinstance(obj.__dataclass_struct__, DataclassStructInternal) ) @@ -456,7 +456,7 @@ def _make_pack_method() -> Callable: func = """ def pack(self) -> bytes: '''Pack to bytes using struct.pack.''' - return self.__dataclass_struct__.pack(self) + return self.__dataclass_struct__._pack(self) """ scope: dict[str, Any] = {} @@ -468,7 +468,7 @@ def _make_unpack_method(cls: type) -> classmethod: func = """ def from_packed(cls, data: bytes) -> cls_type: '''Unpack from bytes.''' - return cls.__dataclass_struct__.unpack(data) + return cls.__dataclass_struct__._unpack(data) """ scope: dict[str, Any] = {"cls_type": cls} @@ -507,7 +507,7 @@ def _make_class( setattr( # noqa: B010 cls, "__dataclass_struct__", - _DataclassStructInternal("".join(struct_format), cls, fields), + DataclassStructInternal("".join(struct_format), cls, fields), ) setattr(cls, "pack", _make_pack_method()) # noqa: B010 setattr(cls, "from_packed", _make_unpack_method(cls)) # noqa: B010 diff --git a/dataclasses_struct/ext/mypy_plugin.py b/dataclasses_struct/ext/mypy_plugin.py index 696cb5a..b57af20 100644 --- a/dataclasses_struct/ext/mypy_plugin.py +++ b/dataclasses_struct/ext/mypy_plugin.py @@ -36,7 +36,7 @@ def transform_dataclass_struct(ctx: ClassDefContext) -> bool: ctx.cls, "__dataclass_struct__", ctx.api.named_type( - "dataclasses_struct.dataclass._DataclassStructInternal" + "dataclasses_struct.dataclass.DataclassStructInternal" ), is_classvar=True, ) From d521443b41c81a9680ea3c784f15f4dfc2528c41 Mon Sep 17 00:00:00 2001 From: Harry Mander Date: Tue, 17 Jun 2025 10:45:11 +1200 Subject: [PATCH 2/7] Add more docstrings --- dataclasses_struct/dataclass.py | 120 +++++++++++++++++++++++++++++--- dataclasses_struct/types.py | 96 ++++++++++++++++++++++++- 2 files changed, 204 insertions(+), 12 deletions(-) diff --git a/dataclasses_struct/dataclass.py b/dataclasses_struct/dataclass.py index 0e9da7c..11bd4c4 100644 --- a/dataclasses_struct/dataclass.py +++ b/dataclasses_struct/dataclass.py @@ -89,14 +89,28 @@ class DataclassStructInternal(Generic[T]): @property def format(self) -> str: + """ + The format string used by the `struct` module to pack/unpack data. + + See https://docs.python.org/3/library/struct.html#format-strings. + """ return self.struct.format @property def size(self) -> int: + """Size of the packed representation in bytes.""" return self.struct.size @property def mode(self) -> str: + """ + The `struct` mode character that determines size, alignment, and + byteorder. + + This is the first character of the `format` field. See + https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment + for more info. + """ return self.format[0] def __init__( @@ -183,11 +197,42 @@ def _unpack(self, data: bytes) -> T: class DataclassStructProtocol(Protocol): __dataclass_struct__: DataclassStructInternal + """ + Internal data used by the library for packing and unpacking structs. + + See + [`DataclassStructInternal`][dataclasses_struct.DataclassStructInternal]. + """ @classmethod - def from_packed(cls: type[T], data: bytes) -> T: ... + def from_packed(cls: type[T], data: bytes) -> T: + """Return an instance of the class from its packed representation. + + Args: + data: The packed representation of the class as returned by + [`pack`][dataclasses_struct.DataclassStructProtocol.pack]. + + Returns: + An instance of the class unpacked from `data`. + + Raises: + struct.error: If `data` is the wrong length. + """ + ... + + def pack(self) -> bytes: + """Return the packed representation in `bytes` of the object. + + Returns: + The packed representation. Can be used to instantiate a new object + with + [`from_packed`][dataclasses_struct.DataclassStructProtocol.from_packed]. - def pack(self) -> bytes: ... + Raises: + struct.error: If any of the fields are out of range or the wrong + type. + """ + ... @overload @@ -206,9 +251,15 @@ def is_dataclass_struct( TypeGuard[DataclassStructProtocol], TypeGuard[type[DataclassStructProtocol]], ]: - """ - Returns True if obj is a class that has been decorated with - dataclasses_struct.dataclass or an instance of one. + """Determine whether a type or object is a dataclass-struct. + + Args: + obj: A class or object. + + Returns: + `True` if obj is a class that has been decorated with + [`dataclass_struct`][dataclasses_struct.dataclass_struct] or is an + instance of one. """ return ( dataclasses.is_dataclass(obj) @@ -217,10 +268,19 @@ def is_dataclass_struct( ) -def get_struct_size(cls_or_obj) -> int: - """ - Returns the size of the packed representation of the struct in bytes. - Accepts either a class or an instance of a dataclass_struct. +def get_struct_size(cls_or_obj: object) -> int: + """Get the size of the packed representation of the struct in bytes. + + Args: + cls_or_obj: A class that has been decorated with + [`dataclass_struct`][dataclasses_struct.dataclass_struct] or an + instance of one. + + Returns: + The size of the packed representation in bytes. + + Raises: + TypeError: if `cls_or_obj` is not a dataclass-struct. """ if not is_dataclass_struct(cls_or_obj): raise TypeError(f"{cls_or_obj} is not a dataclass_struct") @@ -563,6 +623,48 @@ def dataclass_struct( validate_defaults: bool = True, **dataclass_kwargs: Unpack[DataclassKwargs], ) -> Callable[[type], type]: + """Create a dataclass struct. + + Should be used as a decorator on a class: + + ```python + import dataclasses_struct as dcs + + @dcs.dataclass_struct() + class A: + data: dcs.Pointer + size: dcs.UnsignedSize + ``` + + The allowed `size` and `byteorder` argument combinations are as as follows. + + | `size` | `byteorder` | Notes | + | ---------- | ----------- | ------------------------------------------------------------------ | + | `"native"` | `"native"` | The default. Native alignment and padding. | + | `"std"` | `"native"` | Standard integer sizes and system endianness, no alignment/padding. | + | `"std"` | `"little"` | Standard integer sizes and little endian, no alignment/padding. | + | `"std"` | `"big"` | Standard integer sizes and big endian, no alignment/padding. | + | `"std"` | `"network"` | Equivalent to `byteorder="big"`. | + + Args: + size: The size mode. + byteorder: The byte order of the generated struct. If `size="native"`, + only `"native"` is allowed. + validate_defaults: Whether to validate the default values of any + fields. + dataclass_kwargs: Any additional keyword arguments to pass to the + [stdlib + `dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass) + decorator. The `slots` and `weakref_slot` keyword arguments are not + supported. + + Raises: + ValueError: If the `size` and `byteorder` args are invalid or if + `validate_defaults=True` and any of the fields' default values are + invalid for their type. + TypeError: If any of the fields' type annotations are invalid or + not supported. + """ # noqa: E501 is_native = size == "native" if is_native: if byteorder != "native": diff --git a/dataclasses_struct/types.py b/dataclasses_struct/types.py index bc505e0..fc41e69 100644 --- a/dataclasses_struct/types.py +++ b/dataclasses_struct/types.py @@ -2,45 +2,101 @@ from . import field -# Single char type Char = bytes +"""Single char type. Supported in both size modes.""" -# Boolean type Bool = Annotated[bool, field.BoolField()] +"""Boolean type. Supported in both size modes.""" -# Standard integer types I8 = Annotated[int, field.SignedStdIntField(1)] +"""Fixed-width 8-bit signed integer. Supported with `size="std"`.""" + U8 = Annotated[int, field.UnsignedStdIntField(1)] +"""Fixed-width 8-bit unsigned integer. Supported with `size="std"`.""" + I16 = Annotated[int, field.SignedStdIntField(2)] +"""Fixed-width 16-bit signed integer. Supported with `size="std"`.""" + U16 = Annotated[int, field.UnsignedStdIntField(2)] +"""Fixed-width 16-bit unsigned integer. Supported with `size="std"`.""" + I32 = Annotated[int, field.SignedStdIntField(4)] +"""Fixed-width 32-bit signed integer. Supported with `size="std"`.""" + U32 = Annotated[int, field.UnsignedStdIntField(4)] +"""Fixed-width 32-bit unsigned integer. Supported with `size="std"`.""" + I64 = Annotated[int, field.SignedStdIntField(8)] +"""Fixed-width 64-bit signed integer. Supported with `size="std"`.""" + U64 = Annotated[int, field.UnsignedStdIntField(8)] +"""Fixed-width 64-bit unsigned integer. Supported with `size="std"`.""" + # Native integer types SignedChar = Annotated[int, field.NativeIntField("b", "byte")] +"""Equivalent to native C `signed char`. Supported with `size="native"`.""" + UnsignedChar = Annotated[int, field.NativeIntField("B", "ubyte")] +"""Equivalent to native C `unsigned char`. Supported with `size="native"`.""" + Short = Annotated[int, field.NativeIntField("h", "short")] +"""Equivalent to native C `short`. Supported with `size="native"`.""" + UnsignedShort = Annotated[int, field.NativeIntField("H", "ushort")] +"""Equivalent to native C `unsigned short`. Supported with `size="native"`.""" + Int = Annotated[int, field.NativeIntField("i", "int")] +"""Equivalent to native C `int`. Supported with `size="native"`.""" + UnsignedInt = Annotated[int, field.NativeIntField("I", "uint")] +"""Equivalent to native C `unsigned int`. Supported with `size="native"`.""" + Long = Annotated[int, field.NativeIntField("l", "long")] +"""Equivalent to native C `long`. Supported with `size="native"`.""" + UnsignedLong = Annotated[int, field.NativeIntField("L", "ulong")] +"""Equivalent to native C `unsigned long`. Supported with `size="native"`.""" + LongLong = Annotated[int, field.NativeIntField("q", "longlong")] +"""Equivalent to native C `long long`. Supported with `size="native"`.""" + UnsignedLongLong = Annotated[int, field.NativeIntField("Q", "ulonglong")] +"""Equivalent to native C `unsigned long long`. Supported with +`size="native"`.""" + # Native size types UnsignedSize = Annotated[int, field.SizeField(signed=False)] +"""Equivalent to native C `size_t`. Supported with `size="native"`.""" + SignedSize = Annotated[int, field.SizeField(signed=True)] +"""Equivalent to native C `ssize_t` (a POSIX extension type). Supported with +`size="native"`.""" # Native pointer types Pointer = Annotated[int, field.PointerField()] +"""Equivalent to native C `void *` pointer. Supported with `size="native"`.""" # Floating point types F16 = Annotated[float, field.FloatingPointField("e")] +"""Half-precision floating point number. Supported in both size modes. + +Some compilers provide support for half precision floats on certain platforms +(e.g. [GCC](https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html), +[Clang](https://clang.llvm.org/docs/LanguageExtensions.html#half-precision-floating-point)). +It is also available as +[`std::float16_t`](https://en.cppreference.com/w/cpp/types/floating-point.html) +in C++23. +""" + F32 = Annotated[float, field.FloatingPointField("f")] +"""Single-precision floating point number, equivalent to `float` in C. +Supported in both size modes.""" + F64 = Annotated[float, field.FloatingPointField("d")] +"""Double-precision floating point number, equivalent to `double` in C. +Supported in both size modes.""" class _Padding: @@ -56,6 +112,23 @@ def __repr__(self) -> str: class PadBefore(_Padding): + """Add zero-bytes padding before the field. + + Should be used with `typing.Annotated`. + + ```python + from typing import Annotated + import dataclasses_struct as dcs + + @dcs.dataclass_struct() + class Padded: + x: Annotated[int, dcs.PadBefore(5)] + ``` + + Args: + size: The number of padding bytes to add before the field. + """ + before = True def __init__(self, size: int): @@ -63,6 +136,23 @@ def __init__(self, size: int): class PadAfter(_Padding): + """Add zero-bytes padding after the field. + + Should be used with `typing.Annotated`. + + ```python + from typing import Annotated + import dataclasses_struct as dcs + + @dcs.dataclass_struct() + class Padded: + x: Annotated[int, dcs.PadAfter(5)] + ``` + + Args: + size: The number of padding bytes to add after the field. + """ + before = False def __init__(self, size: int): From 9e006f20561322301248dd9693af82ca8a61da39 Mon Sep 17 00:00:00 2001 From: Harry Mander Date: Tue, 17 Jun 2025 10:46:55 +1200 Subject: [PATCH 3/7] Add docs built with mkdocs-material --- .gitignore | 1 + .pre-commit-config.yaml | 3 + docs/api-reference.md | 6 + docs/guide.md | 440 ++++++++++++++++++++++++++++++++++++ docs/index.md | 135 +++++++++++ docs/types-reference.md | 7 + mkdocs.yml | 45 ++++ pyproject.toml | 5 + requirements-docs.txt | 401 +++++++++++++++++++++++++++++++++ uv.lock | 482 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1525 insertions(+) create mode 100644 docs/api-reference.md create mode 100644 docs/guide.md create mode 100644 docs/index.md create mode 100644 docs/types-reference.md create mode 100644 mkdocs.yml create mode 100644 requirements-docs.txt diff --git a/.gitignore b/.gitignore index 49b9c04..2f64a29 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ .coverage .dmypy.json /dist/ +/site/ __pycache__/ coverage.xml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 51212fe..6787aa5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,3 +9,6 @@ repos: hooks: - id: uv-lock args: ["--check"] + - id: uv-export + args: ["--quiet", "--only-group", "docs", "-o", "requirements-docs.txt"] + files: ^(requirements-docs\.txt|pyproject\.toml|uv\.lock)$ diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 0000000..c6b0c09 --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,6 @@ +# API reference + +::: dataclasses_struct.dataclass + options: + show_root_heading: false + show_root_toc_entry: false diff --git a/docs/guide.md b/docs/guide.md new file mode 100644 index 0000000..95f8ab9 --- /dev/null +++ b/docs/guide.md @@ -0,0 +1,440 @@ +# Guide + +## The `dataclass_struct` decorator + +Use the [`dataclass_struct`][dataclasses_struct.dataclass_struct] decorator to convert a class into a [stdlib +`dataclass`](https://docs.python.org/3/library/dataclasses.html) with struct +packing/unpacking functionality: + +```python +def dataclass_struct( + *, + size: Literal["native", "std"] = "native", + byteorder: Literal["native", "big", "little", "network"] = "native", + validate_defaults: bool = True, + **dataclass_kwargs, +): + ... +``` + +The `size` argument can be either `"native"` (the default) or `"std"` and +controls the size and alignment of fields: + +| `size` | `byteorder` | Notes | +| ------------------------------- | ----------- | ------------------------------------------------------------------ | +| [`"native"`](#native-size-mode) | `"native"` | The default. Native alignment and padding. | +| [`"std"`](#standard-size-mode) | `"native"` | Standard integer sizes and system endianness, no alignment/padding. | +| [`"std"`](#standard-size-mode) | `"little"` | Standard integer sizes and little endian, no alignment/padding. | +| [`"std"`](#standard-size-mode) | `"big"` | Standard integer sizes and big endian, no alignment/padding. | +| [`"std"`](#standard-size-mode) | `"network"` | Equivalent to `byteorder="big"`. | + +Decorated classes are transformed to a standard Python +[dataclass](https://docs.python.org/3/library/dataclasses.html) with boilerplate +`__init__`, `__repr__`, `__eq__` etc. auto-generated. Additionally, two methods +are added to the class: +[`pack`][dataclasses_struct.DataclassStructProtocol.pack], a method for packing +an instance of the class to `bytes`, and +[`from_packed`][dataclasses_struct.DataclassStructProtocol.from_packed], a class +method that returns a new instance of the class from its packed `bytes` +representation. The additional `dataclass_kwargs` keyword arguments will be +passed through to the [stdlib `dataclass` +decorator](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass): +all standard keyword arguments are supported except for `slots` and +`weakref_slot`. + +Default attribute values will be validated against their expected type and +allowable value range. For example, + +```python +import dataclasses_struct as dcs + +@dcs.dataclass_struct() +class Test: + x: dcs.UnsignedChar = -1 +``` + +will raise a `ValueError`. This can be disabled by passing +`validate_defaults=False` to the decorator. + +## Inspecting dataclass-structs + +A class or object can be checked to see if it is a dataclass-struct using the +[`is_dataclass_struct`][dataclasses_struct.is_dataclass_struct] function. + + +```python +>>> dcs.is_dataclass_struct(Test) +True +>>> t = Test(100) +>>> dcs.is_dataclass_struct(t) +True +``` + +The [`get_struct_size`][dataclasses_struct.get_struct_size] function will return +the size in bytes of the packed representation of a dataclass-struct class or an +instance of one. + +```python +>>> dcs.get_struct_size(Test) +234 +``` + +An additional class attribute, +[`__dataclass_struct__`][dataclasses_struct.DataclassStructProtocol.__dataclass_struct__], +is added to the decorated class that contains the packed size, [`struct` format +string](https://docs.python.org/3/library/struct.html#format-strings), and +`struct` mode. + +```python +>>> Test.__dataclass_struct__.size +234 +>>> Test.__dataclass_struct__.format +'@cc??bBhHiIQqqNnPfdd100s4xqq2x3xq2x' +>>> Test.__dataclass_struct__.mode +'@' +``` + +## Native size mode + +In `"native"` mode (the default), the struct is packed based on the platform and +compiler on which Python was built: padding bytes may be added to maintain +proper alignment of the fields and byte ordering (endianness) follows that of +the platform. (The `byteorder` argument must also be `"native"`.) + +In `"native"` size mode, integer type sizes follow those of the standard C +integer types of the platform (`int`, `unsigned short` etc.). + +```python +@dcs.dataclass_struct() +class NativeStruct: + signed_char: dcs.SignedChar + signed_short: dcs.Short + unsigned_long_long: dcs.UnsignedLongLong + void_pointer: dcs.Pointer +``` + +## Standard size mode + +In `"std"` mode, the struct is packed without any additional padding for +alignment. + +The `"std"` size mode supports four different `byteorder` values: `"native"` +(the default), `"little"`, `"big"`, and `"network"`. The `"native"` setting uses +the system byte order (similar to `"native"` size mode, but without alignment). +The `"network"` setting is equivalent to `"big"`. + +The `"std"` size uses platform-independent integer sizes, similar to using the +integer types from `stdint.h` in C. When used with `byteorder` set to +`"little"`, `"big"`, or `"network"`, it is appropriate for marshalling data +across different platforms. + +```python +@dcs.dataclass_struct(size="std", byteorder="native") +class NativeStruct: + int8_t: dcs.I8 + uint64_t: dcs.U64 +``` + +## Supported type annotations + +See the [reference page](types-reference.md) for the complete list of type +annotations. + +### Native integer types + +These types are only supported in `"native"` size mode. Their native Python +types are all `int`. + +| Type annotation | Equivalent C type | +| ------------------------------------ | --------------------------- | +| `SignedChar` | `signed char` | +| `UnsignedChar` | `unsigned char` | +| `Short` | `short` | +| `UnsignedShort` | `unsigned short` | +| `Int` | `int` | +| `int` (builtin type, alias to `Int`) | `int` | +| `UnsignedInt` | `unsigned int` | +| `Long` | `long` | +| `UnsignedLong` | `unsigned long` | +| `LongLong` | `long long` | +| `UnsignedLongLong` | `unsigned long long` | +| `UnsignedSize` | `size_t` | +| `SignedSize` | `ssize_t` (POSIX extension) | +| `Pointer` | `void *` | + +### Standard integer types + +These types are only supported in `"std"` size mode. Their native Python types +are all `int`. + +| Type annotation | Equivalent C type | +| ------------------------------------ | --------------------------- | +| `I8` | `int8_t` | +| `U8` | `uint8_t` | +| `I16` | `int16_t` | +| `U16` | `uint16_t` | +| `I32` | `int32_t` | +| `U32` | `uint32_t` | +| `I64` | `int64_t` | +| `U64` | `uint64_t` | + +### Floating point types + +Supported in both size modes. The native Python type is `float`. + +| Type annotation | Equivalent C type | +| ------------------------------------ | --------------------------- | +| `F16` | Extension type (see below) | +| `F32` | `float` | +| `F64` | `double` | +| `float` (builtin alias to `F64`) | `double` | + +`F16` is a half precision floating point. Some compilers provide support for it +on certain platforms (e.g. +[GCC](https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html), +[Clang](https://clang.llvm.org/docs/LanguageExtensions.html#half-precision-floating-point)). +It is also available as +[`std::float16_t`](https://en.cppreference.com/w/cpp/types/floating-point.html) +in C++23. + +Note that floating point fields are always packed and unpacked using the IEEE +754 format, regardless of the underlying format used by the platform. + +### Boolean + +The builtin `bool` type or `dataclasses_struct.Bool` type can be used to +represent a boolean, which uses a single byte in either native or standard size +modes. + + +### Nested structs + +Classes decorated with `dataclass_struct` can be used as fields in other +classes, as long as they have the same `size` and `byteorder` settings. + +```python +@dcs.dataclass_struct() +class Vector2d: + x: float + y: float + +@dcs.dataclass_struct() +class Vectors: + direction: Vector2d + velocity: Vector2d + +# Will raise TypeError: +@dcs.dataclass_struct(size="std") +class VectorsStd: + direction: Vector2d + velocity: Vector2d +``` + +Default values for nested class fields cannot be set directly, as Python doesn't +allow using mutable default values in dataclasses. To get around this, pass +`frozen=True` to the inner class' `dataclass_struct` decorator. Alternatively, +pass a zero-argument callable that returns an instance of the class to the +`default_factory` keyword argument of +[`dataclasses.field`](https://docs.python.org/3/library/dataclasses.html#dataclasses.field). +For example: + +```python +from dataclasses import field + +@dcs.dataclass_struct() +class VectorsStd: + direction: Vector2d + velocity: Vector2d = field(default_factory=lambda: Vector2d(0, 0)) +``` + +The return type of the `default_factory` will be validated unless +`validate_defaults=False` is passed to the `dataclass_struct` decorator. Note +that this means the callable passed to `default_factory` will be called once +during class creation. + +### Characters + +In both size modes, a single byte can be packed by annotating a field with the +builtin `bytes` type or the `dataclasses_struct.Char` type. The field's +unpacked Python representation will be a `bytes` of length 1. + +```python +@dcs.dataclass_struct() +class Chars: + char: dcs.Char = b'x' + builtin: bytes = b'\x04' +``` + +### Bytes arrays + +Fixed-length byte arrays can be represented in both size modes by annotating a +field with `typing.Annotated` and a positive length. The field's unpacked Python +representation will be a `bytes` object zero-padded or truncated to the +specified length. + +```python +from typing import Annotated + +@dcs.dataclass_struct() +class FixedLength: + fixed: Annotated[bytes, 10] +``` + +```python +>>> FixedLength.from_packed(FixedLength(b'Hello, world!').pack()) +FixedLength(fixed=b'Hello, wor') +``` + +### Fixed-length arrays + +Fixed-length arrays can be represented by annotating a `list` field with +`typing.Annotated` and a positive length. + +```python +from typing import Annotated + +@dcs.dataclass_struct() +class FixedLength: + fixed: Annotated[list[int], 5] +``` + +```python +>>> FixedLength.from_packed(FixedLength([1, 2, 3, 4, 5]).pack()) +FixedLength(fixed=[1, 2, 3, 4, 5]) +``` + +The values stored in fixed-length arrays can also be classes +decorated with `dataclass_struct`. + +```python +from typing import Annotated + +@dcs.dataclass_struct() +class Vector2d: + x: float + y: float + +@dcs.dataclass_struct() +class FixedLength: + fixed: Annotated[list[Vector2d], 3] +``` + +```python +>>> FixedLength.from_packed(FixedLength([Vector2d(1.0, 2.0), Vector2d(3.0, 4.0), Vector2d(5.0, 6.0)]).pack()) +FixedLength(fixed=[Vector2d(x=1.0, y=2.0), Vector2d(x=3.0, y=4.0), Vector2d(x=5.0, y=6.0)]) +``` + +Fixed-length arrays can also be multi-dimensional by nesting Annotated +`list` types. + +```python +from typing import Annotated + +@dcs.dataclass_struct() +class TwoDimArray: + fixed: Annotated[list[Annotated[list[int], 2]], 3] +``` + +```python +>>> TwoDimArray.from_packed(TwoDimArray([[1, 2], [3, 4], [5, 6]]).pack()) +TwoDimArray(fixed=[[1, 2], [3, 4], [5, 6]]) +``` + +As with [nested structs](#nested-structs), a `default_factory` must be used to +set a default value. For example: + +```python +from dataclasses import field +from typing import Annotated + +@dcs.dataclass_struct() +class DefaultArray: + x: Annotated[list[int], 3] = field(default_factory=lambda: [1, 2, 3]) +``` + +The returned default value's length and type and values of its items will be +validated unless `validate_defaults=False` is passed to the `dataclass_struct` +decorator. + +### Manual padding + +Padding can be manually controlled by annotating a type with +[`PadBefore`][dataclasses_struct.PadBefore] or +[`PadAfter`][dataclasses_struct.PadAfter]: + +```python +@dcs.dataclass_struct() +class WithPadding: + # 4 padding bytes will be added before this field + pad_before: Annotated[int, dcs.PadBefore(4)] + + # 2 padding bytes will be added before this field + pad_after: Annotated[int, dcs.PadAfter(2)] + + # 3 padding bytes will be added before this field and 2 after + pad_before_and_after: Annotated[int, dcs.PadBefore(3), dcs.PadAfter(2)] +``` + +A `b'\x00'` will be inserted into the packed representation for each padding +byte. + +```python +>>> padded = WithPadding(100, 200, 300) +>>> packed = padded.pack() +>>> packed +b'\x00\x00\x00\x00d\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x01\x00\x00\x00\x00' +>>> WithPadding.from_packed(packed) +WithPadding(pad_before=100, pad_after=200, pad_before_and_after=300) +``` + +## Type checking + +### Mypy + +To work correctly with [`mypy`](https://www.mypy-lang.org/), an extension is +required; add to your `mypy.ini`: + +```ini +[mypy] +plugins = dataclasses_struct.ext.mypy_plugin +``` + +### Pyright/Pylance + +Due to current limitations, Microsoft's +[Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) +Visual Studio extension and its open-source core +[Pyright](https://github.com/microsoft/pyright) will report an [attribute access +error](https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportAttributeAccessIssue) +on the `pack` and `from_packed` methods: + +```python +import dataclasses_struct as dcs + +@dcs.dataclass_struct() +class Test: + x: int + +t = Test(10) +t.pack() +# pyright error: Cannot access attribute "pack" for class "Test" +``` + +A fix for this is planned in the future. As a workaround in the meantime, you +can add stubs for the generated functions to the class: + +```python +from typing import TYPE_CHECKING +import dataclasses_struct as dcs + +@dcs.dataclass_struct() +class Test: + x: int + + if TYPE_CHECKING: + + def pack(self) -> bytes: ... + + @classmethod + def from_packed(cls, data: bytes) -> "Test": ... +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..a202a83 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,135 @@ +# dataclasses-struct + +[![PyPI version](https://img.shields.io/pypi/v/dataclasses-struct)](https://pypi.org/project/dataclasses-struct/) +[![Python versions](https://img.shields.io/pypi/pyversions/dataclasses-struct)](https://pypi.org/project/dataclasses-struct/) +[![Tests status](https://github.com/harrymander/dataclasses-struct/actions/workflows/test.yml/badge.svg?event=push)]() +[![Code coverage](https://img.shields.io/codecov/c/gh/harrymander/dataclasses-struct)](https://app.codecov.io/gh/harrymander/dataclasses-struct) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/harrymander/dataclasses-struct/blob/main/LICENSE) + +A simple Python package that combines +[`dataclasses`](https://docs.python.org/3/library/dataclasses.html) with +[`struct`](https://docs.python.org/3/library/struct.html) for packing and +unpacking Python dataclasses to fixed-length `bytes` representations. + +## Installation + +This package is available on pypi: + +``` +pip install dataclasses-struct +``` + +To work correctly with [`mypy`](https://www.mypy-lang.org/), an extension is +required; add to your `mypy.ini`: + +```ini +[mypy] +plugins = dataclasses_struct.ext.mypy_plugin +``` + +## Quick start + +By default, dataclass-structs use native sizes, alignment, and byte ordering +(endianness). + +```python +import dataclasses +from typing import Annotated + +import dataclasses_struct as dcs # (1)! + +@dcs.dataclass_struct(size="native", byteorder="native") # (2)! +class Vector2d: + x: dcs.F64 # (3)! + y: float #(4)! + +@dcs.dataclass_struct(kw_only=True) #(5)! +class Object: + position: Vector2d #(6)! + velocity: Vector2d = dataclasses.field( # (7)! + default_factory=lambda: Vector2d(0, 0) + ) + name: Annotated[bytes, 8] #(8)! +``` + +1. This convention of importing `dataclasses_struct` under the alias `dcs` is + used throughout these docs, but you don't have to follow this if you don't + want to. +2. The `size` and `byteorder` keyword arguments control the size, alignment, and + endianness of the class' packed binary representation. The default mode + `"native"` is native for both arguments. +3. A double precision floating point, equivalent to `double` in C. +4. The builtin `float` type is an alias to `dcs.F64`. +5. The [`dataclass_struct`][dataclasses_struct.dataclass_struct] decorator + supports most of the keyword arguments supported by the stdlib + [`dataclass`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass) + decorator. +6. Classes decorated with + [`dcs.dataclass_struct`][dataclasses_struct.dataclass_struct] can be used as + fields in other dataclass-structs provided they have the same size and + byteorder modes. +7. The stdlib + [`dataclasses.field`](https://docs.python.org/3/library/dataclasses.html#dataclasses.field) + function can be used for more complex field configurations, such as using + a mutable value as a field default. +8. Fixed-length `bytes` arrays can be represented by annotating a field with + a non-zero positive integer using `typing.Annotated`. Values longer than the + length will be truncated and values shorted will be zero-padded. + +Instances of decorated classes have a +[`pack`][dataclasses_struct.DataclassStructProtocol.pack] method that returns +the packed representation of the object in `bytes`: + +```python +>>> obj = Object(position=Vector2d(1.5, -5.6), name=b"object1") +>>> obj +Object(position=Vector2d(x=1.5, y=-5.6), velocity=Vector2d(x=0, y=0), name=b'object1') +>>> packed = obj.pack() +>>> packed +b'\x00\x00\x00\x00\x00\x00\xf8?ffffff\x16\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00object1\x00' +``` + +Decorated classes have an +[`from_packed`][dataclasses_struct.DataclassStructProtocol.from_packed] class +method that takes the packed representation and returns an instance of the +class: + +```python +>>> Object.from_packed(packed) +Object(position=Vector2d(x=1.5, y=-5.6), velocity=Vector2d(x=0.0, y=0.0), name=b'object1\x00') +``` + +In `size="native"` mode, integer type names follow the standard C integer type +names: + +```python +@dcs.dataclass_struct() +class NativeIntegers: + c_int: dcs.Int + c_int_alias: int # (1)! + c_unsigned_short: dcs.UnsignedShort + void_pointer: dcs.Pointer # (2)! + size_t: dcs.UnsignedSize + + # etc. +``` + +1. Alias to `dcs.Int`. +2. Equivalent to `void *` pointer in C. + +In `size="std"` mode, integer type names follow the standard [fixed-width +integer type](https://en.cppreference.com/w/c/types/integer.html#Types) names in +C: + +```python +@dcs.dataclass_struct(size="std") +class StdIntegers: + int8_t: dcs.I8 + int32_t: dcs.I32 + uint64_t: dcs.U64 + + # etc. +``` + +See [the guide](guide.md#supported-type-annotations) for the full list of +supported field types. diff --git a/docs/types-reference.md b/docs/types-reference.md new file mode 100644 index 0000000..ab303af --- /dev/null +++ b/docs/types-reference.md @@ -0,0 +1,7 @@ +# Type annotations reference + +::: dataclasses_struct.types + options: + show_root_heading: false + show_root_toc_entry: false + members_order: source diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..ec9b557 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,45 @@ +site_name: dataclasses-struct +site_url: https://dataclasses-struct.readthedocs.io +nav: + - index.md + - guide.md + - api-reference.md + - types-reference.md +repo_url: https://github.com/harrymander/dataclasses-struct +theme: + name: material + features: + - content.code.annotate + icon: + repo: fontawesome/brands/github + palette: + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference +plugins: + - search + - mkdocstrings +markdown_extensions: + - attr_list + - md_in_html + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - pymdownx.magiclink +watch: + - dataclasses_struct diff --git a/pyproject.toml b/pyproject.toml index f0ca593..3e5ba0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,11 @@ dev = [ "pytest-mypy-plugins>=3.1.2", "ruff>=0.9.4", ] +docs = [ + "mkdocs-material>=9.6.14", + "mkdocstrings[python]>=0.29.1", + "ruff>=0.9.4", +] [build-system] requires = ["hatchling"] diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 0000000..5a6e717 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,401 @@ +# This file was autogenerated by uv via the following command: +# uv export --only-group docs -o requirements-docs.txt +babel==2.17.0 \ + --hash=sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d \ + --hash=sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2 + # via mkdocs-material +backrefs==5.8 \ + --hash=sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd \ + --hash=sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b \ + --hash=sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc \ + --hash=sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486 \ + --hash=sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d \ + --hash=sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585 + # via mkdocs-material +certifi==2025.4.26 \ + --hash=sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6 \ + --hash=sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3 + # via requests +charset-normalizer==3.4.2 \ + --hash=sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4 \ + --hash=sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7 \ + --hash=sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0 \ + --hash=sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7 \ + --hash=sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d \ + --hash=sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0 \ + --hash=sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db \ + --hash=sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b \ + --hash=sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8 \ + --hash=sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff \ + --hash=sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e \ + --hash=sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471 \ + --hash=sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148 \ + --hash=sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a \ + --hash=sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836 \ + --hash=sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e \ + --hash=sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63 \ + --hash=sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c \ + --hash=sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366 \ + --hash=sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5 \ + --hash=sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c \ + --hash=sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597 \ + --hash=sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b \ + --hash=sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0 \ + --hash=sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941 \ + --hash=sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0 \ + --hash=sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86 \ + --hash=sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7 \ + --hash=sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6 \ + --hash=sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0 \ + --hash=sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3 \ + --hash=sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1 \ + --hash=sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6 \ + --hash=sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981 \ + --hash=sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c \ + --hash=sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980 \ + --hash=sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645 \ + --hash=sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7 \ + --hash=sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12 \ + --hash=sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd \ + --hash=sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef \ + --hash=sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f \ + --hash=sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2 \ + --hash=sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d \ + --hash=sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5 \ + --hash=sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3 \ + --hash=sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd \ + --hash=sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e \ + --hash=sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214 \ + --hash=sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd \ + --hash=sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a \ + --hash=sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c \ + --hash=sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba \ + --hash=sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f \ + --hash=sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28 \ + --hash=sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691 \ + --hash=sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82 \ + --hash=sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a \ + --hash=sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7 \ + --hash=sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518 \ + --hash=sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf \ + --hash=sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b \ + --hash=sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9 \ + --hash=sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544 \ + --hash=sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509 \ + --hash=sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a \ + --hash=sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f + # via requests +click==8.1.8 ; python_full_version < '3.10' \ + --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ + --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a + # via mkdocs +click==8.2.1 ; python_full_version >= '3.10' \ + --hash=sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202 \ + --hash=sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b + # via mkdocs +colorama==0.4.6 \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via + # click + # griffe + # mkdocs + # mkdocs-material +ghp-import==2.1.0 \ + --hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \ + --hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343 + # via mkdocs +griffe==1.7.3 \ + --hash=sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b \ + --hash=sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75 + # via mkdocstrings-python +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 + # via requests +importlib-metadata==8.7.0 ; python_full_version < '3.10' \ + --hash=sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000 \ + --hash=sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd + # via + # markdown + # mkdocs + # mkdocs-get-deps + # mkdocstrings +jinja2==3.1.5 \ + --hash=sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb \ + --hash=sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb + # via + # mkdocs + # mkdocs-material + # mkdocstrings +markdown==3.8 \ + --hash=sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc \ + --hash=sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f + # via + # mkdocs + # mkdocs-autorefs + # mkdocs-material + # mkdocstrings + # pymdown-extensions +markupsafe==3.0.2 \ + --hash=sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4 \ + --hash=sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30 \ + --hash=sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0 \ + --hash=sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9 \ + --hash=sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 \ + --hash=sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13 \ + --hash=sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028 \ + --hash=sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca \ + --hash=sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557 \ + --hash=sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832 \ + --hash=sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0 \ + --hash=sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b \ + --hash=sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579 \ + --hash=sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a \ + --hash=sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c \ + --hash=sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff \ + --hash=sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c \ + --hash=sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22 \ + --hash=sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094 \ + --hash=sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb \ + --hash=sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e \ + --hash=sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5 \ + --hash=sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a \ + --hash=sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d \ + --hash=sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a \ + --hash=sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b \ + --hash=sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8 \ + --hash=sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225 \ + --hash=sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c \ + --hash=sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144 \ + --hash=sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f \ + --hash=sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87 \ + --hash=sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d \ + --hash=sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93 \ + --hash=sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf \ + --hash=sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158 \ + --hash=sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 \ + --hash=sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb \ + --hash=sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48 \ + --hash=sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171 \ + --hash=sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c \ + --hash=sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6 \ + --hash=sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd \ + --hash=sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d \ + --hash=sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1 \ + --hash=sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d \ + --hash=sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca \ + --hash=sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a \ + --hash=sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29 \ + --hash=sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe \ + --hash=sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798 \ + --hash=sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c \ + --hash=sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8 \ + --hash=sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f \ + --hash=sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f \ + --hash=sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a \ + --hash=sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178 \ + --hash=sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0 \ + --hash=sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79 \ + --hash=sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430 \ + --hash=sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50 + # via + # jinja2 + # mkdocs + # mkdocs-autorefs + # mkdocstrings +mergedeep==1.3.4 \ + --hash=sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8 \ + --hash=sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307 + # via + # mkdocs + # mkdocs-get-deps +mkdocs==1.6.1 \ + --hash=sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2 \ + --hash=sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e + # via + # mkdocs-autorefs + # mkdocs-material + # mkdocstrings +mkdocs-autorefs==1.4.2 \ + --hash=sha256:83d6d777b66ec3c372a1aad4ae0cf77c243ba5bcda5bf0c6b8a2c5e7a3d89f13 \ + --hash=sha256:e2ebe1abd2b67d597ed19378c0fff84d73d1dbce411fce7a7cc6f161888b6749 + # via + # mkdocstrings + # mkdocstrings-python +mkdocs-get-deps==0.2.0 \ + --hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \ + --hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134 + # via mkdocs +mkdocs-material==9.6.14 \ + --hash=sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754 \ + --hash=sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b +mkdocs-material-extensions==1.3.1 \ + --hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \ + --hash=sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31 + # via mkdocs-material +mkdocstrings==0.29.1 \ + --hash=sha256:37a9736134934eea89cbd055a513d40a020d87dfcae9e3052c2a6b8cd4af09b6 \ + --hash=sha256:8722f8f8c5cd75da56671e0a0c1bbed1df9946c0cef74794d6141b34011abd42 + # via mkdocstrings-python +mkdocstrings-python==1.16.12 \ + --hash=sha256:22ded3a63b3d823d57457a70ff9860d5a4de9e8b1e482876fc9baabaf6f5f374 \ + --hash=sha256:9b9eaa066e0024342d433e332a41095c4e429937024945fea511afe58f63175d + # via mkdocstrings +packaging==24.2 \ + --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ + --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f + # via mkdocs +paginate==0.5.7 \ + --hash=sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945 \ + --hash=sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591 + # via mkdocs-material +pathspec==0.12.1 \ + --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ + --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 + # via mkdocs +platformdirs==4.3.8 \ + --hash=sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc \ + --hash=sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4 + # via mkdocs-get-deps +pygments==2.19.1 \ + --hash=sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f \ + --hash=sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c + # via mkdocs-material +pymdown-extensions==10.15 \ + --hash=sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7 \ + --hash=sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f + # via + # mkdocs-material + # mkdocstrings +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via ghp-import +pyyaml==6.0.2 \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 + # via + # mkdocs + # mkdocs-get-deps + # pymdown-extensions + # pyyaml-env-tag +pyyaml-env-tag==1.1 \ + --hash=sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04 \ + --hash=sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff + # via mkdocs +requests==2.32.4 \ + --hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \ + --hash=sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422 + # via mkdocs-material +ruff==0.11.10 \ + --hash=sha256:1067245bad978e7aa7b22f67113ecc6eb241dca0d9b696144256c3a879663bca \ + --hash=sha256:2f071b0deed7e9245d5820dac235cbdd4ef99d7b12ff04c330a241ad3534319f \ + --hash=sha256:3afead355f1d16d95630df28d4ba17fb2cb9c8dfac8d21ced14984121f639bad \ + --hash=sha256:4a60e3a0a617eafba1f2e4186d827759d65348fa53708ca547e384db28406a0b \ + --hash=sha256:5a94acf798a82db188f6f36575d80609072b032105d114b0f98661e1679c9125 \ + --hash=sha256:5b6a9cc5b62c03cc1fea0044ed8576379dbaf751d5503d718c973d5418483641 \ + --hash=sha256:5cc725fbb4d25b0f185cb42df07ab6b76c4489b4bfb740a175f3a59c70e8a224 \ + --hash=sha256:607ecbb6f03e44c9e0a93aedacb17b4eb4f3563d00e8b474298a201622677947 \ + --hash=sha256:7b3a522fa389402cd2137df9ddefe848f727250535c70dafa840badffb56b7a4 \ + --hash=sha256:859a7bfa7bc8888abbea31ef8a2b411714e6a80f0d173c2a82f9041ed6b50f58 \ + --hash=sha256:8b4564e9f99168c0f9195a0fd5fa5928004b33b377137f978055e40008a082c5 \ + --hash=sha256:968220a57e09ea5e4fd48ed1c646419961a0570727c7e069842edd018ee8afed \ + --hash=sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6 \ + --hash=sha256:da8ec977eaa4b7bf75470fb575bea2cb41a0e07c7ea9d5a0a97d13dbca697bf2 \ + --hash=sha256:dc061a98d32a97211af7e7f3fa1d4ca2fcf919fb96c28f39551f35fc55bdbc19 \ + --hash=sha256:ddf8967e08227d1bd95cc0851ef80d2ad9c7c0c5aab1eba31db49cf0a7b99523 \ + --hash=sha256:ef69637b35fb8b210743926778d0e45e1bffa850a7c61e428c6b971549b5f5d1 \ + --hash=sha256:f4854fd09c7aed5b1590e996a81aeff0c9ff51378b084eb5a0b9cd9518e6cff2 +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via python-dateutil +typing-extensions==4.12.2 ; python_full_version < '3.11' \ + --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ + --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 + # via mkdocstrings-python +urllib3==2.4.0 \ + --hash=sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466 \ + --hash=sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813 + # via requests +watchdog==6.0.0 \ + --hash=sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a \ + --hash=sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2 \ + --hash=sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f \ + --hash=sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c \ + --hash=sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c \ + --hash=sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c \ + --hash=sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0 \ + --hash=sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13 \ + --hash=sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134 \ + --hash=sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa \ + --hash=sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e \ + --hash=sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379 \ + --hash=sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a \ + --hash=sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11 \ + --hash=sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282 \ + --hash=sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b \ + --hash=sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f \ + --hash=sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c \ + --hash=sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112 \ + --hash=sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948 \ + --hash=sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881 \ + --hash=sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860 \ + --hash=sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3 \ + --hash=sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680 \ + --hash=sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26 \ + --hash=sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26 \ + --hash=sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e \ + --hash=sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8 \ + --hash=sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c \ + --hash=sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2 + # via mkdocs +zipp==3.23.0 ; python_full_version < '3.10' \ + --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \ + --hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166 + # via importlib-metadata diff --git a/uv.lock b/uv.lock index 34cc3cf..0e5396b 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,10 @@ version = 1 revision = 2 requires-python = ">=3.9.0" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] [[package]] name = "attrs" @@ -11,6 +15,141 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152, upload-time = "2025-01-25T11:30:10.164Z" }, ] +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "backrefs" +version = "5.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/46/caba1eb32fa5784428ab401a5487f73db4104590ecd939ed9daaf18b47e0/backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd", size = 6773994, upload-time = "2025-02-25T18:15:32.003Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/cb/d019ab87fe70e0fe3946196d50d6a4428623dc0c38a6669c8cae0320fbf3/backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d", size = 380337, upload-time = "2025-02-25T16:53:14.607Z" }, + { url = "https://files.pythonhosted.org/packages/a9/86/abd17f50ee21b2248075cb6924c6e7f9d23b4925ca64ec660e869c2633f1/backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b", size = 392142, upload-time = "2025-02-25T16:53:17.266Z" }, + { url = "https://files.pythonhosted.org/packages/b3/04/7b415bd75c8ab3268cc138c76fa648c19495fcc7d155508a0e62f3f82308/backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486", size = 398021, upload-time = "2025-02-25T16:53:26.378Z" }, + { url = "https://files.pythonhosted.org/packages/04/b8/60dcfb90eb03a06e883a92abbc2ab95c71f0d8c9dd0af76ab1d5ce0b1402/backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585", size = 399915, upload-time = "2025-02-25T16:53:28.167Z" }, + { url = "https://files.pythonhosted.org/packages/0c/37/fb6973edeb700f6e3d6ff222400602ab1830446c25c7b4676d8de93e65b8/backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc", size = 380336, upload-time = "2025-02-25T16:53:29.858Z" }, +] + +[[package]] +name = "certifi" +version = "2025.4.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671, upload-time = "2025-05-02T08:34:12.696Z" }, + { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744, upload-time = "2025-05-02T08:34:14.665Z" }, + { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993, upload-time = "2025-05-02T08:34:17.134Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382, upload-time = "2025-05-02T08:34:19.081Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536, upload-time = "2025-05-02T08:34:21.073Z" }, + { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349, upload-time = "2025-05-02T08:34:23.193Z" }, + { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365, upload-time = "2025-05-02T08:34:25.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499, upload-time = "2025-05-02T08:34:27.359Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735, upload-time = "2025-05-02T08:34:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786, upload-time = "2025-05-02T08:34:31.858Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203, upload-time = "2025-05-02T08:34:33.88Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436, upload-time = "2025-05-02T08:34:35.907Z" }, + { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772, upload-time = "2025-05-02T08:34:37.935Z" }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -110,6 +249,11 @@ dev = [ { name = "pytest-mypy-plugins" }, { name = "ruff" }, ] +docs = [ + { name = "mkdocs-material" }, + { name = "mkdocstrings", extra = ["python"] }, + { name = "ruff" }, +] [package.metadata] requires-dist = [{ name = "typing-extensions", marker = "python_full_version < '3.11'", specifier = ">=4.12.2" }] @@ -122,6 +266,11 @@ dev = [ { name = "pytest-mypy-plugins", specifier = ">=3.1.2" }, { name = "ruff", specifier = ">=0.9.4" }, ] +docs = [ + { name = "mkdocs-material", specifier = ">=9.6.14" }, + { name = "mkdocstrings", extras = ["python"], specifier = ">=0.29.1" }, + { name = "ruff", specifier = ">=0.9.4" }, +] [[package]] name = "decorator" @@ -141,6 +290,51 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453, upload-time = "2024-07-12T22:25:58.476Z" }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, +] + +[[package]] +name = "griffe" +version = "1.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/3e/5aa9a61f7c3c47b0b52a1d930302992229d191bf4bc76447b324b731510a/griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b", size = 395137, upload-time = "2025-04-23T11:29:09.147Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/c6/5c20af38c2a57c15d87f7f38bee77d63c1d2a3689f74fefaf35915dd12b2/griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75", size = 129303, upload-time = "2025-04-23T11:29:07.145Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -189,6 +383,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459, upload-time = "2024-10-08T12:29:30.439Z" }, ] +[[package]] +name = "markdown" +version = "3.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/15/222b423b0b88689c266d9eac4e61396fe2cc53464459d6a37618ac863b24/markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f", size = 360906, upload-time = "2025-04-11T14:42:50.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/3f/afe76f8e2246ffbc867440cbcf90525264df0e658f8a5ca1f872b3f6192a/markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc", size = 106210, upload-time = "2025-04-11T14:42:49.178Z" }, +] + [[package]] name = "markupsafe" version = "3.0.2" @@ -257,6 +463,139 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506, upload-time = "2024-10-18T15:21:52.974Z" }, ] +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/0c/c9826f35b99c67fa3a7cddfa094c1a6c43fafde558c309c6e4403e5b37dc/mkdocs_autorefs-1.4.2.tar.gz", hash = "sha256:e2ebe1abd2b67d597ed19378c0fff84d73d1dbce411fce7a7cc6f161888b6749", size = 54961, upload-time = "2025-05-20T13:09:09.886Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/dc/fc063b78f4b769d1956319351704e23ebeba1e9e1d6a41b4b602325fd7e4/mkdocs_autorefs-1.4.2-py3-none-any.whl", hash = "sha256:83d6d777b66ec3c372a1aad4ae0cf77c243ba5bcda5bf0c6b8a2c5e7a3d89f13", size = 24969, upload-time = "2025-05-20T13:09:08.237Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.6.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fa/0101de32af88f87cf5cc23ad5f2e2030d00995f74e616306513431b8ab4b/mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754", size = 3951707, upload-time = "2025-05-13T13:27:57.173Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/a1/7fdb959ad592e013c01558822fd3c22931a95a0f08cf0a7c36da13a5b2b5/mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b", size = 8703767, upload-time = "2025-05-13T13:27:54.089Z" }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, +] + +[[package]] +name = "mkdocstrings" +version = "0.29.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "pymdown-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/e8/d22922664a627a0d3d7ff4a6ca95800f5dde54f411982591b4621a76225d/mkdocstrings-0.29.1.tar.gz", hash = "sha256:8722f8f8c5cd75da56671e0a0c1bbed1df9946c0cef74794d6141b34011abd42", size = 1212686, upload-time = "2025-03-31T08:33:11.997Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/14/22533a578bf8b187e05d67e2c1721ce10e3f526610eebaf7a149d557ea7a/mkdocstrings-0.29.1-py3-none-any.whl", hash = "sha256:37a9736134934eea89cbd055a513d40a020d87dfcae9e3052c2a6b8cd4af09b6", size = 1631075, upload-time = "2025-03-31T08:33:09.661Z" }, +] + +[package.optional-dependencies] +python = [ + { name = "mkdocstrings-python" }, +] + +[[package]] +name = "mkdocstrings-python" +version = "1.16.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffe" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocstrings" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ed/b886f8c714fd7cccc39b79646b627dbea84cd95c46be43459ef46852caf0/mkdocstrings_python-1.16.12.tar.gz", hash = "sha256:9b9eaa066e0024342d433e332a41095c4e429937024945fea511afe58f63175d", size = 206065, upload-time = "2025-06-03T12:52:49.276Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/dd/a24ee3de56954bfafb6ede7cd63c2413bb842cc48eb45e41c43a05a33074/mkdocstrings_python-1.16.12-py3-none-any.whl", hash = "sha256:22ded3a63b3d823d57457a70ff9860d5a4de9e8b1e482876fc9baabaf6f5f374", size = 124287, upload-time = "2025-06-03T12:52:47.819Z" }, +] + [[package]] name = "mypy" version = "1.16.0" @@ -320,6 +659,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451, upload-time = "2024-11-08T09:47:44.722Z" }, ] +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -329,6 +677,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] +[[package]] +name = "platformdirs" +version = "4.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -338,6 +695,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" }, ] +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" }, +] + +[[package]] +name = "pymdown-extensions" +version = "10.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/92/a7296491dbf5585b3a987f3f3fc87af0e632121ff3e490c14b5f2d2b4eb5/pymdown_extensions-10.15.tar.gz", hash = "sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7", size = 852320, upload-time = "2025-04-27T23:48:29.183Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/d1/c54e608505776ce4e7966d03358ae635cfd51dff1da6ee421c090dbc797b/pymdown_extensions-10.15-py3-none-any.whl", hash = "sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f", size = 265845, upload-time = "2025-04-27T23:48:27.359Z" }, +] + [[package]] name = "pytest" version = "8.3.4" @@ -388,6 +767,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/de/84/6c9af99c416e52286fbf6df91a2ae19ef4da5d6f2dab626f8ff1a61280b5/pytest_mypy_plugins-3.2.0-py3-none-any.whl", hash = "sha256:46e24e8d9eaeabcddd0a5dc5fb089c021903d5952e0c9d8af79383db99b9ffae", size = 20627, upload-time = "2024-12-21T20:11:33.723Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -441,6 +832,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, ] +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + [[package]] name = "referencing" version = "0.36.2" @@ -540,6 +943,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/7b/e59b7f7c91ae110d154370c24133f947262525b5d6406df65f23422acc17/regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983", size = 274110, upload-time = "2024-11-06T20:12:29.368Z" }, ] +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, +] + [[package]] name = "rpds-py" version = "0.22.3" @@ -675,6 +1093,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/3a/2e8704d19f376c799748ff9cb041225c1d59f3e7711bc5596c8cfdc24925/ruff-0.11.10-py3-none-win_arm64.whl", hash = "sha256:ef69637b35fb8b210743926778d0e45e1bffa850a7c61e428c6b971549b5f5d1", size = 10765278, upload-time = "2025-05-15T14:08:54.56Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "tomli" version = "2.2.1" @@ -731,3 +1158,58 @@ sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec3 wheels = [ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438, upload-time = "2024-06-07T18:52:13.582Z" }, ] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390, upload-time = "2024-11-01T14:06:49.325Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386, upload-time = "2024-11-01T14:06:50.536Z" }, + { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017, upload-time = "2024-11-01T14:06:51.717Z" }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, + { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903, upload-time = "2024-11-01T14:06:57.052Z" }, + { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381, upload-time = "2024-11-01T14:06:58.193Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] From 40678a6f0cfee20a050bc1aaaa4ae027ac917e68 Mon Sep 17 00:00:00 2001 From: Harry Mander Date: Tue, 17 Jun 2025 10:47:10 +1200 Subject: [PATCH 4/7] Add a readthedocs config --- .readthedocs.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..1da3bf0 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,11 @@ +version: 2 +build: + os: ubuntu-24.04 + tools: + python: "3.12" +mkdocs: + configuration: mkdocs.yml + fail_on_warning: true +python: + install: + - requirements: requirements-docs.txt From 8a22e4702b8637046fc1f4b55dd3d099e20a6fc7 Mon Sep 17 00:00:00 2001 From: Harry Mander Date: Tue, 17 Jun 2025 10:47:27 +1200 Subject: [PATCH 5/7] README: remove usage section and link to docs --- README.md | 385 +----------------------------------------------------- 1 file changed, 4 insertions(+), 381 deletions(-) diff --git a/README.md b/README.md index 59c928e..40a74f0 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,14 @@ [![Tests status](https://github.com/harrymander/dataclasses-struct/actions/workflows/test.yml/badge.svg?event=push)]() [![Code coverage](https://img.shields.io/codecov/c/gh/harrymander/dataclasses-struct)](https://app.codecov.io/gh/harrymander/dataclasses-struct) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/harrymander/dataclasses-struct/blob/main/LICENSE) +[![Documentation](https://img.shields.io/badge/Documentation-blue)](https://harrymander.xyz/dataclasses-struct) A simple Python package that combines [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) with [`struct`](https://docs.python.org/3/library/struct.html) for packing and unpacking Python dataclasses to fixed-length `bytes` representations. -1. [Example](#example) -2. [Installation](#installation) -3. [Usage](#usage) -4. [Development and contributing](#development-and-contributing) +**Documentation**: https://harrymander.xyz/dataclasses-struct ## Example @@ -71,383 +69,8 @@ required; add to your `mypy.ini`: plugins = dataclasses_struct.ext.mypy_plugin ``` -## Usage - -1. [Native size mode](#native-size-mode) -2. [Standard size mode](#standard-size-mode) -3. [Supported type annotations](#supported-type-annotations) - 1. [Native integer types](#native-integer-types) - 2. [Standard integer types](#standard-integer-types) - 3. [Floating point types](#floating-point-types) - 4. [Boolean](#boolean) - 5. [Characters and bytes arrays](#characters-and-bytes-arrays) - 6. [Nested structs](#nested-structs) - 7. [Fixed-length arrays](#fixed-length-arrays) - 8. [Manual padding](#manual-padding) - -Use the `dataclass_struct` decorator to convert a class into a [stdlib -`dataclass`](https://docs.python.org/3/library/dataclasses.html) with struct -packing/unpacking functionality: - -```python -def dataclass_struct( - *, - size: Literal["native", "std"] = "native", - byteorder: Literal["native", "big", "little", "network"] = "native", - validate_defaults: bool = True, - **dataclass_kwargs, -): - ... -``` - -The `size` argument can be either `"native"` (the default) or `"std"` and -controls the size and alignment of fields. See below for more details on the -different `size` and `byteorder` argument values. - -Decorated classes are transformed to a standard Python -[dataclass](https://docs.python.org/3/library/dataclasses.html) with boilerplate -`__init__`, `__repr__`, `__eq__` etc. auto-generated. Additionally, two methods -are added to the class: `pack`, a method for packing an instance of the class to -`bytes`, and `from_packed`, a class method that returns a new instance of the -class from its packed `bytes` representation. The additional `dataclass_kwargs` -keyword arguments will be passed through to the [stdlib `dataclass` -decorator](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass): -all standard keyword arguments are supported except for `slots` and -`weakref_slot`. - -Default attribute values will be validated against their expected type and -allowable value range. For example, - -```python -import dataclasses_struct as dcs - -@dcs.dataclass_struct() -class Test: - x: dcs.UnsignedChar = -1 -``` - -will raise a `ValueError`. This can be disabled by passing -`validate_defaults=False` to the decorator. - -A class or object can be checked to see if it is a dataclass-struct using the -`is_dataclass_struct` function. - - -```python ->>> dcs.is_dataclass_struct(Test) -True ->>> t = Test(100) ->>> dcs.is_dataclass_struct(t) -True -``` - -The `get_struct_size` function will return the size in bytes of the packed -representation of a dataclass-struct class or an instance of one. - -```python ->>> dcs.get_struct_size(Test) -234 -``` - -An additional class attribute, `__dataclass_struct__`, is added to the decorated -class that contains the packed size, [`struct` format -string](https://docs.python.org/3/library/struct.html#format-strings), and -`struct` mode. - -```python ->>> Test.__dataclass_struct__.size -234 ->>> Test.__dataclass_struct__.format -'@cc??bBhHiIQqqNnPfdd100s4xqq2x3xq2x' ->>> Test.__dataclass_struct__.mode -'@' -``` - -### Native size mode - -In `"native"` mode (the default), the struct is packed based on the platform and -compiler on which Python was built: padding bytes may be added to maintain -proper alignment of the fields and byte ordering (endianness) follows that of -the platform. (The `byteorder` argument must also be `"native"`.) - -In `"native"` size mode, integer type sizes follow those of the standard C -integer types of the platform (`int`, `unsigned short` etc.). - -```python -@dcs.dataclass_struct() -class NativeStruct: - signed_char: dcs.SignedChar - signed_short: dcs.Short - unsigned_long_long: dcs.UnsignedLongLong - void_pointer: dcs.Pointer -``` - -### Standard size mode - -In `"std"` mode, the struct is packed without any additional padding for -alignment. - -The `"std"` size mode supports four different `byteorder` values: `"native"` -(the default), `"little"`, `"big"`, and `"network"`. The `"native"` setting uses -the system byte order (similar to `"native"` size mode, but without alignment). -The `"network"` setting is equivalent to `"big"`. - -The `"std"` size uses platform-independent integer sizes, similar to using the -integer types from `stdint.h` in C. When used with `byteorder` set to -`"little"`, `"big"`, or `"network"`, it is appropriate for marshalling data -across different platforms. - -```python -@dcs.dataclass_struct(size="std", byteorder="native") -class NativeStruct: - int8_t: dcs.I8 - uint64_t: dcs.U64 -``` - -### Supported type annotations - -#### Native integer types - -These types are only supported in `"native"` size mode. Their native Python -types are all `int`. - -| Type annotation | Equivalent C type | -| ------------------------------------ | --------------------------- | -| `SignedChar` | `signed char` | -| `UnsignedChar` | `unsigned char` | -| `Short` | `short` | -| `UnsignedShort` | `unsigned short` | -| `Int` | `int` | -| `int` (builtin type, alias to `Int`) | `int` | -| `UnsignedInt` | `unsigned int` | -| `Long` | `long` | -| `UnsignedLong` | `unsigned long` | -| `LongLong` | `long long` | -| `UnsignedLongLong` | `unsigned long long` | -| `UnsignedSize` | `size_t` | -| `SignedSize` | `ssize_t` (POSIX extension) | -| `Pointer` | `void *` | - -#### Standard integer types - -These types are only supported in `"std"` size mode. Their native Python types -are all `int`. - -| Type annotation | Equivalent C type | -| ------------------------------------ | --------------------------- | -| `I8` | `int8_t` | -| `U8` | `uint8_t` | -| `I16` | `int16_t` | -| `U16` | `uint16_t` | -| `I32` | `int32_t` | -| `U32` | `uint32_t` | -| `I64` | `int64_t` | -| `U64` | `uint64_t` | - -#### Floating point types - -Supported in both size modes. The native Python type is `float`. - -| Type annotation | Equivalent C type | -| ------------------------------------ | --------------------------- | -| `F16` | Extension type (see below) | -| `F32` | `float` | -| `F64` | `double` | -| `float` (builtin alias to `F64`) | `double` | - -`F16` is a half precision floating point. Some compilers provide support for it -on certain platforms (e.g. -[GCC](https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html), -[Clang](https://clang.llvm.org/docs/LanguageExtensions.html#half-precision-floating-point)). -It is also available as -[`std::float16_t`](https://en.cppreference.com/w/cpp/types/floating-point.html) -in C++23. - -Note that floating point fields are always packed and unpacked using the IEEE -754 format, regardless of the underlying format used by the platform. - -#### Boolean - -The builtin `bool` type or `dataclasses_struct.Bool` type can be used to -represent a boolean, which uses a single byte in either native or standard size -modes. - -#### Characters and bytes arrays - -In both size modes, a single byte can be packed by annotating a field with the -builtin `bytes` type or the `dataclasses_struct.Char` type. The field's -unpacked Python representation will be a `bytes` of length 1. - -```python -@dcs.dataclass_struct() -class Chars: - char: dcs.Char = b'x' - builtin: bytes = b'\x04' -``` - -Fixed-length byte arrays can be represented by annotating a field with -`typing.Annotated` and a positive length. The field's unpacked Python -representation will be a `bytes` object zero-padded or truncated to the -specified length. - -```python -from typing import Annotated - -@dcs.dataclass_struct() -class FixedLength: - fixed: Annotated[bytes, 10] -``` - -```python ->>> FixedLength.from_packed(FixedLength(b'Hello, world!').pack()) -FixedLength(fixed=b'Hello, wor') -``` - -#### Nested structs - -Classes decorated with `dataclass_struct` can be used as fields in other -classes, as long as they have the same `size` and `byteorder` settings. - -```python -@dcs.dataclass_struct() -class Vector2d: - x: float - y: float - -@dcs.dataclass_struct() -class Vectors: - direction: Vector2d - velocity: Vector2d - -# Will raise TypeError: -@dcs.dataclass_struct(size="std") -class VectorsStd: - direction: Vector2d - velocity: Vector2d -``` - -Default values for nested class fields cannot be set directly, as Python doesn't -allow using mutable default values in dataclasses. To get around this, pass -`frozen=True` to the inner class' `dataclass_struct` decorator. Alternatively, -pass a zero-argument callable that returns an instance of the class to the -`default_factory` keyword argument of -[`dataclasses.field`](https://docs.python.org/3/library/dataclasses.html#dataclasses.field). -For example: - -```python -from dataclasses import field - -@dcs.dataclass_struct() -class VectorsStd: - direction: Vector2d - velocity: Vector2d = field(default_factory=lambda: Vector2d(0, 0)) -``` - -The return type of the `default_factory` will be validated unless -`validate_defaults=False` is passed to the `dataclass_struct` decorator. Note -that this means the callable passed to `default_factory` will be called once -during class creation. - -#### Fixed-length arrays - -Fixed-length arrays can be represented by annotating a `list` field with -`typing.Annotated` and a positive length. - -```python -from typing import Annotated - -@dcs.dataclass_struct() -class FixedLength: - fixed: Annotated[list[int], 5] -``` - -```python ->>> FixedLength.from_packed(FixedLength([1, 2, 3, 4, 5]).pack()) -FixedLength(fixed=[1, 2, 3, 4, 5]) -``` - -The values stored in fixed-length arrays can also be classes -decorated with `dataclass_struct`. - -```python -from typing import Annotated - -@dcs.dataclass_struct() -class Vector2d: - x: float - y: float - -@dcs.dataclass_struct() -class FixedLength: - fixed: Annotated[list[Vector2d], 3] -``` - -```python ->>> FixedLength.from_packed(FixedLength([Vector2d(1.0, 2.0), Vector2d(3.0, 4.0), Vector2d(5.0, 6.0)]).pack()) -FixedLength(fixed=[Vector2d(x=1.0, y=2.0), Vector2d(x=3.0, y=4.0), Vector2d(x=5.0, y=6.0)]) -``` - -Fixed-length arrays can also be multi-dimensional by nesting Annotated -`list` types. - -```python -from typing import Annotated - -@dcs.dataclass_struct() -class TwoDimArray: - fixed: Annotated[list[Annotated[list[int], 2]], 3] -``` - -```python ->>> TwoDimArray.from_packed(TwoDimArray([[1, 2], [3, 4], [5, 6]]).pack()) -TwoDimArray(fixed=[[1, 2], [3, 4], [5, 6]]) -``` - -As with [nested structs](#nested-structs), a `default_factory` must be used to -set a default value. For example: - -```python -from dataclasses import field -from typing import Annotated - -@dcs.dataclass_struct() -class DefaultArray: - x: Annotated[list[int], 3] = field(default_factory=lambda: [1, 2, 3]) -``` - -The returned default value's length and type and values of its items will be -validated unless `validate_defaults=False` is passed to the `dataclass_struct` -decorator. - -#### Manual padding - -Padding can be manually controlled by annotating a type with `PadBefore` or -`PadAfter`: - -```python -@dcs.dataclass_struct() -class WithPadding: - # 4 padding bytes will be added before this field - pad_before: Annotated[int, dcs.PadBefore(4)] - - # 2 padding bytes will be added before this field - pad_after: Annotated[int, dcs.PadAfter(2)] - - # 3 padding bytes will be added before this field and 2 after - pad_before_and_after: Annotated[int, dcs.PadBefore(3), dcs.PadAfter(2)] -``` - -A `b'\x00'` will be inserted into the packed representation for each padding -byte. - -```python ->>> padded = WithPadding(100, 200, 300) ->>> packed = padded.pack() ->>> packed -b'\x00\x00\x00\x00d\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x01\x00\x00\x00\x00' ->>> WithPadding.from_packed(packed) -WithPadding(pad_before=100, pad_after=200, pad_before_and_after=300) -``` +See [the docs](https://harrymander.xyz/dataclasses-struct/guide/#type-checking) +for more info on type checking. ## Development and contributing From bfb46cae33bd8ab51c17467fa6328900ba2e065f Mon Sep 17 00:00:00 2001 From: Harry Mander Date: Tue, 17 Jun 2025 10:50:10 +1200 Subject: [PATCH 6/7] github: build and publish docs in workflow, rename workflow to 'CI' --- .github/workflows/ci.yml | 124 +++++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 70 --------------------- .pre-commit-config.yaml | 2 +- 3 files changed, 125 insertions(+), 71 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..19c341d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,124 @@ +name: CI + +on: + push: + branches: ["*"] + pull_request: + branches: ["*"] + +jobs: + pre-commit-checks: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Run pre-commit checks + uses: pre-commit/action@v3.0.1 + + tests: + needs: pre-commit-checks + strategy: + matrix: + pyversion: ["3.9", "3.10", "3.11", "3.12", "3.13"] + os: ["Ubuntu", "Windows", "macOS"] + include: + - os: Windows + image: windows-latest + - os: Ubuntu + image: ubuntu-latest + - os: macOS + image: macos-latest + name: ${{ matrix.os }} / ${{ matrix.pyversion }} + runs-on: ${{ matrix.image }} + defaults: + run: + shell: bash + env: + UV_LOCKED: true + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.pyversion }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.pyversion }} + + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + version: "0.7.12" + enable-cache: true + + - name: Install Python dependencies + run: uv sync + + - name: Python type checking + run: uv run mypy + + - name: Configure MSVC + uses: ilammy/msvc-dev-cmd@v1 + if: matrix.os == 'Windows' + + - name: Run tests + run: uv run pytest --color=yes --cov --cov-report=xml + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4 + if: matrix.os == 'Ubuntu' && matrix.pyversion == '3.13' + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.xml + + build-docs: + needs: tests + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + version: "0.7.12" + enable-cache: true + cache-dependency-glob: requirements-docs.txt + - name: Install Python dependencies + run: uv pip install --system -r requirements-docs.txt + - name: Configure mkdocs for GitHub Pages + run: | + sed -i 's,^site_url: .*$,site_url: https://harrymander.xyz/dataclasses-struct/,g' mkdocs.yml + diff=$(git diff mkdocs.yml) + if [ -z "$diff" ]; then + echo "mkdocs.yml was not modified!" + exit 1 + fi + - name: Build site + run: mkdocs build --strict --site-dir site + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + id: deployment + with: + path: site/ + + publish-docs: + if: "${{ github.ref == 'refs/heads/main' }}" + needs: build-docs + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + # The current branch must have access to the github-pages environment. + # See 'Deployment branches and tags' settings for 'github-pages' + # environment in + # github.com/harrymander/dataclasses-struct/settings/environments + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index c893f8f..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Tests - -on: - push: - branches: ["*"] - pull_request: - branches: ["*"] - -jobs: - pre-commit-checks: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - name: Run pre-commit checks - uses: pre-commit/action@v3.0.1 - tests: - needs: pre-commit-checks - strategy: - matrix: - pyversion: ["3.9", "3.10", "3.11", "3.12", "3.13"] - os: ["Ubuntu", "Windows", "macOS"] - include: - - os: Windows - image: windows-latest - - os: Ubuntu - image: ubuntu-latest - - os: macOS - image: macos-latest - name: ${{ matrix.os }} / ${{ matrix.pyversion }} - runs-on: ${{ matrix.image }} - defaults: - run: - shell: bash - env: - UV_LOCKED: true - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.pyversion }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.pyversion }} - - - name: Setup uv - uses: astral-sh/setup-uv@v5 - with: - version: "0.7.5" - enable-cache: true - - - name: Install Python dependencies - run: uv sync - - - name: Python type checking - run: uv run mypy - - - name: Configure MSVC - uses: ilammy/msvc-dev-cmd@v1 - if: matrix.os == 'Windows' - - - name: Run tests - run: uv run pytest --color=yes --cov --cov-report=xml - - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4 - if: matrix.os == 'Ubuntu' && matrix.pyversion == '3.13' - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: coverage.xml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6787aa5..6d3cbee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: ruff - id: ruff-format - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.7.4 + rev: 0.7.12 hooks: - id: uv-lock args: ["--check"] From bc439d9f518504b2095b4536b4de2044307371f9 Mon Sep 17 00:00:00 2001 From: Harry Mander Date: Tue, 17 Jun 2025 10:50:19 +1200 Subject: [PATCH 7/7] pyproject.toml: update documentation link --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3e5ba0f..9954cfc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ [project.urls] Homepage = "https://github.com/harrymander/dataclasses-struct" Repository = "https://github.com/harrymander/dataclasses-struct" -Documentation = "https://github.com/harrymander/dataclasses-struct/blob/main/README.md#usage" +Documentation = "https://harrymander.xyz/dataclasses-struct" [dependency-groups] dev = [