yorm.decorators   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 68
dl 0
loc 139
ccs 65
cts 65
cp 1
rs 10
c 0
b 0
f 0
wmc 18

5 Functions

Rating   Name   Duplication   Size   Complexity  
A sync() 0 15 4
A sync_instances() 0 43 3
A attr() 0 21 4
A _ordered() 0 5 3
A sync_object() 0 33 4
1
"""Functions to enable mapping on classes and instances."""
2
3 1
import uuid
4 1
from collections import OrderedDict
5 1
import logging
6
7 1
from . import common
8 1
from .bases.mappable import patch_methods
9 1
from .mapper import Mapper
10
11 1
log = logging.getLogger(__name__)
12
13
14 1
def sync(*args, **kwargs):
15
    """Decorate class or map object based on arguments.
16
17
    This function will call either:
18
19
    * `sync_object` - when given an unmapped object
20
    * `sync_instances` - when used as the class decorator
21
22
    Consult the signature of each call for more information.
23
24
    """
25 1
    if 'path_format' in kwargs or args and isinstance(args[0], str):
26 1
        return sync_instances(*args, **kwargs)
27
    else:
28 1
        return sync_object(*args, **kwargs)
29
30
31 1
def sync_object(instance, path, attrs=None, **kwargs):
32
    """Enable YAML mapping on an object.
33
34
    :param instance: object to patch with YAML mapping behavior
35
    :param path: file path for dump/parse
36
    :param attrs: dictionary of attribute names mapped to converter classes
37
38
    :param auto_create: automatically create the file to save attributes
39
    :param auto_save: automatically save attribute changes to the file
40
    :param auto_track: automatically add new attributes from the file
41
42
    """
43 1
    log.info("Mapping %r to %s...", instance, path)
44
45 1
    common.get_mapper(instance, expected=False)
46 1
    patch_methods(instance)
47
48 1
    attrs = _ordered(attrs) or common.attrs[instance.__class__]
49 1
    mapper = Mapper(instance, path, attrs, **kwargs)
50
51 1
    if mapper.missing:
52 1
        if mapper.auto_create:
53 1
            mapper.create()
54 1
            if mapper.auto_save:
55 1
                mapper.save()
56 1
                mapper.load()
57
    else:
58 1
        mapper.load()
59
60 1
    common.set_mapper(instance, mapper)
61 1
    log.info("Mapped %r to %s", instance, path)
62
63 1
    return instance
64
65
66 1
def sync_instances(path_format, format_spec=None, attrs=None, **kwargs):
67
    """Decorate class to enable YAML mapping after instantiation.
68
69
    :param path_format: formatting string to create file paths for dump/parse
70
    :param format_spec: dictionary to use for string formatting
71
    :param attrs: dictionary of attribute names mapped to converter classes
72
73
    :param auto_create: automatically create the file to save attributes
74
    :param auto_save: automatically save attribute changes to the file
75
    :param auto_track: automatically add new attributes from the file
76
77
    """
78 1
    format_spec = format_spec or {}
79 1
    attrs = attrs or OrderedDict()
80
81 1
    def decorator(cls):
82
        """Class decorator to map instances to files."""
83 1
        common.path_formats[cls] = path_format
84
        init = cls.__init__
85 1
86
        def modified_init(self, *_args, **_kwargs):
87 1
            init(self, *_args, **_kwargs)
88
89 1
            log.info("Mapping instance of %r to '%s'...", cls, path_format)
90
91 1
            format_values = {}
92 1
            for key, value in format_spec.items():
93 1
                format_values[key] = getattr(self, value)
94 1
            if '{' + common.UUID + '}' in path_format:
95 1
                format_values[common.UUID] = uuid.uuid4().hex
96 1
            format_values['self'] = self
97
98 1
            common.attrs[cls].update(attrs)
99 1
            common.attrs[cls].update(common.attrs[self.__class__])
100 1
            path = path_format.format(**format_values)
101 1
            sync_object(self, path, **kwargs)
102
103 1
        modified_init.__doc__ = init.__doc__
104 1
        cls.__init__ = modified_init
105
106 1
        return cls
107
108 1
    return decorator
109
110
111 1
def attr(**kwargs):
112
    """Class decorator to map attributes to types.
113
114
    :param kwargs: keyword arguments mapping attribute name to converter class
115
116
    """
117 1
    if len(kwargs) != 1:
118 1
        raise ValueError("Single attribute required: {}".format(kwargs))
119
120 1
    def decorator(cls):
121
        """Class decorator."""
122 1
        previous = common.attrs[cls]
123 1
        common.attrs[cls] = OrderedDict()
124 1
        for name, converter in kwargs.items():
125 1
            common.attrs[cls][name] = converter
126 1
        for name, converter in previous.items():
127 1
            common.attrs[cls][name] = converter
128
129 1
        return cls
130
131 1
    return decorator
132
133
134 1
def _ordered(data):
135
    """Sort a dictionary-like object by key."""
136 1
    if data is None:
137 1
        return None
138
    return OrderedDict(sorted(data.items(), key=lambda pair: pair[0]))
139