Completed
Pull Request — develop (#95)
by Jace
01:54
created

gdm.update()   C

Complexity

Conditions 7

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7
Metric Value
cc 7
dl 0
loc 38
rs 5.5
ccs 16
cts 16
cp 1
crap 7
1
"""Functions to manage the installation of dependencies."""
2
3 1
import os
4 1
import functools
5 1
import logging
6
7 1
from . import common
8 1
from .config import load
9
10 1
log = logging.getLogger(__name__)
11
12
13 1
def restore_cwd(func):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
14 1
    @functools.wraps(func)
15
    def wrapped(*args, **kwargs):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
16 1
        cwd = os.getcwd()
17 1
        result = func(*args, **kwargs)
18 1
        os.chdir(cwd)
19 1
        return result
20 1
    return wrapped
21
22
23 1
@restore_cwd
24 1
def install(*names, root=None, depth=None,
25
            force=False, fetch=False, clean=True):
26
    """Install dependencies for a project.
27
28
    Optional arguments:
29
30
    - `*names`: optional list of dependency directory names to filter on
31
    - `root`: specifies the path to the root working tree
32
    - `depth`: number of levels of dependencies to traverse
33
    - `force`: indicates uncommitted changes can be overwritten
34
    - `fetch`: indicates the latest branches should always be fetched
35
    - `clean`: indicates untracked files should be deleted from dependencies
36
37
    """
38 1
    log.info("%sInstalling dependencies: %s",
39
             'force-' if force else '',
40
             ', '.join(names) if names else '<all>')
41 1
    count = None
42
43 1
    root = _find_root(root)
44 1
    config = load(root)
45
46 1
    if config:
47 1
        common.show("Installing dependencies...", log=False)
48 1
        common.show()
49 1
        count = config.install_deps(*names, update=False, depth=depth,
50
                                    force=force, fetch=fetch, clean=clean)
51
52 1
    return _display_result("install", "Installed", count)
53
54
55 1
@restore_cwd
56 1
def update(*names, root=None, depth=None,
57
           recurse=False, force=False, clean=True, lock=None):  # pylint: disable=redefined-outer-name
58
    """Update dependencies for a project.
59
60
    Optional arguments:
61
62
    - `*names`: optional list of dependency directory names to filter on
63
    - `root`: specifies the path to the root working tree
64
    - `depth`: number of levels of dependencies to traverse
65
    - `recurse`: indicates nested dependencies should also be updated
66
    - `force`: indicates uncommitted changes can be overwritten
67
    - `clean`: indicates untracked files should be deleted from dependencies
68
    - `lock`: indicates actual dependency versions should be recorded
69
70
    """
71 1
    log.info("%s dependencies%s: %s",
72
             'Force updating' if force else 'Updating',
73
             ', recursively' if recurse else '',
74
             ', '.join(names) if names else '<all>')
75 1
    count = None
76
77 1
    root = _find_root(root)
78 1
    config = load(root)
79
80 1
    if config:
81 1
        common.show("Updating dependencies...", log=False)
82 1
        common.show()
83 1
        count = config.install_deps(
84
            *names, update=True, depth=depth,
85
            recurse=recurse, force=force, fetch=True, clean=clean)
86 1
        common.dedent(level=0)
87 1
        if count and lock is not False:
88 1
            common.show("Recording installed versions...", log=False)
89 1
            common.show()
90 1
            config.lock_deps(*names, obey_existing=lock is None)
91
92 1
    return _display_result("update", "Updated", count)
93
94
95 1
@restore_cwd
96 1
def display(root=None, depth=None, allow_dirty=True):
97
    """Display installed dependencies for a project.
98
99
    Optional arguments:
100
101
    - `root`: specifies the path to the root working tree
102
    - `depth`: number of levels of dependencies to traverse
103
    - `allow_dirty`: causes uncommitted changes to be ignored
104
105
    """
106 1
    log.info("Displaying dependencies...")
107 1
    count = None
108
109 1
    root = _find_root(root)
110 1
    config = load(root)
111
112 1
    if config:
113
        common.show("Displaying current dependency versions...", log=False)
114
        common.show()
115
        count = len(list(config.get_deps(depth=depth, allow_dirty=allow_dirty)))
116
117 1
    return _display_result("display", "Displayed", count)
118
119
120 1
@restore_cwd
121 1
def lock(*names, root=None):
122
    """Lock current dependency versions for a project.
123
124
    Optional arguments:
125
126
    - `*names`: optional list of dependency directory names to filter on
127
    - `root`: specifies the path to the root working tree
128
129
    """
130 1
    log.info("Locking dependencies...")
131 1
    count = None
132
133 1
    root = _find_root(root)
134 1
    config = load(root)
135
136 1
    if config:
137 1
        common.show("Locking dependencies...", log=False)
138 1
        common.show()
139 1
        count = config.lock_deps(*names, obey_existing=False)
140 1
        common.dedent(level=0)
141
142 1
    return _display_result("lock", "Locked", count)
143
144
145 1
@restore_cwd
146 1
def delete(root=None, force=False):
147
    """Delete dependencies for a project.
148
149
    Optional arguments:
150
151
    - `root`: specifies the path to the root working tree
152
    - `force`: indicates uncommitted changes can be overwritten
153
154
    """
155 1
    log.info("Deleting dependencies...")
156 1
    count = None
157
158 1
    root = _find_root(root)
159 1
    config = load(root)
160
161 1
    if config:
162 1
        common.show("Checking for uncommitted changes...", log=False)
163 1
        common.show()
164 1
        count = len(list(config.get_deps(allow_dirty=force)))
165 1
        common.dedent(level=0)
166 1
        common.show("Deleting all dependencies...", log=False)
167 1
        common.show()
168 1
        config.uninstall_deps()
169
170 1
    return _display_result("delete", "Deleted", count, allow_zero=True)
171
172
173 1
def _find_root(root, cwd=None):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
174 1
    if cwd is None:
175 1
        cwd = os.getcwd()
176 1
        log.info("Current directory: %s", cwd)
177
178 1
    if root:
179 1
        root = os.path.abspath(root)
180 1
        log.info("Specified root: %s", root)
181
    else:
182 1
        path = cwd
183 1
        prev = None
184
185 1
        log.info("Searching for root...")
186 1
        while path != prev:
187 1
            log.debug("Checking path: %s", path)
188 1
            if '.git' in os.listdir(path):
189 1
                root = path
190 1
                break
191 1
            prev = path
192 1
            path = os.path.dirname(path)
193
194 1
        if root:
195 1
            log.info("Found root: %s", root)
196
        else:
197 1
            root = cwd
198 1
            log.warning("No root found, default: %s", root)
199
200 1
    return root
201
202
203 1
def _display_result(present, past, count, allow_zero=False):
204
    """Convert a command's dependency count to a return status.
205
206
    >>> _display_result("sample", "Sampled", 1)
207
    True
208
209
    >>> _display_result("sample", "Sampled", None)
210
    False
211
212
    >>> _display_result("sample", "Sampled", 0)
213
    False
214
215
    >>> _display_result("sample", "Sampled", 0, allow_zero=True)
216
    True
217
218
    """
219 1
    if count is None:
220 1
        log.warning("No dependencies to %s", present)
221 1
    elif count == 1:
222 1
        log.info("%s 1 dependency", past)
223
    else:
224 1
        log.info("%s %s dependencies", past, count)
225
226 1
    if count:
227 1
        return True
228 1
    elif count is None:
229 1
        return False
230
    else:
231 1
        assert count == 0
232
        return allow_zero
233