Completed
Push — master ( 0c9be4...21dee9 )
by Alexandre M.
51s
created

hansel._touch()   D

Complexity

Conditions 8

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 8
dl 0
loc 33
rs 4
1
# -*- coding: utf-8 -*-
2
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3
# vi: set ft=python sts=4 ts=4 sw=4 et:
4
"""
5
Crumb manipulation utilities
6
"""
7
import functools
8
import os
9
import os.path as op
10
import warnings
11
12
from   six import string_types
13
14
15
def _dict_popitems(adict, **kwargs):
16
    if not adict:
17
        return
18
19
    if not kwargs:
20
        return
21
22
    _ = [adict.pop(k) for k in kwargs if k in adict]
23
24
25
def _get_path(crumb_path):
26
    """ Return the path string from `crumb_path`.
27
    Parameters
28
    ----------
29
    crumb_path: str or Crumb
30
31
    Returns
32
    -------
33
    path: str
34
    """
35
    if hasattr(crumb_path, '_path'):
36
        crumb_path = crumb_path._path
37
38
    if not isinstance(crumb_path, string_types):
39
        raise TypeError("Expected `crumb_path` to be a {}, got {}.".format(string_types, type(crumb_path)))
40
41
    return crumb_path
42
43
44
def _is_crumb_arg(crumb_arg, start_end_syms=('{', '}')):
45
    """ Returns True if `crumb_arg` is a well formed crumb argument, i.e.,
46
    is a string that starts with `start_sym` and ends with `end_sym`. False otherwise."""
47
    if not isinstance(crumb_arg, string_types):
48
        return False
49
50
    start_sym, end_sym = start_end_syms
51
    return crumb_arg.startswith(start_sym) and crumb_arg.endswith(end_sym)
52
53
54
def _arg_params(arg, start_end_syms=('{', '}'), reg_sym=':'):
55
    """ Return the name and the regex of the argument given its crumb representation.
56
    Parameters
57
    ----------
58
    arg_crumb: str
59
60
    start_end_syms: 2-tuple of str
61
        The strings that indicate the start and end of a crumb argument
62
63
    reg_sym: str
64
        The string that separate the crumb argument name from the
65
        crumb argument regular expression.
66
67
    Returns
68
    -------
69
    arg_name: str
70
71
    arg_regex: str
72
    """
73
    arg_content = _arg_content(arg, start_end_syms=start_end_syms)
74
75
    if reg_sym in arg_content:
76
        return tuple(arg_content.split(reg_sym))
77
    else:
78
        return arg_content, None
79
80
81
def _arg_name(arg, start_end_syms=('{', '}'), reg_sym=':'):
82
    """ Return the name of the argument given its crumb representation.
83
    Parameters
84
    ----------
85
    arg_crumb: str
86
87
    start_end_syms: 2-tuple of str
88
        The strings that indicate the start and end of a crumb argument
89
90
    reg_sym: str
91
        The string that separate the crumb argument name from the
92
        crumb argument regular expression.
93
94
    Returns
95
    -------
96
    arg_name: str
97
    """
98
    arg_content = _arg_content(arg, start_end_syms=start_end_syms)
99
100
    if reg_sym in arg_content:
101
        return arg_content.split(reg_sym)[0]
102
    else:
103
        return arg_content
104
105
106
def _arg_regex(arg, start_end_syms=('{', '}'), reg_sym=':'):
107
    """ Return the name of the argument given its crumb representation.
108
    Parameters
109
    ----------
110
    arg_crumb: str
111
112
    start_end_syms: 2-tuple of str
113
        The strings that indicate the start and end of a crumb argument
114
115
    reg_sym: str
116
        The string that separate the crumb argument name from the
117
        crumb argument regular expression.
118
119
    Returns
120
    -------
121
    arg_name: str
122
    """
123
    arg_content = _arg_content(arg, start_end_syms=start_end_syms)
124
125
    if reg_sym in arg_content:
126
        return arg_content.split(reg_sym)[1]
127
    else:
128
        return None
129
130
131
def _arg_content(arg, start_end_syms=('{', '}')):
132
    """ Return the name of the argument given its crumb representation.
133
    Parameters
134
    ----------
135
    arg_crumb: str
136
137
    start_end_syms: 2-tuple of str
138
        The strings that indicate the start and end of a crumb argument
139
140
    Returns
141
    -------
142
    arg_name: str
143
    """
144
    if not _is_crumb_arg(arg):
145
        raise ValueError("Expected an well formed crumb argument, "
146
                         "got {}.".format(arg))
147
148
    start_sym, end_sym = start_end_syms
149
    return arg[len(start_sym):-len(end_sym)]
150
151
152
def _arg_format(arg_name, start_end_syms=('{', '}'), reg_sym=':', regex=None):
153
    """ Return the crumb argument for its string `format()` representation.
154
    Parameters
155
    ----------
156
    arg_name: str
157
158
    Returns
159
    -------
160
    arg_format: str
161
    """
162
    start_sym, end_sym = start_end_syms
163
164
    arg_fmt = start_sym + arg_name
165
    if regex is not None:
166
        arg_fmt += reg_sym + regex
167
    arg_fmt += end_sym
168
169
    return arg_fmt
170
171
172
def is_valid(crumb_path, start_end_syms=('{', '}')):
173
    """ Return True if `crumb_path` is a well formed path with crumb arguments,
174
    False otherwise.
175
    Parameters
176
    ----------
177
    crumb_path: str
178
179
    Returns
180
    -------
181
    is_valid: bool
182
    """
183
    crumb_path = _get_path(crumb_path)
184
185
    start_sym, end_sym = start_end_syms
186
187
    splt = crumb_path.split(op.sep)
188
    for crumb in splt:
189
        if op.isdir(crumb):
190
            continue
191
192
        if _is_crumb_arg(crumb, start_end_syms=start_end_syms):
193
            crumb = _arg_name(crumb, start_end_syms=start_end_syms)
194
195
        if start_sym in crumb or end_sym in crumb:
196
            return False
197
198
    return True
199
200
201
def has_crumbs(crumb_path, start_end_syms=('{', '}')):
202
    """ Return True if the `crumb_path.split(op.sep)` has item which is a crumb argument
203
    that starts with '{' and ends with '}'."""
204
    crumb_path = _get_path(crumb_path)
205
206
    splt = crumb_path.split(op.sep)
207
    for i in splt:
208
        if _is_crumb_arg(i, start_end_syms=start_end_syms):
209
            return True
210
211
    return False
212
213
214
def _replace(crumb_path, start_end_syms=('{', '}'), regexes=None, **kwargs):
215
    """ Return `crumb_path` where every crumb argument found in `kwargs` has been
216
    replaced by the given value in `kwargs."""
217
    if not kwargs:
218
        return crumb_path
219
220
    if regexes is None:
221
        regexes = {}
222
223
    for k in kwargs:
224
        karg = _arg_format(k, start_end_syms=start_end_syms, regex=regexes.get(k, None))
225
        if k not in crumb_path:
226
            raise KeyError("Could not find argument {} in"
227
                           " `crumb_path` {}.".format(k, crumb_path))
228
229
        crumb_path = crumb_path.replace(karg, kwargs[k])
230
231
    return crumb_path
232
233
234
def _split(crumb_path, start_end_syms=('{', '}')):
235
    """ Split `crumb_path` in two parts, the first is the base folder without any crumb argument
236
        and the second is the rest of `crumb_path` beginning with the first crumb argument.
237
        If `crumb_path` has no crumb arguments or starts with a crumb argument, return `crumb_path`.
238
    """
239
    crumb_path = _get_path(crumb_path)
240
241
    if not has_crumbs(crumb_path, start_end_syms=start_end_syms):
242
        return crumb_path
243
244
    if not is_valid(crumb_path, start_end_syms=start_end_syms):
245
        raise ValueError('Crumb path {} is not valid.'.format(crumb_path))
246
247
    start_sym, _ = start_end_syms
248
    if crumb_path.startswith(start_sym):
249
        return crumb_path
250
251
    idx = crumb_path.find(start_sym)
252
    base = crumb_path[0:idx]
253
    if base.endswith(op.sep):
254
        base = base[:-1]
255
256
    rest = crumb_path[idx:]
257
258
    return base, rest
259
260
261
def _touch(crumb_path, exist_ok=True, start_end_syms=('{', '}')):
262
    """ Create a leaf directory and all intermediate ones
263
    using the non crumbed part of `crumb_path`.
264
    If the target directory already exists, raise an IOError
265
    if exist_ok is False. Otherwise no exception is raised.
266
    Parameters
267
    ----------
268
    crumb_path: str
269
270
    exist_ok: bool
271
        Default = True
272
273
    Returns
274
    -------
275
    nupath: str
276
        The new path created.
277
    """
278
    if has_crumbs(crumb_path, start_end_syms=start_end_syms):
279
        nupath = _split(crumb_path, start_end_syms=start_end_syms)[0]
280
    else:
281
        nupath = crumb_path
282
283
    if op.exists(nupath) and not exist_ok:
284
        raise IOError("Folder {} already exists.".format(nupath))
285
    elif op.exists(nupath) and exist_ok:
286
        return nupath
287
288
    try:
289
        os.makedirs(nupath)
290
    except:
291
        raise
292
    else:
293
        return nupath
294
295
296
def _split_exists(crumb_path, start_end_syms=('{', '}')):
297
    """ Return True if the part without crumb arguments of `crumb_path`
298
    is an existing path or a symlink, False otherwise.
299
    Returns
300
    -------
301
    exists: bool
302
    """
303
    if has_crumbs(crumb_path):
304
        rpath = _split(crumb_path, start_end_syms=start_end_syms)[0]
305
    else:
306
        rpath = str(crumb_path)
307
308
    return op.exists(rpath) or op.islink(rpath)
309
310
311
def _check_is_subset(list1, list2):
312
    """ Raise an error if `list1` is not a subset of `list2`."""
313
    if not set(list1).issubset(set(list2)):
314
        raise KeyError('The `list1` argument should be a subset of `list2`, '
315
                       'got {} and {}.'.format(list1, list2))
316
317
318
def deprecated(replacement=None):
319
    """A decorator which can be used to mark functions as deprecated.
320
    replacement is a callable that will be called with the same args
321
    as the decorated function.
322
323
    >>> @deprecated()
324
    ... def foo(x):
325
    ...     return x
326
    ...
327
    >>> ret = foo(1)
328
    DeprecationWarning: foo is deprecated
329
    >>> ret
330
    1
331
    >>>
332
    >>>
333
    >>> def newfun(x):
334
    ...     return 0
335
    ...
336
    >>> @deprecated(newfun)
337
    ... def foo(x):
338
    ...     return x
339
    ...
340
    >>> ret = foo(1)
341
    DeprecationWarning: foo is deprecated; use newfun instead
342
    >>> ret
343
    0
344
    >>>
345
    """
346
    def outer(fun):
347
        msg = "psutil.%s is deprecated" % fun.__name__
348
        if replacement is not None:
349
            msg += "; use %s instead" % replacement
350
        if fun.__doc__ is None:
351
            fun.__doc__ = msg
352
353
        @functools.wraps(fun)
354
        def inner(*args, **kwargs):
355
            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
356
            return fun(*args, **kwargs)
357
358
        return inner
359
    return outer
360