Completed
Push — develop ( b4a21c...b1df34 )
by Jace
02:37
created

Float   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 14
Duplicated Lines 0 %

Test Coverage

Coverage 100%
Metric Value
wmc 3
dl 0
loc 14
ccs 9
cts 9
cp 1
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A to_value() 0 8 3
1
"""Convertible classes for builtin immutable types."""
2
3 1
from .. import common, exceptions
4 1
from ..bases import Converter
5
6 1
log = common.logger(__name__)
7
8
9 1
class Object(Converter):
10
    """Base class for immutable types."""
11
12 1
    TYPE = None  # type for inferred types (set in subclasses)
13 1
    DEFAULT = None  # default value for conversion (set in subclasses)
14
15 1
    @classmethod
16
    def create_default(cls):
17 1
        return cls.DEFAULT
18
19 1
    @classmethod
20
    def to_value(cls, obj):
21 1
        return obj
22
23 1
    @classmethod
24
    def to_data(cls, obj):
25 1
        return cls.to_value(obj)
26
27
28 1
class String(Object):
29
    """Convertible for the `str` type."""
30
31 1
    TYPE = str
32 1
    DEFAULT = ""
33
34 1
    @classmethod
35
    def to_value(cls, obj):
36 1
        if isinstance(obj, cls.TYPE):
37 1
            return obj
38 1
        elif obj is True:
39 1
            return "true"
40 1
        elif obj is False:
41 1
            return "false"
42 1
        elif obj:
43 1
            try:
44 1
                return ', '.join(str(item) for item in obj)
45 1
            except TypeError:
46 1
                return str(obj)
47
        else:
48 1
            return cls.DEFAULT
49
50 1
    @classmethod
51
    def to_data(cls, obj):
52 1
        value = cls.to_value(obj)
53 1
        return cls._optimize_for_quoting(value)
54
55 1
    @staticmethod
56
    def _optimize_for_quoting(value):
57 1
        if value == "true":
58 1
            return True
59 1
        if value == "false":
60 1
            return False
61 1
        for number_type in (int, float):
62 1
            try:
63 1
                return number_type(value)
64 1
            except (TypeError, ValueError):
65 1
                continue
66 1
        return value
67
68
69 1
class Integer(Object):
70
    """Convertible for the `int` type."""
71
72 1
    TYPE = int
73 1
    DEFAULT = 0
74
75 1
    @classmethod
76
    def to_value(cls, obj):
77 1
        if all((isinstance(obj, cls.TYPE),
78
                obj is not True,
79
                obj is not False)):
80 1
            return obj
81 1
        elif obj:
82 1
            try:
83 1
                return int(obj)
84 1
            except ValueError:
85 1
                return int(float(obj))
86
        else:
87 1
            return cls.DEFAULT
88
89
90 1
class Float(Object):
91
    """Convertible for the `float` type."""
92
93 1
    TYPE = float
94 1
    DEFAULT = 0.0
95
96 1
    @classmethod
97
    def to_value(cls, obj):
98 1
        if isinstance(obj, cls.TYPE):
99 1
            return obj
100 1
        elif obj:
101 1
            return float(obj)
102
        else:
103 1
            return cls.DEFAULT
104
105
106 1
class Boolean(Object):
107
    """Convertible for the `bool` type."""
108
109 1
    TYPE = bool
110 1
    DEFAULT = False
111
112 1
    FALSY = ('false', 'f', 'no', 'n', 'disabled', 'off', '0')
113
114 1
    @classmethod
115
    def to_value(cls, obj):
116 1
        if isinstance(obj, str) and obj.lower().strip() in cls.FALSY:
117 1
            return False
118 1
        elif obj is not None:
119 1
            return bool(obj)
120
        else:
121 1
            return cls.DEFAULT
122
123
124 1
def match(name, data, nested=False):
125
    """Determine the appropriate converter for new data."""
126 1
    nested = " nested" if nested else ""
127 1
    msg = "Determining converter for new%s: '%s' = %r"
128 1
    log.debug(msg, nested, name, repr(data))
129
130 1
    types = Object.__subclasses__()  # pylint: disable=no-member
131 1
    log.trace("Converter options: {}".format(types))
132
133 1
    for converter in types:
134 1
        if converter.TYPE and type(data) == converter.TYPE:  # pylint: disable=unidiomatic-typecheck
0 ignored issues
show
introduced by
Bad option value 'unidiomatic-typecheck'
Loading history...
135 1
            log.debug("Matched converter: %s", converter)
136 1
            log.info("New%s attribute: %s", nested, name)
137 1
            return converter
138
139 1
    if data is None or isinstance(data, (dict, list)):
140 1
        log.info("Default converter: %s", Object)
141 1
        log.warning("New%s attribute with unknown type: %s", nested, name)
142 1
        return Object
143
144 1
    msg = "No converter available for: {}".format(data)
145
    raise exceptions.ConversionError(msg)
146