rm.domain의 소스 코드

import struct

from abc import (
    ABC,
    abstractmethod,
)
from dataclasses import (
    dataclass,
)

from .error import (
    RmInvalidDomainValueError,
    RmInvalidDomainStringLengthError,
)


__all__ = (
    'RmValue',
    'RmDomain',
    'RmInt',
    'RmFloat',
    'RmString',
)


RmValue = int | float | str
"""
value의 타입.
"""


[문서] class RmDomain(ABC): """ domain의 기반 추상 클래스입니다. """
[문서] @abstractmethod def get_format(self) -> str: # pragma: no cover """ domain의 format을 반환합니다. Returns: str: domain의 format (`struct` 모듈에서 사용하는 format). """ pass
[문서] @abstractmethod def calc_size(self) -> int: # pragma: no cover """ domain의 크기를 반환합니다. Returns: int: domain의 크기. """ pass
[문서] @abstractmethod def validate(self, value: RmValue) -> None: # pragma: no cover """ value의 유효성을 검사합니다. Args: value (RmValue): 대상 value. Raises: RmInvalidDomainValueError: `value` 가 유효하지 않은 경우. """ pass
[문서] @abstractmethod def encode(self, value: RmValue) -> bytes: # pragma: no cover """ value를 bytes로 인코딩합니다. Args: value (RmValue): 대상 value. Returns: bytes: value를 bytes로 인코딩한 결과. """ pass
[문서] @abstractmethod def decode(self, data: bytes) -> RmValue: # pragma: no cover """ bytes를 value로 디코딩합니다. Args: data (bytes): 대상 bytes. Returns: RmValue: bytes를 value로 디코딩한 결과. """ pass
[문서] @dataclass(frozen=True) class RmInt(RmDomain): """ INT domain을 나타내는 클래스입니다. """
[문서] def get_format(self) -> str: return '<i'
[문서] def calc_size(self) -> int: return struct.calcsize(self.get_format())
[문서] def validate(self, value: RmValue) -> None: if not isinstance(value, int): raise RmInvalidDomainValueError( f"RmInt의 `value`는 `int` 타입만 가능합니다 " f"(현재 타입: {type(value)}).", )
[문서] def encode(self, value: RmValue) -> bytes: value = int(value) data = struct.pack(self.get_format(), value) return data
[문서] def decode(self, data: bytes) -> int: (value,) = struct.unpack(self.get_format(), data) return int(value)
[문서] @dataclass(frozen=True) class RmFloat(RmDomain): """ FLOAT domain을 나타내는 클래스입니다. """
[문서] def get_format(self) -> str: return '<f'
[문서] def calc_size(self) -> int: return struct.calcsize(self.get_format())
[문서] def validate(self, value: RmValue) -> None: if not isinstance(value, float): raise RmInvalidDomainValueError( f"RmFloat의 `value`는 `float` 타입만 가능합니다 " f"(현재 타입: {type(value)}).", )
[문서] def encode(self, value: RmValue) -> bytes: value = float(value) data = struct.pack(self.get_format(), value) return data
[문서] def decode(self, data: bytes) -> float: (value,) = struct.unpack(self.get_format(), data) return float(value)
[문서] @dataclass(frozen=True) class RmString(RmDomain): """ STRING domain을 나타내는 클래스입니다. Attributes: length (int): domain의 길이. """ MAX_LENGTH = 255 """ domain의 최대 길이. """ length: int """ domain의 길이. """
[문서] def __post_init__(self) -> None: """ domain 초기화 후, `length` 를 검사합니다. Raises: RmInvalidDomainStringLengthError: `length` 가 유효하지 않은 경우. """ if self.length < 1 or self.length > RmString.MAX_LENGTH: raise RmInvalidDomainStringLengthError( f"RmString의 길이는 1 이상, {RmString.MAX_LENGTH} 이하만 가능합니다 " f"(현재 길이: {self.length}).", )
[문서] def get_format(self) -> str: return f'<{self.length}s'
[문서] def calc_size(self) -> int: return self.length
[문서] def validate(self, value: RmValue) -> None: if not isinstance(value, str): raise RmInvalidDomainValueError( f"RmString의 `value`는 `str` 타입만 가능합니다 " f"(현재 타입: {type(value)}).", )
[문서] def encode(self, value: RmValue) -> bytes: value = str(value) data = value.encode()[:self.length] data = data.ljust(self.length, b'\x00') return data
[문서] def decode(self, data: bytes) -> str: data = data[:self.length] value = data.decode() value = value.rstrip('\x00') return str(value)