Completed
Pull Request — develop (#143)
by
unknown
08:35
created

_instantiate()   A

Complexity

Conditions 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
c 2
b 0
f 0
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 3
rs 9.4285
1
"""Functions to interact with mapped classes and instances."""
2
3 1
import inspect
4 1
import logging
5
import string
6 1
import glob
7
import types
8 1
import parse
9
10
from . import common, exceptions
11 1
12
log = logging.getLogger(__name__)
0 ignored issues
show
Coding Style Naming introduced by
The name log does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
13
14
15
def create(class_or_instance, *args, overwrite=False, **kwargs):
16
    """Create a new mapped object.
17 1
18 1
    NOTE: Calling this function is unnecessary with 'auto_create' enabled.
19
20 1
    """
21 1
    instance = _instantiate(class_or_instance, *args, **kwargs)
22 1
    mapper = common.get_mapper(instance, expected=True)
23
24 1
    if mapper.exists and not overwrite:
25
        msg = "{!r} already exists".format(mapper.path)
26
        raise exceptions.DuplicateMappingError(msg)
27 1
28
    return load(save(instance))
29 1
30 1
31
def find(class_or_instance, *args, create=False, **kwargs):  # pylint: disable=redefined-outer-name
0 ignored issues
show
introduced by
Locally disabling redefined-outer-name (W0621)
Loading history...
32 1
    """Find a matching mapped object or return None."""
33 1
    instance = _instantiate(class_or_instance, *args, **kwargs)
34 1
    mapper = common.get_mapper(instance, expected=True)
35 1
36
    if mapper.exists:
37 1
        return instance
38
    elif create:
39
        return save(instance)
40 1
    else:
41
        return None
42 1
43 1
44
class GlobFormatter(string.Formatter):
45
    """
46 1
    Uses '*' for all unknown fields
47
    """
48
49
    WILDCARD = object()
50
51
    def get_value(self, key, args, kwargs):
52
        try:
53 1
            return super().get_value(key, args, kwargs)
54
        except (KeyError, IndexError):
55 1
            return self.WILDCARD
56
57 1
    def convert_field(self, value, conversion):
58
        if value is self.WILDCARD:
59
            return self.WILDCARD
60 1
        else:
61
            return super().convert_field(value, conversion)
62
63
    def format_field(self, value, format_spec):
64
        if value is self.WILDCARD:
65
            return '*'
66 1
        else:
67
            return super().format_field(value, format_spec)
68 1
69 1
70 1
def _unpack_parsed_fields(pathfields):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
71
    return {
72 1
        (k[len('self.'):] if k.startswith('self.') else k): v
73 1
        for k, v in pathfields.items()
74
    }
75 1
76
77 1
def match(cls_or_path, factory=None, **kwargs):
78
    """match(class, [callable], ...) -> instance, ...
79
    match(str, callable, ...) -> instance, ...
80 1
81
    Yield all matching mapped objects. Can be used two ways:
82 1
    * With a YORM-decorated class, optionally with a factory callable
83
    * With a Python 3-style string template and a factory callable
84 1
85
    The factory callable must accept keyuword arguments, extracted from the file
86 1
    name merged with those passed to match(). If no factory is given, the class
87
    itself is used as the factory (same signature).
88
    """
89 1
    if isinstance(cls_or_path, type):
90 1
        path_format = ...(cls_or_path)
91 1
        if factory is None:
92
            factory = cls_or_path
93 1
    else:
94 1
        path_format = cls_or_path
95
        if factory is None:
96 1
            raise TypeError("Factory must be given if a string template is used")
97
98
    log.debug((path_format, factory, kwargs))
99
    gf = GlobFormatter()
0 ignored issues
show
Coding Style Naming introduced by
The name gf does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
100
    mock = types.SimpleNamespace(**kwargs)
101
102
    posix_pattern = gf.format(path_format, self=mock, **kwargs)
0 ignored issues
show
Unused Code introduced by
Argument 'self' passed by position and keyword in method call
Loading history...
103
    py_pattern = parse.compile(path_format)
0 ignored issues
show
Bug introduced by
The Module parse does not seem to have a member named compile.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
104
105
    for filename in glob.iglob(posix_pattern, recursive=False):
0 ignored issues
show
Bug introduced by
The keyword recursive does not seem to exist for the function call.
Loading history...
106
        pathfields = py_pattern.parse(filename)
107
        fields = _unpack_parsed_fields(pathfields)
108
        fields.update(kwargs)
109
        yield factory(**fields)
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
110
111
112
def load(instance):
113
    """Force the loading of a mapped object's file.
114
115
    NOTE: Calling this function is unnecessary. It exists for the
116
        aesthetic purpose of having symmetry between save and load.
117
118
    """
119
    mapper = common.get_mapper(instance, expected=True)
120
121
    mapper.load()
122
123
    return instance
124
125
126
def save(instance):
127
    """Save a mapped object to file.
128
129
    NOTE: Calling this function is unnecessary with 'auto_save' enabled.
130
131
    """
132
    mapper = common.get_mapper(instance, expected=True)
133
134
    if mapper.deleted:
135
        msg = "{!r} was deleted".format(mapper.path)
136
        raise exceptions.DeletedFileError(msg)
137
138
    if not mapper.exists:
139
        mapper.create()
140
141
    mapper.save()
142
143
    return instance
144
145
146
def delete(instance):
147
    """Delete a mapped object's file."""
148
    mapper = common.get_mapper(instance, expected=True)
149
150
    mapper.delete()
151
152
    return None
153
154
155
def _instantiate(class_or_instance, *args, **kwargs):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
156
    if inspect.isclass(class_or_instance):
157
        instance = class_or_instance(*args, **kwargs)
158
    else:
159
        assert not args
160
        instance = class_or_instance
161
162
    return instance
163