Repository: thetacom/byteclasses (language: python)

Issue:
Add allow_overflow override to fixed integers



Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 92dedda..89aeb45 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -61,7 +61,8 @@ repos:
   - id: poetry-lock
   - id: poetry-export
     args: ["-f", "requirements.txt", "-o", "requirements.txt"]
-
+    additional_dependencies:
+      - "poetry-plugin-export"
 - repo: https://github.com/DanielNoord/pydocstringformatter
   rev: v0.7.3
   hooks:
diff --git a/README.md b/README.md
index d981d55..dcf9087 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,10 @@ A python package designed for creating and managing blobs of binary data using d
 
 This library is designed for creating custom Python data classes with builtin structural constraints. Byte Classes behave similar to a combination of Python dataclasses and C structs.
 
+## Docs
+
+[Byteclasses Documentation](https://io.thetacom.info/byteclasses/)
+
 ## Byte Classes
 
 - Collections
diff --git a/byteclasses/handlers/__init__.py b/byteclasses/handlers/__init__.py
new file mode 100644
index 0000000..323c136
--- /dev/null
+++ b/byteclasses/handlers/__init__.py
@@ -0,0 +1,1 @@
+"""Pre-defined Data Handlers Package."""
diff --git a/byteclasses/handlers/_data_handler.py b/byteclasses/handlers/_data_handler.py
new file mode 100644
index 0000000..ee25600
--- /dev/null
+++ b/byteclasses/handlers/_data_handler.py
@@ -0,0 +1,32 @@
+"""Generic Data Handler Class."""
+
+from abc import ABC
+
+
+class _DataHandler(ABC):
+    """A generic data handler class."""
+
+    def __init__(self, data: bytes | bytearray) -> None:
+        """Initialize data handler instance."""
+        self._data: memoryview = memoryview(bytearray(data))
+
+    def __bytes__(self) -> bytes:
+        """Return data handler bytes."""
+        return bytes(self._data)
+
+    def __len__(self) -> int:
+        """Return data handler length."""
+        return len(self._data)
+
+    def __repr__(self) -> str:
+        """Return data handler representation."""
+        return f"{self.__class__.__name__}(data={self._data!r})"
+
+    def __str__(self) -> str:
+        """Return data handler string."""
+        return str(self._data)
+
+    @property
+    def data(self) -> memoryview:
+        """Return data handler data."""
+        return self._data
diff --git a/byteclasses/handlers/fileypes/__init__.py b/byteclasses/handlers/fileypes/__init__.py
new file mode 100644
index 0000000..7d26798
--- /dev/null
+++ b/byteclasses/handlers/fileypes/__init__.py
@@ -0,0 +1,1 @@
+"""Pre-defined File Handlers Package."""
diff --git a/byteclasses/handlers/network/__init__.py b/byteclasses/handlers/network/__init__.py
new file mode 100644
index 0000000..514f3e8
--- /dev/null
+++ b/byteclasses/handlers/network/__init__.py
@@ -0,0 +1,1 @@
+"""Pre-defined Network Data Handlers Package."""
diff --git a/byteclasses/handlers/network/eth_frame.py b/byteclasses/handlers/network/eth_frame.py
new file mode 100644
index 0000000..632ca6b
--- /dev/null
+++ b/byteclasses/handlers/network/eth_frame.py
@@ -0,0 +1,88 @@
+"""Ethernet Frame Class.
+
+[Specification](https://en.wikipedia.org/wiki/Ethernet_frame)
+"""
+
+from collections.abc import ByteString
+from enum import IntEnum
+from functools import cached_property
+
+from .._data_handler import _DataHandler
+from .eth_hdr import EthHdr
+
+
+class EtherType(IntEnum):
+    """Ethernet Frame Types."""
+
+    IPV4 = 0x0800
+    ARP = 0x0806
+    WOL = 0x0842
+    CDP = 0x2000
+    SRP = 0x22EA
+    AVTP = 0x22F0
+    TRILL = 0x22F3
+    DEC_MOP = 0x6002
+    DECNET_DNA_ROUTING = 0x6003
+    DEC_LAT = 0x6004
+    RARP = 0x8035
+    ETHERTALK = 0x809B
+    AARP = 0x80F3
+    VLAN_TAGGED = 0x8100
+    SLPP = 0x8102
+    VLACP = 0x8103
+    IPX = 0x8137
+    QNX = 0x8204
+    IPV6 = 0x86DD
+    FLOW_CONTROL = 0x8808
+    LACP = 0x8809
+    COBRANET = 0x8819
+    MPLS_UNICAST = 0x8847
+    MPLS_MULTICAST = 0x8848
+
+
+class EthFrame(_DataHandler):
+    """Ethernet Frame Data Handler Class."""
+
+    def __init__(self, data: bytes | bytearray = bytearray(b"")) -> None:
+        """Initialize Ethernet Frame instance."""
+        super().__init__(data)
+        self._hdr = EthHdr()
+        try:
+            self._hdr.attach(memoryview(self._data))  # type: ignore
+        except AttributeError as err:
+            raise ValueError("Insufficient data") from err
+
+    def __str__(self) -> str:
+        """Return Ether Frame string."""
+        return f"{self.__class__.__name__}(src_mac={self.src_mac}, dst_mac={self.dst_mac}, type={self.type})"
+
+    @property
+    def hdr(self) -> EthHdr:
+        """Return data header."""
+        return self._hdr
+
+    @property
+    def dst_mac(self) -> str:
+        """Return dst mac address."""
+        return f"{':'.join([hex(val)[2:].rjust(2,'0') for val in self.hdr.dst_mac])}"
+
+    @property
+    def src_mac(self) -> str:
+        """Return src mac address."""
+        return f"{':'.join([hex(val)[2:].rjust(2,'0') for val in self.hdr.src_mac])}"
+
+    @property
+    def type(self) -> str:
+        """Return Ethernet Frame Type."""
+        try:
+            return EtherType(int(self.hdr.ether_type)).name
+        except ValueError:
+            return hex(self.hdr.ether_type)
+
+    @cached_property
+    def payload(self) -> _DataHandler | ByteString:
+        """Return frame payload."""
+        payload_data = self._data[len(self.hdr) :]  # type: ignore
+        # if self.type == EtherType.IPV4:
+        #     return IPv4Packet(data=payload_data)
+        return payload_data
diff --git a/byteclasses/handlers/network/eth_hdr.py b/byteclasses/handlers/network/eth_hdr.py
new file mode 100644
index 0000000..51b5ec5
--- /dev/null
+++ b/byteclasses/handlers/network/eth_hdr.py
@@ -0,0 +1,13 @@
+"""Ethernet Header implemented with byteclasses."""
+
+from ...types.collections import FixedArray, member, structure
+from ...types.primitives.integers import UInt16
+
+
+@structure(byte_order=b"!", packed=True)
+class EthHdr:
+    """Ethernet Header Class."""
+
+    dst_mac: FixedArray = member(default_factory=lambda: FixedArray(6))  # type: ignore
+    src_mac: FixedArray = member(default_factory=lambda: FixedArray(6))  # type: ignore
+    ether_type: UInt16
diff --git a/byteclasses/handlers/network/ipv4_hdr.py b/byteclasses/handlers/network/ipv4_hdr.py
new file mode 100644
index 0000000..8b84ddd
--- /dev/null
+++ b/byteclasses/handlers/network/ipv4_hdr.py
@@ -0,0 +1,51 @@
+"""IPv4 Header class implemented with byteclasses."""
+
+from byteclasses.types.collections import FixedArray, member, structure, union
+from byteclasses.types.primitives.generics import BitField, BitPos
+from byteclasses.types.primitives.integers import UInt8, UInt16, UInt32
+
+
+class VerIhl(BitField):
+    """IPv4 Version and Header Length BitField."""
+
+    version = BitPos(0, bit_width=4)
+    ihl = BitPos(4, bit_width=4)
+
+
+class DscpEcn(BitField):
+    """IPv4 DSCP and ECN BitField."""
+
+    dscp = BitPos(0, bit_width=6)
+    ecn = BitPos(6, bit_width=2)
+
+
+class FlagsOff(BitField):
+    """IPv4 Flags BitField."""
+
+    byte_length = 2
+    flags = BitPos(0, bit_width=3)
+    fragment_offset = BitPos(3, bit_width=13)
+
+
+@union(byte_order=b"!")
+class IPv4Addr:
+    """IPv4 Address."""
+
+    uint8: FixedArray = member(default_factory=lambda: FixedArray(4, byte_order=b"!"))  # type: ignore
+    uint32: UInt32
+
+
+@structure(byte_order=b"!", packed=True)
+class IPv4Hdr:
+    """IPv4 Header Structure."""
+
+    ver_ihl: VerIhl
+    dscp_ecn: DscpEcn
+    total_length: UInt16
+    identification: UInt16
+    flags_off: FlagsOff
+    time_to_live: UInt8
+    protocol: UInt8
+    header_checksum: UInt16
+    src_ip: IPv4Addr
+    dst_ip: IPv4Addr
diff --git a/byteclasses/handlers/network/ipv6_hdr.py b/byteclasses/handlers/network/ipv6_hdr.py
new file mode 100644
index 0000000..e2296d3
--- /dev/null
+++ b/byteclasses/handlers/network/ipv6_hdr.py
@@ -0,0 +1,74 @@
+"""IPv6 Header class implemented with byteclasses."""
+
+from byteclasses.types.collections import FixedArray, member, structure, union
+from byteclasses.types.primitives.generics import BitField, BitPos
+from byteclasses.types.primitives.integers import UInt8, UInt16, UInt32, UInt64
+
+
+class VTF(BitField):
+    """IPv6 Version/Traffic/Flow Label BitField."""
+
+    byte_length = 4
+    version = BitPos(0, bit_width=4)
+    traffic_class = BitPos(4, bit_width=8)
+    flow_label = BitPos(12, bit_width=20)
+
+
+@union(byte_order=b"!")
+class IPv6Addr:
+    """IPv6 Address."""
+
+    uint8: FixedArray = member(default_factory=lambda: FixedArray(16, byte_order=b"!"))  # type: ignore
+    uint32: UInt32 = member(default_factory=lambda: FixedArray(4, UInt32, byte_order=b"!"))  # type: ignore
+    uint64: UInt32 = member(default_factory=lambda: FixedArray(2, UInt64, byte_order=b"!"))  # type: ignore
+
+
+@structure(byte_order=b"!", packed=True)
+class IPv6Hdr:
+    """IPv6 Header Structure."""
+
+    vtf: VTF
+    payload_length: UInt16
+    next_hdr: UInt8
+    hop_limit: UInt8
+    src_addr: IPv6Addr
+    dst_addr: IPv6Addr
+
+
+@structure(byte_order=b"!", packed=True)
+class HopByHopExtHdr:
+    """Hop-by-Hop Options extension header."""
+
+    next_hdr: UInt8
+    hdr_ext_length: UInt8
+    options: FixedArray = member(default_factory=lambda: FixedArray(14, byte_order=b"!"))  # type: ignore
+
+
+@structure(byte_order=b"!", packed=True)
+class RoutingExtHdr:
+    """Routing extension header."""
+
+    next_hdr: UInt8
+    hdr_ext_length: UInt8
+    routing_type: UInt8
+    segments_left: UInt8
+    type_data: FixedArray = member(default_factory=lambda: FixedArray(12, byte_order=b"!"))  # type: ignore
+
+
+class FragmentBitField(BitField):
+    """IPv6 Fragment Offset and more follows flag bitfield."""
+
+    byte_length = 2
+    offset = BitPos(0, bit_width=13)
+    reserved = BitPos(13, bit_width=2)
+    more = BitPos(15)
+
+
+@structure(byte_order=b"!", packed=True)
+class FragmentExtHdr:
+    """Fragment extension header."""
+
+    next_hdr: UInt8
+    reserved: UInt8
+    fragment: FragmentBitField
+    identification: UInt32
diff --git a/byteclasses/handlers/network/tcp_hdr.py b/byteclasses/handlers/network/tcp_hdr.py
new file mode 100644
index 0000000..19d59d2
--- /dev/null
+++ b/byteclasses/handlers/network/tcp_hdr.py
@@ -0,0 +1,27 @@
+"""TCP Header implemented with byteclasses."""
+
+from byteclasses.types.collections import structure
+from byteclasses.types.primitives.generics import BitField, BitPos
+from byteclasses.types.primitives.integers import UInt16, UInt32
+
+
+class OffFlag(BitField):
+    """Offset and Flag BitField."""
+
+    byte_length = 2
+    data_offset = BitPos(0, bit_width=4)
+    flags = BitPos(4, bit_width=12)
+
+
+@structure(byte_order=b"!", packed=True)
+class TCPHdr:
+    """TCP Header Class."""
+
+    src_port: UInt16
+    dst_port: UInt16
+    seq_number: UInt32
+    ack_number: UInt32
+    off_flag: OffFlag
+    window_size: UInt16
+    checksum: UInt16
+    urgent_pointer: UInt16
diff --git a/byteclasses/handlers/network/udp_hdr.py b/byteclasses/handlers/network/udp_hdr.py
new file mode 100644
index 0000000..8b27eeb
--- /dev/null
+++ b/byteclasses/handlers/network/udp_hdr.py
@@ -0,0 +1,14 @@
+"""UDP Header implemented with byteclasses."""
+
+from byteclasses.types.collections import structure
+from byteclasses.types.primitives.integers import UInt16
+
+
+@structure(byte_order=b"!", packed=True)
+class UDPHdr:
+    """UDP Header Class."""
+
+    src_port: UInt16
+    dst_port: UInt16
+    length: UInt16
+    checksum: UInt16
diff --git a/byteclasses/types/_fixed_numeric_type.py b/byteclasses/types/_fixed_numeric_type.py
index f7bc9e1..3600510 100644
--- a/byteclasses/types/_fixed_numeric_type.py
+++ b/byteclasses/types/_fixed_numeric_type.py
@@ -9,31 +9,38 @@
 
 import math
 from abc import abstractmethod
+from collections.abc import ByteString
 from numbers import Integral, Number, Real
 from struct import pack, unpack
-from typing import Any
+from typing import Any, TypeVar
 
 from .._enums import ByteOrder
 from ..types._fixed_size_type import _FixedSizeType
 
 __all__: list[str] = []
 
+ValType = TypeVar("ValType")  # pylint: disable=C0103
+
 
 class _FixedNumericType(_FixedSizeType):
     """Base class for fixed size numeric types."""
 
     def __init__(
         self,
-        value: int | float = 0,
+        value: int | float | None = None,
         *,
         byte_order: bytes | ByteOrder = ByteOrder.NATIVE.value,
+        data: ByteString | None = None,
     ) -> None:
         """Initialize the instance."""
-        super().__init__(byte_order)
-        if isinstance(value, _FixedNumericType):
-            self.value = value.value
-        else:
-            self.value = value
+        if value is not None and data is not None:
+            raise ValueError("Cannot specify both value and data arguments.")
+        super().__init__(byte_order=byte_order, data=data)
+        if value is not None:
+            if isinstance(value, _FixedNumericType):
+                self.value = value.value
+            else:
+                self.value = value
 
     def __str__(self) -> str:
         """Return the numeric representation of the instance."""
@@ -66,8 +73,6 @@ def __radd__(self, other: Any):
 
         Satisfies Complex interface.
         """
-        if isinstance(other, _FixedNumericType):
-            return self.value + other.value
         if isinstance(other, Real):
             return self.value + other
         return NotImplemented
@@ -120,8 +125,6 @@ def __rdivmod__(self, other: Any):
 
         Satisfies Real interface.
         """
-        if isinstance(other, _FixedNumericType):
-            return divmod(other.value, self.value)
         if isinstance(other, Real):
             return divmod(other, self.value)
         return NotImplemented
@@ -150,8 +153,6 @@ def __floordiv__(self, other: Any):
 
     def __rfloordiv__(self, other: Any):
         """Return the floor division of the instance and other."""
-        if isinstance(other, _FixedNumericType):
-            return other.value // self.value
         if isinstance(other, Real):
             return other // self.value
         return NotImplemented
@@ -217,8 +218,6 @@ def __rmod__(self, other: Any):
 
         Satisfies Real interface.
         """
-        if isinstance(other, _FixedNumericType):
-            return other.value % self.value
         if isinstance(other, Real):
             return other % self.value
         return NotImplemented
@@ -239,10 +238,10 @@ def __rmul__(self, other: Any):
 
         Satisfies Complex interface.
         """
-        if isinstance(other, _FixedNumericType):
-            return self.value * other.value
         if isinstance(other, Real):
             return self.value * other
+        if isinstance(other, (str, bytes, bytearray)):
+            return other * self.value
         return NotImplemented
 
     def __neg__(self):
@@ -257,7 +256,7 @@ def __pos__(self):
 
         Satisfies Real interface.
         """
-        return +self.value
+        return abs(self.value)
 
     def __pow__(self, exponent: Any, modulus=None):
         """Return the power of the instance and other.
@@ -275,8 +274,6 @@ def __rpow__(self, base: Any):
 
         Satisfies Complex interface.
         """
-        if isinstance(base, _FixedNumericType):
-            return base.value**self.value
         if isinstance(base, int):
             return base**self.value
         return NotImplemented
@@ -304,8 +301,6 @@ def __rsub__(self, other: Any):
 
         Satisfies Complex interface.
         """
-        if isinstance(other, _FixedNumericType):
-            return other.value - self.value
         if isinstance(other, Real):
             return other - self.value
         return NotImplemented
@@ -328,8 +323,6 @@ def __rtruediv__(self, other: Any):
 
         Satisfies Complex interface.
         """
-        if isinstance(other, _FixedNumericType):
-            return other.value / self.value
         if isinstance(other, Real):
             return other / self.value
         return NotImplemented
@@ -339,20 +332,14 @@ def __trunc__(self) -> int:
 
         Satisfies Real interface.
         """
-        raise NotImplementedError
+        raise NotImplementedError("__trunc__ method not implemented")
 
-    def _validate_value(self, value: Any) -> None:
-        """Validate the value."""
-        raise NotImplementedError("_validate_value method not implemented")
+    def _bound_value(self, value: Any) -> Any:
+        """Bound the value."""
+        raise NotImplementedError("_bound_value method not implemented")
 
-    def _get_value(self):
+    def _get_value(self) -> Any:
         """Return the value of the instance."""
-        if self._data is None:
-            raise ValueError("value is None")
-        if not isinstance(self._data, (bytearray, memoryview)):
-            raise ValueError("Invalid type for internal data property")
-        if len(self._data) < len(self):
-            raise ValueError("Internal data property too short")
         return unpack(self.fmt, self._data[: len(self)])[0]
 
     def _set_value(self, new_value: Any, val_cls: type) -> None:
@@ -363,7 +350,7 @@ def _set_value(self, new_value: Any, val_cls: type) -> None:
             value_ = val_cls(new_value)
         else:
             raise TypeError(f"Value cannot be {type(new_value)}, must be number or FixedNumericType")
-        self._validate_value(value_)
+        value_ = self._bound_value(value_)
         self.data = pack(self.fmt, value_)
 
     @property
diff --git a/byteclasses/types/_fixed_size_type.py b/byteclasses/types/_fixed_size_type.py
index 70fd4fa..f75b5af 100644
--- a/byteclasses/types/_fixed_size_type.py
+++ b/byteclasses/types/_fixed_size_type.py
@@ -2,6 +2,7 @@
 
 from abc import ABC
 from collections.abc import ByteString
+from functools import cached_property
 from typing import SupportsBytes
 
 from .._enums import ByteOrder
@@ -16,17 +17,20 @@ class _FixedSizeType(ABC, SupportsBytes):
 
     _length: int = NotImplemented
 
-    def __init__(
-        self,
-        byte_order: bytes | ByteOrder,
-    ) -> None:
+    def __init__(self, *, byte_order: bytes | ByteOrder, data: ByteString | None = None) -> None:
         """Initialize the instance."""
         if self._type_char is NotImplemented:
             raise NotImplementedError(f"{self.__class__.__name__} does not implement '_type_char'")
         if self._length is NotImplemented:
             raise NotImplementedError(f"{self.__class__.__name__} does not implement '_length'")
         self._byte_order = ByteOrder(byte_order)
-        self._data: bytearray | memoryview = bytearray(len(self))
+        if data is None:
+            self._data: bytearray | memoryview = bytearray(len(self))
+        else:
+            if isinstance(data, bytes):
+                self._data = bytearray(data)
+            else:
+                self._data = data
 
     def __getitem__(self, sliced):
         return self._data[sliced]
@@ -37,13 +41,20 @@ def __bytes__(self) -> bytes:
 
     def __len__(self) -> int:
         """Return the byte length of the instance."""
-        if self._length is NotImplemented:
-            raise NotImplementedError(f"{self.__class__.__name__}._length")
         return self._length if self._length >= 0 else 0
 
     def __str__(self) -> str:
-        """Return the byte representation of the instance."""
-        return f"0x{self.__bytes__().hex()}"
+        """Return the string representation of the instance."""
+        return f"{bytes(self)!r}"
+
+    def __repr__(self) -> str:
+        """Return the raw representation of the instance."""
+        return f"{self.__class__.__name__}(data={bytes(self)!r}, byte_order={self.byte_order.value!r})"
+
+    @cached_property
+    def bit_length(self) -> int:
+        """Return bit length."""
+        return self._length * 8
 
     @property
     def byte_order(self) -> ByteOrder:
@@ -88,21 +99,25 @@ def data(self, new_value: bytes | bytearray | None = None) -> None:
     @property
     def type_char(self) -> bytes:
         """Return the type character of the class."""
-        if self._type_char is NotImplemented:
-            raise NotImplementedError(f"Type character not implemented for {self}")
         return self._type_char
 
-    def attach(self, mv: memoryview, retain_value: bool = True) -> None:
+    def attach(self, new_data: ByteString, retain_value: bool = True) -> None:
         """Replace internal data and attach provided memoryview.
 
         Memoryview length must match byte length of fixed length type.
         """
-        if not isinstance(mv, memoryview):
-            raise TypeError("Only memoryviews can be attached to fixed length types.")
-        mv_len = len(mv)
+        data_len = len(new_data)
         self_len = len(self)
-        if mv_len != len(self):
-            raise ValueError(f"Memoryview length ({mv_len} bytes) must be {self_len} bytes.")
+        if data_len != self_len:
+            raise ValueError(f"Data length ({data_len} bytes) must be {self_len} bytes.")
+        if isinstance(new_data, memoryview):
+            mv: memoryview = new_data
+        elif isinstance(new_data, bytearray):
+            mv = memoryview(new_data)
+        elif isinstance(new_data, bytes):
+            mv = memoryview(bytearray(new_data))
+        else:
+            raise TypeError(f"Unsupported data type ({type(new_data)})")
         temp = self._data
         self._data = mv
         if retain_value:
diff --git a/byteclasses/types/collections/_collection.py b/byteclasses/types/collections/_collection.py
index 960eb7d..264923f 100644
--- a/byteclasses/types/collections/_collection.py
+++ b/byteclasses/types/collections/_collection.py
@@ -18,8 +18,7 @@ def create_collection(
     allowed_types: tuple[type, ...] | None = None,
 ) -> FixedSizeCollection | Callable[[type], FixedSizeCollection]:
     """Create custom collection class."""
-    if isinstance(byte_order, str):
-        byte_order = ByteOrder(bytes(byte_order, encoding="utf8"))
+    byte_order = ByteOrder(byte_order)
 
     def outer_wrapper(cls: type) -> FixedSizeCollection:
         spec = _CollectionClassSpec(
diff --git a/byteclasses/types/collections/_fixed_collection_type.py b/byteclasses/types/collections/_fixed_collection_type.py
index b858b76..c286756 100644
--- a/byteclasses/types/collections/_fixed_collection_type.py
+++ b/byteclasses/types/collections/_fixed_collection_type.py
@@ -1,10 +1,10 @@
 """Fixed collection types and internal utility functions."""
 
-import copy
 import inspect
 import sys
 from abc import update_abstractmethods
 from collections.abc import Callable
+from copy import deepcopy
 from typing import Any, cast
 
 from .._fixed_size_type import _FixedSizeType
@@ -203,15 +203,18 @@ def _add_members(spec: _CollectionClassSpec, members_: dict[str, Member]) -> Non
         # If the class attribute (which is the default value for this member) exists
         # and is of type 'Member', replace it with the real default.  This is so that
         # normal class introspection sees a real default value, not a Member.
-        if isinstance(getattr(spec.base_cls, member_.name), Member):
-            if member_.default is MISSING:
-                # If there's no default, delete the class attribute.
-                # If we're using a default factory. The class attribute should not
-                # be set at all in the post-processed class.
-                delattr(spec.base_cls, member_.name)
-            else:
-                setattr(spec.base_cls, member_.name, member_.default)
-
+        try:
+            if isinstance(getattr(spec.base_cls, member_.name), Member):
+                if member_.default is MISSING:
+                    # If there's no default, delete the class attribute.
+                    # If we're using a default factory. The class attribute should not
+                    # be set at all in the post-processed class.
+                    delattr(spec.base_cls, member_.name)
+                else:
+                    setattr(spec.base_cls, member_.name, deepcopy(member_.default))
+        except AttributeError:
+            # Allow missing members to use type annotation as default factory.
+            pass
     # Do we have any Members that don't also have annotations?
     for name, value in spec.base_cls.__dict__.items():
         if isinstance(value, Member) and name not in cls_annotations:
@@ -396,7 +399,7 @@ def _as_dict_inner(obj, dict_factory):
         return type(obj)(_as_dict_inner(v, dict_factory) for v in obj)
     if isinstance(obj, dict):
         return type(obj)((_as_dict_inner(k, dict_factory), _as_dict_inner(v, dict_factory)) for k, v in obj.items())
-    return copy.deepcopy(obj)
+    return deepcopy(obj)
 
 
 def collection_as_tuple(obj, *, tuple_factory=tuple):
@@ -450,4 +453,4 @@ def _as_tuple_inner(obj, tuple_factory):
             )
             for k, v in obj.items()
         )
-    return copy.deepcopy(obj)
+    return deepcopy(obj)
diff --git a/byteclasses/types/collections/_methods.py b/byteclasses/types/collections/_methods.py
index a6b8658..460bcd6 100644
--- a/byteclasses/types/collections/_methods.py
+++ b/byteclasses/types/collections/_methods.py
@@ -186,7 +186,7 @@ def _build_collection_init_method(
         # Initialize _data
         _member_assign("_data", "memoryview(bytearray(length))", spec.self_name),
         # Attach members to slices of _data memoryview
-        f"{spec.self_name}._attach_members()",
+        f"{spec.self_name}._attach_members(retain_value=True)",
     ]
     return _create_method(
         "_collection_init",
@@ -209,20 +209,29 @@ def _build_attach_method(
     locals_ = {
         "cls": spec.base_cls,
         "memoryview": memoryview,
+        "bytearray": bytearray,
+        "bytes": bytes,
+        "ByteString": ByteString,
     }
     body = (
-        "if not isinstance(mv, memoryview):",
-        "  raise AttributeError('Only memoryviews can be attached to collection.')",
-        f"if len(mv) != len({spec.self_name}):",
-        "  raise AttributeError(f'Memoryview length ({len(mv)}) must match collection length ({len("
-        + spec.self_name
-        + ")}).')",
-        f"{spec.self_name}._data = mv",
-        f"{spec.self_name}._attach_members()",
+        "data_len = len(new_data)",
+        f"self_len = len({spec.self_name})",
+        "if data_len < self_len:",
+        "  raise AttributeError(f'Data length ({data_len}) must greater than or equal to {self_len} bytes.')",
+        "if isinstance(new_data, memoryview):",
+        "  mv: memoryview = new_data",
+        "elif isinstance(new_data, bytearray):",
+        "  mv = memoryview(new_data)",
+        "elif isinstance(new_data, bytes):",
+        "  mv = memoryview(bytearray(new_data))",
+        "else:",
+        "  raise TypeError(f'Unsupported data type ({type(new_data)})')",
+        f"{spec.self_name}._data = mv[:self_len]",
+        f"{spec.self_name}._attach_members(retain_value)",
     )
     return _create_method(
         "attach",
-        (spec.self_name, "mv: memoryview"),
+        (spec.self_name, "new_data: ByteString", "retain_value: bool = False"),
         body,
         locals_=locals_,
         globals_=globals_,
@@ -247,12 +256,12 @@ def _build_attach_members_method(
         body.append(
             f"{spec.self_name}.{member_.name}.attach("
             f"{spec.self_name}._data[{spec.self_name}._member_offsets[{idx}]:"
-            f"{spec.self_name}._member_offsets[{idx}] + member_lengths[{idx}]])"
+            f"{spec.self_name}._member_offsets[{idx}] + member_lengths[{idx}]], retain_value)"
         )
 
     return _create_method(
         "attach",
-        (spec.self_name,),
+        (spec.self_name, "retain_value: bool = False"),
         body,
         locals_=locals_,
         globals_=globals_,
diff --git a/byteclasses/types/collections/fixed_array.py b/byteclasses/types/collections/fixed_array.py
index 756ed5b..8cad712 100644
--- a/byteclasses/types/collections/fixed_array.py
+++ b/byteclasses/types/collections/fixed_array.py
@@ -1,12 +1,13 @@
 """Fixed length byte array types for binary data."""
 
-from collections.abc import ByteString
-from copy import deepcopy
+from collections.abc import ByteString, Iterable, Iterator
 from numbers import Number
 
 from ..._enums import ByteOrder
 from ...types._fixed_numeric_type import _FixedNumericType
 from ...types._fixed_size_type import _FixedSizeType
+from ..primitives.integers import UInt8
+from .member import _MEMBERS
 
 
 class FixedArray:
@@ -14,43 +15,33 @@ class FixedArray:
 
     def __init__(
         self,
-        item: _FixedSizeType | type[_FixedSizeType],
-        /,
         item_count: int,
+        item_type: type[_FixedSizeType] = UInt8,
+        /,
+        *,
         byte_order: bytes | ByteOrder = ByteOrder.NATIVE,
     ) -> None:
         """Initialize a fixed length array.
 
         Args:
-            item_type: The type of the items in the array.
             item_count: The number of items in array.
+            item_type: The type of the items in the array. Default: UInt8
         """
         self._byte_order: ByteOrder = ByteOrder(byte_order)
         if item_count < 2:
             raise ValueError(f"Invalid item_count: {item_count}; must be >= 2")
-        if isinstance(item, _FixedSizeType):
-            self._items: list[_FixedSizeType] = []
-            for _ in range(item_count):
-                new_item = deepcopy(item)
-                new_item.byte_order = self._byte_order
-                self._items.append(new_item)
-            item_instance = item
-        elif isinstance(item, type):
-            if issubclass(item, _FixedSizeType):
-                self._items = [item(byte_order=self._byte_order) for _ in range(item_count)]
+        if isinstance(item_type, type):
+            if issubclass(item_type, _FixedSizeType):
+                self._items = [item_type(byte_order=self._byte_order) for _ in range(item_count)]
             item_instance = self._items[0]
         else:
-            raise TypeError(
-                f"Invalid item type ({item.__class__.__name__}): Must be a FixedSizeType class or instance."
-            )
+            raise TypeError(f"Invalid item type ({item_type.__class__.__name__}): Must be a FixedSizeType class.")
         self._type_char: bytes = item_instance.type_char * item_count
         item_length = len(item_instance)
         byte_length = item_length * item_count
         self._length = byte_length
-        self._data = bytearray(byte_length)
-        data_mv = memoryview(self._data)
-        for i, idx in enumerate(range(0, self._length, item_length)):
-            self._items[i].attach(data_mv[idx : idx + item_length])
+        self._data = memoryview(bytearray(byte_length))
+        self._attach_members()
 
     def __repr__(self) -> str:
         """Return string representation of fixed array."""
@@ -64,13 +55,17 @@ def __bytes__(self) -> bytes:
         """Return array bytes."""
         return bytes(self._data)
 
+    def __iter__(self) -> Iterator:
+        """Return an item iterator."""
+        return iter(self._items)
+
     def __getitem__(self, key: int | slice) -> _FixedSizeType | list[_FixedSizeType]:
         """Implement getitem descriptor."""
         if isinstance(key, (int, slice)):
             return self._items[key]
         return NotImplemented
 
-    def __setitem__(self, key: int | slice, value: ByteString | int | _FixedSizeType) -> None:
+    def __setitem__(self, key: int | slice, value: ByteString | int | _FixedSizeType) -> None:  # pylint: disable=R0912
         """Implement setitem descriptor."""
         if isinstance(key, int):
             item = self._items[key]
@@ -84,15 +79,25 @@ def __setitem__(self, key: int | slice, value: ByteString | int | _FixedSizeType
             else:
                 raise NotImplementedError
         elif isinstance(key, slice):
-            for i in range(key.start, key.stop, key.step):
+            step = 1 if key.step is None else key.step
+            if key.start is None:
+                start = 0 if step > 0 else self._length - 1
+            else:
+                start = key.start
+            if key.stop is None:
+                stop = self._length if step > 0 else -1
+            else:
+                stop = key.stop
+            for i in range(start, stop, step):
                 item = self._items[i]
                 if isinstance(value, Number) and isinstance(item, _FixedNumericType):
                     item.value = value
-                    self._items[i] = item
+                elif isinstance(item, _FixedNumericType) and isinstance(value, Iterable):
+                    item.value = value[i]
                 elif isinstance(value, _FixedSizeType) and type(self._items[i] == type(value)):
-                    self._items[i].data = value.data
+                    item.data = value.data
                 elif isinstance(value, (bytes, bytearray)):
-                    self._items[i].data = value
+                    item.data = value
                 else:
                     raise NotImplementedError
         else:
@@ -109,3 +114,21 @@ def data(self, new_data: ByteString) -> None:
         if len(new_data) != self._length:
             raise ValueError(f"Invalid data length, expected {self._length}, receieved {len(new_data)}")
         self._data[:] = new_data
+
+    def attach(self, mv: memoryview, retain_value: bool = False) -> None:
+        """Attach memoryview to underlying data attribute."""
+        if not isinstance(mv, memoryview):
+            raise AttributeError("Only memoryviews can be attached to collection.")
+        if len(mv) != len(self):
+            raise AttributeError(f"Memoryview length ({len(mv)}) must match collection length ({len(self)}).")
+        self._data = mv
+        self._attach_members(retain_value)
+
+    def _attach_members(self, retain_value: bool = True) -> None:
+        """Attach member items to internal data attribute."""
+        item_length = len(self._items[0])
+        for i, idx in enumerate(range(0, self._length, item_length)):
+            self._items[i].attach(self._data[idx : idx + item_length], retain_value)
+
+
+setattr(FixedArray, _MEMBERS, [])
diff --git a/byteclasses/types/collections/member.py b/byteclasses/types/collections/member.py
index 58d15fe..b58e791 100644
--- a/byteclasses/types/collections/member.py
+++ b/byteclasses/types/collections/member.py
@@ -6,7 +6,6 @@
 from types import GenericAlias, MappingProxyType, MemberDescriptorType
 from typing import TYPE_CHECKING, Any
 
-from ..._enums import ByteOrder
 from ...types._fixed_size_type import _FixedSizeType
 
 if TYPE_CHECKING:
@@ -165,16 +164,23 @@ def _init_members(spec: "_CollectionClassSpec", globals_: dict[str, Any]) -> lis
     """Initialize all class members."""
     body: list[str] = []
     for member_ in spec.members:
-        line = _init_member(member_, globals_, spec.byte_order, spec.self_name)
+        line = _init_member(member_, globals_, spec.self_name)
         if line:
-            body.append(line)
+            body.extend(
+                [
+                    line,
+                    "try:",
+                    f"  {spec.self_name}.{member_.name}.byte_order = {spec.byte_order.value!r}",
+                    "except AttributeError:",
+                    "  pass",
+                ]
+            )
     return body
 
 
 def _init_member(
     member_: Member,
     globals_: dict[str, Any],
-    byte_order: ByteOrder,
     self_name: str,
 ) -> str:
     # Return the text of the line in the body of __init__ that will
@@ -190,13 +196,7 @@ def _init_member(
         else:
             # No default factory. Use member type as constructor.
             globals_[default_name] = member_.type
-        if is_fixed_collection(member_.type):
-            factory_params = ""
-        else:
-            factory_params = f"byte_order={repr(byte_order.value)}"
-        value = (
-            f"{default_name}({factory_params}) " f"if {member_.name} is _HAS_DEFAULT_FACTORY " f"else {member_.name}"
-        )
+        value = f"{default_name}() " f"if {member_.name} is _HAS_DEFAULT_FACTORY " f"else {member_.name}"
     if member_.name is None:
         raise ValueError("Member name cannot be None.")
     # Now, actually generate the member assignment.
diff --git a/byteclasses/types/collections/string.py b/byteclasses/types/collections/string.py
index 83fa608..f5df2f1 100644
--- a/byteclasses/types/collections/string.py
+++ b/byteclasses/types/collections/string.py
@@ -17,7 +17,7 @@ def __init__(
     ) -> None:
         """Initialize String instance."""
         self._null_terminated: bool = null_terminated
-        super().__init__(UChar, item_count=length)
+        super().__init__(length, UChar)
         if data is not None and value is not None:
             raise ValueError("Cannot specify both value and data.")
         if data is not None:
@@ -50,7 +50,7 @@ def data(self, new_data: ByteString) -> None:
     @property
     def value(self) -> str:
         """Return String value."""
-        return self._data.decode(encoding="utf8").rstrip("\x00")
+        return bytes(self._data).decode(encoding="utf8").rstrip("\x00")
 
     @value.setter
     def value(self, new_value: str) -> None:
diff --git a/byteclasses/types/primitives/bit_pos.py b/byteclasses/types/primitives/bit_pos.py
index dc864d9..41dc744 100644
--- a/byteclasses/types/primitives/bit_pos.py
+++ b/byteclasses/types/primitives/bit_pos.py
@@ -6,16 +6,22 @@
 class BitPos:
     """A member class representing a pit position for use in a BitField class."""
 
-    def __init__(self, idx: int) -> None:
+    def __init__(self, idx: int, *, bit_width: int = 1) -> None:
         """Initialize BitPos."""
         super().__init__()
         self._idx = idx
+        self._bit_width = bit_width
 
     def __get__(self, instance, owner=None):
         """Implement get descriptor."""
         if not isinstance(instance, BitField):
             raise TypeError("BitPos only intended for use on BitField classes.")
-        return instance[self.idx]
+        if self._bit_width == 1:
+            return instance[self.idx]
+        val = 0
+        for i in instance[self.idx : self.idx + self.bit_width][::-1]:
+            val = val << 1 | int(i)
+        return val
 
     def __set__(self, instance, value) -> None:
         """Implement set descriptor."""
@@ -23,6 +29,11 @@ def __set__(self, instance, value) -> None:
             raise TypeError("BitPos only intended for use on BitField classes.")
         instance[self.idx] = value
 
+    @property
+    def bit_width(self) -> int:
+        """Return BitPos bit width."""
+        return self._bit_width
+
     @property
     def idx(self) -> int:
         """Return read-only index value."""
diff --git a/byteclasses/types/primitives/bitfield.py b/byteclasses/types/primitives/bitfield.py
index 63e504c..91d9f71 100644
--- a/byteclasses/types/primitives/bitfield.py
+++ b/byteclasses/types/primitives/bitfield.py
@@ -1,7 +1,6 @@
 """BitField Fixed Length Class."""
 
 from collections.abc import ByteString, Sequence
-from functools import cached_property
 from struct import calcsize
 from typing import overload
 
@@ -26,9 +25,7 @@ def __init__(
             raise ValueError("byte_length must be at least 1 byte")
         self._type_char = TypeChar.BYTE.value * self.byte_length
         self._length: int = calcsize(self._type_char)
-        super().__init__(byte_order)
-        if data:
-            self.data = data
+        super().__init__(byte_order=byte_order, data=data)
 
     def __str__(self) -> str:
         """Return bitfield string representation."""
@@ -47,22 +44,13 @@ def __getitem__(self, key: int | slice) -> bool | list[bool]:
                 raise IndexError(f"index {key} out of range")
             return self.get_bit(key)
         if isinstance(key, slice):
-            if key.step is None:
-                step = 1
-            else:
-                step = key.step
+            step = 1 if key.step is None else key.step
             if key.start is None:
-                if step > 0:
-                    start = 0
-                else:
-                    start = self.bit_length - 1
+                start = 0 if step > 0 else self.bit_length - 1
             else:
                 start = key.start
             if key.stop is None:
-                if step > 0:
-                    stop = self.bit_length
-                else:
-                    stop = -1
+                stop = self.bit_length if step > 0 else -1
             else:
                 stop = key.stop
             return [self.get_bit(idx) for idx in range(start, stop, step)]
@@ -120,11 +108,6 @@ def _set_bits_by_slice(self, slc: slice, value: int | bool | Sequence[int | bool
             for idx in range(start, stop, step):
                 self.set_bit(idx, value[idx])
 
-    @cached_property
-    def bit_length(self) -> int:
-        """Calculate instance bit length."""
-        return self._length * 8
-
     def clear_bit(self, idx: int):
         """Clear bit in bitfield instance."""
         self.set_bit(idx, 0)
diff --git a/byteclasses/types/primitives/characters.py b/byteclasses/types/primitives/characters.py
index 692221e..ec6bff5 100644
--- a/byteclasses/types/primitives/characters.py
+++ b/byteclasses/types/primitives/characters.py
@@ -30,6 +30,8 @@ def __init__(
         byte_order: bytes | ByteOrder = ByteOrder.NATIVE.value,
     ) -> None:
         """Initialize UChar instance."""
+        if not isinstance(char, str) or len(char) != 1:
+            raise ValueError("Character value must a single character string.")
         super().__init__(char, byte_order=byte_order)
 
     def __str__(self) -> str:
diff --git a/byteclasses/types/primitives/floats.py b/byteclasses/types/primitives/floats.py
index 2a94e2a..d3b6e69 100644
--- a/byteclasses/types/primitives/floats.py
+++ b/byteclasses/types/primitives/floats.py
@@ -1,10 +1,10 @@
 """Fixed length floating point types."""
 
-from numbers import Number
+from collections.abc import ByteString
 from struct import calcsize
 from typing import Any, cast
 
-from ..._enums import TypeChar
+from ..._enums import ByteOrder, TypeChar
 from ...types._fixed_numeric_type import _FixedNumericType
 
 __all__ = [
@@ -20,11 +20,22 @@
 class _FixedFloat(_FixedNumericType):
     """Generic Fixed Size Float."""
 
-    def _validate_value(self, value: Number) -> None:
-        """Validate the value of the instance."""
+    def __init__(
+        self,
+        value: float | None = None,
+        *,
+        byte_order: bytes | ByteOrder = ByteOrder.NATIVE.value,
+        data: ByteString | None = None,
+    ) -> None:
+        """Initialize Fixed Float instance."""
+        super().__init__(value, byte_order=byte_order, data=data)
+
+    def _bound_value(self, value: float) -> float:
+        """Bound the value of the instance."""
+        return value
 
     @property
-    def value(self):
+    def value(self) -> float:
         """Return the value of the instance."""
         return cast(float, self._get_value())
 
diff --git a/byteclasses/types/primitives/generics.py b/byteclasses/types/primitives/generics.py
index 49ad8af..fa35084 100644
--- a/byteclasses/types/primitives/generics.py
+++ b/byteclasses/types/primitives/generics.py
@@ -1,5 +1,6 @@
 """Fixed Size Integer Types."""
 
+from collections.abc import ByteString
 from enum import IntEnum
 from struct import calcsize
 from typing import Any
@@ -31,18 +32,20 @@ class _FixedSizeGeneric(_FixedSizeType):
 
     def __init__(
         self,
-        value: bytes | None = None,
+        value: ByteString | None = None,
         *,
         byte_order: bytes | ByteOrder = ByteOrder.NATIVE.value,
+        data: ByteString | None = None,
     ) -> None:
         """Initialize Fixed Size Generic instance."""
-        super().__init__(byte_order=byte_order)
-        if not value:
-            self.value = b"\x00" * len(self)
-        elif isinstance(value, bytes):
-            self.value = value
-        else:
-            raise TypeError(f"Invalid value type ({type(value)}): expected bytes")
+        if value is not None and data is not None:
+            raise ValueError("Cannot specify both value and data arguments.")
+        super().__init__(byte_order=byte_order, data=data)
+        if value is not None:
+            if isinstance(value, (bytes, bytearray, memoryview)):
+                self.value = value
+            else:
+                raise TypeError(f"Invalid value type ({type(value)}): expected bytes")
 
     @property
     def value(self) -> Any:
diff --git a/byteclasses/types/primitives/integers.py b/byteclasses/types/primitives/integers.py
index 77f9ef3..7954e8b 100644
--- a/byteclasses/types/primitives/integers.py
+++ b/byteclasses/types/primitives/integers.py
@@ -1,5 +1,6 @@
 """Fixed Size Integer Types."""
 
+from collections.abc import ByteString
 from functools import cached_property
 from numbers import Integral
 from struct import calcsize
@@ -45,24 +46,32 @@ class _FixedInt(_FixedNumericType):
 
     def __init__(
         self,
-        value=0,
+        value: Any | None = None,
         *,
         byte_order: bytes | ByteOrder = ByteOrder.NATIVE.value,
+        data: ByteString | None = None,
+        allow_overflow: bool = False,
     ) -> None:
         """Initialize Fixed Int instance."""
+        self._allow_overflow = allow_overflow
         if self._signed is NotImplemented:
             raise NotImplementedError(f"{self.__class__.__name__} does not implement '_signed'")
-        super().__init__(value, byte_order=byte_order)
+        super().__init__(value, byte_order=byte_order, data=data)
+
+    @property
+    def allow_overflow(self) -> bool:
+        """Return allow overflow status."""
+        return self._allow_overflow
 
     @cached_property
     def max(self) -> int:
         """Return the maximum value."""
-        return (1 << (self._length * 8 - 1)) - 1 if self._signed else (1 << (self._length * 8)) - 1
+        return (1 << (self.bit_length - 1)) - 1 if self._signed else (1 << self.bit_length) - 1
 
     @cached_property
     def min(self) -> int:
         """Return the minimum value."""
-        return -(1 << (self._length * 8 - 1)) if self._signed else 0
+        return -(1 << (self.bit_length - 1)) if self._signed else 0
 
     @property
     def signed(self) -> bool:
@@ -70,7 +79,7 @@ def signed(self) -> bool:
         return self._signed
 
     @property
-    def value(self):
+    def value(self) -> int:
         """Return the value of the instance."""
         return cast(int, self._get_value())
 
@@ -183,11 +192,21 @@ def __rxor__(self, other: Any):
         """Return the bitwise XOR of the instance and other."""
         return self.__xor__(other)
 
-    def _validate_value(self, value: Any) -> None:
-        if value < self.min:
-            raise UnderflowError(f"UnderfowError: value ({value}) below {self.__class__.__name__} min ({self.min})")
-        if value > self.max:
-            raise OverflowError(f"OverfowError: value ({value}) exceeded {self.__class__.__name__} max ({self.max})")
+    def _bound_value(self, value: int) -> int:
+        if value < self.min or value > self.max:
+            if self._allow_overflow:
+                mod_value = 1 << self.bit_length
+                if self._signed:
+                    offset = 1 << (self.bit_length - 1)
+                    return ((value + offset) % mod_value) - offset
+                return value % mod_value
+            if value < self.min:
+                raise UnderflowError(f"UnderfowError: value ({value}) below {self.__class__.__name__} min ({self.min})")
+            if value > self.max:
+                raise OverflowError(
+                    f"OverfowError: value ({value}) exceeded {self.__class__.__name__} max ({self.max})"
+                )
+        return value
 
 
 class Int8(_FixedInt):
diff --git a/docs/api/byteclasses.headers.network.rst b/docs/api/byteclasses.headers.network.rst
new file mode 100644
index 0000000..bdbc4ae
--- /dev/null
+++ b/docs/api/byteclasses.headers.network.rst
@@ -0,0 +1,53 @@
+byteclasses.headers.network package
+===================================
+
+Submodules
+----------
+
+byteclasses.headers.network.ethernet module
+-------------------------------------------
+
+.. automodule:: byteclasses.headers.network.ethernet
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+byteclasses.headers.network.ipv4 module
+---------------------------------------
+
+.. automodule:: byteclasses.headers.network.ipv4
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+byteclasses.headers.network.ipv6 module
+---------------------------------------
+
+.. automodule:: byteclasses.headers.network.ipv6
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+byteclasses.headers.network.tcp module
+--------------------------------------
+
+.. automodule:: byteclasses.headers.network.tcp
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+byteclasses.headers.network.udp module
+--------------------------------------
+
+.. automodule:: byteclasses.headers.network.udp
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: byteclasses.headers.network
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/api/byteclasses.headers.rst b/docs/api/byteclasses.headers.rst
new file mode 100644
index 0000000..0388a3d
--- /dev/null
+++ b/docs/api/byteclasses.headers.rst
@@ -0,0 +1,18 @@
+byteclasses.headers package
+===========================
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 4
+
+   byteclasses.headers.network
+
+Module contents
+---------------
+
+.. automodule:: byteclasses.headers
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/api/byteclasses.rst b/docs/api/byteclasses.rst
index 7f83b35..b7331f0 100644
--- a/docs/api/byteclasses.rst
+++ b/docs/api/byteclasses.rst
@@ -7,6 +7,7 @@ Subpackages
 .. toctree::
    :maxdepth: 4
 
+   byteclasses.headers
    byteclasses.types
 
 Submodules
diff --git a/docs/api/byteclasses.types.collections.rst b/docs/api/byteclasses.types.collections.rst
index df0a058..ba432bf 100644
--- a/docs/api/byteclasses.types.collections.rst
+++ b/docs/api/byteclasses.types.collections.rst
@@ -36,6 +36,14 @@ byteclasses.types.collections.member module
    :undoc-members:
    :show-inheritance:
 
+byteclasses.types.collections.string module
+-------------------------------------------
+
+.. automodule:: byteclasses.types.collections.string
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
 byteclasses.types.collections.structure module
 ----------------------------------------------
 
diff --git a/docs/collections/fixed_array.md b/docs/collections/fixed_array.md
index 5e8d08b..aa0e8f0 100644
--- a/docs/collections/fixed_array.md
+++ b/docs/collections/fixed_array.md
@@ -5,9 +5,9 @@ Fixed size arrays provide a concrete class that accepts an instance of a bytecla
 > `FixedArray` has a minimum qty of two.
 
 ```python
-fa1 = FixedArray(Int16,2)
-fa2 = FixedArray(Int16(2), 2)
-fa3 = FixedArray(Int16(2), 4, byte_order=b"!")
+fa1 = FixedArray(2) # Item type for FixedArrays are UInt8
+fa2 = FixedArray(2, Int16)
+fa3 = FixedArray(4, Int16, byte_order=b"!")
 ```
 
 > The `FixedArray` `byte_order` will override the item `byte_order` regardless if the item `byte_order` is specified.
@@ -17,7 +17,7 @@ fa3 = FixedArray(Int16(2), 4, byte_order=b"!")
 Array items can be accessed by indexing an array. The raw array bytes can be interacted with via the `data` attribute.
 
 ```python
-fa = FixedArray(Int8, 10)
+fa = FixedArray(10, Int8)
 print(fa)
 print(fa.data)
 print(fa[0])
diff --git a/docs/collections/structure.md b/docs/collections/structure.md
index e965e4a..dba237f 100644
--- a/docs/collections/structure.md
+++ b/docs/collections/structure.md
@@ -7,8 +7,8 @@ A Structure is a dynamic byteclass created using the `@structure` decorator.
 class MyStructure:
     """My Structure Byteclass."""
 
-    var1: UInt64 = UInt64()
-    var2: UInt32 = UInt32()
+    var1: UInt64
+    var2: UInt32
 ```
 
 Similar to a C `struct`, each member of a byteclass `structure` has a sequential offset. By default, `structure` are padded so each member is size aligned. The `structure` decorator accepts a boolean `padding` parameter to override the default padding behavior.
@@ -19,10 +19,20 @@ A Structure byteclass can contain both byteclass primitives and other byteclass
 
 Each Structure member has its own `byte_order`. However, the `structure` decorator accepts a `byte_order` parameter which is used for any members that rely on a `default_factory` for instantiation.
 
+> If no member value is specified, the member type annotation is used as a `default_factory`.
+
 ```python
-@structure(byte_order=b">")
-class FactoryStructure:
-    """A structure byteclass with member using default factory."""
+@structure(byte_order=b"@")
+class Structure1:
+    """A structure byteclass with one member using default factory."""
 
     a: UInt64 = member(default_factory=UInt64)
+
+@structure
+class Structure2:
+    """A structure byteclass with one member using default factory."""
+
+    a: UInt64
 ```
+
+The `Structure1` and `Structure2` defined above are functionally equivalent.
diff --git a/docs/collections/union.md b/docs/collections/union.md
index 54518de..609850b 100644
--- a/docs/collections/union.md
+++ b/docs/collections/union.md
@@ -7,8 +7,8 @@ A Union is a dynamic byteclass created using the `@union` decorator.
 class MyUnion:
     """My Union Byteclass."""
 
-    var1: UInt64 = UInt64()
-    var2: UInt32 = UInt32()
+    var1: UInt64
+    var2: UInt32
 ```
 
 Similar to a C `union`, each member of a byteclass `union` has an offset of 0 and overlay onto the same underlying data.
@@ -20,9 +20,17 @@ A Union byteclass can contain both byteclass primitives and other byteclass coll
 Each Union member has its own `byte_order`. However, the `union` decorator accepts a `byte_order` parameter which is used for any members that rely on a `default_factory` for instantiation.
 
 ```python
-@union(byte_order=b">")
-class FactoryUnion:
-    """A union byteclass with member using default factory."""
+@union(byte_order=b"@")
+class Union1:
+    """A union byteclass with one member using default factory."""
 
     a: UInt64 = member(default_factory=UInt64)
+
+@union
+class Union2:
+    """A union byteclass with one member using default factory."""
+
+    a: UInt64
 ```
+
+The `Union1` and `Union2` defined above are functionally equivalent.
diff --git a/docs/examples/advanced/ethernet.ipynb b/docs/examples/advanced/ethernet.ipynb
new file mode 100644
index 0000000..14ac058
--- /dev/null
+++ b/docs/examples/advanced/ethernet.ipynb
@@ -0,0 +1,191 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Ethernet Header and Frame"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.handlers.network.eth_hdr import EthHdr\n",
+    "from byteclasses.handlers.network.eth_frame import EthFrame\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "eth = EthHdr()\n",
+    "data = bytearray(b\"\\x00\\x0c\\x29\\xb4\\x90\\x14\\xec\\xf4\\xbb\\x96\\x12\\x0e\\x08\\x00\")\n",
+    "eth.attach(memoryview(data))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                                  Ethernet Header                                                  </span>\n",
+       "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name           </span>┃<span style=\"font-weight: bold\"> Value                                                                                          </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(eth)       │ 14                                                                                             │\n",
+       "│ str(eth)       │ EthHdr(dst_mac=[UInt8(0), UInt8(12), UInt8(41), UInt8(180), UInt8(144), UInt8(20)],            │\n",
+       "│                │ src_mac=[UInt8(236), UInt8(244), UInt8(187), UInt8(150), UInt8(18), UInt8(14)],                │\n",
+       "│                │ ether_type=UInt16(2048))                                                                       │\n",
+       "│ repr(eth)      │ EthHdr(dst_mac=[UInt8(0), UInt8(12), UInt8(41), UInt8(180), UInt8(144), UInt8(20)],            │\n",
+       "│                │ src_mac=[UInt8(236), UInt8(244), UInt8(187), UInt8(150), UInt8(18), UInt8(14)],                │\n",
+       "│                │ ether_type=UInt16(2048))                                                                       │\n",
+       "│ eth.data       │ bytearray(b'\\x00\\x0c)\\xb4\\x90\\x14\\xec\\xf4\\xbb\\x96\\x12\\x0e\\x08\\x00')                            │\n",
+       "│ eth.src_mac    │ [UInt8(236), UInt8(244), UInt8(187), UInt8(150), UInt8(18), UInt8(14)]                         │\n",
+       "│ eth.dst_mac    │ [UInt8(0), UInt8(12), UInt8(41), UInt8(180), UInt8(144), UInt8(20)]                            │\n",
+       "│ eth.ether_type │ 2048                                                                                           │\n",
+       "└────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                                  Ethernet Header                                                  \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName          \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue                                                                                         \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(eth)       │ 14                                                                                             │\n",
+       "│ str(eth)       │ EthHdr(dst_mac=[UInt8(0), UInt8(12), UInt8(41), UInt8(180), UInt8(144), UInt8(20)],            │\n",
+       "│                │ src_mac=[UInt8(236), UInt8(244), UInt8(187), UInt8(150), UInt8(18), UInt8(14)],                │\n",
+       "│                │ ether_type=UInt16(2048))                                                                       │\n",
+       "│ repr(eth)      │ EthHdr(dst_mac=[UInt8(0), UInt8(12), UInt8(41), UInt8(180), UInt8(144), UInt8(20)],            │\n",
+       "│                │ src_mac=[UInt8(236), UInt8(244), UInt8(187), UInt8(150), UInt8(18), UInt8(14)],                │\n",
+       "│                │ ether_type=UInt16(2048))                                                                       │\n",
+       "│ eth.data       │ bytearray(b'\\x00\\x0c)\\xb4\\x90\\x14\\xec\\xf4\\xbb\\x96\\x12\\x0e\\x08\\x00')                            │\n",
+       "│ eth.src_mac    │ [UInt8(236), UInt8(244), UInt8(187), UInt8(150), UInt8(18), UInt8(14)]                         │\n",
+       "│ eth.dst_mac    │ [UInt8(0), UInt8(12), UInt8(41), UInt8(180), UInt8(144), UInt8(20)]                            │\n",
+       "│ eth.ether_type │ 2048                                                                                           │\n",
+       "└────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"Ethernet Header\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(eth)\", str(len(eth)))\n",
+    "table.add_row(\"str(eth)\", str(eth))\n",
+    "table.add_row(\"repr(eth)\", repr(eth))\n",
+    "table.add_row(\"eth.data\", str(eth.data))\n",
+    "table.add_row(\"eth.src_mac\", str(eth.src_mac))\n",
+    "table.add_row(\"eth.dst_mac\", str(eth.dst_mac))\n",
+    "table.add_row(\"eth.ether_type\", str(eth.ether_type))\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "data = b\"\\xf0\\x9f\\xc2\\xdf\\x16\\x1f\\x00\\x0c\\x29\\xfa\\xa3\\x37\\x08\\x00\\x45\\x00\\x00\\x54\\x39\\x03\\x40\\x00\\x40\\x01\\x68\\xd8\\xc0\\xa8\\xc8\\x15\\x08\\x08\\x08\\x08\\x08\\x00\\xda\\xda\\x20\\x62\\x00\\x01\\x52\\xf2\\xa8\\x62\\x00\\x00\\x00\\x00\\x3d\\x9a\\x05\\x00\\x00\\x00\\x00\\x00\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c\\x2d\\x2e\\x2f\\x30\\x31\\x32\\x33\\x34\\x35\\x36\\x37\"\n",
+    "ef = EthFrame(data=data)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                      Ethernet Frame                                      </span>\n",
+       "┏━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name       </span>┃<span style=\"font-weight: bold\"> Value                                                                     </span>┃\n",
+       "┡━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(ef)    │ 98                                                                        │\n",
+       "│ str(ef)    │ EthFrame(src_mac=00:0c:29:fa:a3:37, dst_mac=f0:9f:c2:df:16:1f, type=IPV4) │\n",
+       "│ repr(ef)   │ EthFrame(data=&lt;memory at 0x10b1abf40&gt;)                                    │\n",
+       "│ ef.data    │ &lt;memory at 0x10b1abf40&gt;                                                   │\n",
+       "│ ef.src_mac │ 00:0c:29:fa:a3:37                                                         │\n",
+       "│ ef.dst_mac │ f0:9f:c2:df:16:1f                                                         │\n",
+       "│ ef.type    │ IPV4                                                                      │\n",
+       "└────────────┴───────────────────────────────────────────────────────────────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                      Ethernet Frame                                      \u001b[0m\n",
+       "┏━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName      \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue                                                                    \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(ef)    │ 98                                                                        │\n",
+       "│ str(ef)    │ EthFrame(src_mac=00:0c:29:fa:a3:37, dst_mac=f0:9f:c2:df:16:1f, type=IPV4) │\n",
+       "│ repr(ef)   │ EthFrame(data=<memory at 0x10b1abf40>)                                    │\n",
+       "│ ef.data    │ <memory at 0x10b1abf40>                                                   │\n",
+       "│ ef.src_mac │ 00:0c:29:fa:a3:37                                                         │\n",
+       "│ ef.dst_mac │ f0:9f:c2:df:16:1f                                                         │\n",
+       "│ ef.type    │ IPV4                                                                      │\n",
+       "└────────────┴───────────────────────────────────────────────────────────────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"Ethernet Frame\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(ef)\", str(len(ef)))\n",
+    "table.add_row(\"str(ef)\", str(ef))\n",
+    "table.add_row(\"repr(ef)\", repr(ef))\n",
+    "table.add_row(\"ef.data\", str(ef.data))\n",
+    "table.add_row(\"ef.src_mac\", str(ef.src_mac))\n",
+    "table.add_row(\"ef.dst_mac\", str(ef.dst_mac))\n",
+    "table.add_row(\"ef.type\", str(ef.type))\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/examples/advanced/index.rst b/docs/examples/advanced/index.rst
index eeac639..b256f31 100644
--- a/docs/examples/advanced/index.rst
+++ b/docs/examples/advanced/index.rst
@@ -3,6 +3,7 @@ Advanced Examples
 
 .. toctree::
 
+   ethernet
    ipv4
    ipv6
    tcp
diff --git a/docs/examples/advanced/ipv4.ipynb b/docs/examples/advanced/ipv4.ipynb
index 49cf995..4cf9ec8 100644
--- a/docs/examples/advanced/ipv4.ipynb
+++ b/docs/examples/advanced/ipv4.ipynb
@@ -4,18 +4,211 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# IPv4 Header Structure"
+    "# IPv4 Header"
    ]
   },
   {
-   "cell_type": "markdown",
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.handlers.network.ipv4_hdr import IPv4Hdr\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ipv4_hdr = IPv4Hdr()\n",
+    "data = bytearray(b\"\\x45\\x00\\x00\\x34\\x1d\\x12\\x40\\x00\\x80\\x06\\xcb\\xc3\\xc0\\xa8\\xc8\\x87\\xc0\\xa8\\xc8\\x15\")\n",
+    "ipv4_hdr.attach(memoryview(data))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                                    IPv4 Header                                                    </span>\n",
+       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name                               </span>┃<span style=\"font-weight: bold\"> Value                                                                      </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(ipv4_hdr)                      │ 20                                                                         │\n",
+       "│ str(ipv4_hdr)                      │ IPv4Hdr(ver_ihl=VerIhl(data=b'E'), dscp_ecn=DscpEcn(data=b'\\x00'),         │\n",
+       "│                                    │ total_length=UInt16(52), identification=UInt16(7442),                      │\n",
+       "│                                    │ flags_off=FlagsOff(data=b'@\\x00'), time_to_live=UInt8(128),                │\n",
+       "│                                    │ protocol=UInt8(6), header_checksum=UInt16(52163),                          │\n",
+       "│                                    │ src_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(135)],    │\n",
+       "│                                    │ uint32=UInt32(3232286855)), dst_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), │\n",
+       "│                                    │ UInt8(200), UInt8(21)], uint32=UInt32(3232286741)))                        │\n",
+       "│ repr(ipv4_hdr)                     │ IPv4Hdr(ver_ihl=VerIhl(data=b'E'), dscp_ecn=DscpEcn(data=b'\\x00'),         │\n",
+       "│                                    │ total_length=UInt16(52), identification=UInt16(7442),                      │\n",
+       "│                                    │ flags_off=FlagsOff(data=b'@\\x00'), time_to_live=UInt8(128),                │\n",
+       "│                                    │ protocol=UInt8(6), header_checksum=UInt16(52163),                          │\n",
+       "│                                    │ src_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(135)],    │\n",
+       "│                                    │ uint32=UInt32(3232286855)), dst_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), │\n",
+       "│                                    │ UInt8(200), UInt8(21)], uint32=UInt32(3232286741)))                        │\n",
+       "│ ipv4_hdr.ver_ihl                   │ 10100010                                                                   │\n",
+       "│ ipv4_hdr.ver_ihl.version           │ 5                                                                          │\n",
+       "│ ipv4_hdr.ver_ihl.ihl               │ 4                                                                          │\n",
+       "│ ipv4_hdr.dscp_ecn                  │ 00000000                                                                   │\n",
+       "│ ipv4_hdr.dscp_ecn.dscp             │ 0                                                                          │\n",
+       "│ ipv4_hdr.dscp_ecn.ecn              │ 0                                                                          │\n",
+       "│ ipv4_hdr.identification            │ 7442                                                                       │\n",
+       "│ ipv4_hdr.flags_off                 │ 0000001000000000                                                           │\n",
+       "│ ipv4_hdr.flags_off.flags           │ 0                                                                          │\n",
+       "│ ipv4_hdr.flags_off.fragment_offset │ 8                                                                          │\n",
+       "│ ipv4_hdr.time_to_live              │ 128                                                                        │\n",
+       "│ ipv4_hdr.protocol                  │ 6                                                                          │\n",
+       "│ ipv4_hdr.header_checksum           │ 52163                                                                      │\n",
+       "│ ipv4_hdr.src_ip                    │ IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(135)],           │\n",
+       "│                                    │ uint32=UInt32(3232286855))                                                 │\n",
+       "│ ipv4_hdr.dst_ip                    │ IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(21)],            │\n",
+       "│                                    │ uint32=UInt32(3232286741))                                                 │\n",
+       "└────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                                    IPv4 Header                                                    \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName                              \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue                                                                     \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(ipv4_hdr)                      │ 20                                                                         │\n",
+       "│ str(ipv4_hdr)                      │ IPv4Hdr(ver_ihl=VerIhl(data=b'E'), dscp_ecn=DscpEcn(data=b'\\x00'),         │\n",
+       "│                                    │ total_length=UInt16(52), identification=UInt16(7442),                      │\n",
+       "│                                    │ flags_off=FlagsOff(data=b'@\\x00'), time_to_live=UInt8(128),                │\n",
+       "│                                    │ protocol=UInt8(6), header_checksum=UInt16(52163),                          │\n",
+       "│                                    │ src_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(135)],    │\n",
+       "│                                    │ uint32=UInt32(3232286855)), dst_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), │\n",
+       "│                                    │ UInt8(200), UInt8(21)], uint32=UInt32(3232286741)))                        │\n",
+       "│ repr(ipv4_hdr)                     │ IPv4Hdr(ver_ihl=VerIhl(data=b'E'), dscp_ecn=DscpEcn(data=b'\\x00'),         │\n",
+       "│                                    │ total_length=UInt16(52), identification=UInt16(7442),                      │\n",
+       "│                                    │ flags_off=FlagsOff(data=b'@\\x00'), time_to_live=UInt8(128),                │\n",
+       "│                                    │ protocol=UInt8(6), header_checksum=UInt16(52163),                          │\n",
+       "│                                    │ src_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(135)],    │\n",
+       "│                                    │ uint32=UInt32(3232286855)), dst_ip=IPv4Addr(uint8=[UInt8(192), UInt8(168), │\n",
+       "│                                    │ UInt8(200), UInt8(21)], uint32=UInt32(3232286741)))                        │\n",
+       "│ ipv4_hdr.ver_ihl                   │ 10100010                                                                   │\n",
+       "│ ipv4_hdr.ver_ihl.version           │ 5                                                                          │\n",
+       "│ ipv4_hdr.ver_ihl.ihl               │ 4                                                                          │\n",
+       "│ ipv4_hdr.dscp_ecn                  │ 00000000                                                                   │\n",
+       "│ ipv4_hdr.dscp_ecn.dscp             │ 0                                                                          │\n",
+       "│ ipv4_hdr.dscp_ecn.ecn              │ 0                                                                          │\n",
+       "│ ipv4_hdr.identification            │ 7442                                                                       │\n",
+       "│ ipv4_hdr.flags_off                 │ 0000001000000000                                                           │\n",
+       "│ ipv4_hdr.flags_off.flags           │ 0                                                                          │\n",
+       "│ ipv4_hdr.flags_off.fragment_offset │ 8                                                                          │\n",
+       "│ ipv4_hdr.time_to_live              │ 128                                                                        │\n",
+       "│ ipv4_hdr.protocol                  │ 6                                                                          │\n",
+       "│ ipv4_hdr.header_checksum           │ 52163                                                                      │\n",
+       "│ ipv4_hdr.src_ip                    │ IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(135)],           │\n",
+       "│                                    │ uint32=UInt32(3232286855))                                                 │\n",
+       "│ ipv4_hdr.dst_ip                    │ IPv4Addr(uint8=[UInt8(192), UInt8(168), UInt8(200), UInt8(21)],            │\n",
+       "│                                    │ uint32=UInt32(3232286741))                                                 │\n",
+       "└────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"IPv4 Header\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(ipv4_hdr)\", str(len(ipv4_hdr)))\n",
+    "table.add_row(\"str(ipv4_hdr)\", str(ipv4_hdr))\n",
+    "table.add_row(\"repr(ipv4_hdr)\", repr(ipv4_hdr))\n",
+    "table.add_row(\"ipv4_hdr.ver_ihl\", str(ipv4_hdr.ver_ihl))\n",
+    "table.add_row(\"ipv4_hdr.ver_ihl.version\", str(ipv4_hdr.ver_ihl.version))\n",
+    "table.add_row(\"ipv4_hdr.ver_ihl.ihl\", str(ipv4_hdr.ver_ihl.ihl))\n",
+    "table.add_row(\"ipv4_hdr.dscp_ecn\", str(ipv4_hdr.dscp_ecn))\n",
+    "table.add_row(\"ipv4_hdr.dscp_ecn.dscp\", str(ipv4_hdr.dscp_ecn.dscp))\n",
+    "table.add_row(\"ipv4_hdr.dscp_ecn.ecn\", str(ipv4_hdr.dscp_ecn.ecn))\n",
+    "table.add_row(\"ipv4_hdr.identification\", str(ipv4_hdr.identification))\n",
+    "table.add_row(\"ipv4_hdr.flags_off\", str(ipv4_hdr.flags_off))\n",
+    "table.add_row(\"ipv4_hdr.flags_off.flags\", str(ipv4_hdr.flags_off.flags))\n",
+    "table.add_row(\"ipv4_hdr.flags_off.fragment_offset\", str(ipv4_hdr.flags_off.fragment_offset))\n",
+    "table.add_row(\"ipv4_hdr.time_to_live\", str(ipv4_hdr.time_to_live))\n",
+    "table.add_row(\"ipv4_hdr.protocol\", str(ipv4_hdr.protocol))\n",
+    "table.add_row(\"ipv4_hdr.header_checksum\", str(ipv4_hdr.header_checksum))\n",
+    "table.add_row(\"ipv4_hdr.src_ip\", str(ipv4_hdr.src_ip))\n",
+    "table.add_row(\"ipv4_hdr.dst_ip\", str(ipv4_hdr.dst_ip))\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "0xc0a80101 0x7f000001\n",
+      "bytearray(b'\\xc0\\xa8\\x01\\x01') bytearray(b'\\x7f\\x00\\x00\\x01')\n"
+     ]
+    }
+   ],
+   "source": [
+    "ipv4_hdr.src_ip.uint8[:] = [192,168,1,1]\n",
+    "ipv4_hdr.dst_ip.uint8[:] = [127, 0, 0, 1]\n",
+    "print(hex(ipv4_hdr.src_ip.uint32), hex(ipv4_hdr.dst_ip.uint32))\n",
+    "print(ipv4_hdr.src_ip.data, ipv4_hdr.dst_ip.data)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
    "metadata": {},
-   "source": []
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "bytearray(b'\\xde\\xad\\xbe\\xef') bytearray(b'\\xca\\xfe\\xba\\xbe')\n",
+      "bytearray(b'E\\x00\\x004\\x1d\\x12@\\x00\\x80\\x06\\xcb\\xc3\\xde\\xad\\xbe\\xef\\xca\\xfe\\xba\\xbe')\n"
+     ]
+    }
+   ],
+   "source": [
+    "ipv4_hdr.src_ip.uint32 = 0xdeadbeef\n",
+    "ipv4_hdr.dst_ip.uint32 = 0xcafebabe\n",
+    "print(ipv4_hdr.src_ip.data, ipv4_hdr.dst_ip.data)\n",
+    "print(ipv4_hdr.data)"
+   ]
   }
  ],
  "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
   "language_info": {
-   "name": "python"
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
   }
  },
  "nbformat": 4,
diff --git a/docs/examples/advanced/ipv6.ipynb b/docs/examples/advanced/ipv6.ipynb
index 0b902d8..fb83abd 100644
--- a/docs/examples/advanced/ipv6.ipynb
+++ b/docs/examples/advanced/ipv6.ipynb
@@ -4,18 +4,203 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# IPv6 Header Structure"
+    "# IPv6 Header"
    ]
   },
   {
-   "cell_type": "markdown",
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.handlers.network.ipv6_hdr import IPv6Hdr\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "ipv6_hdr = IPv6Hdr()\n",
+    "data = bytearray(b\"\\x60\\x00\\x00\\x00\\x00\\x18\\x06\\x80\\x20\\x02\\x51\\x83\\x43\\x83\\x00\\x00\\x00\\x00\\x00\\x00\\x51\\x83\\x43\\x83\\x20\\x01\\x06\\x38\\x09\\x02\\x00\\x01\\x02\\x01\\x02\\xff\\xfe\\xe2\\x75\\x96\")\n",
+    "ipv6_hdr.attach(memoryview(data))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
    "metadata": {},
-   "source": []
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                                    ipv6 Header                                                    </span>\n",
+       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name                       </span>┃<span style=\"font-weight: bold\"> Value                                                                              </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(ipv6_hdr)              │ 40                                                                                 │\n",
+       "│ str(ipv6_hdr)              │ IPv6Hdr(vtf=VTF(data=b'`\\x00\\x00\\x00'), payload_length=UInt16(24),                 │\n",
+       "│                            │ next_hdr=UInt8(6), hop_limit=UInt8(128), src_addr=IPv6Addr(uint8=[UInt8(32),       │\n",
+       "│                            │ UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), UInt8(0), UInt8(0),        │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131), UInt8(67),          │\n",
+       "│                            │ UInt8(131)], uint32=[UInt32(537022851), UInt32(1132658688), UInt32(0),             │\n",
+       "│                            │ UInt32(1367556995)], uint64=[UInt64(2306495583382339584), UInt64(1367556995)]),    │\n",
+       "│                            │ dst_addr=IPv6Addr(uint8=[UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9),       │\n",
+       "│                            │ UInt8(2), UInt8(0), UInt8(1), UInt8(2), UInt8(1), UInt8(2), UInt8(255),            │\n",
+       "│                            │ UInt8(254), UInt8(226), UInt8(117), UInt8(150)], uint32=[UInt32(536938040),        │\n",
+       "│                            │ UInt32(151126017), UInt32(33620735), UInt32(4276254102)],                          │\n",
+       "│                            │ uint64=[UInt64(2306131321929465857), UInt64(144399961568736662)]))                 │\n",
+       "│ repr(ipv6_hdr)             │ IPv6Hdr(vtf=VTF(data=b'`\\x00\\x00\\x00'), payload_length=UInt16(24),                 │\n",
+       "│                            │ next_hdr=UInt8(6), hop_limit=UInt8(128), src_addr=IPv6Addr(uint8=[UInt8(32),       │\n",
+       "│                            │ UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), UInt8(0), UInt8(0),        │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131), UInt8(67),          │\n",
+       "│                            │ UInt8(131)], uint32=[UInt32(537022851), UInt32(1132658688), UInt32(0),             │\n",
+       "│                            │ UInt32(1367556995)], uint64=[UInt64(2306495583382339584), UInt64(1367556995)]),    │\n",
+       "│                            │ dst_addr=IPv6Addr(uint8=[UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9),       │\n",
+       "│                            │ UInt8(2), UInt8(0), UInt8(1), UInt8(2), UInt8(1), UInt8(2), UInt8(255),            │\n",
+       "│                            │ UInt8(254), UInt8(226), UInt8(117), UInt8(150)], uint32=[UInt32(536938040),        │\n",
+       "│                            │ UInt32(151126017), UInt32(33620735), UInt32(4276254102)],                          │\n",
+       "│                            │ uint64=[UInt64(2306131321929465857), UInt64(144399961568736662)]))                 │\n",
+       "│ ipv6_hdr.vtf               │ 00000110000000000000000000000000                                                   │\n",
+       "│ ipv6_hdr.vtf.version       │ 0                                                                                  │\n",
+       "│ ipv6_hdr.vtf.traffic_class │ 6                                                                                  │\n",
+       "│ ipv6_hdr.vtf.flow_label    │ 0                                                                                  │\n",
+       "│ ipv6_hdr.payload_length    │ 24                                                                                 │\n",
+       "│ ipv6_hdr.next_hdr          │ 6                                                                                  │\n",
+       "│ ipv6_hdr.hop_limit         │ 128                                                                                │\n",
+       "│ ipv6_hdr.src_addr          │ IPv6Addr(uint8=[UInt8(32), UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131), │\n",
+       "│                            │ UInt8(67), UInt8(131)], uint32=[UInt32(537022851), UInt32(1132658688), UInt32(0),  │\n",
+       "│                            │ UInt32(1367556995)], uint64=[UInt64(2306495583382339584), UInt64(1367556995)])     │\n",
+       "│ ipv6_hdr.src_addr.uint8    │ [UInt8(32), UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), UInt8(0),      │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131),           │\n",
+       "│                            │ UInt8(67), UInt8(131)]                                                             │\n",
+       "│ ipv6_hdr.src_addr.uint32   │ [UInt32(537022851), UInt32(1132658688), UInt32(0), UInt32(1367556995)]             │\n",
+       "│ ipv6_hdr.src_addr.uint64   │ [UInt64(2306495583382339584), UInt64(1367556995)]                                  │\n",
+       "│ ipv6_hdr.dst_addr          │ IPv6Addr(uint8=[UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9), UInt8(2),      │\n",
+       "│                            │ UInt8(0), UInt8(1), UInt8(2), UInt8(1), UInt8(2), UInt8(255), UInt8(254),          │\n",
+       "│                            │ UInt8(226), UInt8(117), UInt8(150)], uint32=[UInt32(536938040), UInt32(151126017), │\n",
+       "│                            │ UInt32(33620735), UInt32(4276254102)], uint64=[UInt64(2306131321929465857),        │\n",
+       "│                            │ UInt64(144399961568736662)])                                                       │\n",
+       "│ ipv6_hdr.dst_addr.uint8    │ [UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9), UInt8(2), UInt8(0), UInt8(1), │\n",
+       "│                            │ UInt8(2), UInt8(1), UInt8(2), UInt8(255), UInt8(254), UInt8(226), UInt8(117),      │\n",
+       "│                            │ UInt8(150)]                                                                        │\n",
+       "│ ipv6_hdr.dst_addr.uint32   │ [UInt32(536938040), UInt32(151126017), UInt32(33620735), UInt32(4276254102)]       │\n",
+       "│ ipv6_hdr.dst_addr.uint64   │ [UInt64(2306131321929465857), UInt64(144399961568736662)]                          │\n",
+       "└────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                                    ipv6 Header                                                    \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName                      \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue                                                                             \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(ipv6_hdr)              │ 40                                                                                 │\n",
+       "│ str(ipv6_hdr)              │ IPv6Hdr(vtf=VTF(data=b'`\\x00\\x00\\x00'), payload_length=UInt16(24),                 │\n",
+       "│                            │ next_hdr=UInt8(6), hop_limit=UInt8(128), src_addr=IPv6Addr(uint8=[UInt8(32),       │\n",
+       "│                            │ UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), UInt8(0), UInt8(0),        │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131), UInt8(67),          │\n",
+       "│                            │ UInt8(131)], uint32=[UInt32(537022851), UInt32(1132658688), UInt32(0),             │\n",
+       "│                            │ UInt32(1367556995)], uint64=[UInt64(2306495583382339584), UInt64(1367556995)]),    │\n",
+       "│                            │ dst_addr=IPv6Addr(uint8=[UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9),       │\n",
+       "│                            │ UInt8(2), UInt8(0), UInt8(1), UInt8(2), UInt8(1), UInt8(2), UInt8(255),            │\n",
+       "│                            │ UInt8(254), UInt8(226), UInt8(117), UInt8(150)], uint32=[UInt32(536938040),        │\n",
+       "│                            │ UInt32(151126017), UInt32(33620735), UInt32(4276254102)],                          │\n",
+       "│                            │ uint64=[UInt64(2306131321929465857), UInt64(144399961568736662)]))                 │\n",
+       "│ repr(ipv6_hdr)             │ IPv6Hdr(vtf=VTF(data=b'`\\x00\\x00\\x00'), payload_length=UInt16(24),                 │\n",
+       "│                            │ next_hdr=UInt8(6), hop_limit=UInt8(128), src_addr=IPv6Addr(uint8=[UInt8(32),       │\n",
+       "│                            │ UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), UInt8(0), UInt8(0),        │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131), UInt8(67),          │\n",
+       "│                            │ UInt8(131)], uint32=[UInt32(537022851), UInt32(1132658688), UInt32(0),             │\n",
+       "│                            │ UInt32(1367556995)], uint64=[UInt64(2306495583382339584), UInt64(1367556995)]),    │\n",
+       "│                            │ dst_addr=IPv6Addr(uint8=[UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9),       │\n",
+       "│                            │ UInt8(2), UInt8(0), UInt8(1), UInt8(2), UInt8(1), UInt8(2), UInt8(255),            │\n",
+       "│                            │ UInt8(254), UInt8(226), UInt8(117), UInt8(150)], uint32=[UInt32(536938040),        │\n",
+       "│                            │ UInt32(151126017), UInt32(33620735), UInt32(4276254102)],                          │\n",
+       "│                            │ uint64=[UInt64(2306131321929465857), UInt64(144399961568736662)]))                 │\n",
+       "│ ipv6_hdr.vtf               │ 00000110000000000000000000000000                                                   │\n",
+       "│ ipv6_hdr.vtf.version       │ 0                                                                                  │\n",
+       "│ ipv6_hdr.vtf.traffic_class │ 6                                                                                  │\n",
+       "│ ipv6_hdr.vtf.flow_label    │ 0                                                                                  │\n",
+       "│ ipv6_hdr.payload_length    │ 24                                                                                 │\n",
+       "│ ipv6_hdr.next_hdr          │ 6                                                                                  │\n",
+       "│ ipv6_hdr.hop_limit         │ 128                                                                                │\n",
+       "│ ipv6_hdr.src_addr          │ IPv6Addr(uint8=[UInt8(32), UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131), │\n",
+       "│                            │ UInt8(67), UInt8(131)], uint32=[UInt32(537022851), UInt32(1132658688), UInt32(0),  │\n",
+       "│                            │ UInt32(1367556995)], uint64=[UInt64(2306495583382339584), UInt64(1367556995)])     │\n",
+       "│ ipv6_hdr.src_addr.uint8    │ [UInt8(32), UInt8(2), UInt8(81), UInt8(131), UInt8(67), UInt8(131), UInt8(0),      │\n",
+       "│                            │ UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(81), UInt8(131),           │\n",
+       "│                            │ UInt8(67), UInt8(131)]                                                             │\n",
+       "│ ipv6_hdr.src_addr.uint32   │ [UInt32(537022851), UInt32(1132658688), UInt32(0), UInt32(1367556995)]             │\n",
+       "│ ipv6_hdr.src_addr.uint64   │ [UInt64(2306495583382339584), UInt64(1367556995)]                                  │\n",
+       "│ ipv6_hdr.dst_addr          │ IPv6Addr(uint8=[UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9), UInt8(2),      │\n",
+       "│                            │ UInt8(0), UInt8(1), UInt8(2), UInt8(1), UInt8(2), UInt8(255), UInt8(254),          │\n",
+       "│                            │ UInt8(226), UInt8(117), UInt8(150)], uint32=[UInt32(536938040), UInt32(151126017), │\n",
+       "│                            │ UInt32(33620735), UInt32(4276254102)], uint64=[UInt64(2306131321929465857),        │\n",
+       "│                            │ UInt64(144399961568736662)])                                                       │\n",
+       "│ ipv6_hdr.dst_addr.uint8    │ [UInt8(32), UInt8(1), UInt8(6), UInt8(56), UInt8(9), UInt8(2), UInt8(0), UInt8(1), │\n",
+       "│                            │ UInt8(2), UInt8(1), UInt8(2), UInt8(255), UInt8(254), UInt8(226), UInt8(117),      │\n",
+       "│                            │ UInt8(150)]                                                                        │\n",
+       "│ ipv6_hdr.dst_addr.uint32   │ [UInt32(536938040), UInt32(151126017), UInt32(33620735), UInt32(4276254102)]       │\n",
+       "│ ipv6_hdr.dst_addr.uint64   │ [UInt64(2306131321929465857), UInt64(144399961568736662)]                          │\n",
+       "└────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"ipv6 Header\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(ipv6_hdr)\", str(len(ipv6_hdr)))\n",
+    "table.add_row(\"str(ipv6_hdr)\", str(ipv6_hdr))\n",
+    "table.add_row(\"repr(ipv6_hdr)\", repr(ipv6_hdr))\n",
+    "table.add_row(\"ipv6_hdr.vtf\", str(ipv6_hdr.vtf))\n",
+    "table.add_row(\"ipv6_hdr.vtf.version\", str(ipv6_hdr.vtf.version))\n",
+    "table.add_row(\"ipv6_hdr.vtf.traffic_class\", str(ipv6_hdr.vtf.traffic_class))\n",
+    "table.add_row(\"ipv6_hdr.vtf.flow_label\", str(ipv6_hdr.vtf.flow_label))\n",
+    "table.add_row(\"ipv6_hdr.payload_length\", str(ipv6_hdr.payload_length))\n",
+    "table.add_row(\"ipv6_hdr.next_hdr\", str(ipv6_hdr.next_hdr))\n",
+    "table.add_row(\"ipv6_hdr.hop_limit\", str(ipv6_hdr.hop_limit))\n",
+    "table.add_row(\"ipv6_hdr.src_addr\", str(ipv6_hdr.src_addr))\n",
+    "table.add_row(\"ipv6_hdr.src_addr.uint8\", str(ipv6_hdr.src_addr.uint8))\n",
+    "table.add_row(\"ipv6_hdr.src_addr.uint32\", str(ipv6_hdr.src_addr.uint32))\n",
+    "table.add_row(\"ipv6_hdr.src_addr.uint64\", str(ipv6_hdr.src_addr.uint64))\n",
+    "table.add_row(\"ipv6_hdr.dst_addr\", str(ipv6_hdr.dst_addr))\n",
+    "table.add_row(\"ipv6_hdr.dst_addr.uint8\", str(ipv6_hdr.dst_addr.uint8))\n",
+    "table.add_row(\"ipv6_hdr.dst_addr.uint32\", str(ipv6_hdr.dst_addr.uint32))\n",
+    "table.add_row(\"ipv6_hdr.dst_addr.uint64\", str(ipv6_hdr.dst_addr.uint64))\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
   }
  ],
  "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
   "language_info": {
-   "name": "python"
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
   }
  },
  "nbformat": 4,
diff --git a/docs/examples/advanced/tcp.ipynb b/docs/examples/advanced/tcp.ipynb
index 6155f75..2a08b7f 100644
--- a/docs/examples/advanced/tcp.ipynb
+++ b/docs/examples/advanced/tcp.ipynb
@@ -8,14 +8,140 @@
    ]
   },
   {
-   "cell_type": "markdown",
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.handlers.network.tcp_hdr import TCPHdr\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "tcp_hdr = TCPHdr()\n",
+    "data = bytearray(\n",
+    "    b\"\\x1e\\xc3\\x07\\xd0\\x1f\\xbc\\x16\\xd2\\x00\\x00\\x00\\x00\\x80\\x02\\xfa\\xf0\\x05\\x10\\x00\\x00\"\n",
+    ")\n",
+    "tcp_hdr.attach(memoryview(data))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
    "metadata": {},
-   "source": []
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                                tcp_hdrernet Header                                                </span>\n",
+       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name                         </span>┃<span style=\"font-weight: bold\"> Value                                                                            </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(tcp_hdr)                 │ 20                                                                               │\n",
+       "│ str(tcp_hdr)                 │ TCPHdr(src_port=UInt16(7875), dst_port=UInt16(2000),                             │\n",
+       "│                              │ seq_number=UInt32(532420306), ack_number=UInt32(0),                              │\n",
+       "│                              │ off_flag=OffFlag(data=b'\\x80\\x02'), window_size=UInt16(64240),                   │\n",
+       "│                              │ checksum=UInt16(1296), urgent_pointer=UInt16(0))                                 │\n",
+       "│ repr(tcp_hdr)                │ TCPHdr(src_port=UInt16(7875), dst_port=UInt16(2000),                             │\n",
+       "│                              │ seq_number=UInt32(532420306), ack_number=UInt32(0),                              │\n",
+       "│                              │ off_flag=OffFlag(data=b'\\x80\\x02'), window_size=UInt16(64240),                   │\n",
+       "│                              │ checksum=UInt16(1296), urgent_pointer=UInt16(0))                                 │\n",
+       "│ tcp_hdr.data                 │ bytearray(b'\\x1e\\xc3\\x07\\xd0\\x1f\\xbc\\x16\\xd2\\x00\\x00\\x00\\x00\\x80\\x02\\xfa\\xf0\\x0… │\n",
+       "│ tcp_hdr.src_port             │ 7875                                                                             │\n",
+       "│ tcp_hdr.dst_port             │ 2000                                                                             │\n",
+       "│ tcp_hdr.seq_number           │ 532420306                                                                        │\n",
+       "│ tcp_hdr.ack_number           │ 0                                                                                │\n",
+       "│ tcp_hdr.off_flag             │ 0000000101000000                                                                 │\n",
+       "│ tcp_hdr.off_flag.data_offset │ 0                                                                                │\n",
+       "│ tcp_hdr.off_flag.flags       │ 0b101000                                                                         │\n",
+       "│ tcp_hdr.window_size          │ 64240                                                                            │\n",
+       "│ tcp_hdr.checksum             │ 1296                                                                             │\n",
+       "│ tcp_hdr.urgent_pointer       │ 0                                                                                │\n",
+       "└──────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                                tcp_hdrernet Header                                                \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName                        \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue                                                                           \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(tcp_hdr)                 │ 20                                                                               │\n",
+       "│ str(tcp_hdr)                 │ TCPHdr(src_port=UInt16(7875), dst_port=UInt16(2000),                             │\n",
+       "│                              │ seq_number=UInt32(532420306), ack_number=UInt32(0),                              │\n",
+       "│                              │ off_flag=OffFlag(data=b'\\x80\\x02'), window_size=UInt16(64240),                   │\n",
+       "│                              │ checksum=UInt16(1296), urgent_pointer=UInt16(0))                                 │\n",
+       "│ repr(tcp_hdr)                │ TCPHdr(src_port=UInt16(7875), dst_port=UInt16(2000),                             │\n",
+       "│                              │ seq_number=UInt32(532420306), ack_number=UInt32(0),                              │\n",
+       "│                              │ off_flag=OffFlag(data=b'\\x80\\x02'), window_size=UInt16(64240),                   │\n",
+       "│                              │ checksum=UInt16(1296), urgent_pointer=UInt16(0))                                 │\n",
+       "│ tcp_hdr.data                 │ bytearray(b'\\x1e\\xc3\\x07\\xd0\\x1f\\xbc\\x16\\xd2\\x00\\x00\\x00\\x00\\x80\\x02\\xfa\\xf0\\x0… │\n",
+       "│ tcp_hdr.src_port             │ 7875                                                                             │\n",
+       "│ tcp_hdr.dst_port             │ 2000                                                                             │\n",
+       "│ tcp_hdr.seq_number           │ 532420306                                                                        │\n",
+       "│ tcp_hdr.ack_number           │ 0                                                                                │\n",
+       "│ tcp_hdr.off_flag             │ 0000000101000000                                                                 │\n",
+       "│ tcp_hdr.off_flag.data_offset │ 0                                                                                │\n",
+       "│ tcp_hdr.off_flag.flags       │ 0b101000                                                                         │\n",
+       "│ tcp_hdr.window_size          │ 64240                                                                            │\n",
+       "│ tcp_hdr.checksum             │ 1296                                                                             │\n",
+       "│ tcp_hdr.urgent_pointer       │ 0                                                                                │\n",
+       "└──────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"tcp_hdrernet Header\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(tcp_hdr)\", str(len(tcp_hdr)))\n",
+    "table.add_row(\"str(tcp_hdr)\", str(tcp_hdr))\n",
+    "table.add_row(\"repr(tcp_hdr)\", repr(tcp_hdr))\n",
+    "table.add_row(\"tcp_hdr.data\", str(tcp_hdr.data))\n",
+    "table.add_row(\"tcp_hdr.src_port\", str(tcp_hdr.src_port))\n",
+    "table.add_row(\"tcp_hdr.dst_port\", str(tcp_hdr.dst_port))\n",
+    "table.add_row(\"tcp_hdr.seq_number\", str(tcp_hdr.seq_number))\n",
+    "table.add_row(\"tcp_hdr.ack_number\", str(tcp_hdr.ack_number))\n",
+    "table.add_row(\"tcp_hdr.off_flag\", str(tcp_hdr.off_flag))\n",
+    "table.add_row(\"tcp_hdr.off_flag.data_offset\", str(tcp_hdr.off_flag.data_offset))\n",
+    "table.add_row(\"tcp_hdr.off_flag.flags\", bin(tcp_hdr.off_flag.flags))\n",
+    "table.add_row(\"tcp_hdr.window_size\", str(tcp_hdr.window_size))\n",
+    "table.add_row(\"tcp_hdr.checksum\", str(tcp_hdr.checksum))\n",
+    "table.add_row(\"tcp_hdr.urgent_pointer\", str(tcp_hdr.urgent_pointer))\n",
+    "\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
   }
  ],
  "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
   "language_info": {
-   "name": "python"
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
   }
  },
  "nbformat": 4,
diff --git a/docs/examples/advanced/udp.ipynb b/docs/examples/advanced/udp.ipynb
index a6c4b3f..9349c37 100644
--- a/docs/examples/advanced/udp.ipynb
+++ b/docs/examples/advanced/udp.ipynb
@@ -8,14 +8,111 @@
    ]
   },
   {
-   "cell_type": "markdown",
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.handlers.network.udp_hdr import UDPHdr\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "udp_hdr = UDPHdr()\n",
+    "data = bytearray(b\"\\x80\\x00\\x04\\xd2\\x00\\x00\\x38\\x45\")\n",
+    "udp_hdr.attach(memoryview(data))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
    "metadata": {},
-   "source": []
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                                    UDP Header                                                     </span>\n",
+       "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name             </span>┃<span style=\"font-weight: bold\"> Value                                                                                        </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(udp_hdr)     │ 8                                                                                            │\n",
+       "│ str(udp_hdr)     │ UDPHdr(src_port=UInt16(32768), dst_port=UInt16(1234), length=UInt16(0),                      │\n",
+       "│                  │ checksum=UInt16(14405))                                                                      │\n",
+       "│ repr(udp_hdr)    │ UDPHdr(src_port=UInt16(32768), dst_port=UInt16(1234), length=UInt16(0),                      │\n",
+       "│                  │ checksum=UInt16(14405))                                                                      │\n",
+       "│ udp_hdr.data     │ bytearray(b'\\x80\\x00\\x04\\xd2\\x00\\x008E')                                                     │\n",
+       "│ udp_hdr.src_port │ 32768                                                                                        │\n",
+       "│ udp_hdr.dst_port │ 1234                                                                                         │\n",
+       "│ udp_hdr.length   │ 0                                                                                            │\n",
+       "│ udp_hdr.checksum │ 14405                                                                                        │\n",
+       "└──────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                                    UDP Header                                                     \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName            \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue                                                                                       \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(udp_hdr)     │ 8                                                                                            │\n",
+       "│ str(udp_hdr)     │ UDPHdr(src_port=UInt16(32768), dst_port=UInt16(1234), length=UInt16(0),                      │\n",
+       "│                  │ checksum=UInt16(14405))                                                                      │\n",
+       "│ repr(udp_hdr)    │ UDPHdr(src_port=UInt16(32768), dst_port=UInt16(1234), length=UInt16(0),                      │\n",
+       "│                  │ checksum=UInt16(14405))                                                                      │\n",
+       "│ udp_hdr.data     │ bytearray(b'\\x80\\x00\\x04\\xd2\\x00\\x008E')                                                     │\n",
+       "│ udp_hdr.src_port │ 32768                                                                                        │\n",
+       "│ udp_hdr.dst_port │ 1234                                                                                         │\n",
+       "│ udp_hdr.length   │ 0                                                                                            │\n",
+       "│ udp_hdr.checksum │ 14405                                                                                        │\n",
+       "└──────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"UDP Header\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(udp_hdr)\", str(len(udp_hdr)))\n",
+    "table.add_row(\"str(udp_hdr)\", str(udp_hdr))\n",
+    "table.add_row(\"repr(udp_hdr)\", repr(udp_hdr))\n",
+    "table.add_row(\"udp_hdr.data\", str(udp_hdr.data))\n",
+    "table.add_row(\"udp_hdr.src_port\", str(udp_hdr.src_port))\n",
+    "table.add_row(\"udp_hdr.dst_port\", str(udp_hdr.dst_port))\n",
+    "table.add_row(\"udp_hdr.length\", str(udp_hdr.length))\n",
+    "table.add_row(\"udp_hdr.checksum\", str(udp_hdr.checksum))\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
   }
  ],
  "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
   "language_info": {
-   "name": "python"
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
   }
  },
  "nbformat": 4,
diff --git a/docs/examples/basic/bitfield.ipynb b/docs/examples/basic/bitfield.ipynb
index da55277..642ed73 100644
--- a/docs/examples/basic/bitfield.ipynb
+++ b/docs/examples/basic/bitfield.ipynb
@@ -4,7 +4,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# BitField Primitive Examples"
+    "# BitField Primitive"
    ]
   },
   {
diff --git a/docs/examples/basic/characters.ipynb b/docs/examples/basic/characters.ipynb
new file mode 100644
index 0000000..df065eb
--- /dev/null
+++ b/docs/examples/basic/characters.ipynb
@@ -0,0 +1,180 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Character Primitives"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.types.primitives.characters import UChar, SChar\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "uchar_var = UChar(\"A\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">         UChar Primitive         </span>\n",
+       "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name             </span>┃<span style=\"font-weight: bold\"> Value      </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n",
+       "│ len(uchar_var)   │ 1          │\n",
+       "│ str(uchar_var)   │ A          │\n",
+       "│ repr(uchar_var)  │ UChar('A') │\n",
+       "│ uchar_var.value  │ A          │\n",
+       "│ uchar_var.data   │ b'A'       │\n",
+       "│ uchar_var.signed │ False      │\n",
+       "└──────────────────┴────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m         UChar Primitive         \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName            \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue     \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n",
+       "│ len(uchar_var)   │ 1          │\n",
+       "│ str(uchar_var)   │ A          │\n",
+       "│ repr(uchar_var)  │ UChar('A') │\n",
+       "│ uchar_var.value  │ A          │\n",
+       "│ uchar_var.data   │ b'A'       │\n",
+       "│ uchar_var.signed │ False      │\n",
+       "└──────────────────┴────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"UChar Primitive\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(uchar_var)\", str(len(uchar_var)))\n",
+    "table.add_row(\"str(uchar_var)\", str(uchar_var))\n",
+    "table.add_row(\"repr(uchar_var)\", repr(uchar_var))\n",
+    "table.add_row(\"uchar_var.value\", str(uchar_var.value))\n",
+    "table.add_row(\"uchar_var.data\", str(uchar_var.data))\n",
+    "table.add_row(\"uchar_var.signed\", str(uchar_var.signed))\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "schar_var = SChar(65)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">        SChar Primitive         </span>\n",
+       "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name             </span>┃<span style=\"font-weight: bold\"> Value     </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩\n",
+       "│ len(schar_var)   │ 1         │\n",
+       "│ str(schar_var)   │ 65        │\n",
+       "│ repr(schar_var)  │ SChar(65) │\n",
+       "│ schar_var.value  │ 65        │\n",
+       "│ schar_var.data   │ b'A'      │\n",
+       "│ schar_var.signed │ True      │\n",
+       "└──────────────────┴───────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m        SChar Primitive         \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName            \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue    \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩\n",
+       "│ len(schar_var)   │ 1         │\n",
+       "│ str(schar_var)   │ 65        │\n",
+       "│ repr(schar_var)  │ SChar(65) │\n",
+       "│ schar_var.value  │ 65        │\n",
+       "│ schar_var.data   │ b'A'      │\n",
+       "│ schar_var.signed │ True      │\n",
+       "└──────────────────┴───────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"SChar Primitive\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(schar_var)\", str(len(schar_var)))\n",
+    "table.add_row(\"str(schar_var)\", str(schar_var))\n",
+    "table.add_row(\"repr(schar_var)\", repr(schar_var))\n",
+    "table.add_row(\"schar_var.value\", str(schar_var.value))\n",
+    "table.add_row(\"schar_var.data\", str(schar_var.data))\n",
+    "table.add_row(\"schar_var.signed\", str(schar_var.signed))\n",
+    "\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/examples/basic/fixed_array.ipynb b/docs/examples/basic/fixed_array.ipynb
index 23e80a4..2330693 100644
--- a/docs/examples/basic/fixed_array.ipynb
+++ b/docs/examples/basic/fixed_array.ipynb
@@ -4,7 +4,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# FixeArray Byteclass Collection Examples"
+    "# FixeArray Collection"
    ]
   },
   {
@@ -36,7 +36,7 @@
     }
    ],
    "source": [
-    "fa1 = FixedArray(UInt16,8)\n",
+    "fa1 = FixedArray(8, UInt16)\n",
     "print(fa1)\n",
     "print(bytes(fa1))\n",
     "fa1[1] = 0xaaaa\n",
@@ -66,7 +66,7 @@
     }
    ],
    "source": [
-    "fa2 = FixedArray(Float32, 8)\n",
+    "fa2 = FixedArray(8, Float32)\n",
     "print(fa2)\n",
     "print(bytes(fa2))\n",
     "fa2[1] = 0xAAAA\n",
diff --git a/docs/examples/basic/generics.ipynb b/docs/examples/basic/generics.ipynb
new file mode 100644
index 0000000..fc52b49
--- /dev/null
+++ b/docs/examples/basic/generics.ipynb
@@ -0,0 +1,116 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Generic Primitives"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.types.primitives.generics import Bit, Byte, Word, DWord, QWord\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "bit = Bit(True)\n",
+    "byte = Byte(b\"\\x11\")\n",
+    "word = Word(b\"\\x11\\x22\")\n",
+    "dword = DWord(b\"\\x11\\x22\\x33\\x44\")\n",
+    "qword = QWord(b\"\\x11\\x22\\x33\\x44\\x55\\x66\\x77\\x88\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                           Generic Primitives                                           </span>\n",
+       "┏━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name  </span>┃<span style=\"font-weight: bold\"> len </span>┃<span style=\"font-weight: bold\"> str               </span>┃<span style=\"font-weight: bold\"> repr                                           </span>┃<span style=\"font-weight: bold\"> value/data        </span>┃\n",
+       "┡━━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ Byte  │ 1   │ b'\\x11'           │ Byte(data=b'\\x11', byte_order=b'@')            │ b'\\x11'           │\n",
+       "│       │     │                   │                                                │ b'\\x11'           │\n",
+       "│ Word  │ 2   │ b'\\x11\"'          │ Word(data=b'\\x11\"', byte_order=b'@')           │ b'\\x11\"'          │\n",
+       "│       │     │                   │                                                │ b'\\x11\"'          │\n",
+       "│ DWord │ 4   │ b'\\x11\"3D'        │ DWord(data=b'\\x11\"3D', byte_order=b'@')        │ b'\\x11\"3D'        │\n",
+       "│       │     │                   │                                                │ b'\\x11\"3D'        │\n",
+       "│ QWord │ 8   │ b'\\x11\"3DUfw\\x88' │ QWord(data=b'\\x11\"3DUfw\\x88', byte_order=b'@') │ b'\\x11\"3DUfw\\x88' │\n",
+       "│       │     │                   │                                                │ b'\\x11\"3DUfw\\x88' │\n",
+       "└───────┴─────┴───────────────────┴────────────────────────────────────────────────┴───────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                           Generic Primitives                                           \u001b[0m\n",
+       "┏━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mlen\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mstr              \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mrepr                                          \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mvalue/data       \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━╇━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ Byte  │ 1   │ b'\\x11'           │ Byte(data=b'\\x11', byte_order=b'@')            │ b'\\x11'           │\n",
+       "│       │     │                   │                                                │ b'\\x11'           │\n",
+       "│ Word  │ 2   │ b'\\x11\"'          │ Word(data=b'\\x11\"', byte_order=b'@')           │ b'\\x11\"'          │\n",
+       "│       │     │                   │                                                │ b'\\x11\"'          │\n",
+       "│ DWord │ 4   │ b'\\x11\"3D'        │ DWord(data=b'\\x11\"3D', byte_order=b'@')        │ b'\\x11\"3D'        │\n",
+       "│       │     │                   │                                                │ b'\\x11\"3D'        │\n",
+       "│ QWord │ 8   │ b'\\x11\"3DUfw\\x88' │ QWord(data=b'\\x11\"3DUfw\\x88', byte_order=b'@') │ b'\\x11\"3DUfw\\x88' │\n",
+       "│       │     │                   │                                                │ b'\\x11\"3DUfw\\x88' │\n",
+       "└───────┴─────┴───────────────────┴────────────────────────────────────────────────┴───────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"Generic Primitives\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"len\")\n",
+    "table.add_column(\"str\")\n",
+    "table.add_column(\"repr\")\n",
+    "table.add_column(\"value/data\")\n",
+    "\n",
+    "table.add_row(\"Byte\", str(len(byte)), str(byte), repr(byte), str(byte.value) + \"\\n\" + str(byte.data))\n",
+    "table.add_row(\"Word\", str(len(word)), str(word), repr(word), str(word.value) + \"\\n\" + str(word.data))\n",
+    "table.add_row(\"DWord\", str(len(dword)), str(dword), repr(dword), str(dword.value) + \"\\n\" + str(dword.data))\n",
+    "table.add_row(\"QWord\", str(len(qword)), str(qword), repr(qword), str(qword.value) + \"\\n\" + str(qword.data))\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/examples/basic/index.rst b/docs/examples/basic/index.rst
index 7d67fea..a73117d 100644
--- a/docs/examples/basic/index.rst
+++ b/docs/examples/basic/index.rst
@@ -4,7 +4,10 @@ Basic Examples
 .. toctree::
 
    primitives
+   characters
+   generics
    bitfield
    fixed_array
-   union
+   string
    structure
+   union
diff --git a/docs/examples/basic/primitives.ipynb b/docs/examples/basic/primitives.ipynb
index b294542..4850653 100644
--- a/docs/examples/basic/primitives.ipynb
+++ b/docs/examples/basic/primitives.ipynb
@@ -4,7 +4,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# Fixed Size Primitive Examples"
+    "# Integer and Float Primitives"
    ]
   },
   {
@@ -16,12 +16,12 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
     "from byteclasses.types.primitives.floats import Half, Float, Float16, Float32, Float64, Double\n",
-    "from byteclasses.types.primitives.integers import Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Byte, Short, UShort, Long, LongLong, ULong, ULongLong, SChar, UChar, Int, UInt, Word, DWord"
+    "from byteclasses.types.primitives.integers import Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Short, UShort, Long, LongLong, ULong, ULongLong, Int, UInt"
    ]
   },
   {
@@ -33,14 +33,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [],
    "source": [
     "from itertools import chain\n",
     "\n",
     "fixed_integer_types = [Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Long, ULong]\n",
-    "fixed_integer_type_aliases = [Byte, SChar, UChar, Short, UShort, Word, Int, UInt, DWord, LongLong, ULongLong]\n",
+    "fixed_integer_type_aliases = [Short, UShort, Int, UInt, LongLong, ULongLong]\n",
     "fixed_float_types = [Float16, Float32, Float64]\n",
     "fixed_float_type_aliases = [Half, Float, Double]\n",
     "\n",
@@ -55,7 +55,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
@@ -75,17 +75,12 @@
        "│<span style=\"color: #008080; text-decoration-color: #008080\">  UInt64(7) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    7     </span>│      8      │ 0                    │ 18446744073709551615 │ 7     │ b'\\x07\\x00\\x00\\x00… │\n",
        "│<span style=\"color: #008080; text-decoration-color: #008080\">    Long(8) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    8     </span>│      8      │ -9223372036854775808 │ 9223372036854775807  │ 8     │ b'\\x08\\x00\\x00\\x00… │\n",
        "│<span style=\"color: #008080; text-decoration-color: #008080\">   ULong(9) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    9     </span>│      8      │ 0                    │ 18446744073709551615 │ 9     │ b'\\t\\x00\\x00\\x00\\x… │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\">  UInt8(10) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    10    </span>│      1      │ 0                    │ 255                  │ 10    │ b'\\n'               │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\">   Int8(11) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    11    </span>│      1      │ -128                 │ 127                  │ 11    │ b'\\x0b'             │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\">  UInt8(12) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    12    </span>│      1      │ 0                    │ 255                  │ 12    │ b'\\x0c'             │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\">  Int16(13) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    13    </span>│      2      │ -32768               │ 32767                │ 13    │ b'\\r\\x00'           │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt16(14) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    14    </span>│      2      │ 0                    │ 65535                │ 14    │ b'\\x0e\\x00'         │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt16(15) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    15    </span>│      2      │ 0                    │ 65535                │ 15    │ b'\\x0f\\x00'         │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\">  Int32(16) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    16    </span>│      4      │ -2147483648          │ 2147483647           │ 16    │ b'\\x10\\x00\\x00\\x00' │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt32(17) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    17    </span>│      4      │ 0                    │ 4294967295           │ 17    │ b'\\x11\\x00\\x00\\x00' │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt32(18) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    18    </span>│      4      │ 0                    │ 4294967295           │ 18    │ b'\\x12\\x00\\x00\\x00' │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\">  Int64(19) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    19    </span>│      8      │ -9223372036854775808 │ 9223372036854775807  │ 19    │ b'\\x13\\x00\\x00\\x00… │\n",
-       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt64(20) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    20    </span>│      8      │ 0                    │ 18446744073709551615 │ 20    │ b'\\x14\\x00\\x00\\x00… │\n",
+       "│<span style=\"color: #008080; text-decoration-color: #008080\">  Int16(10) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    10    </span>│      2      │ -32768               │ 32767                │ 10    │ b'\\n\\x00'           │\n",
+       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt16(11) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    11    </span>│      2      │ 0                    │ 65535                │ 11    │ b'\\x0b\\x00'         │\n",
+       "│<span style=\"color: #008080; text-decoration-color: #008080\">  Int32(12) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    12    </span>│      4      │ -2147483648          │ 2147483647           │ 12    │ b'\\x0c\\x00\\x00\\x00' │\n",
+       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt32(13) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    13    </span>│      4      │ 0                    │ 4294967295           │ 13    │ b'\\r\\x00\\x00\\x00'   │\n",
+       "│<span style=\"color: #008080; text-decoration-color: #008080\">  Int64(14) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    14    </span>│      8      │ -9223372036854775808 │ 9223372036854775807  │ 14    │ b'\\x0e\\x00\\x00\\x00… │\n",
+       "│<span style=\"color: #008080; text-decoration-color: #008080\"> UInt64(15) </span>│<span style=\"color: #800080; text-decoration-color: #800080\">    15    </span>│      8      │ 0                    │ 18446744073709551615 │ 15    │ b'\\x0f\\x00\\x00\\x00… │\n",
        "└────────────┴──────────┴─────────────┴──────────────────────┴──────────────────────┴───────┴─────────────────────┘\n",
        "</pre>\n"
       ],
@@ -104,17 +99,12 @@
        "│\u001b[36m \u001b[0m\u001b[36m UInt64(7)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   7    \u001b[0m\u001b[35m \u001b[0m│      8      │ 0                    │ 18446744073709551615 │ 7     │ b'\\x07\\x00\\x00\\x00… │\n",
        "│\u001b[36m \u001b[0m\u001b[36m   Long(8)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   8    \u001b[0m\u001b[35m \u001b[0m│      8      │ -9223372036854775808 │ 9223372036854775807  │ 8     │ b'\\x08\\x00\\x00\\x00… │\n",
        "│\u001b[36m \u001b[0m\u001b[36m  ULong(9)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   9    \u001b[0m\u001b[35m \u001b[0m│      8      │ 0                    │ 18446744073709551615 │ 9     │ b'\\t\\x00\\x00\\x00\\x… │\n",
-       "│\u001b[36m \u001b[0m\u001b[36m UInt8(10)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   10   \u001b[0m\u001b[35m \u001b[0m│      1      │ 0                    │ 255                  │ 10    │ b'\\n'               │\n",
-       "│\u001b[36m \u001b[0m\u001b[36m  Int8(11)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   11   \u001b[0m\u001b[35m \u001b[0m│      1      │ -128                 │ 127                  │ 11    │ b'\\x0b'             │\n",
-       "│\u001b[36m \u001b[0m\u001b[36m UInt8(12)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   12   \u001b[0m\u001b[35m \u001b[0m│      1      │ 0                    │ 255                  │ 12    │ b'\\x0c'             │\n",
-       "│\u001b[36m \u001b[0m\u001b[36m Int16(13)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   13   \u001b[0m\u001b[35m \u001b[0m│      2      │ -32768               │ 32767                │ 13    │ b'\\r\\x00'           │\n",
-       "│\u001b[36m \u001b[0m\u001b[36mUInt16(14)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   14   \u001b[0m\u001b[35m \u001b[0m│      2      │ 0                    │ 65535                │ 14    │ b'\\x0e\\x00'         │\n",
-       "│\u001b[36m \u001b[0m\u001b[36mUInt16(15)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   15   \u001b[0m\u001b[35m \u001b[0m│      2      │ 0                    │ 65535                │ 15    │ b'\\x0f\\x00'         │\n",
-       "│\u001b[36m \u001b[0m\u001b[36m Int32(16)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   16   \u001b[0m\u001b[35m \u001b[0m│      4      │ -2147483648          │ 2147483647           │ 16    │ b'\\x10\\x00\\x00\\x00' │\n",
-       "│\u001b[36m \u001b[0m\u001b[36mUInt32(17)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   17   \u001b[0m\u001b[35m \u001b[0m│      4      │ 0                    │ 4294967295           │ 17    │ b'\\x11\\x00\\x00\\x00' │\n",
-       "│\u001b[36m \u001b[0m\u001b[36mUInt32(18)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   18   \u001b[0m\u001b[35m \u001b[0m│      4      │ 0                    │ 4294967295           │ 18    │ b'\\x12\\x00\\x00\\x00' │\n",
-       "│\u001b[36m \u001b[0m\u001b[36m Int64(19)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   19   \u001b[0m\u001b[35m \u001b[0m│      8      │ -9223372036854775808 │ 9223372036854775807  │ 19    │ b'\\x13\\x00\\x00\\x00… │\n",
-       "│\u001b[36m \u001b[0m\u001b[36mUInt64(20)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   20   \u001b[0m\u001b[35m \u001b[0m│      8      │ 0                    │ 18446744073709551615 │ 20    │ b'\\x14\\x00\\x00\\x00… │\n",
+       "│\u001b[36m \u001b[0m\u001b[36m Int16(10)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   10   \u001b[0m\u001b[35m \u001b[0m│      2      │ -32768               │ 32767                │ 10    │ b'\\n\\x00'           │\n",
+       "│\u001b[36m \u001b[0m\u001b[36mUInt16(11)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   11   \u001b[0m\u001b[35m \u001b[0m│      2      │ 0                    │ 65535                │ 11    │ b'\\x0b\\x00'         │\n",
+       "│\u001b[36m \u001b[0m\u001b[36m Int32(12)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   12   \u001b[0m\u001b[35m \u001b[0m│      4      │ -2147483648          │ 2147483647           │ 12    │ b'\\x0c\\x00\\x00\\x00' │\n",
+       "│\u001b[36m \u001b[0m\u001b[36mUInt32(13)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   13   \u001b[0m\u001b[35m \u001b[0m│      4      │ 0                    │ 4294967295           │ 13    │ b'\\r\\x00\\x00\\x00'   │\n",
+       "│\u001b[36m \u001b[0m\u001b[36m Int64(14)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   14   \u001b[0m\u001b[35m \u001b[0m│      8      │ -9223372036854775808 │ 9223372036854775807  │ 14    │ b'\\x0e\\x00\\x00\\x00… │\n",
+       "│\u001b[36m \u001b[0m\u001b[36mUInt64(15)\u001b[0m\u001b[36m \u001b[0m│\u001b[35m \u001b[0m\u001b[35m   15   \u001b[0m\u001b[35m \u001b[0m│      8      │ 0                    │ 18446744073709551615 │ 15    │ b'\\x0f\\x00\\x00\\x00… │\n",
        "└────────────┴──────────┴─────────────┴──────────────────────┴──────────────────────┴───────┴─────────────────────┘\n"
       ]
      },
@@ -199,7 +189,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
@@ -216,7 +206,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [
     {
@@ -233,7 +223,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [
     {
@@ -257,7 +247,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
@@ -291,7 +281,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [
     {
@@ -319,7 +309,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
@@ -345,7 +335,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [
     {
@@ -370,7 +360,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [
     {
@@ -395,7 +385,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [
     {
diff --git a/docs/examples/basic/string.ipynb b/docs/examples/basic/string.ipynb
new file mode 100644
index 0000000..368c04e
--- /dev/null
+++ b/docs/examples/basic/string.ipynb
@@ -0,0 +1,134 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# String Collection"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from byteclasses.types.collections import String\n",
+    "from rich.console import Console\n",
+    "from rich.table import Table"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "string = String(32, value=\"my string collection\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                                             Generic String Collection                                             </span>\n",
+       "┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃<span style=\"font-weight: bold\"> Name              </span>┃<span style=\"font-weight: bold\"> Value                                                                                       </span>┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(string)       │ 32                                                                                          │\n",
+       "│ len(string.value) │ 20                                                                                          │\n",
+       "│ str(string)       │ my string collection                                                                        │\n",
+       "│ repr(string)      │ String(32, value='my string collection')                                                    │\n",
+       "│ string.value      │ my string collection                                                                        │\n",
+       "│ string.data       │ b'my string collection\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'                     │\n",
+       "│ string[:]         │ [UChar('m'), UChar('y'), UChar(' '), UChar('s'), UChar('t'), UChar('r'), UChar('i'),        │\n",
+       "│                   │ UChar('n'), UChar('g'), UChar(' '), UChar('c'), UChar('o'), UChar('l'), UChar('l'),         │\n",
+       "│                   │ UChar('e'), UChar('c'), UChar('t'), UChar('i'), UChar('o'), UChar('n'), UChar('\\x00'),      │\n",
+       "│                   │ UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'),   │\n",
+       "│                   │ UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00')]                  │\n",
+       "│ string[::2]       │ [UChar('m'), UChar(' '), UChar('t'), UChar('i'), UChar('g'), UChar('c'), UChar('l'),        │\n",
+       "│                   │ UChar('e'), UChar('t'), UChar('o'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'),            │\n",
+       "│                   │ UChar('\\x00'), UChar('\\x00'), UChar('\\x00')]                                                │\n",
+       "│ string[0]         │ m                                                                                           │\n",
+       "│ string[0].value   │ m                                                                                           │\n",
+       "│ string[0].data    │ b'm'                                                                                        │\n",
+       "└───────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘\n",
+       "</pre>\n"
+      ],
+      "text/plain": [
+       "\u001b[3m                                             Generic String Collection                                             \u001b[0m\n",
+       "┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
+       "┃\u001b[1m \u001b[0m\u001b[1mName             \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mValue                                                                                      \u001b[0m\u001b[1m \u001b[0m┃\n",
+       "┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
+       "│ len(string)       │ 32                                                                                          │\n",
+       "│ len(string.value) │ 20                                                                                          │\n",
+       "│ str(string)       │ my string collection                                                                        │\n",
+       "│ repr(string)      │ String(32, value='my string collection')                                                    │\n",
+       "│ string.value      │ my string collection                                                                        │\n",
+       "│ string.data       │ b'my string collection\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'                     │\n",
+       "│ string[:]         │ [UChar('m'), UChar('y'), UChar(' '), UChar('s'), UChar('t'), UChar('r'), UChar('i'),        │\n",
+       "│                   │ UChar('n'), UChar('g'), UChar(' '), UChar('c'), UChar('o'), UChar('l'), UChar('l'),         │\n",
+       "│                   │ UChar('e'), UChar('c'), UChar('t'), UChar('i'), UChar('o'), UChar('n'), UChar('\\x00'),      │\n",
+       "│                   │ UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'),   │\n",
+       "│                   │ UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00')]                  │\n",
+       "│ string[::2]       │ [UChar('m'), UChar(' '), UChar('t'), UChar('i'), UChar('g'), UChar('c'), UChar('l'),        │\n",
+       "│                   │ UChar('e'), UChar('t'), UChar('o'), UChar('\\x00'), UChar('\\x00'), UChar('\\x00'),            │\n",
+       "│                   │ UChar('\\x00'), UChar('\\x00'), UChar('\\x00')]                                                │\n",
+       "│ string[0]         │ m                                                                                           │\n",
+       "│ string[0].value   │ m                                                                                           │\n",
+       "│ string[0].data    │ b'm'                                                                                        │\n",
+       "└───────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────┘\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "table = Table(title=\"Generic String Collection\")\n",
+    "\n",
+    "table.add_column(\"Name\")\n",
+    "table.add_column(\"Value\")\n",
+    "\n",
+    "table.add_row(\"len(string)\", str(len(string)))\n",
+    "table.add_row(\"len(string.value)\", str(len(string.value)))\n",
+    "table.add_row(\"str(string)\", str(string))\n",
+    "table.add_row(\"repr(string)\", repr(string))\n",
+    "table.add_row(\"string.value\", str(string.value))\n",
+    "table.add_row(\"string.data\", str(string.data))\n",
+    "table.add_row(\"string[:]\", str(string[:]))\n",
+    "table.add_row(\"string[::2]\", str(string[::2]))\n",
+    "table.add_row(\"string[0]\", str(string[0]))\n",
+    "table.add_row(\"string[0].value\", str(string[0].value))\n",
+    "table.add_row(\"string[0].data\", str(string[0].data))\n",
+    "console = Console()\n",
+    "console.print(table)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": ".venv",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/examples/basic/structure.ipynb b/docs/examples/basic/structure.ipynb
index 7514655..cc20ccc 100644
--- a/docs/examples/basic/structure.ipynb
+++ b/docs/examples/basic/structure.ipynb
@@ -4,7 +4,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# Structure Byteclass Collection Examples"
+    "# Structure Collection"
    ]
   },
   {
@@ -16,7 +16,9 @@
     "from typing import ClassVar\n",
     "from byteclasses import ByteOrder\n",
     "from byteclasses.types.collections import FixedSizeCollection, structure, member\n",
-    "from byteclasses.types.primitives.integers import Int8, UInt8, UInt16, UInt32, UChar, Byte\n",
+    "from byteclasses.types.primitives.characters import UChar\n",
+    "from byteclasses.types.primitives.generics import Byte\n",
+    "from byteclasses.types.primitives.integers import Int8, UInt8, UInt16, UInt32\n",
     "from byteclasses.types.primitives.floats import Float32"
    ]
   },
@@ -103,19 +105,19 @@
      "output_type": "stream",
      "text": [
       "13\n",
-      "SimpleStruct(a=UInt8(0), b=UInt16(0), c=UInt8(0), d=Float32(1.100000023841858), e=UInt8(0))\n",
-      "bytearray(b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xcd\\xcc\\x8c?\\x00')\n"
+      "SimpleStruct(a=UInt8(0), b=UInt16(0), c=UChar('\\x00'), d=Float32(0.0), e=UInt8(0))\n",
+      "bytearray(b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00')\n"
      ]
     }
    ],
    "source": [
-    "@structure(byte_order=\"!\")\n",
+    "@structure(byte_order=b\"!\")\n",
     "class SimpleStruct():\n",
-    "    a:UInt8 = UInt8()\n",
-    "    b:UInt16 = UInt16()\n",
-    "    c:UChar = UChar()\n",
-    "    d:Float32 = Float32(1.1)\n",
-    "    e: UInt8 = member(default_factory=UInt8)\n",
+    "    a:UInt8\n",
+    "    b:UInt16\n",
+    "    c:UChar\n",
+    "    d:Float32\n",
+    "    e: UInt8\n",
     "ss1: FixedSizeCollection = SimpleStruct()\n",
     "print(len(ss1))\n",
     "print(ss1)\n",
@@ -160,7 +162,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "SimpleStruct(a=UInt8(1), b=UInt16(2), c=UInt8(3), d=Float32(4.0), e=UInt8(0))\n",
+      "SimpleStruct(a=UInt8(1), b=UInt16(512), c=UInt8(3), d=Float32(4.600743118071239e-41), e=UInt8(0))\n",
       "bytearray(b'\\x01\\x00\\x02\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x80@\\x00')\n"
      ]
     }
@@ -187,15 +189,15 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "NATIVE\n",
+      "NET\n",
       "b'\\x01'\n",
       "bytearray(b'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xcd\\xcc\\x8c?\\x00')\n",
-      "b'\\x02\\x00'\n",
-      "bytearray(b'\\x01\\x00\\x02\\x00\\x00\\x00\\x00\\x00\\xcd\\xcc\\x8c?\\x00')\n",
-      "b'\\x03'\n",
-      "bytearray(b'\\x01\\x00\\x02\\x00\\x03\\x00\\x00\\x00\\xcd\\xcc\\x8c?\\x00')\n",
-      "b'\\x00\\x00\\x80@'\n",
-      "bytearray(b'\\x01\\x00\\x02\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x80@\\x00')\n"
+      "b'\\x00\\x02'\n",
+      "bytearray(b'\\x01\\x00\\x00\\x02\\x00\\x00\\x00\\x00\\xcd\\xcc\\x8c?\\x00')\n",
+      "b'c'\n",
+      "bytearray(b'\\x01\\x00\\x00\\x02c\\x00\\x00\\x00\\xcd\\xcc\\x8c?\\x00')\n",
+      "b'@\\x80\\x00\\x00'\n",
+      "bytearray(b'\\x01\\x00\\x00\\x02c\\x00\\x00\\x00@\\x80\\x00\\x00\\x00')\n"
      ]
     }
    ],
@@ -207,7 +209,7 @@
     "ss1.b = 2\n",
     "print(ss1.b.data)\n",
     "print(ss1.data)\n",
-    "ss1.c = 3\n",
+    "ss1.c = \"c\"\n",
     "print(ss1.c.data)\n",
     "print(ss1.data)\n",
     "ss1.d = 4\n",
@@ -231,8 +233,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "bytearray(b'\\x01\\x06\\x07\\x08\\x03\\x00\\x00\\x00\\x00\\x00\\x80@\\x00')\n",
-      "SimpleStruct(a=UInt8(1), b=UInt16(2055), c=UInt8(3), d=Float32(4.0), e=UInt8(0))\n"
+      "bytearray(b'\\x01\\x06\\x07\\x08c\\x00\\x00\\x00@\\x80\\x00\\x00\\x00')\n",
+      "SimpleStruct(a=UInt8(1), b=UInt16(1800), c=UChar('c'), d=Float32(4.0), e=UInt8(0))\n"
      ]
     }
    ],
@@ -290,8 +292,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "OuterStruct(var4=Int8(0), var5=UInt32(65535), var6=Float32(1.100000023841858), var7=InnerStruct(var2=UInt8(0)))\n",
-      "InnerStruct(var2=UInt8(0))\n",
+      "OuterStruct(var4=Int8(0), var5=UInt32(65535), var6=Float32(1.100000023841858), var7=InnerStruct(var2=Byte(data=b'\\x00', byte_order=b'@')))\n",
+      "InnerStruct(var2=Byte(data=b'\\x00', byte_order=b'@'))\n",
       "b'\\x00\\xff\\xff\\x00\\x00\\xcd\\xcc\\x8c?\\x00'\n"
      ]
     }
@@ -319,7 +321,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "OuterStruct(var4=Int8(1), var5=UInt32(65535), var6=Float32(1.100000023841858), var7=InnerStruct(var2=UInt8(0)))\n",
+      "OuterStruct(var4=Int8(1), var5=UInt32(65535), var6=Float32(1.100000023841858), var7=InnerStruct(var2=Byte(data=b'\\x00', byte_order=b'@')))\n",
       "bytearray(b'\\x01\\xff\\xff\\x00\\x00\\xcd\\xcc\\x8c?\\x00')\n"
      ]
     }
@@ -346,7 +348,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "OuterStruct(var4=Int8(-7), var5=UInt32(4126603256), var6=Float32(-2.4060893954673178e+30), var7=InnerStruct(var2=UInt8(240)))\n",
+      "OuterStruct(var4=Int8(-7), var5=UInt32(4126603256), var6=Float32(-2.4060893954673178e+30), var7=InnerStruct(var2=Byte(data=b'\\xf0', byte_order=b'@')))\n",
       "bytearray(b'\\xf9\\xf8\\xf7\\xf6\\xf5\\xf4\\xf3\\xf2\\xf1\\xf0')\n"
      ]
     }
@@ -373,7 +375,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "OuterStruct(var4=Int8(-7), var5=UInt32(134744312), var6=Float32(-2.4060893954673178e+30), var7=InnerStruct(var2=UInt8(240)))\n",
+      "OuterStruct(var4=Int8(-7), var5=UInt32(134744312), var6=Float32(-2.4060893954673178e+30), var7=InnerStruct(var2=Byte(data=b'\\xf0', byte_order=b'@')))\n",
       "bytearray(b'\\xf9\\xf8\\x08\\x08\\x08\\xf4\\xf3\\xf2\\xf1\\xf0')\n"
      ]
     }
diff --git a/docs/examples/basic/union.ipynb b/docs/examples/basic/union.ipynb
index 71b6d6f..df0899e 100644
--- a/docs/examples/basic/union.ipynb
+++ b/docs/examples/basic/union.ipynb
@@ -4,12 +4,12 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "# Union Byteclass Collection Examples"
+    "# Union Collection"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 1,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -26,7 +26,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [
     {
@@ -57,7 +57,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 3,
    "metadata": {},
    "outputs": [
     {
@@ -65,7 +65,7 @@
      "output_type": "stream",
      "text": [
       "len(lu)=8\n",
-      "LargeUnion(uint64=UInt64(18014948265296384), uint32=UInt32(4194432), uint16_be=UInt16(64), uint16_le=UInt16(16384), int16=Int16(64), uint8=UInt8(0), int8=Int8(0))\n"
+      "LargeUnion(uint64=UInt64(18014948265296384), uint32=UInt32(4194432), uint16_be=UInt16(64), uint16_le=UInt16(64), int16=Int16(64), uint8=UInt8(0), int8=Int8(0))\n"
      ]
     }
    ],
@@ -89,7 +89,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [
     {
@@ -116,7 +116,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [
     {
@@ -124,7 +124,7 @@
      "output_type": "stream",
      "text": [
       "uint16_be: b'\\xfa\\x00' 64000\n",
-      "uint16_le: b'\\xfa\\x00' 250\n"
+      "uint16_le: b'\\xfa\\x00' 64000\n"
      ]
     }
    ],
diff --git a/docs/getting_started.md b/docs/getting_started.md
index 5fc1354..9ded1e6 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -9,26 +9,30 @@ The components provided by this package can be grouped into two basic categories
 
 Primitive Types - The primitive types provide the base level elements used to compose larger and more complex data structures.
 
+* `generics` - Generic fixed length types.
+  * `Byte`, `Word`, `DWord`, `QWord`
+* `bitfield` - A generic primitive for easily extracting bit values from a byte. Fixed length is one (1) byte by default. Can be subclassed for multibyte bitfields.
+  * `BitField`, `BitPos`
 * `integers` - All integer primitives have a fixed length with a configurable `byte_order`.
-  * `Int8`, `UInt8`, `Int16`, `UInt16`, `Int32`, `Long`, `ULong`, `Int64`, `UInt64`
-  * Aliases: `SChar`, `UChar`, `Byte`, `Short`, `UShort`, `Word`, `Int`, `UInt`, `DWord`, `LongLong`, `ULongLong`
-  * Special: `Bit`
+  * `Int8`, `UInt8`, `Int16` (`Short`), `UInt16` (`UShort`), `Int32` (`Int`), `UInt32` (`UInt`), `Long`, `ULong`, `Int64` (`LongLong`), `UInt64` (`ULongLong`)
 * `floats` - All float primitives have a fixed length with a configurable `byte_order`.
-  * `Float16`, `Float32`, `Float64`
-  * Aliases: `Half`, `Float`, `Double`
-* `bitfield` - A generic primitive for easily extracting bit values from a byte. Fixed length is one (1) byte by default. Can be subclassed for multibyte bitfields.
-  * `BitField`
+  * `Float16` (`Half`), `Float32` (`Float`), `Float64` (`Double`)
+* `characters` - Single byte character classes.
+  * `UChar` (`Char`), `SChar`
 
 ## `collections`
 
 Collection Types - Collection types allow you to combine primitives or other collections based on their specified behavior.
 
 * `FixedArray` - A concrete fixed size collection class containing a single primitive type or specified quantity.
-* `Union` - A dynamic byteclass implemented via the `@union` decorator. Behaves similar to a C `union`.
+* `String` - A specialized subclass of FixedArray to add string convenience methods.
 * `Structure` - A dynamic byteclass implemented via the `@structure` decorator. Behaves similar to a C `struct`.
+* `Union` - A dynamic byteclass implemented via the `@union` decorator. Behaves similar to a C `union`.
 
 ## Attaching external data
 
 Byteclasses can be attached to any bytes like external data.
-Use the byteclass `.attach(mv)` method to connect a `memoryview` of the external data to the internal data attribute of the byteclass instance.
+Use the byteclass `.attach(data)` method to connect a `memoryview` of the external data to the internal data attribute of the byteclass instance.
 > The length of the `memoryview` must match the length of the item it is being attached to.
+
+> If a `bytes` or `bytearray` object is passed to `attach`, a `bytearray` `memoryview` will be created. If you intend for byteclasses to all interact with a single data instance, create a `memoryview` of your data and pass to appropriate slice of the `memoryview` to `attach`.
diff --git a/docs/index.rst b/docs/index.rst
index 8200e15..0b2d5b6 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -4,7 +4,7 @@ Welcome to Byteclasses's documentation!
 A Python package for handling binary data using dataclass like classes.
 
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
    :caption: Documentation:
 
    getting_started
diff --git a/poetry.lock b/poetry.lock
index ee07e29..53c5627 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -134,33 +134,33 @@ lxml = ["lxml"]
 
 [[package]]
 name = "black"
-version = "24.3.0"
+version = "24.4.0"
 description = "The uncompromising code formatter."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
-    {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
-    {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
-    {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
-    {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
-    {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
-    {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
-    {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
-    {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
-    {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
-    {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
-    {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
-    {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
-    {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
-    {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
-    {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
-    {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
-    {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
-    {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
-    {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
-    {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
-    {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
+    {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"},
+    {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"},
+    {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"},
+    {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"},
+    {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"},
+    {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"},
+    {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"},
+    {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"},
+    {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"},
+    {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"},
+    {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"},
+    {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"},
+    {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"},
+    {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"},
+    {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"},
+    {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"},
+    {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"},
+    {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"},
+    {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"},
+    {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"},
+    {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"},
+    {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"},
 ]
 
 [package.dependencies]
@@ -196,6 +196,52 @@ webencodings = "*"
 [package.extras]
 css = ["tinycss2 (>=1.1.0,<1.3)"]
 
+[[package]]
+name = "build"
+version = "1.2.1"
+description = "A simple, correct Python build frontend"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "build-1.2.1-py3-none-any.whl", hash = "sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4"},
+    {file = "build-1.2.1.tar.gz", hash = "sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "os_name == \"nt\""}
+importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""}
+packaging = ">=19.1"
+pyproject_hooks = "*"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+
+[package.extras]
+docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"]
+test = ["build[uv,virtualenv]", "filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"]
+typing = ["build[uv]", "importlib-metadata (>=5.1)", "mypy (>=1.9.0,<1.10.0)", "tomli", "typing-extensions (>=3.7.4.3)"]
+uv = ["uv (>=0.1.18)"]
+virtualenv = ["virtualenv (>=20.0.35)"]
+
+[[package]]
+name = "cachecontrol"
+version = "0.14.0"
+description = "httplib2 caching for requests"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"},
+    {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"},
+]
+
+[package.dependencies]
+filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""}
+msgpack = ">=0.5.2,<2.0.0"
+requests = ">=2.16.0"
+
+[package.extras]
+dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"]
+filecache = ["filelock (>=3.8.0)"]
+redis = ["redis (>=2.10.5)"]
+
 [[package]]
 name = "certifi"
 version = "2024.2.2"
@@ -381,6 +427,21 @@ files = [
     {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
 ]
 
+[[package]]
+name = "cleo"
+version = "2.1.0"
+description = "Cleo allows you to create beautiful and testable command-line interfaces."
+optional = false
+python-versions = ">=3.7,<4.0"
+files = [
+    {file = "cleo-2.1.0-py3-none-any.whl", hash = "sha256:4a31bd4dd45695a64ee3c4758f583f134267c2bc518d8ae9a29cf237d009b07e"},
+    {file = "cleo-2.1.0.tar.gz", hash = "sha256:0b2c880b5d13660a7ea651001fb4acb527696c01f15c9ee650f377aa543fd523"},
+]
+
+[package.dependencies]
+crashtest = ">=0.4.1,<0.5.0"
+rapidfuzz = ">=3.0.0,<4.0.0"
+
 [[package]]
 name = "click"
 version = "8.1.7"
@@ -490,6 +551,71 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
 [package.extras]
 toml = ["tomli"]
 
+[[package]]
+name = "crashtest"
+version = "0.4.1"
+description = "Manage Python errors with ease"
+optional = false
+python-versions = ">=3.7,<4.0"
+files = [
+    {file = "crashtest-0.4.1-py3-none-any.whl", hash = "sha256:8d23eac5fa660409f57472e3851dab7ac18aba459a8d19cbbba86d3d5aecd2a5"},
+    {file = "crashtest-0.4.1.tar.gz", hash = "sha256:80d7b1f316ebfbd429f648076d6275c877ba30ba48979de4191714a75266f0ce"},
+]
+
+[[package]]
+name = "cryptography"
+version = "42.0.5"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"},
+    {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"},
+    {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"},
+    {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"},
+    {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"},
+    {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"},
+    {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"},
+    {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"},
+    {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"},
+    {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"},
+    {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"},
+    {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"},
+    {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"},
+    {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"},
+    {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"},
+    {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"},
+    {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"},
+    {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"},
+    {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"},
+    {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"},
+    {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"},
+    {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"},
+    {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"},
+    {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"},
+    {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"},
+    {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"},
+    {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"},
+    {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"},
+    {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"},
+    {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"},
+    {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"},
+    {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"},
+]
+
+[package.dependencies]
+cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
+
+[package.extras]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
+docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
+nox = ["nox"]
+pep8test = ["check-sdist", "click", "mypy", "ruff"]
+sdist = ["build"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
+test-randomorder = ["pytest-randomly"]
+
 [[package]]
 name = "css-html-js-minify"
 version = "2.5.5"
@@ -591,6 +717,93 @@ files = [
     {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"},
 ]
 
+[[package]]
+name = "dulwich"
+version = "0.21.7"
+description = "Python Git Library"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d4c0110798099bb7d36a110090f2688050703065448895c4f53ade808d889dd3"},
+    {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bc12697f0918bee324c18836053644035362bb3983dc1b210318f2fed1d7132"},
+    {file = "dulwich-0.21.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:471305af74790827fcbafe330fc2e8bdcee4fb56ca1177c8c481b1c8f806c4a4"},
+    {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54c9d0e845be26f65f954dff13a1cd3f2b9739820c19064257b8fd7435ab263"},
+    {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12d61334a575474e707614f2e93d6ed4cdae9eb47214f9277076d9e5615171d3"},
+    {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e274cebaf345f0b1e3b70197f2651de92b652386b68020cfd3bf61bc30f6eaaa"},
+    {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:817822f970e196e757ae01281ecbf21369383285b9f4a83496312204cf889b8c"},
+    {file = "dulwich-0.21.7-cp310-cp310-win32.whl", hash = "sha256:7836da3f4110ce684dcd53489015fb7fa94ed33c5276e3318b8b1cbcb5b71e08"},
+    {file = "dulwich-0.21.7-cp310-cp310-win_amd64.whl", hash = "sha256:4a043b90958cec866b4edc6aef5fe3c2c96a664d0b357e1682a46f6c477273c4"},
+    {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ce8db196e79c1f381469410d26fb1d8b89c6b87a4e7f00ff418c22a35121405c"},
+    {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:62bfb26bdce869cd40be443dfd93143caea7089b165d2dcc33de40f6ac9d812a"},
+    {file = "dulwich-0.21.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c01a735b9a171dcb634a97a3cec1b174cfbfa8e840156870384b633da0460f18"},
+    {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa4d14767cf7a49c9231c2e52cb2a3e90d0c83f843eb6a2ca2b5d81d254cf6b9"},
+    {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bca4b86e96d6ef18c5bc39828ea349efb5be2f9b1f6ac9863f90589bac1084d"},
+    {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7b5624b02ef808cdc62dabd47eb10cd4ac15e8ac6df9e2e88b6ac6b40133673"},
+    {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3a539b4696a42fbdb7412cb7b66a4d4d332761299d3613d90a642923c7560e1"},
+    {file = "dulwich-0.21.7-cp311-cp311-win32.whl", hash = "sha256:675a612ce913081beb0f37b286891e795d905691dfccfb9bf73721dca6757cde"},
+    {file = "dulwich-0.21.7-cp311-cp311-win_amd64.whl", hash = "sha256:460ba74bdb19f8d498786ae7776745875059b1178066208c0fd509792d7f7bfc"},
+    {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4c51058ec4c0b45dc5189225b9e0c671b96ca9713c1daf71d622c13b0ab07681"},
+    {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4bc4c5366eaf26dda3fdffe160a3b515666ed27c2419f1d483da285ac1411de0"},
+    {file = "dulwich-0.21.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a0650ec77d89cb947e3e4bbd4841c96f74e52b4650830112c3057a8ca891dc2f"},
+    {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f18f0a311fb7734b033a3101292b932158cade54b74d1c44db519e42825e5a2"},
+    {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c589468e5c0cd84e97eb7ec209ab005a2cb69399e8c5861c3edfe38989ac3a8"},
+    {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d62446797163317a397a10080c6397ffaaca51a7804c0120b334f8165736c56a"},
+    {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e84cc606b1f581733df4350ca4070e6a8b30be3662bbb81a590b177d0c996c91"},
+    {file = "dulwich-0.21.7-cp312-cp312-win32.whl", hash = "sha256:c3d1685f320907a52c40fd5890627945c51f3a5fa4bcfe10edb24fec79caadec"},
+    {file = "dulwich-0.21.7-cp312-cp312-win_amd64.whl", hash = "sha256:6bd69921fdd813b7469a3c77bc75c1783cc1d8d72ab15a406598e5a3ba1a1503"},
+    {file = "dulwich-0.21.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d8ab29c660125db52106775caa1f8f7f77a69ed1fe8bc4b42bdf115731a25bf"},
+    {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0d2e4485b98695bf95350ce9d38b1bb0aaac2c34ad00a0df789aa33c934469b"},
+    {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e138d516baa6b5bafbe8f030eccc544d0d486d6819b82387fc0e285e62ef5261"},
+    {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f34bf9b9fa9308376263fd9ac43143c7c09da9bc75037bb75c6c2423a151b92c"},
+    {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e2c66888207b71cd1daa2acb06d3984a6bc13787b837397a64117aa9fc5936a"},
+    {file = "dulwich-0.21.7-cp37-cp37m-win32.whl", hash = "sha256:10893105c6566fc95bc2a67b61df7cc1e8f9126d02a1df6a8b2b82eb59db8ab9"},
+    {file = "dulwich-0.21.7-cp37-cp37m-win_amd64.whl", hash = "sha256:460b3849d5c3d3818a80743b4f7a0094c893c559f678e56a02fff570b49a644a"},
+    {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74700e4c7d532877355743336c36f51b414d01e92ba7d304c4f8d9a5946dbc81"},
+    {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c92e72c43c9e9e936b01a57167e0ea77d3fd2d82416edf9489faa87278a1cdf7"},
+    {file = "dulwich-0.21.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d097e963eb6b9fa53266146471531ad9c6765bf390849230311514546ed64db2"},
+    {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:808e8b9cc0aa9ac74870b49db4f9f39a52fb61694573f84b9c0613c928d4caf8"},
+    {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1957b65f96e36c301e419d7adaadcff47647c30eb072468901bb683b1000bc5"},
+    {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4b09bc3a64fb70132ec14326ecbe6e0555381108caff3496898962c4136a48c6"},
+    {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5882e70b74ac3c736a42d3fdd4f5f2e6570637f59ad5d3e684760290b58f041"},
+    {file = "dulwich-0.21.7-cp38-cp38-win32.whl", hash = "sha256:29bb5c1d70eba155ded41ed8a62be2f72edbb3c77b08f65b89c03976292f6d1b"},
+    {file = "dulwich-0.21.7-cp38-cp38-win_amd64.whl", hash = "sha256:25c3ab8fb2e201ad2031ddd32e4c68b7c03cb34b24a5ff477b7a7dcef86372f5"},
+    {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8929c37986c83deb4eb500c766ee28b6670285b512402647ee02a857320e377c"},
+    {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc1e11be527ac06316539b57a7688bcb1b6a3e53933bc2f844397bc50734e9ae"},
+    {file = "dulwich-0.21.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0fc3078a1ba04c588fabb0969d3530efd5cd1ce2cf248eefb6baf7cbc15fc285"},
+    {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dcbd29ba30ba2c5bfbab07a61a5f20095541d5ac66d813056c122244df4ac0"},
+    {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8869fc8ec3dda743e03d06d698ad489b3705775fe62825e00fa95aa158097fc0"},
+    {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d96ca5e0dde49376fbcb44f10eddb6c30284a87bd03bb577c59bb0a1f63903fa"},
+    {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0064363bd5e814359657ae32517fa8001e8573d9d040bd997908d488ab886ed"},
+    {file = "dulwich-0.21.7-cp39-cp39-win32.whl", hash = "sha256:869eb7be48243e695673b07905d18b73d1054a85e1f6e298fe63ba2843bb2ca1"},
+    {file = "dulwich-0.21.7-cp39-cp39-win_amd64.whl", hash = "sha256:404b8edeb3c3a86c47c0a498699fc064c93fa1f8bab2ffe919e8ab03eafaaad3"},
+    {file = "dulwich-0.21.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e598d743c6c0548ebcd2baf94aa9c8bfacb787ea671eeeb5828cfbd7d56b552f"},
+    {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a2d76c96426e791556836ef43542b639def81be4f1d6d4322cd886c115eae1"},
+    {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c88acb60a1f4d31bd6d13bfba465853b3df940ee4a0f2a3d6c7a0778c705b7"},
+    {file = "dulwich-0.21.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ecd315847dea406a4decfa39d388a2521e4e31acde3bd9c2609c989e817c6d62"},
+    {file = "dulwich-0.21.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d05d3c781bc74e2c2a2a8f4e4e2ed693540fbe88e6ac36df81deac574a6dad99"},
+    {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6de6f8de4a453fdbae8062a6faa652255d22a3d8bce0cd6d2d6701305c75f2b3"},
+    {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e25953c7acbbe4e19650d0225af1c0c0e6882f8bddd2056f75c1cc2b109b88ad"},
+    {file = "dulwich-0.21.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4637cbd8ed1012f67e1068aaed19fcc8b649bcf3e9e26649826a303298c89b9d"},
+    {file = "dulwich-0.21.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:858842b30ad6486aacaa607d60bab9c9a29e7c59dc2d9cb77ae5a94053878c08"},
+    {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739b191f61e1c4ce18ac7d520e7a7cbda00e182c3489552408237200ce8411ad"},
+    {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:274c18ec3599a92a9b67abaf110e4f181a4f779ee1aaab9e23a72e89d71b2bd9"},
+    {file = "dulwich-0.21.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2590e9b431efa94fc356ae33b38f5e64f1834ec3a94a6ac3a64283b206d07aa3"},
+    {file = "dulwich-0.21.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed60d1f610ef6437586f7768254c2a93820ccbd4cfdac7d182cf2d6e615969bb"},
+    {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8278835e168dd097089f9e53088c7a69c6ca0841aef580d9603eafe9aea8c358"},
+    {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffc27fb063f740712e02b4d2f826aee8bbed737ed799962fef625e2ce56e2d29"},
+    {file = "dulwich-0.21.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61e3451bd3d3844f2dca53f131982553be4d1b1e1ebd9db701843dd76c4dba31"},
+    {file = "dulwich-0.21.7.tar.gz", hash = "sha256:a9e9c66833cea580c3ac12927e4b9711985d76afca98da971405d414de60e968"},
+]
+
+[package.dependencies]
+urllib3 = ">=1.25"
+
+[package.extras]
+fastimport = ["fastimport"]
+https = ["urllib3 (>=1.24.1)"]
+paramiko = ["paramiko"]
+pgp = ["gpg"]
+
 [[package]]
 name = "exceptiongroup"
 version = "1.2.0"
@@ -685,6 +898,25 @@ files = [
     {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"},
 ]
 
+[[package]]
+name = "importlib-metadata"
+version = "7.1.0"
+description = "Read metadata from Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
+    {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
+]
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+perf = ["ipython"]
+testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
+
 [[package]]
 name = "iniconfig"
 version = "2.0.0"
@@ -696,6 +928,17 @@ files = [
     {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
 ]
 
+[[package]]
+name = "installer"
+version = "0.7.0"
+description = "A library for installing Python wheels."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "installer-0.7.0-py3-none-any.whl", hash = "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53"},
+    {file = "installer-0.7.0.tar.gz", hash = "sha256:a26d3e3116289bb08216e0d0f7d925fcef0b0194eedfa0c944bcaaa106c4b631"},
+]
+
 [[package]]
 name = "ipykernel"
 version = "6.29.4"
@@ -781,6 +1024,24 @@ files = [
 [package.extras]
 colors = ["colorama (>=0.4.6)"]
 
+[[package]]
+name = "jaraco-classes"
+version = "3.4.0"
+description = "Utility functions for Python class constructs"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"},
+    {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"},
+]
+
+[package.dependencies]
+more-itertools = "*"
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
+
 [[package]]
 name = "jedi"
 version = "0.19.1"
@@ -800,6 +1061,21 @@ docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alab
 qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
 testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
 
+[[package]]
+name = "jeepney"
+version = "0.8.0"
+description = "Low-level, pure Python DBus protocol wrapper."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"},
+    {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"},
+]
+
+[package.extras]
+test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"]
+trio = ["async_generator", "trio"]
+
 [[package]]
 name = "jinja2"
 version = "3.1.3"
@@ -905,6 +1181,29 @@ files = [
     {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"},
 ]
 
+[[package]]
+name = "keyring"
+version = "24.3.1"
+description = "Store and access your passwords safely."
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "keyring-24.3.1-py3-none-any.whl", hash = "sha256:df38a4d7419a6a60fea5cef1e45a948a3e8430dd12ad88b0f423c5c143906218"},
+    {file = "keyring-24.3.1.tar.gz", hash = "sha256:c3327b6ffafc0e8befbdb597cacdb4928ffe5c1212f7645f186e6d9957a898db"},
+]
+
+[package.dependencies]
+importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""}
+"jaraco.classes" = "*"
+jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""}
+pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""}
+SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""}
+
+[package.extras]
+completion = ["shtab (>=1.1.0)"]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
+
 [[package]]
 name = "lxml"
 version = "5.2.1"
@@ -1171,13 +1470,13 @@ files = [
 
 [[package]]
 name = "matplotlib-inline"
-version = "0.1.6"
+version = "0.1.7"
 description = "Inline Matplotlib backend for Jupyter"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
 files = [
-    {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
-    {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
+    {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
+    {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
 ]
 
 [package.dependencies]
@@ -1235,6 +1534,129 @@ files = [
     {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"},
 ]
 
+[[package]]
+name = "more-itertools"
+version = "10.2.0"
+description = "More routines for operating on iterables, beyond itertools"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"},
+    {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"},
+]
+
+[[package]]
+name = "msgpack"
+version = "1.0.8"
+description = "MessagePack serializer"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"},
+    {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"},
+    {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"},
+    {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"},
+    {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"},
+    {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"},
+    {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"},
+    {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"},
+    {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"},
+    {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"},
+    {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"},
+    {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"},
+    {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"},
+    {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"},
+    {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"},
+    {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"},
+    {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"},
+    {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"},
+    {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"},
+    {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"},
+    {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"},
+    {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"},
+    {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"},
+    {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"},
+    {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"},
+    {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"},
+    {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"},
+    {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"},
+    {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"},
+    {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"},
+    {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"},
+    {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"},
+    {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"},
+    {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"},
+    {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"},
+    {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"},
+    {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"},
+    {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"},
+    {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"},
+    {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"},
+    {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"},
+    {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"},
+    {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"},
+    {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"},
+    {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"},
+    {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"},
+    {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"},
+    {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"},
+    {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"},
+    {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"},
+    {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"},
+    {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"},
+    {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
+    {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
+    {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
+    {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
+]
+
+[[package]]
+name = "mypy"
+version = "1.9.0"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"},
+    {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"},
+    {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"},
+    {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"},
+    {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"},
+    {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"},
+    {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"},
+    {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"},
+    {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"},
+    {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"},
+    {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"},
+    {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"},
+    {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"},
+    {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"},
+    {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"},
+    {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"},
+    {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"},
+    {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"},
+    {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"},
+    {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"},
+    {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"},
+    {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"},
+    {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"},
+    {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"},
+    {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"},
+    {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"},
+    {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+install-types = ["pip"]
+mypyc = ["setuptools (>=50)"]
+reports = ["lxml"]
+
 [[package]]
 name = "mypy-extensions"
 version = "1.0.0"
@@ -1469,6 +1891,20 @@ files = [
 [package.dependencies]
 ptyprocess = ">=0.5"
 
+[[package]]
+name = "pkginfo"
+version = "1.10.0"
+description = "Query metadata from sdists / bdists / installed packages."
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "pkginfo-1.10.0-py3-none-any.whl", hash = "sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097"},
+    {file = "pkginfo-1.10.0.tar.gz", hash = "sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297"},
+]
+
+[package.extras]
+testing = ["pytest", "pytest-cov", "wheel"]
+
 [[package]]
 name = "platformdirs"
 version = "4.2.0"
@@ -1499,6 +1935,68 @@ files = [
 dev = ["pre-commit", "tox"]
 testing = ["pytest", "pytest-benchmark"]
 
+[[package]]
+name = "poetry"
+version = "1.8.2"
+description = "Python dependency management and packaging made easy."
+optional = false
+python-versions = ">=3.8,<4.0"
+files = [
+    {file = "poetry-1.8.2-py3-none-any.whl", hash = "sha256:b42b400d9a803af6e788a30a6f3e9998020b77860e28df20647eb10b6f414910"},
+    {file = "poetry-1.8.2.tar.gz", hash = "sha256:49cceb3838104647c3e1021f3a4f13c6053704cc18d33f849a90fe687a29cb73"},
+]
+
+[package.dependencies]
+build = ">=1.0.3,<2.0.0"
+cachecontrol = {version = ">=0.14.0,<0.15.0", extras = ["filecache"]}
+cleo = ">=2.1.0,<3.0.0"
+crashtest = ">=0.4.1,<0.5.0"
+dulwich = ">=0.21.2,<0.22.0"
+fastjsonschema = ">=2.18.0,<3.0.0"
+installer = ">=0.7.0,<0.8.0"
+keyring = ">=24.0.0,<25.0.0"
+packaging = ">=23.1"
+pexpect = ">=4.7.0,<5.0.0"
+pkginfo = ">=1.9.4,<2.0.0"
+platformdirs = ">=3.0.0,<5"
+poetry-core = "1.9.0"
+poetry-plugin-export = ">=1.6.0,<2.0.0"
+pyproject-hooks = ">=1.0.0,<2.0.0"
+requests = ">=2.26,<3.0"
+requests-toolbelt = ">=1.0.0,<2.0.0"
+shellingham = ">=1.5,<2.0"
+tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
+tomlkit = ">=0.11.4,<1.0.0"
+trove-classifiers = ">=2022.5.19"
+virtualenv = ">=20.23.0,<21.0.0"
+xattr = {version = ">=1.0.0,<2.0.0", markers = "sys_platform == \"darwin\""}
+
+[[package]]
+name = "poetry-core"
+version = "1.9.0"
+description = "Poetry PEP 517 Build Backend"
+optional = false
+python-versions = ">=3.8,<4.0"
+files = [
+    {file = "poetry_core-1.9.0-py3-none-any.whl", hash = "sha256:4e0c9c6ad8cf89956f03b308736d84ea6ddb44089d16f2adc94050108ec1f5a1"},
+    {file = "poetry_core-1.9.0.tar.gz", hash = "sha256:fa7a4001eae8aa572ee84f35feb510b321bd652e5cf9293249d62853e1f935a2"},
+]
+
+[[package]]
+name = "poetry-plugin-export"
+version = "1.7.1"
+description = "Poetry plugin to export the dependencies to various formats"
+optional = false
+python-versions = ">=3.8,<4.0"
+files = [
+    {file = "poetry_plugin_export-1.7.1-py3-none-any.whl", hash = "sha256:b2258e53ae0d369a73806f957ed0e726eb95c571a0ce8b1f273da686528cc1da"},
+    {file = "poetry_plugin_export-1.7.1.tar.gz", hash = "sha256:cf62cfb6218a904290ba6db3bc1a24aa076d10f81c48c6e48b2ded430131e22e"},
+]
+
+[package.dependencies]
+poetry = ">=1.8.0,<2.0.0"
+poetry-core = ">=1.7.0,<2.0.0"
+
 [[package]]
 name = "pre-commit"
 version = "3.7.0"
@@ -1650,6 +2148,20 @@ tomlkit = ">=0.10.1"
 spelling = ["pyenchant (>=3.2,<4.0)"]
 testutils = ["gitpython (>3)"]
 
+[[package]]
+name = "pyproject-hooks"
+version = "1.0.0"
+description = "Wrappers to call pyproject.toml-based build backend hooks."
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"},
+    {file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"},
+]
+
+[package.dependencies]
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+
 [[package]]
 name = "pytest"
 version = "8.1.1"
@@ -1745,6 +2257,17 @@ files = [
     {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"},
 ]
 
+[[package]]
+name = "pywin32-ctypes"
+version = "0.2.2"
+description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"},
+    {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
+]
+
 [[package]]
 name = "pyyaml"
 version = "6.0.1"
@@ -1807,109 +2330,206 @@ files = [
 
 [[package]]
 name = "pyzmq"
-version = "25.1.2"
+version = "26.0.0"
 description = "Python bindings for 0MQ"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 files = [
-    {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4"},
-    {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0"},
-    {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e"},
-    {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872"},
-    {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d"},
-    {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75"},
-    {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6"},
-    {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979"},
-    {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08"},
-    {file = "pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886"},
-    {file = "pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6"},
-    {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c"},
-    {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1"},
-    {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348"},
-    {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642"},
-    {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840"},
-    {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d"},
-    {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b"},
-    {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b"},
-    {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3"},
-    {file = "pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097"},
-    {file = "pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9"},
-    {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a"},
-    {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e"},
-    {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27"},
-    {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30"},
-    {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee"},
-    {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537"},
-    {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181"},
-    {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe"},
-    {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737"},
-    {file = "pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d"},
-    {file = "pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7b6d09a8962a91151f0976008eb7b29b433a560fde056ec7a3db9ec8f1075438"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967668420f36878a3c9ecb5ab33c9d0ff8d054f9c0233d995a6d25b0e95e1b6b"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5edac3f57c7ddaacdb4d40f6ef2f9e299471fc38d112f4bc6d60ab9365445fb0"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0dabfb10ef897f3b7e101cacba1437bd3a5032ee667b7ead32bbcdd1a8422fe7"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c6441e0398c2baacfe5ba30c937d274cfc2dc5b55e82e3749e333aabffde561"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:16b726c1f6c2e7625706549f9dbe9b06004dfbec30dbed4bf50cbdfc73e5b32a"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:a86c2dd76ef71a773e70551a07318b8e52379f58dafa7ae1e0a4be78efd1ff16"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-win32.whl", hash = "sha256:359f7f74b5d3c65dae137f33eb2bcfa7ad9ebefd1cab85c935f063f1dbb245cc"},
-    {file = "pyzmq-25.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:55875492f820d0eb3417b51d96fea549cde77893ae3790fd25491c5754ea2f68"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8c8a419dfb02e91b453615c69568442e897aaf77561ee0064d789705ff37a92"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8807c87fa893527ae8a524c15fc505d9950d5e856f03dae5921b5e9aa3b8783b"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e319ed7d6b8f5fad9b76daa0a68497bc6f129858ad956331a5835785761e003"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3c53687dde4d9d473c587ae80cc328e5b102b517447456184b485587ebd18b62"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9add2e5b33d2cd765ad96d5eb734a5e795a0755f7fc49aa04f76d7ddda73fd70"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e690145a8c0c273c28d3b89d6fb32c45e0d9605b2293c10e650265bf5c11cfec"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:00a06faa7165634f0cac1abb27e54d7a0b3b44eb9994530b8ec73cf52e15353b"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-win32.whl", hash = "sha256:0f97bc2f1f13cb16905a5f3e1fbdf100e712d841482b2237484360f8bc4cb3d7"},
-    {file = "pyzmq-25.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6cc0020b74b2e410287e5942e1e10886ff81ac77789eb20bec13f7ae681f0fdd"},
-    {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:bef02cfcbded83473bdd86dd8d3729cd82b2e569b75844fb4ea08fee3c26ae41"},
-    {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e10a4b5a4b1192d74853cc71a5e9fd022594573926c2a3a4802020360aa719d8"},
-    {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8c5f80e578427d4695adac6fdf4370c14a2feafdc8cb35549c219b90652536ae"},
-    {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5dde6751e857910c1339890f3524de74007958557593b9e7e8c5f01cd919f8a7"},
-    {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea1608dd169da230a0ad602d5b1ebd39807ac96cae1845c3ceed39af08a5c6df"},
-    {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0f513130c4c361201da9bc69df25a086487250e16b5571ead521b31ff6b02220"},
-    {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:019744b99da30330798bb37df33549d59d380c78e516e3bab9c9b84f87a9592f"},
-    {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e2713ef44be5d52dd8b8e2023d706bf66cb22072e97fc71b168e01d25192755"},
-    {file = "pyzmq-25.1.2-cp38-cp38-win32.whl", hash = "sha256:07cd61a20a535524906595e09344505a9bd46f1da7a07e504b315d41cd42eb07"},
-    {file = "pyzmq-25.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb7e49a17fb8c77d3119d41a4523e432eb0c6932187c37deb6fbb00cc3028088"},
-    {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:94504ff66f278ab4b7e03e4cba7e7e400cb73bfa9d3d71f58d8972a8dc67e7a6"},
-    {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd0d50bbf9dca1d0bdea219ae6b40f713a3fb477c06ca3714f208fd69e16fd8"},
-    {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:004ff469d21e86f0ef0369717351073e0e577428e514c47c8480770d5e24a565"},
-    {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c0b5ca88a8928147b7b1e2dfa09f3b6c256bc1135a1338536cbc9ea13d3b7add"},
-    {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9a79f1d2495b167119d02be7448bfba57fad2a4207c4f68abc0bab4b92925b"},
-    {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:518efd91c3d8ac9f9b4f7dd0e2b7b8bf1a4fe82a308009016b07eaa48681af82"},
-    {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1ec23bd7b3a893ae676d0e54ad47d18064e6c5ae1fadc2f195143fb27373f7f6"},
-    {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db36c27baed588a5a8346b971477b718fdc66cf5b80cbfbd914b4d6d355e44e2"},
-    {file = "pyzmq-25.1.2-cp39-cp39-win32.whl", hash = "sha256:39b1067f13aba39d794a24761e385e2eddc26295826530a8c7b6c6c341584289"},
-    {file = "pyzmq-25.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:8e9f3fabc445d0ce320ea2c59a75fe3ea591fdbdeebec5db6de530dd4b09412e"},
-    {file = "pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05"},
-    {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8"},
-    {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e"},
-    {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4"},
-    {file = "pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d"},
-    {file = "pyzmq-25.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:df0c7a16ebb94452d2909b9a7b3337940e9a87a824c4fc1c7c36bb4404cb0cde"},
-    {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45999e7f7ed5c390f2e87ece7f6c56bf979fb213550229e711e45ecc7d42ccb8"},
-    {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac170e9e048b40c605358667aca3d94e98f604a18c44bdb4c102e67070f3ac9b"},
-    {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b604734bec94f05f81b360a272fc824334267426ae9905ff32dc2be433ab96"},
-    {file = "pyzmq-25.1.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a793ac733e3d895d96f865f1806f160696422554e46d30105807fdc9841b9f7d"},
-    {file = "pyzmq-25.1.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0806175f2ae5ad4b835ecd87f5f85583316b69f17e97786f7443baaf54b9bb98"},
-    {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ef12e259e7bc317c7597d4f6ef59b97b913e162d83b421dd0db3d6410f17a244"},
-    {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea253b368eb41116011add00f8d5726762320b1bda892f744c91997b65754d73"},
-    {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b9b1f2ad6498445a941d9a4fee096d387fee436e45cc660e72e768d3d8ee611"},
-    {file = "pyzmq-25.1.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8b14c75979ce932c53b79976a395cb2a8cd3aaf14aef75e8c2cb55a330b9b49d"},
-    {file = "pyzmq-25.1.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:889370d5174a741a62566c003ee8ddba4b04c3f09a97b8000092b7ca83ec9c49"},
-    {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18fff090441a40ffda8a7f4f18f03dc56ae73f148f1832e109f9bffa85df15"},
-    {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99a6b36f95c98839ad98f8c553d8507644c880cf1e0a57fe5e3a3f3969040882"},
-    {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4345c9a27f4310afbb9c01750e9461ff33d6fb74cd2456b107525bbeebcb5be3"},
-    {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3516e0b6224cf6e43e341d56da15fd33bdc37fa0c06af4f029f7d7dfceceabbc"},
-    {file = "pyzmq-25.1.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:146b9b1f29ead41255387fb07be56dc29639262c0f7344f570eecdcd8d683314"},
-    {file = "pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226"},
+    {file = "pyzmq-26.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:a86409f3f8eae7af5a47babd831a119bdf552e831f04d2225a313305e8e35e7c"},
+    {file = "pyzmq-26.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d36a46975925b8bf14b69fe6d4097bc96c91f94ceb954d56853a2211a5cc3433"},
+    {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcac700269d081ded42ed3833f9d0effe734148376204af9c0ef0fd25a3fea55"},
+    {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49efc420e36d2e8adc5dae41c2c1e8bb37a069e40a880cbe414a032136b194b0"},
+    {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02773b96ef6a17a57680c3609645785c390198be31a4505c01ce0c846f9e7d0e"},
+    {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ce2c53f4963a358ba91b58ccecb84fab6d5f0622230d105c2589f7556ec53cc9"},
+    {file = "pyzmq-26.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:06525d996afdb0da3e8b7df0b654261455f6e86c2c3574c3f00d2bd335be78eb"},
+    {file = "pyzmq-26.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bd3537f049dc0488adb3df29a77635eaff2a8d1d3d29a09714db6e2d10caba1a"},
+    {file = "pyzmq-26.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9ce158ab54994c60fdde83300dc1e447446baacbe4ec9e4e80096f9b9a125c13"},
+    {file = "pyzmq-26.0.0-cp310-cp310-win32.whl", hash = "sha256:271c9178a94b009651f8ad3ff9bb9ca45778aaf66c9e325a44d81a7498fcaa59"},
+    {file = "pyzmq-26.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:4216eee101d104a017042f0e4af0a45875400ff3794f1a59476e210b1a9760e2"},
+    {file = "pyzmq-26.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:44271793067025a07d38ad4be11f08187cce850fafd1890b42046abbcdca2fc0"},
+    {file = "pyzmq-26.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:1e87178437460b6df18e761650ef080d3ad5a41813cc3df7f9fd78714fca04c0"},
+    {file = "pyzmq-26.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0397c7431f3fc2bac497992d7447b036bc0d8bb3e15b158b2013201857ff2354"},
+    {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a5b4dc4d7a3f859026083906724ad1ae743261548b61d0d5abcf2d994122c2b"},
+    {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:952e85c5e86f9ba100b78b60719b76e1ff3e13bb403cb6de687bb92e7b2179e7"},
+    {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fdeac8612a9dca6fcad6cb43c7efb75f53ba75da981fbafa949ddcde1d5662"},
+    {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:39b8ed8d2e5da8b8351c6aa627601b3b52e8eb5e25cf6bcd26b6f012dec7870b"},
+    {file = "pyzmq-26.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f6f618d7d7c9c37053a36e6dc5435c53e9e0c7a67e6fd00b69c209d07a8db4dc"},
+    {file = "pyzmq-26.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72ae3078b1c47552e0e39fd81fc0472e880316897a733dbb3570819be19da48a"},
+    {file = "pyzmq-26.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5d7fcc648445dbfd6ce9973ec7b4a33ee9307b7e88cf4816f4403ccbaf8de9ca"},
+    {file = "pyzmq-26.0.0-cp311-cp311-win32.whl", hash = "sha256:9982799d7d7807beb1b26f1aa9a192baccb1a14c5d00eca881a42a0ae562671b"},
+    {file = "pyzmq-26.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:60f91afc76a3fc5d65dfba4f6b6020c462674b5eab6cbf00dec133d79656072d"},
+    {file = "pyzmq-26.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:120887d773e878136e9b33bbba656df0d4c6e2861694d07d058ec60ce1108b24"},
+    {file = "pyzmq-26.0.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:469f4febd63c26b20132e54cc40048d5698123794b103758ccd21b8a45890dc3"},
+    {file = "pyzmq-26.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c919895132cae5a458d5a17047fd33c9eb271f15bb3485add34429cfd7b76a71"},
+    {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e0e94ca9a8f23000d54e11ecd727b69fb1994baf3b6b1eedb881cdd3196ecec"},
+    {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a824b3301ddd003cdceb9b537804e751ac5922a845b19d4e50b4789d1cd28b24"},
+    {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af9f5b1b76753584c871c1c96db8a18650886b3adf9fc8c7d4019343eb329c28"},
+    {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9691a6ab55d011e83d7438f6711b93b7f8aa21ee8cf3e7ad6d6d9ea26a8f3a1f"},
+    {file = "pyzmq-26.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58176e2437462568b5099acf17401be64205e175e72767a8250eef84ee9ec4f5"},
+    {file = "pyzmq-26.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d492921b398d640a1f796306531bc6911a94ce5528b798ed14e0620abd9b948d"},
+    {file = "pyzmq-26.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f85bb2c47b5fd70e3cbb280e380ab97bdf9f02e1a363cb472fe0a297ac24029d"},
+    {file = "pyzmq-26.0.0-cp312-cp312-win32.whl", hash = "sha256:c2e36399f0433b14a91f956bd7ecf94799c57a6f992889d45440cb05b3de8025"},
+    {file = "pyzmq-26.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:12ca1afb065e5b21a32b1e35bfcbc8762efc0f7555c166acaec36c93b52d7ccf"},
+    {file = "pyzmq-26.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:f66c925f62ce28946525c32a094e346dd8da6c828d568d7ecda97f5ae36089c3"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e495ff09514fc657c5fb2cba0aac082ce0494c6217230783297da9008333a8db"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5736c9a54c27319a65ffc72dbf684538f2773237e94ba50b7f1f74f4e3cb9115"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd62830100b9b1adb51da4094142bd680d51daf9a0f6f3f39e1f80474eddc011"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a7ee271fac41ddc0ba11f4b128ddd5f2bf0a3186d25be331ed8bfbb253536"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:694625c2c22be57149e9439757ee02ee4fb6432f7054dc5008bbbc33ef388d1c"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:90ba8f7c6f34c2c11179b293050417c14661035969ef3f8867200ea6901f9000"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab2e55046263c8b24e64116e80b63cf701df747b44aadcf317aa47c8af2dfe67"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-win32.whl", hash = "sha256:7353d231686bbc96c458b934f134ff9165a1e9dd0a2ea8f724469e44bcc2c07a"},
+    {file = "pyzmq-26.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1df2b992eabc59f078ca916e9ac8b5bd463536bf7828c13940b35b8555ed7861"},
+    {file = "pyzmq-26.0.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2397364289334840c81ff1ef95a5a5ee326de01c1437cc38f7e16785a7b653d9"},
+    {file = "pyzmq-26.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c952cf06edbbd2d67f627037e2c8e3187ca834d6b9a222e3a3037f80d393a345"},
+    {file = "pyzmq-26.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55f390adb763196d75a2e8c18277b4344f8a7f94f223b5d096324c5b47c2471e"},
+    {file = "pyzmq-26.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1da5e11862a994360319df4f425e89662563683334e1079684eb77b9a6478ae2"},
+    {file = "pyzmq-26.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72340614ea23904cff824109eb025648bdf32775d87f5814d3ba6f2335a853f3"},
+    {file = "pyzmq-26.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa7431d12ebb5433a92e99dc326d45eaf52a90046032bac4c558b4bdeee5dc7a"},
+    {file = "pyzmq-26.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a2b13008a693c0ffccaeeebcc5ab5f2398cced3b5bf482ba89a38fe56b00eb10"},
+    {file = "pyzmq-26.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9d68284ce48617c97e675ed8a89db12a098eaa871a026999c9a10351f547f1fe"},
+    {file = "pyzmq-26.0.0-cp38-cp38-win32.whl", hash = "sha256:8783857a8c8df648a70c81ea3ff53ee71e5bf18468ca5ac3414f419fe8f3bd93"},
+    {file = "pyzmq-26.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:36d0f2fcbdba1fda8ff213bd17db7ddcba848aa70480ade3fe70401dce606511"},
+    {file = "pyzmq-26.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:dd87df01bc8eca392f0d505924087ccafdc4885a498e68df9f09eca9fdc736f1"},
+    {file = "pyzmq-26.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abc08b2e688714216870a6ab974733d4a1fcf0437d250ac8feed59c4c5c3f395"},
+    {file = "pyzmq-26.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dd13a30454adcf2f361155ea563ec99036678131a17c6b1a3f74426212c14ddc"},
+    {file = "pyzmq-26.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a0562054930471b386a44b0887504687c4e7adf4ba89bddc2e5959d16c371764"},
+    {file = "pyzmq-26.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc7badded4b025dbc25f34b95503b71c952235e6e40de40995c0c120efb4ff6d"},
+    {file = "pyzmq-26.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f971e77358384b8bcf3e9a7577cf84f97adbd6359f943e30cbff66087afcb279"},
+    {file = "pyzmq-26.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca4ebbef3f5fbd271eafc7c22ebbb88b74232f08b0e51759113f30a8d01f6843"},
+    {file = "pyzmq-26.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc98fbd4ce4ef8a0fbe97ab6d495aaa7764461e5a45f24c04f1d234e7bb80293"},
+    {file = "pyzmq-26.0.0-cp39-cp39-win32.whl", hash = "sha256:a5207bc2a923118e9afb57fee679be016ea138c27d1be5747118966e2d5d9450"},
+    {file = "pyzmq-26.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:e0c08a6070358a2984900a4518e2dacbfaf24aac018ab086d7ac2f6069b13340"},
+    {file = "pyzmq-26.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:eae3dcc185c405cf645480745c45346a1f42afce240f69a589095e41bd2b9e3d"},
+    {file = "pyzmq-26.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:71a8f010e23dfd61c531084a2b72a81885017da28352540f0b7799ca8423c044"},
+    {file = "pyzmq-26.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b48b7e417c56486932fb0c01fecd24916fe6bc359c03a654aa8c63fa33e3d76"},
+    {file = "pyzmq-26.0.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2806942185b40a3477d9b300c6f71354dd2be37e3f61a43193c96caa51e284d1"},
+    {file = "pyzmq-26.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed127aff75a3df142ae7a883c49a85b0b2f863b59fa1b8e4280335f5ebab5fd0"},
+    {file = "pyzmq-26.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:903b77dd2f17286496fa3ec902bc523f4502b0c64a2892df4b021222a2ba95fe"},
+    {file = "pyzmq-26.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:321a6872a9371709a62b3a4a14c1e9b5b47549371197c0c2164d2288510cd6d6"},
+    {file = "pyzmq-26.0.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cac954dc83c84e9d9d65f2359d402d7e79ae094d7808d578c9e9cc2c350c5a64"},
+    {file = "pyzmq-26.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac6f54c399638858e0b2a3153f23934604f3a8c9bb5a9cf865060cc658b1e096"},
+    {file = "pyzmq-26.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40af30c4cd0a046029d7b5272d02a649f9b1f89fb1361bbc90ba08d55ac88273"},
+    {file = "pyzmq-26.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:814245422f1c7707634397621dbcbeea7671fdc5c43d1ae592f4e0e45179e7fb"},
+    {file = "pyzmq-26.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6d3d7ef786e778351e6c51b45906e16506ad98bb78b99304032cb1876dfc81d2"},
+    {file = "pyzmq-26.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:36a85da0eab4c5337d0de7f975cca011208a59e9d0637e0c1b571764f1dd4a8f"},
+    {file = "pyzmq-26.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1d64889bfe4109f4a59a72b1d21416550465020642d6f556efd044951386bd38"},
+    {file = "pyzmq-26.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80fdea3e9e34c480bfccbb910f75380196ae9d1c12880c21743c845ebe6b13aa"},
+    {file = "pyzmq-26.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7129efc54dc48f566eed5422bc555ba4e472e40a1f9de328577c90ade47ccf5d"},
+    {file = "pyzmq-26.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ec5147095d6065b0e3a38a1a34f7859ab46496f3d5ce71134165893e9f83674"},
+    {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a1cc0445038a394479ad36b7e3cf55a19ee40099c031f65de872b8ee7025e79"},
+    {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b377b520e618c30c827966c274dd62ce7e15c72ce8767fae6193b6bdd1deb502"},
+    {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc907b26d287e6981d1e531c8fc21a0f94fe46a17493a8322eb3c75f8b561334"},
+    {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:580dd4b1c2edd51f284df0209bf439899f425ed00cb803a85ddc6cf10c866688"},
+    {file = "pyzmq-26.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:08db8071020181173c70cf2dad239e5e21e5b2e95f95b0ece0da39a70f5a483c"},
+    {file = "pyzmq-26.0.0.tar.gz", hash = "sha256:10ff405db5cee3bbd7aa143d78b25d90356097aed7864e50f0ae644e08759fe9"},
 ]
 
 [package.dependencies]
 cffi = {version = "*", markers = "implementation_name == \"pypy\""}
 
+[[package]]
+name = "rapidfuzz"
+version = "3.8.1"
+description = "rapid fuzzy string matching"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "rapidfuzz-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1b176f01490b48337183da5b4223005bc0c2354a4faee5118917d2fba0bedc1c"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0798e32304b8009d215026bf7e1c448f1831da0a03987b7de30059a41bee92f3"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad4dbd06c1f579eb043b2dcfc635bc6c9fb858240a70f0abd3bed84d8ac79994"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6ec696a268e8d730b42711537e500f7397afc06125c0e8fa9c8211386d315a5"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8a007fdc5cf646e48e361a39eabe725b93af7673c5ab90294e551cae72ff58"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68b185a0397aebe78bcc5d0e1efd96509d4e2f3c4a05996e5c843732f547e9ef"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:267ff42370e031195e3020fff075420c136b69dc918ecb5542ec75c1e36af81f"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:987cd277d27d14301019fdf61c17524f6127f5d364be5482228726049d8e0d10"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bc5a1ec3bd05b55d3070d557c0cdd4412272d51b4966c79aa3e9da207bd33d65"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa223c73c59cc45c12eaa9c439318084003beced0447ff92b578a890288e19eb"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d4276c7ee061db0bac54846933b40339f60085523675f917f37de24a4b3ce0ee"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2ba0e43e9a94d256a704a674c7010e6f8ef9225edf7287cf3e7f66c9894b06cd"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c22b32a57ab47afb207e8fe4bd7bb58c90f9291a63723cafd4e704742166e368"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-win32.whl", hash = "sha256:50db3867864422bf6a6435ea65b9ac9de71ef52ed1e05d62f498cd430189eece"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:bca5acf77508d1822023a85118c2dd8d3c16abdd56d2762359a46deb14daa5e0"},
+    {file = "rapidfuzz-3.8.1-cp310-cp310-win_arm64.whl", hash = "sha256:c763d99cf087e7b2c5be0cf34ae9a0e1b031f5057d2341a0a0ed782458645b7e"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:30c282612b7ebf2d7646ebebfd98dd308c582246a94d576734e4b0162f57baf4"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c6a43446f0cd8ff347b1fbb918dc0d657bebf484ddfa960ee069e422a477428"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4969fe0eb179aedacee53ca8f8f1be3c655964a6d62db30f247fee444b9c52b4"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:799f5f221d639d1c2ed8a2348d1edf5e22aa489b58b2cc99f5bf0c1917e2d0f2"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e62bde7d5df3312acc528786ee801c472cae5078b1f1e42761c853ba7fe1072a"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ea3d2e41d8fac71cb63ee72f75bee0ed1e9c50709d4c58587f15437761c1858"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f34a541895627c2bc9ef7757f16f02428a08d960d33208adfb96b33338d0945"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0643a25937fafe8d117f2907606e9940cd1cc905c66f16ece9ab93128299994"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:63044a7b6791a2e945dce9d812a6886e93159deb0464984eb403617ded257f08"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bbc15985c5658691f637a6b97651771147744edfad2a4be56b8a06755e3932fa"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:48b6e5a337a814aec7c6dda5d6460f947c9330860615301f35b519e16dde3c77"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:8c40da44ca20235cda05751d6e828b6b348e7a7c5de2922fa0f9c63f564fd675"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c21d5c7cfa6078c79897e5e482a7e84ff927143d2f3fb020dd6edd27f5469574"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-win32.whl", hash = "sha256:209bb712c448cdec4def6260b9f059bd4681ec61a01568f5e70e37bfe9efe830"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:6f7641992de44ec2ca54102422be44a8e3fb75b9690ccd74fff72b9ac7fc00ee"},
+    {file = "rapidfuzz-3.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:c458085e067c766112f089f78ce39eab2b69ba027d7bbb11d067a0b085774367"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1905d9319a97bed29f21584ca641190dbc9218a556202b77876f1e37618d2e03"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f176867f438ff2a43e6a837930153ca78fddb3ca94e378603a1e7b860d7869bf"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25498650e30122f4a5ad6b27c7614b4af8628c1d32b19d406410d33f77a86c80"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16153a97efacadbd693ccc612a3285df2f072fd07c121f30c2c135a709537075"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c0264d03dcee1bb975975b77c2fe041820fb4d4a25a99e3cb74ddd083d671ca"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17d79398849c1244f646425cf31d856eab9ebd67b7d6571273e53df724ca817e"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e08b01dc9369941a24d7e512b0d81bf514e7d6add1b93d8aeec3c8fa08a824e"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97c13f156f14f10667e1cfc4257069b775440ce005e896c09ce3aff21c9ae665"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8b76abfec195bf1ee6f9ec56c33ba5e9615ff2d0a9530a54001ed87e5a6ced3b"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b0ba20be465566264fa5580d874ccf5eabba6975dba45857e2c76e2df3359c6d"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:4d5cd86aca3f12e73bfc70015db7e8fc44122da03aa3761138b95112e83f66e4"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:9a16ef3702cecf16056c5fd66398b7ea8622ff4e3afeb00a8db3e74427e850af"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:392582aa784737d95255ca122ebe7dca3c774da900d100c07b53d32cd221a60e"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-win32.whl", hash = "sha256:ceb10039e7346927cec47eaa490b34abb602b537e738ee9914bb41b8de029fbc"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:cc4af7090a626c902c48db9b5d786c1faa0d8e141571e8a63a5350419ea575bd"},
+    {file = "rapidfuzz-3.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:3aff3b829b0b04bdf78bd780ec9faf5f26eac3591df98c35a0ae216c925ae436"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78a0d2a11bb3936463609777c6d6d4984a27ebb2360b58339c699899d85db036"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f8af980695b866255447703bf634551e67e1a4e1c2d2d26501858d9233d886d7"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d1a15fef1938b43468002f2d81012dbc9e7b50eb8533af202b0559c2dc7865d9"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4dbb1ebc9a811f38da33f32ed2bb5f58b149289b89eb11e384519e9ba7ca881"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:41219536634bd6f85419f38450ef080cfb519638125d805cf8626443e677dc61"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e3f882110f2f4894942e314451773c47e8b1b4920b5ea2b6dd2e2d4079dd3135"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c754ce1fab41b731259f100d5d46529a38aa2c9b683c92aeb7e96ef5b2898cd8"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:718ea99f84b16c4bdbf6a93e53552cdccefa18e12ff9a02c5041e621460e2e61"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9441aca94b21f7349cdb231cd0ce9ca251b2355836e8a02bf6ccbea5b442d7a9"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90167a48de3ed7f062058826608a80242b8561d0fb0cce2c610d741624811a61"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8e02425bfc7ebed617323a674974b70eaecd8f07b64a7d16e0bf3e766b93e3c9"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d48657a404fab82b2754faa813a10c5ad6aa594cb1829dca168a49438b61b4ec"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f8b62fdccc429e6643cefffd5df9c7bca65588d06e8925b78014ad9ad983bf5"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-win32.whl", hash = "sha256:63db612bb6da1bb9f6aa7412739f0e714b1910ec07bc675943044fe683ef192c"},
+    {file = "rapidfuzz-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:bb571dbd4cc93342be0ba632f0b8d7de4cbd9d959d76371d33716d2216090d41"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b27cea618601ca5032ea98ee116ca6e0fe67be7b286bcb0b9f956d64db697472"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d5592b08e3cadc9e06ef3af6a9d66b6ef1bf871ed5acd7f9b1e162d78806a65"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:58999b21d01dd353f49511a61937eac20c7a5b22eab87612063947081855d85f"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ee3909f611cc5860cc8d9f92d039fd84241ce7360b49ea88e657181d2b45f6"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00b5ee47b387fa3805f4038362a085ec58149135dc5bc640ca315a9893a16f9e"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4c647795c5b901091a68e210c76b769af70a33a8624ac496ac3e34d33366c0d"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:77ea62879932b32aba77ab23a9296390a67d024bf2f048dee99143be80a4ce26"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fee62ae76e3b8b9fff8aa2ca4061575ee358927ffbdb2919a8c84a98da59f78"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:231dc1cb63b1c8dd78c0597aa3ad3749a86a2b7e76af295dd81609522699a558"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:827ddf2d5d157ac3d1001b52e84c9e20366237a742946599ffc435af7fdd26d0"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c04ef83c9ca3162d200df36e933b3ea0327a2626cee2e01bbe55acbc004ce261"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:747265f39978bbaad356f5c6b6c808f0e8f5e8994875af0119b82b4700c55387"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:14791324f0c753f5a0918df1249b91515f5ddc16281fbaa5ec48bff8fa659229"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-win32.whl", hash = "sha256:b7b9cbc60e3eb08da6d18636c62c6eb6206cd9d0c7ad73996f7a1df3fc415b27"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:2084193fd8fd346db496a2220363437eb9370a06d1d5a7a9dba00a64390c6a28"},
+    {file = "rapidfuzz-3.8.1-cp39-cp39-win_arm64.whl", hash = "sha256:c9597a05d08e8103ad59ebdf29e3fbffb0d0dbf3b641f102cfbeadc3a77bde51"},
+    {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5f4174079dfe8ed1f13ece9bde7660f19f98ab17e0c0d002d90cc845c3a7e238"},
+    {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07d7d4a3c49a15146d65f06e44d7545628ca0437c929684e32ef122852f44d95"},
+    {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ef119fc127c982053fb9ec638dcc3277f83b034b5972eb05941984b9ec4a290"},
+    {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e57f9c2367706a320b78e91f8bf9a3b03bf9069464eb7b54455fa340d03e4c"},
+    {file = "rapidfuzz-3.8.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6d4f1956fe1fc618e34ac79a6ed84fff5a6f23e41a8a476dd3e8570f0b12f02b"},
+    {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:313bdcd16e9cd5e5568b4a31d18a631f0b04cc10a3fd916e4ef75b713e6f177e"},
+    {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a02def2eb526cc934d2125533cf2f15aa71c72ed4397afca38427ab047901e88"},
+    {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9d5d924970b07128c61c08eebee718686f4bd9838ef712a50468169520c953f"},
+    {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1edafc0a2737df277d3ddf401f3a73f76e246b7502762c94a3916453ae67e9b1"},
+    {file = "rapidfuzz-3.8.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:81fd28389bedab28251f0535b3c034b0e63a618efc3ff1d338c81a3da723adb3"},
+    {file = "rapidfuzz-3.8.1.tar.gz", hash = "sha256:a357aae6791118011ad3ab4f2a4aa7bd7a487e5f9981b390e9f3c2c5137ecadf"},
+]
+
+[package.extras]
+full = ["numpy"]
+
 [[package]]
 name = "referencing"
 version = "0.34.0"
@@ -1946,6 +2566,20 @@ urllib3 = ">=1.21.1,<3"
 socks = ["PySocks (>=1.5.6,!=1.5.7)"]
 use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 
+[[package]]
+name = "requests-toolbelt"
+version = "1.0.0"
+description = "A utility belt for advanced users of python-requests"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+    {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"},
+    {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"},
+]
+
+[package.dependencies]
+requests = ">=2.0.1,<3.0.0"
+
 [[package]]
 name = "rich"
 version = "13.7.1"
@@ -2074,46 +2708,72 @@ files = [
 
 [[package]]
 name = "ruff"
-version = "0.3.6"
+version = "0.3.7"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "ruff-0.3.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:732ef99984275534f9466fbc01121523caf72aa8c2bdeb36fd2edf2bc294a992"},
-    {file = "ruff-0.3.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:93699d61116807edc5ca1cdf9d2d22cf8d93335d59e3ff0ca7aee62c1818a736"},
-    {file = "ruff-0.3.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc4006cbc6c11fefc25f122d2eb4731d7a3d815dc74d67c54991cc3f99c90177"},
-    {file = "ruff-0.3.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:878ef1a55ce931f3ca23b690b159cd0659f495a4c231a847b00ca55e4c688baf"},
-    {file = "ruff-0.3.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb87788284af96725643eae9ab3ac746d8cc09aad140268523b019f7ac3cd98"},
-    {file = "ruff-0.3.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b2e79f8e1b6bd5411d7ddad3f2abff3f9d371beda29daef86400d416dedb7e02"},
-    {file = "ruff-0.3.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf48ec2c4bfae7837dc325c431a2932dc23a1485e71c59591c1df471ba234e0e"},
-    {file = "ruff-0.3.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c466a52c522e6a08df0af018f550902f154f5649ad09e7f0d43da766e7399ebc"},
-    {file = "ruff-0.3.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28ccf3fb6d1162a73cd286c63a5e4d885f46a1f99f0b392924bc95ccbd18ea8f"},
-    {file = "ruff-0.3.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b11e09439d9df6cc12d9f622065834654417c40216d271f639512d80e80e3e53"},
-    {file = "ruff-0.3.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:647f1fb5128a3e24ce68878b8050bb55044c45bb3f3ae4710d4da9ca96ede5cb"},
-    {file = "ruff-0.3.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2b0c4c70578ef1871a9ac5c85ed7a8c33470e976c73ba9211a111d2771b5f787"},
-    {file = "ruff-0.3.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e3da499ded004d0b956ab04248b2ae17e54a67ffc81353514ac583af5959a255"},
-    {file = "ruff-0.3.6-py3-none-win32.whl", hash = "sha256:4056480f5cf38ad278667c31b0ef334c29acdfcea617cb89c4ccbc7d96f1637f"},
-    {file = "ruff-0.3.6-py3-none-win_amd64.whl", hash = "sha256:f1aa621beed533f46e9c7d6fe00e7f6e4570155b61d8f020387b72ace2b42e04"},
-    {file = "ruff-0.3.6-py3-none-win_arm64.whl", hash = "sha256:7c8a2a0e0cab077a07465259ffe3b3c090e747ca8097c5dc4c36ca0fdaaac90d"},
-    {file = "ruff-0.3.6.tar.gz", hash = "sha256:26071fb530038602b984e3bbe1443ef82a38450c4dcb1344a9caf67234ff9756"},
+    {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"},
+    {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"},
+    {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"},
+    {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"},
+    {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"},
+    {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"},
+    {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"},
+    {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"},
+    {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"},
+    {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"},
+    {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"},
+    {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"},
+    {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"},
+    {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"},
+    {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"},
+    {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"},
+    {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"},
+]
+
+[[package]]
+name = "secretstorage"
+version = "3.3.3"
+description = "Python bindings to FreeDesktop.org Secret Service API"
+optional = false
+python-versions = ">=3.6"
+files = [
+    {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"},
+    {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"},
 ]
 
+[package.dependencies]
+cryptography = ">=2.0"
+jeepney = ">=0.6"
+
 [[package]]
 name = "setuptools"
-version = "69.2.0"
+version = "69.5.1"
 description = "Easily download, build, install, upgrade, and uninstall Python packages"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"},
-    {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"},
+    {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
+    {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
 ]
 
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
 testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
 
+[[package]]
+name = "shellingham"
+version = "1.5.4"
+description = "Tool to Detect Surrounding Shell"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"},
+    {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
+]
+
 [[package]]
 name = "six"
 version = "1.16.0"
@@ -2149,20 +2809,20 @@ files = [
 
 [[package]]
 name = "sphinx"
-version = "7.2.6"
+version = "7.3.5"
 description = "Python documentation generator"
 optional = false
 python-versions = ">=3.9"
 files = [
-    {file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"},
-    {file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"},
+    {file = "sphinx-7.3.5-py3-none-any.whl", hash = "sha256:e6a8f418321429669ef9c1e20c1f294b3fe1f248206b46044c86e1545480687f"},
+    {file = "sphinx-7.3.5.tar.gz", hash = "sha256:30d03bbaa53b77d38863fd6b95cc4edb4a84a1512787b3b0c12fb3b4fb25d9e9"},
 ]
 
 [package.dependencies]
-alabaster = ">=0.7,<0.8"
+alabaster = ">=0.7.14,<0.8.0"
 babel = ">=2.9"
 colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
-docutils = ">=0.18.1,<0.21"
+docutils = ">=0.18.1,<0.22"
 imagesize = ">=1.3"
 Jinja2 = ">=3.0"
 packaging = ">=21.0"
@@ -2175,11 +2835,12 @@ sphinxcontrib-htmlhelp = ">=2.0.0"
 sphinxcontrib-jsmath = "*"
 sphinxcontrib-qthelp = "*"
 sphinxcontrib-serializinghtml = ">=1.1.9"
+tomli = {version = ">=2", markers = "python_version < \"3.11\""}
 
 [package.extras]
 docs = ["sphinxcontrib-websupport"]
-lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"]
-test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"]
+lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"]
+test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"]
 
 [[package]]
 name = "sphinx-material"
@@ -2415,6 +3076,17 @@ files = [
 docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
 test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"]
 
+[[package]]
+name = "trove-classifiers"
+version = "2024.4.10"
+description = "Canonical source for classifiers on PyPI (pypi.org)."
+optional = false
+python-versions = "*"
+files = [
+    {file = "trove-classifiers-2024.4.10.tar.gz", hash = "sha256:49f40bb6a746b72a1cba4f8d55ee8252169cda0f70802e3fd24f04b7fb25a492"},
+    {file = "trove_classifiers-2024.4.10-py3-none-any.whl", hash = "sha256:678bd6fcc5218d72e3304e27a608acc9b91e17bd00c3f3d8c968497c843ad98b"},
+]
+
 [[package]]
 name = "typing-extensions"
 version = "4.11.0"
@@ -2456,13 +3128,13 @@ zstd = ["zstandard (>=0.18.0)"]
 
 [[package]]
 name = "virtualenv"
-version = "20.25.1"
+version = "20.25.2"
 description = "Virtual Python Environment builder"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"},
-    {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"},
+    {file = "virtualenv-20.25.2-py3-none-any.whl", hash = "sha256:6e1281a57849c8a54da89ba82e5eb7c8937b9d057ff01aaf5bc9afaa3552e90f"},
+    {file = "virtualenv-20.25.2.tar.gz", hash = "sha256:fa7edb8428620518010928242ec17aa7132ae435319c29c1651d1cf4c4173aad"},
 ]
 
 [package.dependencies]
@@ -2471,7 +3143,7 @@ filelock = ">=3.12.2,<4"
 platformdirs = ">=3.9.1,<5"
 
 [package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
 test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
 
 [[package]]
@@ -2496,7 +3168,95 @@ files = [
     {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
 ]
 
+[[package]]
+name = "xattr"
+version = "1.1.0"
+description = "Python wrapper for extended filesystem attributes"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef2fa0f85458736178fd3dcfeb09c3cf423f0843313e25391db2cfd1acec8888"},
+    {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ccab735d0632fe71f7d72e72adf886f45c18b7787430467ce0070207882cfe25"},
+    {file = "xattr-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9013f290387f1ac90bccbb1926555ca9aef75651271098d99217284d9e010f7c"},
+    {file = "xattr-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcd5dfbcee73c7be057676ecb900cabb46c691aff4397bf48c579ffb30bb963"},
+    {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6480589c1dac7785d1f851347a32c4a97305937bf7b488b857fe8b28a25de9e9"},
+    {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08f61cbed52dc6f7c181455826a9ff1e375ad86f67dd9d5eb7663574abb32451"},
+    {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:918e1f83f2e8a072da2671eac710871ee5af337e9bf8554b5ce7f20cdb113186"},
+    {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0f06e0c1e4d06b4e0e49aaa1184b6f0e81c3758c2e8365597918054890763b53"},
+    {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a641ac038a9f53d2f696716147ca4dbd6a01998dc9cd4bc628801bc0df7f4d"},
+    {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7e4ca0956fd11679bb2e0c0d6b9cdc0f25470cc00d8da173bb7656cc9a9cf104"},
+    {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6881b120f9a4b36ccd8a28d933bc0f6e1de67218b6ce6e66874e0280fc006844"},
+    {file = "xattr-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab29d9288aa28e68a6f355ddfc3f0a7342b40c9012798829f3e7bd765e85c2c"},
+    {file = "xattr-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c80bbf55339c93770fc294b4b6586b5bf8e85ec00a4c2d585c33dbd84b5006"},
+    {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1418705f253b6b6a7224b69773842cac83fcbcd12870354b6e11dd1cd54630f"},
+    {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687e7d18611ef8d84a6ecd8f4d1ab6757500c1302f4c2046ce0aa3585e13da3f"},
+    {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6ceb9efe0657a982ccb8b8a2efe96b690891779584c901d2f920784e5d20ae3"},
+    {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b489b7916f239100956ea0b39c504f3c3a00258ba65677e4c8ba1bd0b5513446"},
+    {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0a9c431b0e66516a078125e9a273251d4b8e5ba84fe644b619f2725050d688a0"},
+    {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1a5921ea3313cc1c57f2f53b63ea8ca9a91e48f4cc7ebec057d2447ec82c7efe"},
+    {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6ad2a7bd5e6cf71d4a862413234a067cf158ca0ae94a40d4b87b98b62808498"},
+    {file = "xattr-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0683dae7609f7280b0c89774d00b5957e6ffcb181c6019c46632b389706b77e6"},
+    {file = "xattr-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54cb15cd94e5ef8a0ef02309f1bf973ba0e13c11e87686e983f371948cfee6af"},
+    {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff6223a854229055e803c2ad0c0ea9a6da50c6be30d92c198cf5f9f28819a921"},
+    {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d44e8f955218638c9ab222eed21e9bd9ab430d296caf2176fb37abe69a714e5c"},
+    {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:caab2c2986c30f92301f12e9c50415d324412e8e6a739a52a603c3e6a54b3610"},
+    {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d6eb7d5f281014cd44e2d847a9107491af1bf3087f5afeded75ed3e37ec87239"},
+    {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:47a3bdfe034b4fdb70e5941d97037405e3904accc28e10dbef6d1c9061fb6fd7"},
+    {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:00d2b415cf9d6a24112d019e721aa2a85652f7bbc9f3b9574b2d1cd8668eb491"},
+    {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:78b377832dd0ee408f9f121a354082c6346960f7b6b1480483ed0618b1912120"},
+    {file = "xattr-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6461a43b585e5f2e049b39bcbfcb6391bfef3c5118231f1b15d10bdb89ef17fe"},
+    {file = "xattr-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24d97f0d28f63695e3344ffdabca9fcc30c33e5c8ccc198c7524361a98d526f2"},
+    {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ad47d89968c9097900607457a0c89160b4771601d813e769f68263755516065"},
+    {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc53cab265f6e8449bd683d5ee3bc5a191e6dd940736f3de1a188e6da66b0653"},
+    {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cd11e917f5b89f2a0ad639d9875943806c6c9309a3dd02da5a3e8ef92db7bed9"},
+    {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9c5a78c7558989492c4cb7242e490ffb03482437bf782967dfff114e44242343"},
+    {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cebcf8a303a44fbc439b68321408af7267507c0d8643229dbb107f6c132d389c"},
+    {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b0d73150f2f9655b4da01c2369eb33a294b7f9d56eccb089819eafdbeb99f896"},
+    {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:793c01deaadac50926c0e1481702133260c7cb5e62116762f6fe1543d07b826f"},
+    {file = "xattr-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e189e440bcd04ccaad0474720abee6ee64890823ec0db361fb0a4fb5e843a1bf"},
+    {file = "xattr-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afacebbc1fa519f41728f8746a92da891c7755e6745164bd0d5739face318e86"},
+    {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b1664edf003153ac8d1911e83a0fc60db1b1b374ee8ac943f215f93754a1102"},
+    {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda2684228798e937a7c29b0e1c7ef3d70e2b85390a69b42a1c61b2039ba81de"},
+    {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b735ac2625a4fc2c9343b19f806793db6494336338537d2911c8ee4c390dda46"},
+    {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fa6a7af7a4ada43f15ccc58b6f9adcdbff4c36ba040013d2681e589e07ae280a"},
+    {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1059b2f726e2702c8bbf9bbf369acfc042202a4cc576c2dec6791234ad5e948"},
+    {file = "xattr-1.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e2255f36ebf2cb2dbf772a7437ad870836b7396e60517211834cf66ce678b595"},
+    {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba4f80b9855cc98513ddf22b7ad8551bc448c70d3147799ea4f6c0b758fb466"},
+    {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb70c16e7c3ae6ba0ab6c6835c8448c61d8caf43ea63b813af1f4dbe83dd156"},
+    {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83652910ef6a368b77b00825ad67815e5c92bfab551a848ca66e9981d14a7519"},
+    {file = "xattr-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7a92aff66c43fa3e44cbeab7cbeee66266c91178a0f595e044bf3ce51485743b"},
+    {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4f71b673339aeaae1f6ea9ef8ea6c9643c8cd0df5003b9a0eaa75403e2e06c"},
+    {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a20de1c47b5cd7b47da61799a3b34e11e5815d716299351f82a88627a43f9a96"},
+    {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23705c7079b05761ff2fa778ad17396e7599c8759401abc05b312dfb3bc99f69"},
+    {file = "xattr-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:27272afeba8422f2a9d27e1080a9a7b807394e88cce73db9ed8d2dde3afcfb87"},
+    {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd43978966de3baf4aea367c99ffa102b289d6c2ea5f3d9ce34a203dc2f2ab73"},
+    {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded771eaf27bb4eb3c64c0d09866460ee8801d81dc21097269cf495b3cac8657"},
+    {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca300c0acca4f0cddd2332bb860ef58e1465d376364f0e72a1823fdd58e90d"},
+    {file = "xattr-1.1.0.tar.gz", hash = "sha256:fecbf3b05043ed3487a28190dec3e4c4d879b2fcec0e30bafd8ec5d4b6043630"},
+]
+
+[package.dependencies]
+cffi = ">=1.16.0"
+
+[package.extras]
+test = ["pytest"]
+
+[[package]]
+name = "zipp"
+version = "3.18.1"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+optional = false
+python-versions = ">=3.8"
+files = [
+    {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
+    {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
+
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.10"
-content-hash = "34c34c909d893bdaacd32f0ba728b85bcf821ac91f5c22c4925abdf18c3c33b5"
+content-hash = "ee97531ae96fbf1b3bdd6b64c748ef38c56134c8687e60b2a4d8b818c9548e9e"
diff --git a/pyproject.toml b/pyproject.toml
index 091cc85..c2e6331 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
 
 [tool.poetry]
 name = "byteclasses"
-version = "0.1.0"
+version = "0.2.0"
 description = "A python package designed for creating and managing blobs of binary data using dataclass like structures."
 keywords = ["struct", "structures", "binary", "blobs", "raw", "bytes", "dataclasses"]
 repository = "https://github.com/thetacom/byteclasses"
@@ -36,9 +36,11 @@ rich               = "^13.7.1"
 
 [tool.poetry.group.dev.dependencies]
 bandit             = "^1.7.8"
-black              = "^24.3.0"
+black              = "^24.4.0"
 ipykernel          = "^6.29.3"
 ipython            = "^8.22.2"
+mypy               = "^1.9.0"
+poetry-plugin-export = "^1.7.1"
 pre-commit         = "^3.7.0"
 pycodestyle        = "^2.11.1"
 pylint             = "^3.1.0"
@@ -49,7 +51,6 @@ sphinx             = "^7.2.6"
 sphinx-material    = "^0.0.36"
 myst-parser        = "^2.0.0"
 nbsphinx           = "^0.9.3"
-rich = "^13.7.1"
 
 
 [tool.black]


Test patch:
diff --git a/tests/collections/test_fixed_array.py b/tests/collections/test_fixed_array.py
index 71f7c08..8d11b41 100644
--- a/tests/collections/test_fixed_array.py
+++ b/tests/collections/test_fixed_array.py
@@ -1,14 +1,25 @@
 """Test suite for FixedArray Byteclass."""
 
 from byteclasses.types.collections.fixed_array import FixedArray
-from byteclasses.types.primitives.integers import UInt8
+from byteclasses.types.primitives.integers import UInt8, UInt32
 
 NULL_BYTE = b"\x00"
 
 
 def test_fixed_array_creation():
     """Test FixedArray creation and properties."""
-    expected_length = 8
-    fa = FixedArray(UInt8, expected_length)
+    expected_count = 8
+    expected_length = expected_count * len(UInt8())
+    fa = FixedArray(expected_count)
+    assert len(fa) == expected_count
+    assert fa.data == NULL_BYTE * expected_length
+
+
+def test_fixed_array_creation_with_type():
+    """Test FixedArray creation and properties."""
+    expected_count = 8
+    item_type = UInt32
+    expected_length = expected_count * len(item_type())
+    fa = FixedArray(expected_count, item_type)
     assert len(fa) == expected_length
     assert fa.data == NULL_BYTE * expected_length
diff --git a/tests/primitives/generics/test_bitfield.py b/tests/primitives/generics/test_bitfield.py
index 740386e..c59f673 100644
--- a/tests/primitives/generics/test_bitfield.py
+++ b/tests/primitives/generics/test_bitfield.py
@@ -276,3 +276,24 @@ def test_named_bit_set_with_valid_int():
     assert bf.first_bit is True
     bf.first_bit = 0
     assert bf.first_bit is False
+
+
+def test_wide_bitpos():
+    """Test BitPos with non-standard bit width."""
+
+    class CustomBitField(BitField):
+        """Custom BitField with named multi-bit BitPos."""
+
+        lower = BitPos(0, bit_width=4)
+        upper = BitPos(4, bit_width=4)
+
+    cbf = CustomBitField(data=b"\xF0")
+    assert cbf.data == b"\xf0"
+    assert bin(cbf.data[0]) == "0b11110000"
+    assert cbf.lower == 0
+    assert cbf.upper == 15
+    cbf.data = b"\xa5"
+    assert cbf.data == b"\xa5"
+    assert bin(cbf.data[0]) == "0b10100101"
+    assert cbf.lower == 5
+    assert cbf.upper == 10
diff --git a/tests/primitives/generics/test_generics.py b/tests/primitives/generics/test_generics.py
index ec7a5f5..6038ab3 100644
--- a/tests/primitives/generics/test_generics.py
+++ b/tests/primitives/generics/test_generics.py
@@ -24,12 +24,24 @@ def test_byte_primitive():
     assert byte.data == DATA_BYTE * expected_length
 
 
+def test_byte_init_with_value():
+    """Test Byte init with value."""
+    byte = Byte(b"\x01")
+    assert byte.data == b"\x01"
+
+
 def test_byte_init_with_data():
     """Test Byte init with data."""
-    byte = Byte(b"\x01")
+    byte = Byte(data=b"\x01")
     assert byte.data == b"\x01"
 
 
+def test_byte_init_with_value_and_data():
+    """Test Byte init with value_and_data."""
+    with pytest.raises(ValueError):
+        _ = Byte(b"\x00", data=b"\x01")
+
+
 def test_byte_init_with_invalid_value():
     """Test Byte init with invalid value."""
     with pytest.raises(TypeError):
@@ -64,3 +76,15 @@ def test_qword_primitive():
     assert len(qword) == expected_length
     assert qword.value == DATA_BYTE * expected_length
     assert qword.data == DATA_BYTE * expected_length
+
+
+def test_byte_str():
+    """Test Byte __str__ method."""
+    byte = Byte(b"\x01")
+    assert str(byte) == "b'\\x01'"
+
+
+def test_byte_repr():
+    """Test Byte __repr__ method."""
+    byte = Byte(b"\x01")
+    assert repr(byte) == "Byte(data=b'\\x01', byte_order=b'@')"
diff --git a/tests/primitives/integers/__init__.py b/tests/primitives/integers/__init__.py
deleted file mode 100644
index cc11c72..0000000
--- a/tests/primitives/integers/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-"""Unit tests for byteclasses primitive integer types."""
diff --git a/tests/primitives/integers/test_int16.py b/tests/primitives/integers/test_int16.py
deleted file mode 100644
index 2d969f3..0000000
--- a/tests/primitives/integers/test_int16.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import pytest
-
-from byteclasses.types.primitives.integers import Int16, UnderflowError
-
-
-def test_int16_instance_properties():
-    """Test int16 instance properties."""
-    int16 = Int16(0)
-    assert int16.signed is True
-    assert int16.min == -32768
-    assert int16.max == 32767
-    assert int16.byte_order.value == b"@"
-    assert int16.endianness == "NATIVE"
-    assert int16.fmt == b"@h"
-    int16.value = 0
-    assert int16 == 0
-    int16.value = 1
-    assert int16 == 1
-
-
-def test_int16_bounds():
-    """Test int16 bounds."""
-    int16 = Int16()
-    int16.value = 32767
-    assert int16 == 32767
-    with pytest.raises(OverflowError):
-        int16.value = 32768
-    int16.value = -32768
-    assert int16 == -32768
-    with pytest.raises(UnderflowError):
-        int16.value = -32769
-
-
-def test_int16_value_to_data():
-    """Test int16 value to data."""
-    int16 = Int16(0)
-    assert int16.data == b"\x00\x00"
-    int16.value = 1
-    assert int16.data == b"\x01\x00"
-    int16.value = 32767
-    assert int16.data == b"\xff\x7f"
-    int16.value = -32768
-    assert int16.data == b"\x00\x80"
-    int16.value = -1
-    assert int16.data == b"\xff\xff"
diff --git a/tests/primitives/integers/test_int32.py b/tests/primitives/integers/test_int32.py
deleted file mode 100644
index 930ca10..0000000
--- a/tests/primitives/integers/test_int32.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import pytest
-
-from byteclasses.types.primitives.integers import Int32, UnderflowError
-
-
-def test_int32_instance_properties():
-    """Test int32 instance properties."""
-    int32 = Int32(0)
-    assert int32.signed is True
-    assert int32.min == -2147483648
-    assert int32.max == 2147483647
-    assert int32.byte_order.value == b"@"
-    assert int32.endianness == "NATIVE"
-    assert int32.fmt == b"@i"
-    int32.value = 0
-    assert int32 == 0
-    int32.value = 1
-    assert int32 == 1
-
-
-def test_int32_bounds():
-    """Test int32 bounds."""
-    int32 = Int32()
-    int32.value = 2147483647
-    assert int32 == 2147483647
-    with pytest.raises(OverflowError):
-        int32.value = 2147483648
-    int32.value = -2147483648
-    assert int32 == -2147483648
-    with pytest.raises(UnderflowError):
-        int32.value = -2147483649
-
-
-def test_int32_value_to_data():
-    """Test int32 value to data."""
-    int32 = Int32(0)
-    assert int32.data == b"\x00\x00\x00\x00"
-    int32.value = 1
-    assert int32.data == b"\x01\x00\x00\x00"
-    int32.value = 2147483647
-    assert int32.data == b"\xff\xff\xff\x7f"
-    int32.value = -2147483648
-    assert int32.data == b"\x00\x00\x00\x80"
-    int32.value = -1
-    assert int32.data == b"\xff\xff\xff\xff"
diff --git a/tests/primitives/integers/test_int64.py b/tests/primitives/integers/test_int64.py
deleted file mode 100644
index c4c3efa..0000000
--- a/tests/primitives/integers/test_int64.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import pytest
-
-from byteclasses.types.primitives.integers import Int64, UnderflowError
-
-
-def test_int64_instance_properties():
-    """Test int64 instance properties."""
-    int64 = Int64(0)
-    assert int64.signed is True
-    assert int64.min == -9223372036854775808
-    assert int64.max == 9223372036854775807
-    assert int64.byte_order.value == b"@"
-    assert int64.endianness == "NATIVE"
-    assert int64.fmt == b"@q"
-    int64.value = 0
-    assert int64 == 0
-    int64.value = 1
-    assert int64 == 1
-
-
-def test_int64_bounds():
-    """Test int64 bounds."""
-    int64 = Int64()
-    int64.value = 9223372036854775807
-    assert int64 == 9223372036854775807
-    with pytest.raises(OverflowError):
-        int64.value = 9223372036854775808
-    int64.value = -9223372036854775808
-    assert int64 == -9223372036854775808
-    with pytest.raises(UnderflowError):
-        int64.value = -9223372036854775809
-
-
-def test_int64_value_to_data():
-    """Test int64 value to data."""
-    int64 = Int64(0)
-    assert int64.data == b"\x00\x00\x00\x00\x00\x00\x00\x00"
-    int64.value = 1
-    assert int64.data == b"\x01\x00\x00\x00\x00\x00\x00\x00"
-    int64.value = 9223372036854775807
-    assert int64.data == b"\xff\xff\xff\xff\xff\xff\xff\x7f"
-    int64.value = -9223372036854775808
-    assert int64.data == b"\x00\x00\x00\x00\x00\x00\x00\x80"
-    int64.value = -1
-    assert int64.data == b"\xff\xff\xff\xff\xff\xff\xff\xff"
diff --git a/tests/primitives/integers/test_int8.py b/tests/primitives/integers/test_int8.py
deleted file mode 100644
index 47e0d32..0000000
--- a/tests/primitives/integers/test_int8.py
+++ /dev/null
@@ -1,152 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import math
-import operator
-
-import pytest
-
-from byteclasses.types.primitives.integers import Int8, UnderflowError, _FixedInt
-
-
-def test_fixedint_missing_type_char_property():
-    """Test _FixedInt instance with no `_type_char` class attribute."""
-
-    class InvalidInt(_FixedInt):
-        """Invalid Fixed Int Class definition."""
-
-        _length = 1
-        _signed = False
-
-    with pytest.raises(NotImplementedError):
-        _ = InvalidInt()
-
-
-def test_fixedint_missing_length_property():
-    """Test _FixedInt instance with no `_length` class attribute."""
-
-    class InvalidInt(_FixedInt):
-        """Invalid Fixed Int Class definition."""
-
-        _type_char = b"b"
-        _signed = False
-
-    with pytest.raises(NotImplementedError):
-        _ = InvalidInt()
-
-
-def test_fixedint_missing_signed_property():
-    """Test _FixedInt instance with no `_signed` class attribute."""
-
-    class InvalidInt(_FixedInt):
-        """Invalid Fixed Int Class definition."""
-
-        _type_char: bytes = b"b"
-        _length: int = 1
-
-    with pytest.raises(NotImplementedError):
-        _ = InvalidInt()
-
-
-def test_int8_set_bad_value():
-    """Test int8 set bad value."""
-    with pytest.raises(NotImplementedError):
-        _ = _FixedInt()
-
-
-def test_int8_instance_properties():
-    """Test int8 instance properties."""
-    int8 = Int8(0)
-    assert int8.signed is True
-    assert int8.min == -128
-    assert int8.max == 127
-    assert int8.byte_order.value == b"@"
-    assert int8.endianness == "NATIVE"
-    assert int8.fmt == b"@b"
-    int8.value = 0
-    assert int8 == 0
-    int8.value = 1
-    assert int8 == 1
-
-
-def test_int8_bounds():
-    """Test int8 bounds."""
-    int8 = Int8()
-    int8.value = 127
-    assert int8 == 127
-    with pytest.raises(OverflowError):
-        int8.value = 128
-    int8.value = -128
-    assert int8 == -128
-    with pytest.raises(UnderflowError):
-        int8.value = -129
-
-
-def test_int8_assign_non_int_value():
-    """Test assignment with non-nteger value."""
-    var1 = Int8()
-    var1.value = 1.1
-    assert var1.value == 1
-
-
-def test_int8_value_to_data():
-    """Test int8 value to data."""
-    int8 = Int8(0)
-    assert int8.data == b"\x00"
-    int8.value = 1
-    assert int8.data == b"\x01"
-    int8.value = 127
-    assert int8.data == b"\x7f"
-    int8.value = -128
-    assert int8.data == b"\x80"
-    int8.value = -1
-    assert int8.data == b"\xff"
-
-
-def test_int8_attach_mv_no_retain():
-    """Test attaching memoryview to int8 without retaining value."""
-    test_mv = memoryview(bytearray(2))
-    int8_1 = Int8(0x12)
-    int8_2 = Int8(0x34)
-    int8_1.attach(test_mv[:1], retain_value=False)
-    int8_2.attach(test_mv[1:], retain_value=False)
-    assert int8_1.value == 0x00
-    assert int8_2.value == 0x00
-    assert test_mv == b"\x00\x00"
-    test_mv[:] = b"\x12\x34"
-    assert int8_1.value == 0x12
-    assert int8_2.value == 0x34
-
-
-def test_int8_attach_mv_with_retain():
-    """Test attaching memoryview to int8 while retaining value."""
-    test_mv = memoryview(bytearray(3))
-    int8_1 = Int8(0x12)
-    int8_2 = Int8(0x34)
-    int8_1.attach(test_mv[:1])
-    int8_2.attach(test_mv[2:])
-    assert int8_1.value == 0x12
-    assert int8_2.value == 0x34
-    assert test_mv == b"\x12\x00\x34"
-    test_mv[:] = b"\x01\x00\x23"
-    assert int8_1.value == 0x01
-    assert int8_2.value == 0x23
-
-
-def test_int8_int_dunder():
-    """Test Int8 __int__ method."""
-    var = Int8(8)
-    assert int(var) == 8
-
-
-def test_int8_truncate():
-    """Test Int8 __trunc__ method."""
-    var1 = Int8(10)
-    result = math.trunc(var1)
-    assert result == 10
-
-
-def test_int8_index():
-    """Test Int8 __index__ method."""
-    var1 = Int8(10)
-    result = operator.index(var1)
-    assert result == 10
diff --git a/tests/primitives/integers/test_uint16.py b/tests/primitives/integers/test_uint16.py
deleted file mode 100644
index 68fcdeb..0000000
--- a/tests/primitives/integers/test_uint16.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import pytest
-
-from byteclasses.types.primitives.integers import UInt16, UnderflowError
-
-
-def test_uint16_instance_properties():
-    """Test uint16 instance properties."""
-    uint16 = UInt16(0)
-    assert uint16.signed is False
-    assert uint16.min == 0
-    assert uint16.max == 65535
-    assert uint16.byte_order.value == b"@"
-    assert uint16.endianness == "NATIVE"
-    assert uint16.fmt == b"@H"
-    uint16.value = 0
-    assert uint16 == 0
-    uint16.value = 1
-    assert uint16 == 1
-
-
-def test_uint16_bounds():
-    """Test uint16 bounds."""
-    uint16 = UInt16()
-    uint16.value = 65535
-    assert uint16 == 65535
-    with pytest.raises(OverflowError):
-        uint16.value = 65536
-    uint16.value = 0
-    assert uint16 == 0
-    with pytest.raises(UnderflowError):
-        uint16.value = -1
-
-
-def test_uint16_value_to_data():
-    """Test uint16 value to data."""
-    uint16 = UInt16(0)
-    assert uint16.data == b"\x00\x00"
-    uint16.value = 1
-    assert uint16.data == b"\x01\x00"
-    uint16.value = 65535
-    assert uint16.data == b"\xff\xff"
diff --git a/tests/primitives/integers/test_uint32.py b/tests/primitives/integers/test_uint32.py
deleted file mode 100644
index 3bdc5e0..0000000
--- a/tests/primitives/integers/test_uint32.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import pytest
-
-from byteclasses.types.primitives.integers import UInt32, UnderflowError
-
-
-def test_uint32_instance_properties():
-    """Test uint32 instance properties."""
-    uint32 = UInt32(0)
-    assert uint32.signed is False
-    assert uint32.min == 0
-    assert uint32.max == 4294967295
-    assert uint32.byte_order.value == b"@"
-    assert uint32.endianness == "NATIVE"
-    assert uint32.fmt == b"@I"
-    uint32.value = 0
-    assert uint32 == 0
-    uint32.value = 1
-    assert uint32 == 1
-
-
-def test_uint32_bounds():
-    """Test uint32 bounds."""
-    uint32 = UInt32()
-    uint32.value = 4294967295
-    assert uint32 == 4294967295
-    with pytest.raises(OverflowError):
-        uint32.value = 4294967296
-    uint32.value = 0
-    assert uint32 == 0
-    with pytest.raises(UnderflowError):
-        uint32.value = -1
-
-
-def test_uint32_value_to_data():
-    """Test uint32 value to data."""
-    uint32 = UInt32(0)
-    assert uint32.data == b"\x00\x00\x00\x00"
-    uint32.value = 1
-    assert uint32.data == b"\x01\x00\x00\x00"
-    uint32.value = 4294967295
-    assert uint32.data == b"\xff\xff\xff\xff"
diff --git a/tests/primitives/integers/test_uint64.py b/tests/primitives/integers/test_uint64.py
deleted file mode 100644
index e0c0891..0000000
--- a/tests/primitives/integers/test_uint64.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import pytest
-
-from byteclasses.types.primitives.integers import UInt64, UnderflowError
-
-
-def test_uint64_instance_properties():
-    """Test UInt64 instance properties."""
-    uint64 = UInt64(0)
-    assert uint64.signed is False
-    assert uint64.min == 0
-    assert uint64.max == 18446744073709551615
-    assert uint64.byte_order.value == b"@"
-    assert uint64.endianness == "NATIVE"
-    assert uint64.fmt == b"@Q"
-    uint64.value = 0
-    assert uint64 == 0
-    uint64.value = 1
-    assert uint64 == 1
-
-
-def test_uint64_bounds():
-    """Test UInt64 bounds."""
-    uint64 = UInt64()
-    uint64.value = 18446744073709551615
-    assert uint64 == 18446744073709551615
-    with pytest.raises(OverflowError):
-        uint64.value = 18446744073709551616
-    uint64.value = 0
-    assert uint64 == 0
-    with pytest.raises(UnderflowError):
-        uint64.value = -1
-
-
-def test_uint64_value_to_data():
-    """Test UInt64 value to data."""
-    uint64 = UInt64(0)
-    assert uint64.data == b"\x00\x00\x00\x00\x00\x00\x00\x00"
-    uint64.value = 1
-    assert uint64.data == b"\x01\x00\x00\x00\x00\x00\x00\x00"
-    uint64.value = 18446744073709551615
-    assert uint64.data == b"\xff\xff\xff\xff\xff\xff\xff\xff"
diff --git a/tests/primitives/integers/test_uint8.py b/tests/primitives/integers/test_uint8.py
deleted file mode 100644
index 64a4fe7..0000000
--- a/tests/primitives/integers/test_uint8.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""Unit tests for byteclasses primitive integer type constructors."""
-
-import pytest
-
-from byteclasses.types.primitives.integers import UInt8, UnderflowError
-
-
-def test_uint8_instance_properties():
-    """Test uint8 instance properties."""
-    uint8 = UInt8(0)
-    assert uint8.signed is False
-    assert uint8.min == 0
-    assert uint8.max == 255
-    assert uint8.byte_order.value == b"@"
-    assert uint8.endianness == "NATIVE"
-    assert uint8.fmt == b"@B"
-    uint8.value = 0
-    assert uint8 == 0
-    uint8.value = 1
-    assert uint8 == 1
-
-
-def test_uint8_bounds():
-    """Test uint8 bounds."""
-    uint8 = UInt8()
-    uint8.value = 255
-    assert uint8 == 255
-    with pytest.raises(OverflowError):
-        uint8.value = 256
-    uint8.value = 0
-    assert uint8 == 0
-    with pytest.raises(UnderflowError):
-        uint8.value = -1
-
-
-def test_uint8_value_to_data():
-    """Test uint8 value to data."""
-    uint8 = UInt8(0)
-    assert uint8.data == b"\x00"
-    uint8.value = 1
-    assert uint8.data == b"\x01"
-    uint8.value = 255
-    assert uint8.data == b"\xff"
-    uint8.value = 0
-    assert uint8.data == b"\x00"
diff --git a/tests/primitives/test_arithmetic_operations.py b/tests/primitives/test_arithmetic_operations.py
index 2c20dbb..776ca99 100644
--- a/tests/primitives/test_arithmetic_operations.py
+++ b/tests/primitives/test_arithmetic_operations.py
@@ -7,312 +7,500 @@
 from byteclasses.types.primitives.integers import UInt8, UnderflowError
 
 
-def test_fixed_int_add_to_fixed_int():
-    """Test add between two fixed length integers."""
-    integer1 = UInt8(0x01)
-    integer2 = UInt8(0x02)
-    result = integer1 + integer2
+def test_fixedint_add_to_fixedint():
+    """Test add between two _FixedInts."""
+    var1 = UInt8(0x01)
+    var2 = UInt8(0x02)
+    result = var1 + var2
     assert isinstance(result, Integral)
     assert result == 0x03
 
 
-def test_fixed_int_add_to_int():
+def test_fixedint_add_to_int():
     """Test add between a fixed length integer and an integer."""
-    integer1 = UInt8(0x01)
-    integer2 = 2
-    result = integer1 + integer2
+    var1 = UInt8(0x01)
+    var2 = 2
+    result = var1 + var2
     assert isinstance(result, Integral)
     assert result == 0x03
 
 
-def test_fixed_int_radd_to_int():
+def test_fixedint_add_to_unsupported():
+    """Test add between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x01)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var1 + var2
+
+
+def test_fixedint_radd_to_int():
     """Test radd between a fixed length integer and an integer."""
-    integer1 = 1
-    integer2 = UInt8(0x02)
-    result = integer1 + integer2
+    var1 = 1
+    var2 = UInt8(0x02)
+    result = var1 + var2
     assert isinstance(result, Integral)
     assert result == 0x03
 
 
-def test_fixed_int_add_to_fixed_int_with_overflow():
-    """Test add between two fixed length integers with overflow."""
-    integer1 = UInt8(0xFF)
-    integer2 = UInt8(0x01)
+def test_fixedint_radd_to_unsupported():
+    """Test reverse add between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x01)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var2 + var1
+
+
+def test_fixedint_add_to_fixedint_with_overflow():
+    """Test add between two _FixedInts with overflow."""
+    var1 = UInt8(0xFF)
+    var2 = UInt8(0x01)
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1 + integer2)
+        _ = UInt8(var1 + var2)
 
 
-def test_fixed_int_add_to_int_with_overflow():
+def test_fixedint_add_to_int_with_overflow():
     """Test add between a fixed length integer and an integer with overflow."""
-    integer1 = UInt8(0xFF)
-    integer2 = 1
+    var1 = UInt8(0xFF)
+    var2 = 1
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1 + integer2)
+        _ = UInt8(var1 + var2)
 
 
-def test_fixed_int_radd_to_int_with_overflow():
+def test_fixedint_radd_to_int_with_overflow():
     """Test radd between a fixed length integer and an integer with overflow."""
-    integer1 = 255
-    integer2 = UInt8(0x01)
+    var1 = 255
+    var2 = UInt8(0x01)
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1 + integer2)
+        _ = UInt8(var1 + var2)
 
 
-def test_fixed_int_sub_from_fixed_int():
-    """Test sub between two fixed length integers."""
-    integer1 = UInt8(0x03)
-    integer2 = UInt8(0x02)
-    result = integer1 - integer2
+def test_fixedint_sub_from_fixedint():
+    """Test sub between two _FixedInts."""
+    var1 = UInt8(0x03)
+    var2 = UInt8(0x02)
+    result = var1 - var2
     assert isinstance(result, Integral)
     assert result == 0x01
 
 
-def test_fixed_int_sub_from_fixed_int_with_underflow():
-    """Test sub between two fixed length integers with underflow error."""
-    integer1 = UInt8(0x03)
-    integer2 = UInt8(0x02)
+def test_fixedint_sub_from_fixedint_with_underflow():
+    """Test sub between two _FixedInts with underflow error."""
+    var1 = UInt8(0x03)
+    var2 = UInt8(0x02)
     with pytest.raises(UnderflowError):
-        _ = UInt8(integer2 - integer1)
+        _ = UInt8(var2 - var1)
 
 
-def test_fixed_int_sub_from_int():
+def test_fixedint_sub_with_int():
     """Test sub between a fixed length integer and an integer."""
-    integer1 = UInt8(0x03)
-    integer2 = 2
-    result = integer1 - integer2
+    var1 = UInt8(0x03)
+    var2 = 2
+    result = var1 - var2
     assert isinstance(result, Integral)
     assert result == 0x01
 
 
-def test_fixed_int_rsub_from_int():
+def test_fixedint_sub_with_unsupported_type():
+    """Test sub between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x03)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var1 - var2
+
+
+def test_fixedint_rsub_from_int():
     """Test rsub between a fixed length integer and an integer."""
-    integer1 = 3
-    integer2 = UInt8(0x02)
-    result = integer1 - integer2
+    var1 = 3
+    var2 = UInt8(0x02)
+    result = var1 - var2
     assert isinstance(result, Integral)
     assert result == 0x01
 
 
-def test_fixed_int_rsub_from_int_with_underflow():
-    """Test sub between two fixed length integers with underflow."""
-    integer1 = 0x00
-    integer2 = UInt8(0x01)
+def test_fixedint_rsub_with_unsupported_type():
+    """Test reverse sub between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x03)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var2 - var1
+
+
+def test_fixedint_rsub_from_int_with_underflow():
+    """Test sub between two _FixedInts with underflow."""
+    var1 = 0x00
+    var2 = UInt8(0x01)
     with pytest.raises(UnderflowError):
-        _ = UInt8(integer1 - integer2)
+        _ = UInt8(var1 - var2)
 
 
-def test_fixed_int_sub_from_int_with_underflow():
+def test_fixedint_sub_from_int_with_underflow():
     """Test sub between a fixed length integer and an integer with underflow."""
-    integer1 = UInt8(0x00)
-    integer2 = 1
+    var1 = UInt8(0x00)
+    var2 = 1
     with pytest.raises(UnderflowError):
-        _ = UInt8(integer1 - integer2)
+        _ = UInt8(var1 - var2)
 
 
-def test_fixed_int_rsub_from_int_with_overflow():
+def test_fixedint_rsub_from_int_with_overflow():
     """Test rsub between a fixed length integer and an integer with underflow."""
-    integer1 = 0
-    integer2 = UInt8(0x01)
+    var1 = 0
+    var2 = UInt8(0x01)
     with pytest.raises(UnderflowError):
-        _ = UInt8(integer1 - integer2)
+        _ = UInt8(var1 - var2)
 
 
-def test_fixed_int_mul_to_fixed_int():
-    """Test mul between two fixed length integers."""
-    integer1 = UInt8(0x02)
-    integer2 = UInt8(0x08)
-    result = integer1 * integer2
+def test_fixedint_mul_to_fixedint():
+    """Test mul between two _FixedInts."""
+    var1 = UInt8(0x02)
+    var2 = UInt8(0x08)
+    result = var1 * var2
     assert isinstance(result, Integral)
     assert result == 0x10
 
 
-def test_fixed_int_mul_to_int():
+def test_fixedint_mul_to_int():
     """Test mul between a fixed length integer and an integer."""
-    integer1 = UInt8(0x02)
-    integer2 = 2
-    result = integer1 * integer2
+    var1 = UInt8(0x02)
+    var2 = 2
+    result = var1 * var2
     assert isinstance(result, Integral)
     assert result == 0x04
 
 
-def test_fixed_int_rmul_to_int():
+def test_fixedint_mul_to_unsupported():
+    """Test mul between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x02)
+    var2 = complex(1, 1)
+    with pytest.raises(TypeError):
+        _ = var1 * var2
+
+
+def test_fixedint_rmul_to_int():
     """Test rmul between a fixed length integer and an integer."""
-    integer1 = 2
-    integer2 = UInt8(0x02)
-    result = integer1 * integer2
+    var1 = 2
+    var2 = UInt8(0x02)
+    result = var1 * var2
     assert isinstance(result, Integral)
     assert result == 0x04
 
 
-def test_fixed_int_mul_to_fixed_int_with_overflow():
-    """Test mul between two fixed length integers with overflow."""
-    integer1 = UInt8(0xFF)
-    integer2 = UInt8(0x02)
+def test_fixedint_rmul_to_str():
+    """Test rmul between a fixed length integer and an str."""
+    var1 = "A"
+    var2 = UInt8(0x02)
+    result = var1 * var2
+    assert isinstance(result, str)
+    assert result == "AA"
+
+
+def test_fixedint_rmul_to_unsupported():
+    """Test reverse mul between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x02)
+    var2 = complex(1, 1)
+    with pytest.raises(TypeError):
+        _ = var2 * var1
+
+
+def test_fixedint_mul_to_fixedint_with_overflow():
+    """Test mul between two _FixedInts with overflow."""
+    var1 = UInt8(0xFF)
+    var2 = UInt8(0x02)
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1 * integer2)
+        _ = UInt8(var1 * var2)
 
 
-def test_fixed_int_mul_to_int_with_overflow():
+def test_fixedint_mul_to_int_with_overflow():
     """Test mul between a fixed length integer and an integer with overflow."""
-    integer1 = UInt8(0xFF)
-    integer2 = 2
+    var1 = UInt8(0xFF)
+    var2 = 2
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1 * integer2)
+        _ = UInt8(var1 * var2)
 
 
-def test_fixed_int_rmul_to_int_with_overflow():
+def test_fixedint_rmul_to_int_with_overflow():
     """Test rmul between a fixed length integer and an integer with overflow."""
-    integer1 = 255
-    integer2 = UInt8(0x02)
+    var1 = 255
+    var2 = UInt8(0x02)
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1 * integer2)
+        _ = UInt8(var1 * var2)
 
 
-def test_fixed_int_div_with_fixed_int():
-    """Test div between two fixed length integers."""
-    integer1 = UInt8(0x10)
-    integer2 = UInt8(0x08)
-    result = integer1 / integer2
+def test_fixedint_truediv_with_fixedint():
+    """Test truediv between two _FixedInts."""
+    var1 = UInt8(0x10)
+    var2 = UInt8(0x08)
+    result = var1 / var2
     assert result == 0x2
 
 
-def test_fixed_int_div_with_int():
-    """Test div between a fixed length integer and an integer."""
-    integer1 = UInt8(0x10)
-    integer2 = 0x08
-    result = integer1 / integer2
+def test_fixedint_truediv_with_int():
+    """Test truediv between a fixed length integer and an integer."""
+    var1 = UInt8(0x10)
+    var2 = 0x08
+    result = var1 / var2
     assert result == 0x2
 
 
-def test_fixed_int_rdiv_with_int():
-    """Test rdiv between a fixed length integer and an integer."""
-    integer1 = 0x10
-    integer2 = UInt8(0x08)
-    result = integer1 / integer2
+def test_fixedint_truediv_with_unsupported_type():
+    """Test truediv between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x03)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var1 / var2
+
+
+def test_fixedint_rtruediv_with_int():
+    """Test reverse truediv between a fixed length integer and an integer."""
+    var1 = 0x10
+    var2 = UInt8(0x08)
+    result = var1 / var2
     assert result == 0x2
 
 
-def test_fixed_int_div_with_fixed_int_with_div_error():
-    """Test div between two fixed length integers with division by zero error."""
-    integer1 = UInt8(0x01)
-    integer2 = UInt8(0x00)
+def test_fixedint_rtruediv_with_unsupported_type():
+    """Test reverse truediv between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x03)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var2 / var1
+
+
+def test_fixedint_div_with_fixedint_with_div_error():
+    """Test div between two _FixedInts with division by zero error."""
+    var1 = UInt8(0x01)
+    var2 = UInt8(0x00)
     with pytest.raises(ZeroDivisionError):
-        _ = integer1 / integer2
+        _ = var1 / var2
 
 
-def test_fixed_int_div_with_int_with_div_error():
+def test_fixedint_div_with_int_with_div_error():
     """Test div between a fixed length integer and an integer with div by zero error."""
-    integer1 = UInt8(0x01)
-    integer2 = 0
+    var1 = UInt8(0x01)
+    var2 = 0
     with pytest.raises(ZeroDivisionError):
-        _ = integer1 / integer2
+        _ = var1 / var2
 
 
-def test_fixed_int_rdiv_with_int_with_div_error():
+def test_fixedint_rdiv_with_int_with_div_error():
     """Test rdiv between a fixed length integer and an integer with div by zero error."""
-    integer1 = 1
-    integer2 = UInt8(0x00)
+    var1 = 1
+    var2 = UInt8(0x00)
     with pytest.raises(ZeroDivisionError):
-        _ = integer1 / integer2
+        _ = var1 / var2
 
 
-def test_fixed_int_mod_with_fixed_int():
-    """Test mod between two fixed length integers."""
-    integer1 = UInt8(0x10)
-    integer2 = UInt8(0x07)
-    result = integer1 % integer2
+def test_fixedint_mod_with_fixedint():
+    """Test mod between two _FixedInts."""
+    var1 = UInt8(0x10)
+    var2 = UInt8(0x07)
+    result = var1 % var2
     assert isinstance(result, Integral)
     assert result == 0x02
 
 
-def test_fixed_int_mod_with_int():
+def test_fixedint_mod_with_int():
     """Test mod between a fixed length integer and an integer."""
-    integer1 = UInt8(0x10)
-    integer2 = 7
-    result = integer1 % integer2
+    var1 = UInt8(0x10)
+    var2 = 7
+    result = var1 % var2
     assert isinstance(result, Integral)
     assert result == 0x02
 
 
-def test_fixed_int_rmod_with_int():
+def test_fixedint_mod_with_unsupported():
+    """Test mod between a _FixedInt and an unsupported type."""
+    var1 = UInt8(0x5)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var1 % var2
+
+
+def test_fixedint_rmod_with_int():
     """Test rmod between a fixed length integer and an integer."""
-    integer1 = 16
-    integer2 = UInt8(0x07)
-    result = integer1 % integer2
+    var1 = 16
+    var2 = UInt8(0x07)
+    result = var1 % var2
     assert isinstance(result, Integral)
     assert result == 0x02
 
 
-def test_fixed_int_mod_with_fixed_int_with_div_error():
-    """Test mod between two fixed length integers with div by zero error."""
-    integer1 = UInt8(0x01)
-    integer2 = UInt8(0x00)
+def test_fixedint_rmod_with_unsupported():
+    """Test reverse mod between a _FixedInt and an unsupported type."""
+    var1 = UInt8(0x5)
+    var2 = complex(1, 1)
+    with pytest.raises(TypeError):
+        _ = var2 % var1
+
+
+def test_fixedint_mod_with_fixedint_with_div_error():
+    """Test mod between two _FixedInts with div by zero error."""
+    var1 = UInt8(0x01)
+    var2 = UInt8(0x00)
     with pytest.raises(ZeroDivisionError):
-        _ = integer1 % integer2
+        _ = var1 % var2
 
 
-def test_fixed_int_mod_with_int_with_div_error():
+def test_fixedint_mod_with_int_with_div_error():
     """Test mod between a fixed length integer and an integer with div by zero error."""
-    integer1 = UInt8(0x01)
-    integer2 = 0
+    var1 = UInt8(0x01)
+    var2 = 0
     with pytest.raises(ZeroDivisionError):
-        _ = integer1 % integer2
+        _ = var1 % var2
 
 
-def test_fixed_int_rmod_with_int_with_div_error():
+def test_fixedint_rmod_with_int_with_div_error():
     """Test rmod between a fixed length integer and an integer with div by zero error."""
-    integer1 = 1
-    integer2 = UInt8(0x00)
+    var1 = 1
+    var2 = UInt8(0x00)
     with pytest.raises(ZeroDivisionError):
-        _ = integer1 % integer2
+        _ = var1 % var2
+
 
+def test_fixedint_divmod_with_fixedint():
+    """Test divmod between two _FixedInts."""
+    var1 = UInt8(0x5)
+    var2 = UInt8(0x2)
+    quotient, remainder = divmod(var1, var2)
+    assert quotient == 2
+    assert remainder == 1
 
-def test_fixed_int_pow_with_fixed_int():
-    """Test pow between two fixed length integers."""
-    integer1 = UInt8(0x04)
-    integer2 = UInt8(0x02)
-    result = integer1**integer2
+
+def test_fixedint_divmod_with_int():
+    """Test divmod between a _FixedInt and an int."""
+    var1 = UInt8(0x5)
+    var2 = 0x2
+    quotient, remainder = divmod(var1, var2)
+    assert quotient == 2
+    assert remainder == 1
+
+
+def test_fixedint_divmod_with_unsupported():
+    """Test divmod between a _FixedInt and an unsupported type."""
+    var1 = UInt8(0x5)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = divmod(var1, var2)
+
+
+def test_fixedint_rdivmod_with_int():
+    """Test reverse divmod between a _FixedInt and an int."""
+    var1 = UInt8(0x2)
+    var2 = 0x5
+    quotient, remainder = divmod(var2, var1)
+    assert quotient == 2
+    assert remainder == 1
+
+
+def test_fixedint_rdivmod_with_unsupported():
+    """Test reverse divmod between a _FixedInt and an unsupported type."""
+    var1 = UInt8(0x5)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = divmod(var2, var1)
+
+
+def test_fixedint_floordiv_with_fixedint():
+    """Test floordiv between two _FixedInts."""
+    var1 = UInt8(0x5)
+    var2 = UInt8(0x2)
+    result = var1 // var2
+    assert result == 2
+
+
+def test_fixedint_floordiv_with_int():
+    """Test floordiv between a _FixedInt and an int."""
+    var1 = UInt8(0x5)
+    var2 = 0x2
+    result = var1 // var2
+    assert result == 2
+
+
+def test_fixedint_floordiv_with_unsupported():
+    """Test floordiv between a _FixedInt and an unsupported type."""
+    var1 = UInt8(0x5)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var1 // var2
+
+
+def test_fixedint_rfloordiv_with_int():
+    """Test reverse floordiv between a _FixedInt and an int."""
+    var1 = UInt8(0x2)
+    var2 = 0x5
+    result = var2 // var1
+    assert result == 2
+
+
+def test_fixedint_rfloordiv_with_unsupported():
+    """Test reverse floordiv between a _FixedInt and an unsupported type."""
+    var1 = UInt8(0x5)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var2 // var1
+
+
+def test_fixedint_pow_with_fixedint():
+    """Test pow between two _FixedInts."""
+    var1 = UInt8(0x04)
+    var2 = UInt8(0x02)
+    result = var1**var2
     assert isinstance(result, Integral)
     assert result == 0x10
 
 
-def test_fixed_int_pow_with_int():
+def test_fixedint_pow_with_int():
     """Test pow between a fixed length integer and an integer."""
-    integer1 = UInt8(0x04)
-    integer2 = 2
-    result = integer1**integer2
+    var1 = UInt8(0x04)
+    var2 = 2
+    result = var1**var2
     assert isinstance(result, Integral)
     assert result == 0x10
 
 
-def test_fixed_int_rpow_with_int():
+def test_fixedint_pow_with_unsupported_type():
+    """Test pow between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x04)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var1**var2
+
+
+def test_fixedint_rpow_with_int():
     """Test rpow between a fixed length integer and an integer."""
-    integer1 = 4
-    integer2 = UInt8(0x02)
-    result = integer1**integer2
+    var1 = 4
+    var2 = UInt8(0x02)
+    result = var1**var2
     assert isinstance(result, Integral)
     assert result == 0x10
 
 
-def test_fixed_int_pow_with_fixed_int_with_overflow():
-    """Test pow between two fixed length integers with overflow."""
-    integer1 = UInt8(0xFF)
-    integer2 = UInt8(0xFF)
+def test_fixedint_rpow_with_unsupported_type():
+    """Test reverse pow between a fixed length integer and an unsupported type."""
+    var1 = UInt8(0x04)
+    var2 = "invalid"
+    with pytest.raises(TypeError):
+        _ = var2**var1
+
+
+def test_fixedint_pow_with_fixedint_with_overflow():
+    """Test pow between two _FixedInts with overflow."""
+    var1 = UInt8(0xFF)
+    var2 = UInt8(0xFF)
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1**integer2)
+        _ = UInt8(var1**var2)
 
 
-def test_fixed_int_pow_with_int_with_overflow():
+def test_fixedint_pow_with_int_with_overflow():
     """Test pow between a fixed length integer and an integer with overflow."""
-    integer1 = UInt8(0xFF)
-    integer2 = 255
+    var1 = UInt8(0xFF)
+    var2 = 255
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1**integer2)
+        _ = UInt8(var1**var2)
 
 
-def test_fixed_int_rpow_with_int_with_overflow():
+def test_fixedint_rpow_with_int_with_overflow():
     """Test rpow between a fixed length integer and an integer with overflow."""
-    integer1 = 255
-    integer2 = UInt8(0xFF)
+    var1 = 255
+    var2 = UInt8(0xFF)
     with pytest.raises(OverflowError):
-        _ = UInt8(integer1**integer2)
+        _ = UInt8(var1**var2)
diff --git a/tests/primitives/test_integers.py b/tests/primitives/test_integers.py
new file mode 100644
index 0000000..0c52b92
--- /dev/null
+++ b/tests/primitives/test_integers.py
@@ -0,0 +1,672 @@
+"""Unit tests for byteclasses primitive integer type constructors."""
+
+import math
+import operator
+
+import pytest
+
+from byteclasses.types.primitives.integers import (
+    Int8,
+    Int16,
+    Int32,
+    Int64,
+    UInt8,
+    UInt16,
+    UInt32,
+    UInt64,
+    UnderflowError,
+    _FixedInt,
+)
+
+
+def test_fixedint_missing_type_char_property():
+    """Test _FixedInt instance with no `_type_char` class attribute."""
+
+    class InvalidInt(_FixedInt):
+        """Invalid Fixed Int Class definition."""
+
+        _length = 1
+        _signed = False
+
+    with pytest.raises(NotImplementedError):
+        _ = InvalidInt()
+
+
+def test_fixedint_missing_length_property():
+    """Test _FixedInt instance with no `_length` class attribute."""
+
+    class InvalidInt(_FixedInt):
+        """Invalid Fixed Int Class definition."""
+
+        _type_char = b"b"
+        _signed = False
+
+    with pytest.raises(NotImplementedError):
+        _ = InvalidInt()
+
+
+def test_fixedint_missing_signed_property():
+    """Test _FixedInt instance with no `_signed` class attribute."""
+
+    class InvalidInt(_FixedInt):
+        """Invalid Fixed Int Class definition."""
+
+        _type_char: bytes = b"b"
+        _length: int = 1
+
+    with pytest.raises(NotImplementedError):
+        _ = InvalidInt()
+
+
+def test_fixedint_create_with_invalid_value_type():
+    """Test integer instantiation with invalid value type."""
+    with pytest.raises(TypeError):
+        _ = Int8("invalid")
+
+
+def test_fixedint_create_with_value():
+    """Test integer instantiation with value."""
+    var = Int8(123)
+    assert var == 123
+
+
+def test_fixedint_create_with_bytes():
+    """Test integer instantiation with bytes."""
+    var = Int8(data=b"\x7f")
+    assert var == 127
+
+
+def test_fixedint_create_with_bytearray():
+    """Test integer instantiation with bytearray."""
+    var = Int8(data=bytearray(b"\x7f"))
+    assert var == 127
+
+
+def test_fixedint_create_with_memoryview():
+    """Test integer instantiation with memoryview."""
+    data = bytearray(b"\x7f")
+    var = Int8(data=bytearray(memoryview(data)))
+    assert var == 127
+
+
+def test_fixedint_create_with_value_and_data():
+    """Test integer instantiation with value and data."""
+    with pytest.raises(ValueError):
+        _ = Int8(1, data=b"\x7f")
+
+
+def test_fixedint_assign_non_int_value():
+    """Test assignment with non-nteger value."""
+    var1 = Int8()
+    var1.value = 1.1
+    assert var1.value == 1
+
+
+def test_fixedint_attach_data_mv_no_retain():
+    """Test attaching external data memoryview to int8 without retaining value."""
+    test_mv = memoryview(bytearray(2))
+    int1 = Int8(0x12)
+    int2 = Int8(0x34)
+    int1.attach(test_mv[:1], retain_value=False)
+    int2.attach(test_mv[1:], retain_value=False)
+    assert int1.value == 0x00
+    assert int2.value == 0x00
+    assert test_mv == b"\x00\x00"
+    test_mv[:] = b"\x12\x34"
+    assert int1.value == 0x12
+    assert int2.value == 0x34
+
+
+def test_fixedint_attach_data_mv_with_retain():
+    """Test attaching external data memoryview to integer while retaining value."""
+    test_mv = memoryview(bytearray(3))
+    int1 = Int8(0x12)
+    int2 = Int8(0x34)
+    int1.attach(test_mv[:1])
+    int2.attach(test_mv[2:])
+    assert int1.value == 0x12
+    assert int2.value == 0x34
+    assert test_mv == b"\x12\x00\x34"
+    test_mv[:] = b"\x01\x00\x23"
+    assert int1.value == 0x01
+    assert int2.value == 0x23
+
+
+def test_fixedint_attach_data_bytearray():
+    """Test attaching external data bytearray to integer."""
+    test_data = bytearray(3)
+    int1 = Int8(0x12)
+    int2 = Int8(0x34)
+    int1.attach(test_data[:1])
+    int2.attach(test_data[2:])
+    assert int1.value == 0x12
+    assert int2.value == 0x34
+    assert test_data == b"\x00\x00\x00"
+    test_data[:] = b"\x01\x00\x23"
+    assert int1.value == 0x12
+    assert int2.value == 0x34
+
+
+def test_fixedint_attach_data_bytes():
+    """Test attaching external data bytes to integer."""
+    test_data = bytes(3)
+    int1 = Int8(0x12)
+    int2 = Int8(0x34)
+    int1.attach(test_data[:1])
+    int2.attach(test_data[2:])
+    assert int1.value == 0x12
+    assert int2.value == 0x34
+    assert test_data == b"\x00\x00\x00"
+    assert int1.value == 0x12
+    assert int2.value == 0x34
+
+
+def test_fixedint_str():
+    """Test integer __str__ method."""
+    var = Int8(8)
+    assert str(var) == "8"
+
+
+def test_fixedint_clear_data_with_data_none():
+    """Test _FixedInt clearing data by setting data attribute to None."""
+    var = Int8(8)
+    assert var == 8
+    var.data = None
+    assert var.data == b"\x00"
+
+
+def test_fixedint_set_partial_data():
+    """Test _FixedInt set data with short data."""
+    var = UInt32()
+    var.value = var.max
+    var.data = b"\x00\x00"
+    assert var.data == b"\x00\x00\xff\xff"
+
+
+def test_fixedint_set_oversized_data():
+    """Test _FixedInt set data with oversized data."""
+    var = UInt32()
+    var.value = var.max
+    with pytest.raises(ValueError):
+        var.data = b"\x00\x00\x00\x00\x00"
+
+
+def test_fixedint_repr():
+    """Test integer __repr__ method."""
+    var = Int8(8)
+    assert repr(var) == "Int8(8)"
+
+
+def test_fixedint_bytes():
+    """Test integer __bytes__ method."""
+    var = Int8(8)
+    assert bytes(var) == b"\x08"
+
+
+def test_fixedint_abs():
+    """Test integer __abs__ method."""
+    var = Int8(8)
+    assert abs(var) == 8
+    var.value = -8
+    assert abs(var) == 8
+
+
+def test_fixedint_bool():
+    """Test integer __bool__ method."""
+    var = Int8(8)
+    assert bool(var) is True
+    var.value = 0
+    assert bool(var) is False
+
+
+def test_fixedint_ceil():
+    """Test integer __ceil__ method."""
+    var = Int8(8)
+    assert math.ceil(var) == 8
+
+
+def test_fixedint_int():
+    """Test integer __int__ method."""
+    var = Int8(8)
+    assert int(var) == 8
+
+
+def test_fixedint_complex():
+    """Test integer __complex__ method."""
+    var1 = Int8(8)
+    var2 = complex(var1)
+    assert isinstance(var2, complex)
+    assert var2.real == 8
+    assert var2.imag == 0
+
+
+def test_fixedint_eq_with_unsupported():
+    """Test integer __eq__ method with unsupported."""
+    var1 = Int8(8)
+    var2 = "unsupported"
+    assert var1 != var2
+
+
+def test_fixedint_float():
+    """Test integer __float__ method."""
+    var1 = Int8(8)
+    var2 = float(var1)
+    assert isinstance(var2, float)
+    assert var2 == 8.0
+
+
+def test_fixedint_floor():
+    """Test integer __floor__ method."""
+    var1 = Int8(8)
+    var2 = math.floor(var1)
+    assert isinstance(var2, int)
+    assert var2 == 8
+
+
+def test_fixedint_hash():
+    """Test integer __hash__ method."""
+    var1 = Int8(8)
+    var2 = Int8(9)
+    assert isinstance(hash(var1), int)
+    assert hash(var1) != hash(var2)
+
+
+def test_fixedint_neg():
+    """Test integer __neg__ method."""
+    var1 = Int8(8)
+    assert -var1 == -8
+
+
+def test_fixedint_pos():
+    """Test integer __pos__ method."""
+    var1 = Int8(-8)
+    assert +var1 == 8
+
+
+def test_fixedint_round():
+    """Test integer __round__ method."""
+    var1 = Int8(8)
+    assert round(var1) == 8
+    var1.value = 111
+    assert round(var1, 2) == 111
+
+
+def test_fixedint_truncate():
+    """Test _FixedInt __trunc__ method."""
+    var1 = Int8(10)
+    result = math.trunc(var1)
+    assert result == 10
+
+
+def test_fixedint_index():
+    """Test _FixedInt __index__ method."""
+    var1 = Int8(10)
+    result = operator.index(var1)
+    assert result == 10
+
+
+# Check attributes of each fixed size integer
+
+
+def test_int8_instance_properties():
+    """Test int8 instance properties."""
+    int8 = Int8(0)
+    assert int8.signed is True
+    assert int8.min == -128
+    assert int8.max == 127
+    assert int8.byte_order.value == b"@"
+    assert int8.endianness == "NATIVE"
+    assert int8.fmt == b"@b"
+    assert int8.bit_length == 8
+    assert len(int8) == 1
+    int8.value = 0
+    assert int8 == 0
+    int8.value = 1
+    assert int8 == 1
+
+
+def test_int8_bounds():
+    """Test int8 bounds."""
+    int8 = Int8()
+    assert int8.allow_overflow is False
+    int8.value = 127
+    assert int8 == 127
+    with pytest.raises(OverflowError):
+        int8.value = 128
+    int8.value = -128
+    assert int8 == -128
+    with pytest.raises(UnderflowError):
+        int8.value = -129
+
+
+def test_int8_bounds_allow_overflow():
+    """Test int8 bounds allow_overflow."""
+    int8 = Int8(allow_overflow=True)
+    assert int8.allow_overflow is True
+    int8.value = 127
+    assert int8 == 127
+    int8.value = 128
+    assert int8 == -128
+    int8.value = -129
+    assert int8 == 127
+
+
+def test_int8_value_to_data():
+    """Test int8 value to data."""
+    int8 = Int8(0)
+    assert int8.data == b"\x00"
+    int8.value = 1
+    assert int8.data == b"\x01"
+    int8.value = 127
+    assert int8.data == b"\x7f"
+    int8.value = -128
+    assert int8.data == b"\x80"
+    int8.value = -1
+    assert int8.data == b"\xff"
+
+
+def test_uint8_instance_properties():
+    """Test uint8 instance properties."""
+    uint8 = UInt8(0)
+    assert uint8.signed is False
+    assert uint8.min == 0
+    assert uint8.max == 255
+    assert uint8.byte_order.value == b"@"
+    assert uint8.endianness == "NATIVE"
+    assert uint8.fmt == b"@B"
+    assert uint8.bit_length == 8
+    assert len(uint8) == 1
+    uint8.value = 0
+    assert uint8 == 0
+    uint8.value = 1
+    assert uint8 == 1
+
+
+def test_uint8_bounds():
+    """Test UInt8 bounds."""
+    uint8 = UInt8()
+    assert uint8.allow_overflow is False
+    uint8.value = 255
+    assert uint8 == 255
+    with pytest.raises(OverflowError):
+        uint8.value = 256
+    uint8.value = 0
+    assert uint8 == 0
+    with pytest.raises(UnderflowError):
+        uint8.value = -1
+
+
+def test_uint8_bounds_allow_overflow():
+    """Test uint8 bounds allow_overflow."""
+    uint8 = UInt8(allow_overflow=True)
+    assert uint8.allow_overflow is True
+    uint8.value = 255
+    assert uint8 == 255
+    uint8.value = 256
+    assert uint8 == 0
+    uint8.value = -1
+    assert uint8 == 255
+
+
+def test_uint8_value_to_data():
+    """Test UInt8 value to data."""
+    uint8 = UInt8(0)
+    assert uint8.data == b"\x00"
+    uint8.value = 1
+    assert uint8.data == b"\x01"
+    uint8.value = 255
+    assert uint8.data == b"\xff"
+    uint8.value = 0
+    assert uint8.data == b"\x00"
+
+
+def test_int16_instance_properties():
+    """Test Int16 instance properties."""
+    int16 = Int16(0)
+    assert int16.signed is True
+    assert int16.min == -32768
+    assert int16.max == 32767
+    assert int16.byte_order.value == b"@"
+    assert int16.endianness == "NATIVE"
+    assert int16.fmt == b"@h"
+    assert int16.bit_length == 16
+    assert len(int16) == 2
+    int16.value = 0
+    assert int16 == 0
+    int16.value = 1
+    assert int16 == 1
+
+
+def test_int16_bounds():
+    """Test Int16 bounds."""
+    int16 = Int16()
+    int16.value = 32767
+    assert int16 == 32767
+    with pytest.raises(OverflowError):
+        int16.value = 32768
+    int16.value = -32768
+    assert int16 == -32768
+    with pytest.raises(UnderflowError):
+        int16.value = -32769
+
+
+def test_int16_value_to_data():
+    """Test Int16 value to data."""
+    int16 = Int16(0)
+    assert int16.data == b"\x00\x00"
+    int16.value = 1
+    assert int16.data == b"\x01\x00"
+    int16.value = 32767
+    assert int16.data == b"\xff\x7f"
+    int16.value = -32768
+    assert int16.data == b"\x00\x80"
+    int16.value = -1
+    assert int16.data == b"\xff\xff"
+
+
+def test_uint16_instance_properties():
+    """Test UInt16 instance properties."""
+    uint16 = UInt16(0)
+    assert uint16.signed is False
+    assert uint16.min == 0
+    assert uint16.max == 65535
+    assert uint16.byte_order.value == b"@"
+    assert uint16.endianness == "NATIVE"
+    assert uint16.fmt == b"@H"
+    assert uint16.bit_length == 16
+    assert len(uint16) == 2
+    uint16.value = 0
+    assert uint16 == 0
+    uint16.value = 1
+    assert uint16 == 1
+
+
+def test_uint16_bounds():
+    """Test UInt16 bounds."""
+    uint16 = UInt16()
+    uint16.value = 65535
+    assert uint16 == 65535
+    with pytest.raises(OverflowError):
+        uint16.value = 65536
+    uint16.value = 0
+    assert uint16 == 0
+    with pytest.raises(UnderflowError):
+        uint16.value = -1
+
+
+def test_uint16_value_to_data():
+    """Test UInt16 value to data."""
+    uint16 = UInt16(0)
+    assert uint16.data == b"\x00\x00"
+    uint16.value = 1
+    assert uint16.data == b"\x01\x00"
+    uint16.value = 65535
+    assert uint16.data == b"\xff\xff"
+
+
+def test_int32_instance_properties():
+    """Test Int32 instance properties."""
+    int32 = Int32(0)
+    assert int32.signed is True
+    assert int32.min == -2147483648
+    assert int32.max == 2147483647
+    assert int32.byte_order.value == b"@"
+    assert int32.endianness == "NATIVE"
+    assert int32.fmt == b"@i"
+    assert int32.bit_length == 32
+    assert len(int32) == 4
+    int32.value = 0
+    assert int32 == 0
+    int32.value = 1
+    assert int32 == 1
+
+
+def test_int32_bounds():
+    """Test Int32 bounds."""
+    int32 = Int32()
+    int32.value = 2147483647
+    assert int32 == 2147483647
+    with pytest.raises(OverflowError):
+        int32.value = 2147483648
+    int32.value = -2147483648
+    assert int32 == -2147483648
+    with pytest.raises(UnderflowError):
+        int32.value = -2147483649
+
+
+def test_int32_value_to_data():
+    """Test Int32 value to data."""
+    int32 = Int32(0)
+    assert int32.data == b"\x00\x00\x00\x00"
+    int32.value = 1
+    assert int32.data == b"\x01\x00\x00\x00"
+    int32.value = 2147483647
+    assert int32.data == b"\xff\xff\xff\x7f"
+    int32.value = -2147483648
+    assert int32.data == b"\x00\x00\x00\x80"
+    int32.value = -1
+    assert int32.data == b"\xff\xff\xff\xff"
+
+
+def test_uint32_instance_properties():
+    """Test UInt32 instance properties."""
+    uint32 = UInt32(0)
+    assert uint32.signed is False
+    assert uint32.min == 0
+    assert uint32.max == 4294967295
+    assert uint32.byte_order.value == b"@"
+    assert uint32.endianness == "NATIVE"
+    assert uint32.fmt == b"@I"
+    assert uint32.bit_length == 32
+    assert len(uint32) == 4
+    uint32.value = 0
+    assert uint32 == 0
+    uint32.value = 1
+    assert uint32 == 1
+
+
+def test_uint32_bounds():
+    """Test UInt32 bounds."""
+    uint32 = UInt32()
+    uint32.value = 4294967295
+    assert uint32 == 4294967295
+    with pytest.raises(OverflowError):
+        uint32.value = 4294967296
+    uint32.value = 0
+    assert uint32 == 0
+    with pytest.raises(UnderflowError):
+        uint32.value = -1
+
+
+def test_uint32_value_to_data():
+    """Test UInt32 value to data."""
+    uint32 = UInt32(0)
+    assert uint32.data == b"\x00\x00\x00\x00"
+    uint32.value = 1
+    assert uint32.data == b"\x01\x00\x00\x00"
+    uint32.value = 4294967295
+    assert uint32.data == b"\xff\xff\xff\xff"
+
+
+def test_int64_instance_properties():
+    """Test Int64 instance properties."""
+    int64 = Int64(0)
+    assert int64.signed is True
+    assert int64.min == -9223372036854775808
+    assert int64.max == 9223372036854775807
+    assert int64.byte_order.value == b"@"
+    assert int64.endianness == "NATIVE"
+    assert int64.fmt == b"@q"
+    assert int64.bit_length == 64
+    assert len(int64) == 8
+    int64.value = 0
+    assert int64 == 0
+    int64.value = 1
+    assert int64 == 1
+
+
+def test_int64_bounds():
+    """Test Int64 bounds."""
+    int64 = Int64()
+    int64.value = 9223372036854775807
+    assert int64 == 9223372036854775807
+    with pytest.raises(OverflowError):
+        int64.value = 9223372036854775808
+    int64.value = -9223372036854775808
+    assert int64 == -9223372036854775808
+    with pytest.raises(UnderflowError):
+        int64.value = -9223372036854775809
+
+
+def test_int64_value_to_data():
+    """Test Int64 value to data."""
+    int64 = Int64(0)
+    assert int64.data == b"\x00\x00\x00\x00\x00\x00\x00\x00"
+    int64.value = 1
+    assert int64.data == b"\x01\x00\x00\x00\x00\x00\x00\x00"
+    int64.value = 9223372036854775807
+    assert int64.data == b"\xff\xff\xff\xff\xff\xff\xff\x7f"
+    int64.value = -9223372036854775808
+    assert int64.data == b"\x00\x00\x00\x00\x00\x00\x00\x80"
+    int64.value = -1
+    assert int64.data == b"\xff\xff\xff\xff\xff\xff\xff\xff"
+
+
+def test_uint64_instance_properties():
+    """Test UInt64 instance properties."""
+    uint64 = UInt64(0)
+    assert uint64.signed is False
+    assert uint64.min == 0
+    assert uint64.max == 18446744073709551615
+    assert uint64.byte_order.value == b"@"
+    assert uint64.endianness == "NATIVE"
+    assert uint64.fmt == b"@Q"
+    assert uint64.bit_length == 64
+    assert len(uint64) == 8
+    uint64.value = 0
+    assert uint64 == 0
+    uint64.value = 1
+    assert uint64 == 1
+
+
+def test_uint64_bounds():
+    """Test UInt64 bounds."""
+    uint64 = UInt64()
+    uint64.value = 18446744073709551615
+    assert uint64 == 18446744073709551615
+    with pytest.raises(OverflowError):
+        uint64.value = 18446744073709551616
+    uint64.value = 0
+    assert uint64 == 0
+    with pytest.raises(UnderflowError):
+        uint64.value = -1
+
+
+def test_uint64_value_to_data():
+    """Test UInt64 value to data."""
+    uint64 = UInt64(0)
+    assert uint64.data == b"\x00\x00\x00\x00\x00\x00\x00\x00"
+    uint64.value = 1
+    assert uint64.data == b"\x01\x00\x00\x00\x00\x00\x00\x00"
+    uint64.value = 18446744073709551615
+    assert uint64.data == b"\xff\xff\xff\xff\xff\xff\xff\xff"
diff --git a/tests/primitives/test_logical_operations.py b/tests/primitives/test_logical_operations.py
index 4dd320a..1a787ae 100644
--- a/tests/primitives/test_logical_operations.py
+++ b/tests/primitives/test_logical_operations.py
@@ -7,7 +7,7 @@
 from byteclasses.types.primitives.integers import UInt8
 
 
-def test_fixed_int_and_with_fixed_int():
+def test_fixedint_and_with_fixed_int():
     """Test logical AND between two fixed length integers."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
@@ -16,7 +16,7 @@ def test_fixed_int_and_with_fixed_int():
     assert result == 0x01
 
 
-def test_fixed_int_and_with_int():
+def test_fixedint_and_with_int():
     """Test logical AND between fixed length integer and int."""
     integer = UInt8(0x01)
     result = integer & 0x03
@@ -24,7 +24,7 @@ def test_fixed_int_and_with_int():
     assert result == 0x01
 
 
-def test_int_and_with_fixed_int():
+def test_int_and_with_fixedint():
     """Test logical AND between int and fixed length integer."""
     integer = UInt8(0x01)
     result = 0x03 & integer
@@ -32,14 +32,14 @@ def test_int_and_with_fixed_int():
     assert result == 0x01
 
 
-def test_fixed_int_and_unsupported():
+def test_fixedint_and_unsupported():
     """Test logical AND between fixed length integer and unsupported type."""
     integer = UInt8(0x01)
     with pytest.raises(TypeError):
         _ = integer & "unsupported"
 
 
-def test_fixed_int_or_with_fixed_int():
+def test_fixedint_or_with_fixed_int():
     """Test logical OR between two fixed length integers."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
@@ -48,7 +48,7 @@ def test_fixed_int_or_with_fixed_int():
     assert result == 0x03
 
 
-def test_fixed_int_or_with_int():
+def test_fixedint_or_with_int():
     """Test logical OR between fixed length integer and int."""
     integer = UInt8(0x01)
     result = integer | 0x03
@@ -56,7 +56,7 @@ def test_fixed_int_or_with_int():
     assert result == 0x03
 
 
-def test_int_or_with_fixed_int():
+def test_int_or_with_fixedint():
     """Test logical OR between int and fixed length integer."""
     integer = UInt8(0x01)
     result = 0x03 | integer
@@ -64,14 +64,14 @@ def test_int_or_with_fixed_int():
     assert result == 0x03
 
 
-def test_fixed_int_or_unsupported():
+def test_fixedint_or_unsupported():
     """Test logical OR between fixed length integer and unsupported type."""
     integer = UInt8(0x01)
     with pytest.raises(TypeError):
         _ = integer | "unsupported"
 
 
-def test_fixed_int_xor_with_fixed_int():
+def test_fixedint_xor_with_fixedint():
     """Test logical XOR between two fixed length integers."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
@@ -80,7 +80,7 @@ def test_fixed_int_xor_with_fixed_int():
     assert result == 0x02
 
 
-def test_fixed_int_xor_with_int():
+def test_fixedint_xor_with_int():
     """Test logical XOR between fixed length integer and int."""
     integer = UInt8(0x01)
     result = integer ^ 0x03
@@ -88,14 +88,14 @@ def test_fixed_int_xor_with_int():
     assert result == 0x02
 
 
-def test_fixed_int_xor_unsupported():
+def test_fixedint_xor_unsupported():
     """Test logical XOR between fixed length integer and unsupported type."""
     integer = UInt8(0x01)
     with pytest.raises(TypeError):
         _ = integer ^ "unsupported"
 
 
-def test_fixed_int_rxor_with_int():
+def test_fixedint_rxor_with_int():
     """Test logical reverse XOR between fixed length integer and int."""
     integer = UInt8(0x01)
     result = 0x03 ^ integer
@@ -103,14 +103,14 @@ def test_fixed_int_rxor_with_int():
     assert result == 0x02
 
 
-def test_fixed_int_rxor_unsupported():
+def test_fixedint_rxor_unsupported():
     """Test logical reverse XOR between fixed length integer and unsupported type."""
     integer = UInt8(0x01)
     with pytest.raises(TypeError):
         _ = "unsupported" ^ integer
 
 
-def test_fixed_int_invert():
+def test_fixedint_invert():
     """Test logical not of fixed length integer."""
     integer = UInt8(0x01)
     result = ~integer
@@ -118,7 +118,7 @@ def test_fixed_int_invert():
     assert result == 0xFE
 
 
-def test_fixed_int_left_shift():
+def test_fixedint_left_shift():
     """Test left shift of fixed length integer."""
     integer = UInt8(0x01)
     result = integer << 1
@@ -126,59 +126,59 @@ def test_fixed_int_left_shift():
     assert result == 0x02
 
 
-def test_fixed_int_left_shift_overflow():
+def test_fixedint_left_shift_overflow():
     """Test left shift of fixed length integer with overflow."""
     integer = UInt8(0xFF)
     with pytest.raises(OverflowError):
         _ = UInt8(integer << 1)
 
 
-def test_uint8_lshift_with_fixed_int():
-    """Test UInt8 binary left shift."""
+def test_fixedint_lshift_with_fixedint():
+    """Test _FixedInt binary left shift."""
     var1 = UInt8(1)
     var2 = UInt8(1)
     result = var1 << var2
     assert result == 2
 
 
-def test_uint8_lshift_with_unsupported():
-    """Test UInt8 binary left shift."""
+def test_fixedint_lshift_with_unsupported():
+    """Test _FixedInt binary left shift."""
     var1 = UInt8(1)
     with pytest.raises(TypeError):
         _ = var1 << "unsupported"
 
 
-def test_uint8_rlshift_with_int():
-    """Test UInt8 binary reverse left shift."""
+def test_fixedint_rlshift_with_int():
+    """Test _FixedInt binary reverse left shift."""
     var1 = UInt8(1)
     result = 1 << var1
     assert result == 2
 
 
-def test_uint8_rlshift_with_unsupported():
-    """Test UInt8 binary reverse left shift."""
+def test_fixedint_rlshift_with_unsupported():
+    """Test _FixedInt binary reverse left shift."""
     var1 = UInt8(1)
     with pytest.raises(TypeError):
         _ = "unsupported" << var1
 
 
-def test_fixed_int_right_shift():
-    """Test right shift of fixed length integer."""
+def test_fixedint_right_shift():
+    """Test right shift of _FixedInt."""
     integer = UInt8(0x01)
     result = integer >> 1
     assert isinstance(result, Integral)
     assert result == 0x00
 
 
-def test_uint8_rshift_with_int():
-    """Test UInt8 binary right shift."""
+def test_fixedint_rshift_with_int():
+    """Test _FixedInt binary right shift."""
     var1 = UInt8(2)
     result = var1 >> 1
     assert result == 1
 
 
-def test_uint8_rshift_with_fixed_int():
-    """Test UInt8 binary right shift."""
+def test_fixedint_rshift_with_fixedint():
+    """Test _FixedInt binary right shift."""
     var1 = UInt8(2)
     var2 = UInt8(1)
     result = var1 >> var2
@@ -192,22 +192,22 @@ def test_uint8_rshift_with_unsupported():
         _ = var1 >> "unsupported"
 
 
-def test_uint8_rrshift_with_int():
-    """Test UInt8 binary reverse right shift."""
+def test_fixeding_rrshift_with_int():
+    """Test _FixedInt binary reverse right shift."""
     var1 = UInt8(1)
     result = 2 >> var1
     assert result == 1
 
 
-def test_uint8_rrshift_with_unsupported():
-    """Test UInt8 binary reverse right shift."""
+def test_fixedint_rrshift_with_unsupported():
+    """Test _FixedInt binary reverse right shift."""
     var1 = UInt8(1)
     with pytest.raises(TypeError):
         _ = "unsupported" >> var1
 
 
-def test_fixed_int_eq_with_fixed_int():
-    """Test equality between two fixed length integers."""
+def test_fixedint_eq_with_fixedint():
+    """Test equality between two _FixedInts."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
     result = integer1 == integer2
@@ -220,8 +220,8 @@ def test_fixed_int_eq_with_fixed_int():
     assert result is True
 
 
-def test_fixed_int_eq_with_int():
-    """Test equality between fixed length integer and int."""
+def test_fixedint_eq_with_int():
+    """Test equality between _FixedInt and int."""
     integer = UInt8(0x01)
     result = integer == 0x03
     assert isinstance(result, bool)
@@ -232,8 +232,8 @@ def test_fixed_int_eq_with_int():
     assert result is True
 
 
-def test_fixed_int_ne_with_fixed_int():
-    """Test inequality between two fixed length integers."""
+def test_fixedint_ne_with_fixedint():
+    """Test inequality between two _FixedInts."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
     result = integer1 != integer2
@@ -246,8 +246,8 @@ def test_fixed_int_ne_with_fixed_int():
     assert result is False
 
 
-def test_fixed_int_ne_with_int():
-    """Test inequality between fixed length integer and int."""
+def test_fixedint_ne_with_int():
+    """Test inequality between _FixedInt and int."""
     integer = UInt8(0x01)
     result = integer != 0x03
     assert isinstance(result, bool)
@@ -258,8 +258,8 @@ def test_fixed_int_ne_with_int():
     assert result is False
 
 
-def test_fixed_int_lt_with_fixed_int():
-    """Test less than between two fixed length integers."""
+def test_fixedint_lt_with_fixed_int():
+    """Test less than between two _FixedInts."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
     result = integer1 < integer2
@@ -272,8 +272,8 @@ def test_fixed_int_lt_with_fixed_int():
     assert result is False
 
 
-def test_fixed_int_lt_with_int():
-    """Test less than between fixed length integer and int."""
+def test_fixedint_lt_with_int():
+    """Test less than between _FixedInt and int."""
     integer = UInt8(0x01)
     result = integer < 0x03
     assert isinstance(result, bool)
@@ -284,8 +284,15 @@ def test_fixed_int_lt_with_int():
     assert result is False
 
 
-def test_fixed_int_le_with_fixed_int():
-    """Test less than or equal between two fixed length integers."""
+def test_fixedint_lt_with_unsupported():
+    """Test less than between _FixedInt and an unsupported type."""
+    integer = UInt8(0x01)
+    with pytest.raises(TypeError):
+        _ = integer < "invalid"
+
+
+def test_fixedint_le_with_fixedint():
+    """Test less than or equal between two _FixedInts."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
     result = integer1 <= integer2
@@ -298,8 +305,8 @@ def test_fixed_int_le_with_fixed_int():
     assert result is True
 
 
-def test_fixed_int_le_with_int():
-    """Test less than or equal between fixed length integer and int."""
+def test_fixedint_le_with_int():
+    """Test less than or equal between _FixedInt and int."""
     integer = UInt8(0x01)
     result = integer <= 0x03
     assert isinstance(result, bool)
@@ -310,8 +317,15 @@ def test_fixed_int_le_with_int():
     assert result is True
 
 
-def test_fixed_int_gt_with_fixed_int():
-    """Test greater than between two fixed length integers."""
+def test_fixedint_le_with_unsupported():
+    """Test less than or equal between _FixedInt and an unsupported type."""
+    integer = UInt8(0x01)
+    with pytest.raises(TypeError):
+        _ = integer <= "invalid"
+
+
+def test_fixedint_gt_with_fixedint():
+    """Test greater than between two _FixedInts."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
     result = integer1 > integer2
@@ -324,8 +338,8 @@ def test_fixed_int_gt_with_fixed_int():
     assert result is False
 
 
-def test_fixed_int_gt_with_int():
-    """Test greater than between fixed length integer and int."""
+def test_fixedint_gt_with_int():
+    """Test greater than between _FixedInt and int."""
     integer = UInt8(0x01)
     result = integer > 0x03
     assert isinstance(result, bool)
@@ -336,8 +350,15 @@ def test_fixed_int_gt_with_int():
     assert result is False
 
 
-def test_fixed_int_ge_with_fixed_int():
-    """Test greater than or equal between two fixed length integers."""
+def test_fixedint_gt_with_unsupported():
+    """Test greater than between _FixedInt and an unsupported type."""
+    integer = UInt8(0x01)
+    with pytest.raises(TypeError):
+        _ = integer > "invalid"
+
+
+def test_fixedint_ge_with_fixedint():
+    """Test greater than or equal between two _FixedInts."""
     integer1 = UInt8(0x01)
     integer2 = UInt8(0x03)
     result = integer1 >= integer2
@@ -350,8 +371,8 @@ def test_fixed_int_ge_with_fixed_int():
     assert result is True
 
 
-def test_fixed_int_ge_with_int():
-    """Test greater than or equal between fixed length integer and int."""
+def test_fixedint_ge_with_int():
+    """Test greater than or equal between _FixedInt and int."""
     integer = UInt8(0x01)
     result = integer >= 0x03
     assert isinstance(result, bool)
@@ -360,3 +381,10 @@ def test_fixed_int_ge_with_int():
     result = integer >= 0x7F
     assert isinstance(result, bool)
     assert result is True
+
+
+def test_fixedint_ge_with_unsupported():
+    """Test greater than or equal between _FixedInt and an unsupported type."""
+    integer = UInt8(0x01)
+    with pytest.raises(TypeError):
+        _ = integer >= "invalid"