File: _base.py

package info (click to toggle)
python-typepy 1.3.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 624 kB
  • sloc: python: 2,886; makefile: 78; sh: 7
file content (138 lines) | stat: -rw-r--r-- 3,552 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""

import abc
from typing import Any, Optional

from .._typecode import Typecode
from ..checker._interface import TypeCheckerInterface
from ..converter import ValueConverterInterface
from ..error import TypeConversionError


class AbstractType(TypeCheckerInterface, ValueConverterInterface):
    __slots__ = (
        "_data",
        "_strict_level",
        "_params",
        "__checker",
        "__converter",
        "__is_type_result",
    )

    @abc.abstractproperty
    def typecode(self) -> Typecode:  # pragma: no cover
        pass

    @abc.abstractmethod
    def _create_type_checker(self):  # pragma: no cover
        pass

    @abc.abstractmethod
    def _create_type_converter(self):  # pragma: no cover
        pass

    @property
    def typename(self) -> str:
        return self.typecode.name

    def __init__(self, value: Any, strict_level: int, **kwargs) -> None:
        self._data = value
        self._strict_level = strict_level
        self._params = kwargs

        self.__checker = self._create_type_checker()
        self.__converter = self._create_type_converter()

        self.__is_type_result: Optional[bool] = None

    def __repr__(self) -> str:
        return ", ".join(
            [
                f"value={self._data}",
                f"typename={self.typename}",
                f"strict_level={self._strict_level}",
                f"is_type={self.is_type()}",
                f"try_convert={self.try_convert()}",
            ]
        )

    def is_type(self) -> bool:
        """
        :return:
        :rtype: bool
        """

        if self.__is_type_result is not None:
            return self.__is_type_result

        self.__is_type_result = self.__is_type()

        return self.__is_type_result

    def __is_type(self) -> bool:
        if self.__checker.is_type():
            return True

        if self.__checker.is_exclude_instance():
            return False

        try:
            self._converted_value = self.__converter.force_convert()
        except TypeConversionError:
            return False

        if not self.__checker.is_valid_after_convert(self._converted_value):
            return False

        return True

    def validate(self, error_message: Optional[str] = None) -> None:
        """
        :raises TypeError:
            If the value is not matched the type that the class represented.
        """

        if self.is_type():
            return

        if not error_message:
            error_message = "invalid value type"

        raise TypeError(f"{error_message}: expected={self.typename}, actual={type(self._data)}")

    def convert(self):
        """
        :return: Converted value.
        :raises typepy.TypeConversionError:
            If the value cannot convert.
        """

        if self.is_type():
            return self.force_convert()

        raise TypeConversionError(
            "failed to convert {} from {} to {}".format(
                self._data, type(self._data).__name__, self.typename
            )
        )

    def force_convert(self):
        """
        :return: Converted value.
        :raises typepy.TypeConversionError:
            If the value cannot convert.
        """

        return self.__converter.force_convert()

    def try_convert(self):
        """
        :return: Converted value. |None| if failed to convert.
        """

        try:
            return self.convert()
        except TypeConversionError:
            return None