Completed
Pull Request — develop (#112)
by Jace
02:15
created

gitman.display()   B

Complexity

Conditions 3

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.125
Metric Value
cc 3
dl 0
loc 28
ccs 8
cts 16
cp 0.5
crap 4.125
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 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
        common.show("Displaying current dependency versions...", log=False)
115
        common.show()
116
        config.log(datetime.datetime.now().strftime("%F %T"))
117
        count = 0
118
        for identity in config.get_deps(depth=depth, allow_dirty=allow_dirty):
119
            count += 1
120
            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
        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
151 1
@restore_cwd
152 1
def delete(*, root=None, force=False):
153
    """Delete dependencies for a project.
154
155
    Optional arguments:
156
157
    - `root`: specifies the path to the root working tree
158
    - `force`: indicates uncommitted changes can be overwritten
159
160
    """
161 1
    log.info("Deleting dependencies...")
162 1
    count = None
163
164 1
    root = _find_root(root)
165 1
    config = load_config(root)
166
167 1
    if config:
168 1
        common.show("Checking for uncommitted changes...", log=False)
169 1
        common.show()
170 1
        count = len(list(config.get_deps(allow_dirty=force)))
171 1
        common.dedent(level=0)
172 1
        common.show("Deleting all dependencies...", log=False)
173 1
        common.show()
174 1
        config.uninstall_deps()
175
176 1
    return _display_result("delete", "Deleted", count, allow_zero=True)
177
178
179 1
def show(*names, root=None):
180
    """Display the path of an installed dependency or internal file.
181
182
    - `name`: dependency name or internal file keyword
183
    - `root`: specifies the path to the root working tree
184
185
    """
186
    log.info("Finding paths...")
187
188
    root = _find_root(root)
189
    config = load_config(root)
190
191
    for name in names or [None]:
192
        common.show(config.get_path(name))
193
194
    return True
195
196
197 1
def edit(*, root=None):
198
    """Open the confuration file for a project.
199
200
    Optional arguments:
201
202
    - `root`: specifies the path to the root working tree
203
204
    """
205 1
    log.info("Launching configuration...")
206
207 1
    root = _find_root(root)
208 1
    config = load_config(root)
209
210 1
    if config:
211 1
        return system.launch(config.path)
212
    else:
213 1
        log.error("No configuration found")
214 1
        return False
215
216
217 1
def _find_root(root, cwd=None):
218 1
    if cwd is None:
219 1
        cwd = os.getcwd()
220 1
        log.info("Current directory: %s", cwd)
221
222 1
    if root:
223 1
        root = os.path.abspath(root)
224 1
        log.info("Specified root: %s", root)
225
    else:
226 1
        path = cwd
227 1
        prev = None
228
229 1
        log.info("Searching for root...")
230 1
        while path != prev:
231 1
            log.debug("Checking path: %s", path)
232 1
            if '.git' in os.listdir(path):
233 1
                root = path
234 1
                break
235 1
            prev = path
236 1
            path = os.path.dirname(path)
237
238 1
        if root:
239 1
            log.info("Found root: %s", root)
240
        else:
241 1
            root = cwd
242 1
            log.warning("No root found, default: %s", root)
243
244 1
    return root
245
246
247 1
def _display_result(present, past, count, allow_zero=False):
248
    """Convert a command's dependency count to a return status.
249
250
    >>> _display_result("sample", "Sampled", 1)
251
    True
252
253
    >>> _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 1
    if count is None:
264 1
        log.warning("No dependencies to %s", present)
265 1
    elif count == 1:
266 1
        log.info("%s 1 dependency", past)
267
    else:
268 1
        log.info("%s %s dependencies", past, count)
269
270 1
    if count:
271 1
        return True
272 1
    elif count is None:
273 1
        return False
274
    else:
275 1
        assert count == 0
276
        return allow_zero
277