Completed
Pull Request — develop (#126)
by
unknown
10:07
created

unlock()   A

Complexity

Conditions 2

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 22
ccs 11
cts 11
cp 1
crap 2
rs 9.2
c 0
b 0
f 0
1
"""Functions to manage the installation of dependencies."""
2
3 1
import os
4 1
import functools
5 1
import datetime
6 1
import logging
7
8 1
from . import common, system
9 1
from .models import load_config
10
11 1
log = logging.getLogger(__name__)
12
13
14 1
def restore_cwd(func):
15 1
    @functools.wraps(func)
16
    def wrapped(*args, **kwargs):
17 1
        cwd = os.getcwd()
18 1
        result = func(*args, **kwargs)
19 1
        os.chdir(cwd)
20 1
        return result
21 1
    return wrapped
22
23
24 1
@restore_cwd
25 1
def install(*names, root=None, depth=None,
26
            force=False, fetch=False, clean=True):
27
    """Install dependencies for a project.
28
29
    Optional arguments:
30
31
    - `*names`: optional list of dependency directory names to filter on
32
    - `root`: specifies the path to the root working tree
33
    - `depth`: number of levels of dependencies to traverse
34
    - `force`: indicates uncommitted changes can be overwritten
35
    - `fetch`: indicates the latest branches should always be fetched
36
    - `clean`: indicates untracked files should be deleted from dependencies
37
38
    """
39 1
    log.info("%sInstalling dependencies: %s",
40
             'force-' if force else '',
41
             ', '.join(names) if names else '<all>')
42 1
    count = None
43
44 1
    root = _find_root(root)
45 1
    config = load_config(root)
46
47 1
    if config:
48 1
        common.show("Installing dependencies...", log=False)
49 1
        common.show()
50 1
        count = config.install_deps(*names, update=False, depth=depth,
51
                                    force=force, fetch=fetch, clean=clean)
52
53 1
    return _display_result("install", "Installed", count)
54
55
56 1
@restore_cwd
57 1
def update(*names, root=None, depth=None,
58
           recurse=False, force=False, clean=True, lock=None):  # pylint: disable=redefined-outer-name
59
    """Update dependencies for a project.
60
61
    Optional arguments:
62
63
    - `*names`: optional list of dependency directory names to filter on
64
    - `root`: specifies the path to the root working tree
65
    - `depth`: number of levels of dependencies to traverse
66
    - `recurse`: indicates nested dependencies should also be updated
67
    - `force`: indicates uncommitted changes can be overwritten
68
    - `clean`: indicates untracked files should be deleted from dependencies
69
    - `lock`: indicates actual dependency versions should be recorded
70
71
    """
72 1
    log.info("%s dependencies%s: %s",
73
             'Force updating' if force else 'Updating',
74
             ', recursively' if recurse else '',
75
             ', '.join(names) if names else '<all>')
76 1
    count = None
77
78 1
    root = _find_root(root)
79 1
    config = load_config(root)
80
81 1
    if config:
82 1
        common.show("Updating dependencies...", log=False)
83 1
        common.show()
84 1
        count = config.install_deps(
85
            *names, update=True, depth=depth,
86
            recurse=recurse, force=force, fetch=True, clean=clean)
87 1
        common.dedent(level=0)
88 1
        if count and lock is not False:
89 1
            common.show("Recording installed versions...", log=False)
90 1
            common.show()
91 1
            config.lock_deps(*names, obey_existing=lock is None)
92
93 1
    return _display_result("update", "Updated", count)
94
95
96 1
@restore_cwd
97 1
def display(*, root=None, depth=None, allow_dirty=True):
98
    """Display installed dependencies for a project.
99
100
    Optional arguments:
101
102
    - `root`: specifies the path to the root working tree
103
    - `depth`: number of levels of dependencies to traverse
104
    - `allow_dirty`: causes uncommitted changes to be ignored
105
106
    """
107 1
    log.info("Displaying dependencies...")
108 1
    count = None
109
110 1
    root = _find_root(root)
111 1
    config = load_config(root)
112
113 1
    if config:
114 1
        common.show("Displaying current dependency versions...", log=False)
115 1
        common.show()
116 1
        config.log(datetime.datetime.now().strftime("%F %T"))
117 1
        count = 0
118 1
        for identity in config.get_deps(depth=depth, allow_dirty=allow_dirty):
119 1
            count += 1
120 1
            config.log("{}: {} @ {}", *identity)
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...
121 1
        config.log()
122
123 1
    return _display_result("display", "Displayed", count)
124
125
126 1
@restore_cwd
127 1
def lock(*names, root=None):
128
    """Lock current dependency versions for a project.
129
130
    Optional arguments:
131
132
    - `*names`: optional list of dependency directory names to filter on
133
    - `root`: specifies the path to the root working tree
134
135
    """
136 1
    log.info("Locking dependencies...")
137 1
    count = None
138
139 1
    root = _find_root(root)
140 1
    config = load_config(root)
141
142 1
    if config:
143 1
        common.show("Locking dependencies...", log=False)
144 1
        common.show()
145 1
        count = config.lock_deps(*names, obey_existing=False)
146 1
        common.dedent(level=0)
147
148 1
    return _display_result("lock", "Locked", count)
149
150
@restore_cwd
151 1
def unlock(*names, root=None):
152 1
    """Unlock current dependency versions for a project.
153
154
    Optional arguments:
155
156
    - `root`: specifies the path to the root working tree
157
158
    """
159
    log.info("Unlocking dependencies...")
160
    count = None
161 1
162 1
    root = _find_root(root)
163
    config = load_config(root)
164 1
165 1
    if config:
166
        common.show("Unlocking dependencies...", log=False)
167 1
        common.show()
168 1
        count = config.unlock_deps(*names, obey_existing=False)
169 1
        common.dedent(level=0)
170 1
171 1
    return _display_result("unlock", "Unlocked", count, allow_zero=True)
172 1
173 1
@restore_cwd
174 1
def delete(*, root=None, force=False):
175
    """Delete dependencies for a project.
176 1
177
    Optional arguments:
178
179 1
    - `root`: specifies the path to the root working tree
180
    - `force`: indicates uncommitted changes can be overwritten
181
182
    """
183
    log.info("Deleting dependencies...")
184
    count = None
185
186 1
    root = _find_root(root)
187
    config = load_config(root)
188 1
189 1
    if config:
190 1
        common.show("Checking for uncommitted changes...", log=False)
191 1
        common.show()
192 1
        count = len(list(config.get_deps(allow_dirty=force)))
193
        common.dedent(level=0)
194 1
        common.show("Deleting all dependencies...", log=False)
195 1
        common.show()
196
        config.uninstall_deps()
197 1
198
    return _display_result("delete", "Deleted", count, allow_zero=True)
199
200 1
201
def show(*names, root=None):
202
    """Display the path of an installed dependency or internal file.
203
204
    - `name`: dependency name or internal file keyword
205
    - `root`: specifies the path to the root working tree
206
207
    """
208 1
    log.info("Finding paths...")
209
210 1
    root = _find_root(root)
211 1
    config = load_config(root)
212 1
    if not config:
213 1
        log.error("No configuration found")
214 1
        return False
215
216 1
    for name in names or [None]:
217
        common.show(config.get_path(name))
218
219 1
    return True
220 1
221 1
222 1
def edit(*, root=None):
223
    """Open the confuration file for a project.
224 1
225 1
    Optional arguments:
226 1
227
    - `root`: specifies the path to the root working tree
228
229 1
    """
230 1
    log.info("Launching configuration...")
231 1
232 1
    root = _find_root(root)
233 1
    config = load_config(root)
234 1
    if not config:
235 1
        log.error("No configuration found")
236 1
        return False
237 1
238 1
    return system.launch(config.path)
239 1
240
241 1
def _find_root(base, cwd=None):
242 1
    if cwd is None:
243
        cwd = os.getcwd()
244 1
        log.info("Current directory: %s", cwd)
245 1
246
    if base:
247 1
        root = os.path.abspath(base)
248
        log.info("Specified root: %s", root)
249
250 1
    else:
251
        log.info("Searching for root...")
252
        path = cwd
253
        prev = None
254
        root = None
255
        while path != prev:
256
            log.debug("Checking path: %s", path)
257
            if '.git' in os.listdir(path):
258
                root = path
259
                break
260
            prev = path
261
            path = os.path.dirname(path)
262
263
        if root:
264
            log.info("Found root: %s", root)
265
        else:
266 1
            root = cwd
267 1
            log.warning("No root found, default: %s", root)
268 1
269 1
    return root
270
271 1
272
def _display_result(present, past, count, allow_zero=False):
273 1
    """Convert a command's dependency count to a return status.
274 1
275 1
    >>> _display_result("sample", "Sampled", 1)
276 1
    True
277
278 1
    >>> _display_result("sample", "Sampled", None)
279 1
    False
280
281
    >>> _display_result("sample", "Sampled", 0)
282
    False
283
284
    >>> _display_result("sample", "Sampled", 0, allow_zero=True)
285
    True
286
287
    """
288
    if count is None:
289
        log.warning("No dependencies to %s", present)
290
    elif count == 1:
291
        log.info("%s 1 dependency", past)
292
    else:
293
        log.info("%s %s dependencies", past, count)
294
295
    if count:
296
        return True
297
    elif count is None:
298
        return False
299
    else:
300
        assert count == 0
301
        return allow_zero
302