Completed
Pull Request — develop (#148)
by Jace
05:59
created

Source.run_scripts()   C

Complexity

Conditions 7

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7.0572

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
c 1
b 0
f 0
dl 0
loc 29
ccs 17
cts 19
cp 0.8947
crap 7.0572
rs 5.5
1
"""Wrappers for the dependency configuration files."""
2
3 1
import os
4 1
import logging
5 1
import warnings
6
7 1
import yorm
0 ignored issues
show
Configuration introduced by
The import yorm could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
8 1
from yorm.types import String, List, AttributeDictionary
0 ignored issues
show
Configuration introduced by
The import yorm.types could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
9
10 1
from .. import common, exceptions, shell, git
11 1
12 1
13 1
log = logging.getLogger(__name__)
0 ignored issues
show
Coding Style Naming introduced by
The name log does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
14
15
16 1
@yorm.attr(name=String)
17
@yorm.attr(repo=String)
18
@yorm.attr(rev=String)
19 1
@yorm.attr(link=String)
20 1
@yorm.attr(scripts=List.of_type(String))
21 1
class Source(AttributeDictionary):
22 1
    """A dictionary of `git` and `ln` arguments."""
23 1
24
    DIRTY = '<dirty>'
25
    UNKNOWN = '<unknown>'
26 1
27 1
    def __init__(self, repo, name, rev='master', link=None, scripts=None):
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
28
        super().__init__()
29 1
        self.repo = repo
30 1
        self.name = name
31 1
        self.rev = rev
32 1
        self.link = link
33 1
        self.scripts = scripts or []
34 1
        if not self.repo:
35 1
            msg = "'repo' missing on {}".format(repr(self))
36 1
            raise exceptions.InvalidConfig(msg)
37 1
        if not self.name:
38 1
            msg = "'name' missing on {}".format(repr(self))
39
            raise exceptions.InvalidConfig(msg)
40 1
41 1
    def __repr__(self):
42
        return "<source {}>".format(self)
43 1
44 1
    def __str__(self):
45 1
        pattern = "'{r}' @ '{v}' in '{d}'"
46 1
        if self.link:
47 1
            pattern += " <- '{s}'"
48
        return pattern.format(r=self.repo, v=self.rev, d=self.name, s=self.link)
49 1
50 1
    def __eq__(self, other):
51
        return self.name == other.name
52 1
53 1
    def __ne__(self, other):
54
        return self.name != other.name
55 1
56 1
    def __lt__(self, other):
57
        return self.name < other.name
58 1
59
    def update_files(self, force=False, fetch=False, clean=True):
60 1
        """Ensure the source matches the specified revision."""
61
        log.info("Updating source files...")
62
63 1
        # Clone the repository if needed
64 1
        if not os.path.exists(self.name):
65
            git.clone(self.repo, self.name)
66
67 1
        # Enter the working tree
68 1
        shell.cd(self.name)
69 1
        if not git.valid():
70
            raise self._invalid_repository
71
72 1
        # Check for uncommitted changes
73 1
        if not force:
74 1
            log.debug("Confirming there are no uncommitted changes...")
75
            if git.changes(include_untracked=clean):
76
                msg = "Uncommitted changes in {}".format(os.getcwd())
77
                raise exceptions.UncommittedChanges(msg)
78
79
        # Fetch the desired revision
80 1
        if fetch or self.rev not in (git.get_branch(),
81
                                     git.get_hash(),
82
                                     git.get_tag()):
83 1
            git.fetch(self.repo, self.rev)
84
85
        # Update the working tree to the desired revision
86 1
        git.update(self.rev, fetch=fetch, clean=clean)
87
88 1
    def create_link(self, root, force=False):
89
        """Create a link from the target name to the current directory."""
90 1
        if not self.link:
91 1
            return
92
93 1
        log.info("Creating a symbolic link...")
94
95 1
        if os.name == 'nt':
96
            warnings.warn("Symbolic links are not supported on Windows")
97
            return
98
99 1
        target = os.path.join(root, self.link)
100 1
        source = os.path.relpath(os.getcwd(), os.path.dirname(target))
101
102 1
        if os.path.islink(target):
103 1
            os.remove(target)
104 1
        elif os.path.exists(target):
105 1
            if force:
106 1
                shell.rm(target)
107
            else:
108 1
                msg = "Preexisting link location at {}".format(target)
109 1
                raise exceptions.UncommittedChanges(msg)
110 1
111
        shell.ln(source, target)
112 1
113
    def run_scripts(self, force=False):
0 ignored issues
show
Coding Style introduced by
This method 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...
114 1
        log.info("Running install scripts...")
115
116 1
        # Enter the working tree
117
        shell.cd(self.name)
118 1
        if not git.valid():
119 1
            raise self._invalid_repository
120 1
121
        # Check for scripts
122 1
        if not self.scripts:
123 1
            common.show("(no scripts to run)", color='shell_info')
124 1
            common.newline()
125 1
            return
126 1
127 1
        # Run all scripts
128 1
        for script in self.scripts:
129
            try:
130
                lines = shell.call(script, _shell=True)
131
            except exceptions.ShellError as exc:
132
                common.show(*exc.output, color='shell_error')
133 1
                cmd = exc.program
134 1
                if force:
135 1
                    log.debug("Ignored error from call to '%s'", cmd)
136
                else:
137 1
                    msg = "Command '{}' failed in {}".format(cmd, os.getcwd())
138
                    raise exceptions.ScriptFailure(msg)
139 1
            else:
140
                common.show(*lines, color='shell_output')
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...
141
        common.newline()
142
143 1
    def identify(self, allow_dirty=True, allow_missing=True):
144
        """Get the path and current repository URL and hash."""
145 1
        if os.path.isdir(self.name):
146
147 1
            shell.cd(self.name)
148 1
            if not git.valid():
149 1
                raise self._invalid_repository
150 1
151
            path = os.getcwd()
152 1
            url = git.get_url()
153
            if git.changes(display_status=not allow_dirty, _show=True):
154 1
                if not allow_dirty:
155 1
                    msg = "Uncommitted changes in {}".format(os.getcwd())
156 1
                    raise exceptions.UncommittedChanges(msg)
157
158
                common.show(self.DIRTY, color='git_dirty', log=False)
159
                common.newline()
160
                return path, url, self.DIRTY
161
            else:
162
                rev = git.get_hash(_show=True)
163
                common.show(rev, color='git_rev', log=False)
164
                common.newline()
165
                return path, url, rev
166
167
        elif allow_missing:
168
169
            return os.getcwd(), '<missing>', self.UNKNOWN
170
171
        else:
172
173
            raise self._invalid_repository
174
175
    def lock(self, rev=None):
176
        """Return a locked version of the current source."""
177
        if rev is None:
178
            _, _, rev = self.identify(allow_dirty=False, allow_missing=False)
179
        source = self.__class__(self.repo, self.name, rev,
180
                                self.link, self.scripts)
181
        return source
182
183
    @property
184
    def _invalid_repository(self):
0 ignored issues
show
Coding Style introduced by
This method 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...
185
        path = os.path.join(os.getcwd(), self.name)
186
        msg = "Not a valid repository: {}".format(path)
187
        return exceptions.InvalidRepository(msg)
188