• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bogdandm / json2python-models / 11498135606

pending completion
11498135606

Pull #59

github

web-flow
Merge 2387ea51f into e2606e8f2
Pull Request #59: Modernize project setup and setup cron action job

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.25
/json_to_models/dynamic_typing/string_serializable.py
1
from itertools import permutations
2✔
2
from typing import ClassVar, Collection, Dict, Iterable, List, Set, Tuple, Type, Union
2✔
3

4
from .base import BaseType, ImportPathList
2✔
5

6

7
class StringSerializable(BaseType):
2✔
8
    """
9
    Mixin for classes which are used to (de-)serialize some values in a string form
10
    """
11

12
    class TypeStyle:
2✔
13
        use_actual_type = 'use_actual_type'
2✔
14

15
    actual_type: ClassVar[Type]
2✔
16

17
    @classmethod
2✔
18
    def to_internal_value(cls, value: str) -> 'StringSerializable':
2✔
19
        """
20
        Factory method
21

22
        :raises ValueError: if this class can not represent given value
23
        :param value: some string literal
24
        :return: Instance of this class
25
        """
26
        raise NotImplementedError()
27

28
    def to_representation(self) -> str:
2✔
29
        """
30
        Convert instance to string literal
31

32
        :return: string literal
33
        """
34
        raise NotImplementedError()
35

36
    @classmethod
2✔
37
    def to_typing_code(cls, types_style: Dict[Union['BaseType', Type['BaseType']], dict]) -> Tuple[ImportPathList, str]:
2✔
38
        """
39
        Unlike other BaseType's subclasses it's a class method because StringSerializable instance is not parameterized
40
        as a metadata instance but contains actual data
41
        """
42
        cls_name = cls.__name__
2✔
43
        options = cls.get_options_for_type(cls, types_style)
2✔
44
        if options.get(cls.TypeStyle.use_actual_type):
2✔
45
            if cls.actual_type.__module__ != 'builtins':
2✔
46
                return [(cls.actual_type.__module__, cls.actual_type.__name__)], cls.actual_type.__name__
2✔
47
            return [], cls.actual_type.__name__
2✔
48
        return [('json_to_models.dynamic_typing', cls_name)], cls_name
2✔
49

50
    def __iter__(self):
2✔
51
        return iter(())
×
52

53

54
T_StringSerializable = Type[StringSerializable]
2✔
55

56

57
class StringSerializableRegistry:
2✔
58
    def __init__(self, *types: T_StringSerializable):
2✔
59
        self.types: List[T_StringSerializable] = list(types)
2✔
60
        self.replaces: Set[Tuple[T_StringSerializable, T_StringSerializable]] = set()
2✔
61

62
    def __iter__(self):
2✔
63
        return iter(self.types)
2✔
64

65
    def __contains__(self, item):
2✔
66
        return item in self.types
2✔
67

68
    def add(self, replace_types: Iterable[T_StringSerializable] = (), cls: type = None):
2✔
69
        """
70
        Register decorated class in registry. Can be called as a method if cls argument is passed.
71

72
        :param replace_types: List of classes that is the particular case of decorated one
73
        :param cls: StringSerializable class
74
        :return: decorator
75
        """
76

77
        def decorator(cls):
2✔
78
            self.types.append(cls)
2✔
79
            for t in replace_types:
2✔
80
                self.replaces.add((t, cls))
2✔
81
            return cls
2✔
82

83
        if cls:
2✔
84
            decorator(cls)
2✔
85
            return
86

87
        return decorator
2✔
88

89
    def remove(self, cls: T_StringSerializable):
2✔
90
        """
91
        Unregister given class
92

93
        :param cls: StringSerializable class
94
        """
95
        self.types.remove(cls)
2✔
96
        for base, replace in list(self.replaces):
2✔
97
            if replace is cls or base is cls:
2✔
98
                self.replaces.remove((base, replace))
×
99

100
    def remove_by_name(self, name: str):
2✔
101
        for cls in self.types[:]:
×
102
            if cls.__name__ == name or cls.actual_type.__name__ == name:
×
103
                self.remove(cls)
×
104

105
    def resolve(self, *types: T_StringSerializable) -> Collection[T_StringSerializable]:
2✔
106
        """
107
        Return set of StringSerializable classes which can represent all classes from types argument.
108

109
        :param types: Sequence of StringSerializable classes
110
        :return: Set of StringSerializable
111
        """
112
        # TODO: Resolve common type of 2 different types (e.g str from float and bool)
113
        # Do it by getting all childs of each class with their level then merge it into one list and find one with min(max(levels) for c n childs)
114
        types = set(types)
2✔
115
        flag = True
2✔
116
        while flag:
2✔
117
            flag = False
2✔
118
            filtered: Set[T_StringSerializable] = set()
2✔
119
            for t1, t2 in permutations(types, 2):
2✔
120
                if (t1, t2) in self.replaces:
2✔
121
                    filtered.add(t2)
2✔
122
                    flag = True
2✔
123
            if flag:
2✔
124
                types = filtered
2✔
125
        # noinspection PyUnboundLocalVariable
126
        return types
2✔
127

128

129
# Default registry
130
registry = StringSerializableRegistry()
2✔
131

132

133
@registry.add()
2✔
134
class IntString(StringSerializable, int):
2✔
135
    actual_type = int
2✔
136

137
    @classmethod
2✔
138
    def to_internal_value(cls, value: str) -> 'IntString':
2✔
139
        return cls(value)
2✔
140

141
    def to_representation(self) -> str:
2✔
142
        return str(self)
143

144

145
@registry.add(replace_types=(IntString,))
2✔
146
class FloatString(StringSerializable, float):
2✔
147
    actual_type = float
2✔
148

149
    @classmethod
2✔
150
    def to_internal_value(cls, value: str) -> 'FloatString':
2✔
151
        return cls(value)
2✔
152

153
    def to_representation(self) -> str:
2✔
154
        return str(self)
155

156

157
@registry.add()
2✔
158
class BooleanString(StringSerializable, int):
2✔
159
    # We can't extend bool class, but we can extend int with same result excepting isinstance and issubclass check
160
    actual_type = bool
2✔
161

162
    @classmethod
2✔
163
    def to_internal_value(cls, value: str) -> 'BooleanString':
2✔
164
        b = {"true": True, "false": False}.get(value.lower(), None)
2✔
165
        if b is None:
2✔
166
            raise ValueError(f"invalid literal for bool: '{value}'")
2✔
167
        return cls(b)
2✔
168

169
    def to_representation(self) -> str:
2✔
170
        return str(bool(self)).lower()
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc