Completed
Pull Request — develop (#112)
by Jace
06:16
created

gitman.display()   B

Complexity

Conditions 3

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.1406
Metric Value
cc 3
dl 0
loc 28
ccs 9
cts 12
cp 0.75
crap 3.1406
rs 8.8571
1
"""Functions to manage the installation of dependencies."""
2
3 1
import os
4 1
import functools
5 1
import datetime
6
import logging
7 1
8 1
from . import common, system
9
from .models import load_config
10 1
11
log = logging.getLogger(__name__)
12
13 1
14 1
def restore_cwd(func):
15
    @functools.wraps(func)
16 1
    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
    return wrapped
22
23 1
24 1
@restore_cwd
25
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 1
    """
39
    log.info("%sInstalling dependencies: %s",
40
             'force-' if force else '',
41 1
             ', '.join(names) if names else '<all>')
42
    count = None
43 1
44 1
    root = _find_root(root)
45
    config = load_config(root)
46 1
47 1
    if config:
48 1
        common.show("Installing dependencies...", log=False)
49 1
        common.show()
50
        count = config.install_deps(*names, update=False, depth=depth,
51
                                    force=force, fetch=fetch, clean=clean)
52 1
53
    return _display_result("install", "Installed", count)
54
55 1
56 1
@restore_cwd
57
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 1
    """
72
    log.info("%s dependencies%s: %s",
73
             'Force updating' if force else 'Updating',
74
             ', recursively' if recurse else '',
75 1
             ', '.join(names) if names else '<all>')
76
    count = None
77 1
78 1
    root = _find_root(root)
79
    config = load_config(root)
80 1
81 1
    if config:
82 1
        common.show("Updating dependencies...", log=False)
83 1
        common.show()
84
        count = config.install_deps(
85
            *names, update=True, depth=depth,
86 1
            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
            config.lock_deps(*names, obey_existing=lock is None)
92 1
93
    return _display_result("update", "Updated", count)
94
95 1
96 1
@restore_cwd
97
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 1
    """
107 1
    log.info("Displaying dependencies...")
108
    count = None
109 1
110 1
    root = _find_root(root)
111
    config = load_config(root)
112 1
113
    if config:
114
        common.show("Displaying current dependency versions...", log=False)
115
        common.show()
116
        config.log(datetime.datetime.now().strftime("%F %T"))
117 1
        count = 0
118
        for identity in config.get_deps(depth=depth, allow_dirty=allow_dirty):
119
            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
    return _display_result("display", "Displayed", count)
124
125
126
@restore_cwd
127
def lock(*names, root=None):
128
    """Lock current dependency versions for a project.
129
130 1
    Optional arguments:
131 1
132
    - `*names`: optional list of dependency directory names to filter on
133 1
    - `root`: specifies the path to the root working tree
134 1
135
    """
136 1
    log.info("Locking dependencies...")
137 1
    count = None
138 1
139 1
    root = _find_root(root)
140 1
    config = load_config(root)
141
142 1
    if config:
143
        common.show("Locking dependencies...", log=False)
144
        common.show()
145 1
        count = config.lock_deps(*names, obey_existing=False)
146 1
        common.dedent(level=0)
147
148
    return _display_result("lock", "Locked", count)
149
150
151
@restore_cwd
152
def delete(*, root=None, force=False):
153
    """Delete dependencies for a project.
154
155 1
    Optional arguments:
156 1
157
    - `root`: specifies the path to the root working tree
158 1
    - `force`: indicates uncommitted changes can be overwritten
159 1
160
    """
161 1
    log.info("Deleting dependencies...")
162 1
    count = None
163 1
164 1
    root = _find_root(root)
165 1
    config = load_config(root)
166 1
167 1
    if config:
168 1
        common.show("Checking for uncommitted changes...", log=False)
169
        common.show()
170 1
        count = len(list(config.get_deps(allow_dirty=force)))
171
        common.dedent(level=0)
172
        common.show("Deleting all dependencies...", log=False)
173 1
        common.show()
174 1
        config.uninstall_deps()
175
176
    return _display_result("delete", "Deleted", count, allow_zero=True)
177
178
179
def show(*names, root=None):
180
    """Display the path of an installed dependency or internal file.
181
182 1
    - `name`: dependency name or internal file keyword
183
    - `root`: specifies the path to the root working tree
184 1
185 1
    """
186
    log.info("Finding paths...")
187 1
188 1
    root = _find_root(root)
189
    config = load_config(root)
190 1
191 1
    for name in names or [None]:
192
        common.show(config.get_path(name))
193
194 1
    return True
195 1
196 1
197 1
def edit(*, root=None):
198
    """Open the confuration file for a project.
199 1
200 1
    Optional arguments:
201 1
202
    - `root`: specifies the path to the root working tree
203 1
204 1
    """
205
    log.info("Launching configuration...")
206 1
207 1
    root = _find_root(root)
208 1
    config = load_config(root)
209 1
210 1
    if config:
211 1
        return system.launch(config.path)
212 1
    else:
213 1
        log.error("No configuration found")
214
        return False
215 1
216 1
217
def _find_root(root, cwd=None):
218 1
    if cwd is None:
219 1
        cwd = os.getcwd()
220
        log.info("Current directory: %s", cwd)
221 1
222
    if root:
223
        root = os.path.abspath(root)
224 1
        log.info("Specified root: %s", root)
225
    else:
226
        path = cwd
227
        prev = None
228
229
        log.info("Searching for root...")
230
        while path != prev:
231
            log.debug("Checking path: %s", path)
232
            if '.git' in os.listdir(path):
233
                root = path
234
                break
235
            prev = path
236
            path = os.path.dirname(path)
237
238
        if root:
239
            log.info("Found root: %s", root)
240 1
        else:
241 1
            root = cwd
242 1
            log.warning("No root found, default: %s", root)
243 1
244
    return root
245 1
246
247 1
def _display_result(present, past, count, allow_zero=False):
248 1
    """Convert a command's dependency count to a return status.
249 1
250 1
    >>> _display_result("sample", "Sampled", 1)
251
    True
252 1
253 1
    >>> _display_result("sample", "Sampled", None)
254
    False
255
256
    >>> _display_result("sample", "Sampled", 0)
257
    False
258
259
    >>> _display_result("sample", "Sampled", 0, allow_zero=True)
260
    True
261
262
    """
263
    if count is None:
264
        log.warning("No dependencies to %s", present)
265
    elif count == 1:
266
        log.info("%s 1 dependency", past)
267
    else:
268
        log.info("%s %s dependencies", past, count)
269
270
    if count:
271
        return True
272
    elif count is None:
273
        return False
274
    else:
275
        assert count == 0
276
        return allow_zero
277