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

bogdandm / json2python-models / 11497710154

24 Oct 2024 10:40AM UTC coverage: 90.904% (-7.2%) from 98.072%
11497710154

Pull #59

github

bkalashnikov
Enable pytest workers
Pull Request #59: Modernize project setup and setup cron action job

2 of 6 new or added lines in 2 files covered. (33.33%)

115 existing lines in 7 files now uncovered.

1479 of 1627 relevant lines covered (90.9%)

4.54 hits per line

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
5✔
2
from typing import ClassVar, Collection, Dict, Iterable, List, Set, Tuple, Type, Union
5✔
3

4
from .base import BaseType, ImportPathList
5✔
5

6

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

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

15
    actual_type: ClassVar[Type]
5✔
16

17
    @classmethod
5✔
18
    def to_internal_value(cls, value: str) -> 'StringSerializable':
5✔
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:
5✔
29
        """
30
        Convert instance to string literal
31

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

36
    @classmethod
5✔
37
    def to_typing_code(cls, types_style: Dict[Union['BaseType', Type['BaseType']], dict]) -> Tuple[ImportPathList, str]:
5✔
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__
5✔
43
        options = cls.get_options_for_type(cls, types_style)
5✔
44
        if options.get(cls.TypeStyle.use_actual_type):
5✔
45
            if cls.actual_type.__module__ != 'builtins':
5✔
46
                return [(cls.actual_type.__module__, cls.actual_type.__name__)], cls.actual_type.__name__
5✔
47
            return [], cls.actual_type.__name__
5✔
48
        return [('json_to_models.dynamic_typing', cls_name)], cls_name
5✔
49

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

53

54
T_StringSerializable = Type[StringSerializable]
5✔
55

56

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

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

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

68
    def add(self, replace_types: Iterable[T_StringSerializable] = (), cls: type = None):
5✔
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):
5✔
78
            self.types.append(cls)
5✔
79
            for t in replace_types:
5✔
80
                self.replaces.add((t, cls))
5✔
81
            return cls
5✔
82

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

87
        return decorator
5✔
88

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

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

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

105
    def resolve(self, *types: T_StringSerializable) -> Collection[T_StringSerializable]:
5✔
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)
5✔
115
        flag = True
5✔
116
        while flag:
5✔
117
            flag = False
5✔
118
            filtered: Set[T_StringSerializable] = set()
5✔
119
            for t1, t2 in permutations(types, 2):
5✔
120
                if (t1, t2) in self.replaces:
5✔
121
                    filtered.add(t2)
5✔
122
                    flag = True
5✔
123
            if flag:
5✔
124
                types = filtered
5✔
125
        # noinspection PyUnboundLocalVariable
126
        return types
5✔
127

128

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

132

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

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

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

144

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

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

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

156

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

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

169
    def to_representation(self) -> str:
5✔
170
        return str(bool(self)).lower()
5✔
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