Completed
Push — develop ( 43cfbd...dbd5e6 )
by Jace
01:46
created

yorm.load_file()   B

Complexity

Conditions 6

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.1979
Metric Value
cc 6
dl 0
loc 29
ccs 14
cts 17
cp 0.8235
crap 6.1979
rs 7.5385
1
"""Shared internal classes and functions."""
2
3 1
import os
4 1
import shutil
5 1
import collections
6 1
import logging
7
8 1
import simplejson as json
0 ignored issues
show
Configuration introduced by
The import simplejson could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
9 1
import yaml
0 ignored issues
show
Configuration introduced by
The import yaml could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
10
11 1
from . import exceptions
12
13
14
# CONSTANTS ####################################################################
15
16
17 1
PRINT_VERBOSITY = 0  # minimum verbosity to using `print`
18 1
STR_VERBOSITY = 3  # minimum verbosity to use verbose `__str__`
19 1
MAX_VERBOSITY = 4  # maximum verbosity level implemented
20
21 1
OVERRIDE_MESSAGE = "method must be implemented in subclasses"
22
23
24
# GLOBALS ######################################################################
25
26
27 1
verbosity = 0  # global verbosity setting for controlling string formatting
28
29 1
attrs = collections.defaultdict(dict)
30
31
32
# LOGGING ######################################################################
33
34
35
def _trace(self, message, *args, **kwargs):  # pragma: no cover (manual test)
36
    """Handler for a new TRACE logging level."""
37
    if self.isEnabledFor(logging.DEBUG - 1):
38
        self._log(logging.DEBUG - 1, message, args, **kwargs)  # pylint: disable=W0212
39
40
41 1
logging.addLevelName(logging.DEBUG - 1, "TRACE")
42 1
logging.Logger.trace = _trace
43
44 1
logger = logging.getLogger
45 1
log = logger(__name__)
46
47
48
# DECORATORS ###################################################################
49
50
51 1
class classproperty(object):
52
    """Read-only class property decorator."""
53
54 1
    def __init__(self, getter):
55 1
        self.getter = getter
56
57 1
    def __get__(self, instance, owner):
58 1
        return self.getter(owner)
59
60
61
# FUNCTIONS ####################################################################
62
63
64 1
def create_dirname(path):
65
    """Ensure a parent directory exists for a path."""
66 1
    dirpath = os.path.dirname(path)
67 1
    if dirpath and not os.path.isdir(dirpath):
68 1
        log.trace("creating directory '{}'...".format(dirpath))
69 1
        os.makedirs(dirpath)
70
71
72 1
def read_text(path, encoding='utf-8'):
73
    """Read text from a file.
74
75
    :param path: file path to read from
76
    :param encoding: input file encoding
77
78
    :return: string
79
80
    """
81 1
    log.trace("reading text from '{}'...".format(path))
82 1
    with open(path, 'r', encoding=encoding) as stream:
83 1
        text = stream.read()
84 1
    return text
85
86
87 1
def load_file(text, path, ext='yml'):
88
    """Parse a dictionary from YAML text.
89
90
    :param text: string containing dumped YAML data
91
    :param path: file path for error messages
92
93
    :return: dictionary
94
95
    """
96
    # Load the YAML data
97 1
    data = {}
98 1
    try:
99 1
        if ext in ['yml', 'yaml']:
100 1
            data = yaml.load(text) or {}
101 1
        elif ext in ['json']:
102 1
            data = json.loads(text) or {}
103 1
    except yaml.error.YAMLError as exc:
104 1
        msg = "invalid YAML contents: {}:\n{}".format(path, exc)
105 1
        raise exceptions.ContentError(msg) from None
106
    except json.JSONDecodeError as exc:
107
        msg = "invalid JSON contents: {}:\n{}".format(path, exc)
108
        raise exceptions.ContentError(msg) from None
109
    # Ensure data is a dictionary
110 1
    if not isinstance(data, dict):
111 1
        msg = "invalid file contents: {}".format(path)
112 1
        raise exceptions.ContentError(msg)
113
114
    # Return the parsed data.
115 1
    return data
116
117
118 1
def dump_file(data, ext):
119 1
    if ext in ['yml', 'yaml']:
120 1
        return yaml.dump(data, default_flow_style=False, allow_unicode=True)
121
122 1
    if ext in ['json']:
123 1
        return json.dumps(data, indent=4, sort_keys=True)
124
125
126 1
def write_text(text, path, encoding='utf-8'):
127
    """Write text to a file.
128
129
    :param text: string
130
    :param path: file to write text
131
    :param encoding: output file encoding
132
133
    :return: path of new file
134
135
    """
136 1
    if text:
137 1
        log.trace("writing text to '{}'...".format(path))
138 1
    with open(path, 'wb') as stream:
139 1
        data = text.encode(encoding)
140 1
        stream.write(data)
141 1
    return path
142
143
144 1
def touch(path):
145
    """Ensure a file exists."""
146 1
    if not os.path.exists(path):
147 1
        dirpath = os.path.dirname(path)
148 1
        if dirpath and not os.path.isdir(dirpath):
149 1
            log.trace("creating directory '{}'...".format(dirpath))
150 1
            os.makedirs(dirpath)
151 1
        log.trace("creating empty '{}'...".format(path))
152 1
        write_text('', path)
153
154
155 1
def stamp(path):
156
    """Get the modification timestamp from a file."""
157 1
    return os.path.getmtime(path)
158
159
160 1
def delete(path):
161
    """Delete a file or directory with error handling."""
162 1
    if os.path.isdir(path):
163 1
        try:
164 1
            log.trace("deleting '{}'...".format(path))
165 1
            shutil.rmtree(path)
166
        except IOError:  # pragma: no cover (manual test)
167
            # bug: http://code.activestate.com/lists/python-list/159050
168
            msg = "unable to delete: {}".format(path)
169
            log.warning(msg)
170 1
    elif os.path.isfile(path):
171 1
        log.trace("deleting '{}'...".format(path))
172
        os.remove(path)
173