Passed
Pull Request — master (#230)
by Vinicius
08:26 queued 04:31
created

setup.SimpleCommand.initialize_options()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
"""Setup script.
2
3
Run "python3 setup.py --help-commands" to list all available commands and their
4
descriptions.
5
"""
6
import os
7
import re
8
import sys
9
from abc import abstractmethod
10
# Disabling checks due to https://github.com/PyCQA/pylint/issues/73
11
from pathlib import Path
12
from subprocess import CalledProcessError, call, check_call
13
14
try:
15
    # Check if pip is installed
16
    # pylint: disable=unused-import
17
    import pip  # noqa
18
    from setuptools import Command, find_packages, setup
19
    from setuptools.command.egg_info import egg_info
20
except ModuleNotFoundError:
21
    print('Please install python3-pip and run setup.py again.')
22
    sys.exit(-1)
23
24
BASE_ENV = Path(os.environ.get('VIRTUAL_ENV', '/'))
25
ETC_FILES = []
26
27
28
class SimpleCommand(Command):
29
    """Make Command implementation simpler."""
30
31
    user_options = []
32
33
    def __init__(self, *args, **kwargs):
34
        """Store arguments so it's possible to call other commands later."""
35
        super().__init__(*args, **kwargs)
36
        self._args = args
37
        self._kwargs = kwargs
38
39
    @abstractmethod
40
    def run(self):
41
        """Run when command is invoked.
42
43
        Use *call* instead of *check_call* to ignore failures.
44
        """
45
46
    def initialize_options(self):
47
        """Set default values for options."""
48
49
    def finalize_options(self):
50
        """Post-process options."""
51
52
53
class EggInfo(egg_info):
54
    """Prepare files to be packed."""
55
56
    def run(self):
57
        """Build css."""
58
        self._install_deps_wheels()
59
        super().run()
60
61
    @staticmethod
62
    def _install_deps_wheels():
63
        """Python wheels are much faster (no compiling)."""
64
        print('Installing dependencies...')
65
        check_call([sys.executable, '-m', 'pip', 'install', '-r',
66
                    'requirements/run.txt'])
67
68
69
# pylint: disable=attribute-defined-outside-init, abstract-method
70
class TestCommand(Command):
71
    """Test tags decorators."""
72
73
    user_options = [
74
        ("k=", None, "Specify a pytest -k expression."),
75
    ]
76
77
    def get_args(self):
78
        """Return args to be used in test command."""
79
        if self.k:
80
            return f"-k '{self.k}'"
81
        return ""
82
83
    def initialize_options(self):
84
        """Set default size and type args."""
85
        self.k = ""
86
87
    def finalize_options(self):
88
        """Post-process."""
89
        pass
90
91
92
class Cleaner(SimpleCommand):
93
    """Custom clean command to tidy up the project root."""
94
95
    description = 'clean build, dist, pyc and egg from package and docs'
96
97
    def run(self):
98
        """Clean build, dist, pyc and egg from package and docs."""
99
        call('make clean', shell=True)
100
101
102
class Test(TestCommand):
103
    """Run all tests."""
104
105
    description = "run tests and display results"
106
107
    def run(self):
108
        """Run tests."""
109
        cmd = f"python3 -m pytest tests/ {self.get_args()}"
110
        try:
111
            check_call(cmd, shell=True)
112
        except CalledProcessError as exc:
113
            print(exc)
114
            print('Unit tests failed. Fix the errors above and try again.')
115
            sys.exit(-1)
116
117
118
class TestCoverage(Test):
119
    """Display test coverage."""
120
121
    description = "run tests and display code coverage"
122
123
    def run(self):
124
        """Run tests quietly and display coverage report."""
125
        cmd = f"python3 -m pytest --cov=. tests/ {self.get_args()}"
126
        try:
127
            check_call(cmd, shell=True)
128
        except CalledProcessError as exc:
129
            print(exc)
130
            print('Coverage tests failed. Fix the errors above and try again.')
131
            sys.exit(-1)
132
133
134
class DocTest(SimpleCommand):
135
    """Run documentation tests."""
136
137
    description = 'run documentation tests'
138
139
    def run(self):
140
        """Run doctests using Sphinx Makefile."""
141
        cmd = 'make -C docs/ default doctest'
142
        check_call(cmd, shell=True)
143
144
145
class Linter(SimpleCommand):
146
    """Code linters."""
147
148
    description = 'Lint Python source code'
149
150
    def run(self):
151
        """Run yala."""
152
        print('Yala is running. It may take several seconds...')
153
        try:
154
            check_call('yala setup.py kytos tests', shell=True)
155
            print('No linter error found.')
156
        except CalledProcessError:
157
            print('Linter check failed. Fix the error(s) above and try again.')
158
            sys.exit(-1)
159
160
161
# class InstallMode(install):
162
#     """Class used to overwrite the default installation using setuptools."""
163
164
#     def run(self):
165
#         """Install the package in install mode.
166
167
#         super().run() does not install dependencies when running
168
#         ``python setup.py install`` (pypa/setuptools#456).
169
#         """
170
#         if 'bdist_wheel' in sys.argv:
171
#             # do not use eggs, but wheels
172
#             super().run()
173
#         else:
174
#             # force install of deps' eggs during setup.py install
175
#             self.do_egg_install()
176
177
178
# class DevelopMode(develop):
179
#    """Recommended setup for developers.
180
#
181
#    The following feature are temporarily remove from code:
182
#    Instead of copying the files to the expected directories, a symlink is
183
#    created on the system aiming the current source code.
184
#    """
185
#
186
#    def run(self):
187
#        """Install the package in a developer mode."""
188
#        super().run()
189
190
191
# We are parsing the metadata file as if it was a text file because if we
192
# import it as a python module, necessarily the kytos.core module would be
193
# initialized, which means that kyots/core/__init__.py would be run and, then,
194
# kytos.core.controller.Controller would be called and it will try to import
195
# some modules that are dependencies from this project and that were not yet
196
# installed, since the requirements installation from this project hasn't yet
197
# happened.
198
META_FILE = open("kytos/core/metadata.py").read()
199
METADATA = dict(re.findall(r"(__[a-z]+__)\s*=\s*'([^']+)'", META_FILE))
200
201
setup(name='kytos',
202
      version=METADATA.get('__version__'),
203
      description=METADATA.get('__description__'),
204
      long_description=open("README.pypi.rst", "r").read(),
205
      long_description_content_type='text/x-rst',
206
      url=METADATA.get('__url__'),
207
      author=METADATA.get('__author__'),
208
      author_email=METADATA.get('__author_email__'),
209
      license=METADATA.get('__license__'),
210
      test_suite='tests',
211
      scripts=['bin/kytosd'],
212
      include_package_data=True,
213
      data_files=[(os.path.join(BASE_ENV, 'etc/kytos'), ETC_FILES)],
214
      packages=find_packages(exclude=['tests']),
215
      install_requires=[line.strip()
216
                        for line in open("requirements/run.txt").readlines()
217
                        if not line.startswith('#')],
218
      extras_require={'dev': ['pip-tools >= 2.0', 'pytest==7.0.0',
219
                              'pytest-cov==3.0.0', 'pytest', 'yala', 'tox']},
220
      cmdclass={
221
          'clean': Cleaner,
222
          'coverage': TestCoverage,
223
          'doctest': DocTest,
224
          'egg_info': EggInfo,
225
          'lint': Linter,
226
          'test': Test
227
      },
228
      zip_safe=False,
229
      classifiers=[
230
          'License :: OSI Approved :: MIT License',
231
          'Operating System :: POSIX :: Linux',
232
          'Programming Language :: Python :: 3.6',
233
          'Programming Language :: Python :: 3.7',
234
          'Programming Language :: Python :: 3.8',
235
          'Programming Language :: Python :: 3.9',
236
          'Topic :: System :: Networking',
237
          'Development Status :: 4 - Beta',
238
          'Environment :: Console',
239
          'Environment :: No Input/Output (Daemon)',
240
      ])
241