1
|
|
|
"""Setup script. |
2
|
|
|
|
3
|
|
|
Run "python3 setup --help-commands" to list all available commands and their |
4
|
|
|
descriptions. |
5
|
|
|
""" |
6
|
|
|
import re |
7
|
|
|
import sys |
8
|
|
|
from abc import abstractmethod |
9
|
|
|
# Disabling checks due to https://github.com/PyCQA/pylint/issues/73 |
10
|
|
|
from subprocess import CalledProcessError, call, check_call |
11
|
|
|
|
12
|
|
|
from setuptools import Command, find_packages, setup |
13
|
|
|
|
14
|
|
|
|
15
|
|
|
class SimpleCommand(Command): |
16
|
|
|
"""Make Command implementation simpler.""" |
17
|
|
|
|
18
|
|
|
user_options = [] |
19
|
|
|
|
20
|
|
|
def __init__(self, *args, **kwargs): |
21
|
|
|
"""Store arguments so it's possible to call other commands later.""" |
22
|
|
|
super().__init__(*args, **kwargs) |
23
|
|
|
self._args = args |
24
|
|
|
self._kwargs = kwargs |
25
|
|
|
|
26
|
|
|
@abstractmethod |
27
|
|
|
def run(self): |
28
|
|
|
"""Run when command is invoked. |
29
|
|
|
|
30
|
|
|
Use *call* instead of *check_call* to ignore failures. |
31
|
|
|
""" |
32
|
|
|
|
33
|
|
|
def initialize_options(self): |
34
|
|
|
"""Set default values for options.""" |
35
|
|
|
|
36
|
|
|
def finalize_options(self): |
37
|
|
|
"""Post-process options.""" |
38
|
|
|
|
39
|
|
|
|
40
|
|
|
# pylint: disable=attribute-defined-outside-init, abstract-method |
41
|
|
|
class TestCommand(Command): |
42
|
|
|
"""Test tags decorators.""" |
43
|
|
|
|
44
|
|
|
user_options = [ |
45
|
|
|
("k=", None, "Specify a pytest -k expression."), |
46
|
|
|
] |
47
|
|
|
|
48
|
|
|
def get_args(self): |
49
|
|
|
"""Return args to be used in test command.""" |
50
|
|
|
if self.k: |
51
|
|
|
return f"-k '{self.k}'" |
52
|
|
|
return "" |
53
|
|
|
|
54
|
|
|
def initialize_options(self): |
55
|
|
|
"""Set default size and type args.""" |
56
|
|
|
self.k = "" |
57
|
|
|
|
58
|
|
|
def finalize_options(self): |
59
|
|
|
"""Post-process.""" |
60
|
|
|
pass |
61
|
|
|
|
62
|
|
|
|
63
|
|
|
class Cleaner(SimpleCommand): |
64
|
|
|
"""Custom clean command to tidy up the project root.""" |
65
|
|
|
|
66
|
|
|
description = 'clean build, dist, pyc and egg from package and docs' |
67
|
|
|
|
68
|
|
|
def run(self): |
69
|
|
|
"""Clean build, dist, pyc and egg from package and docs.""" |
70
|
|
|
call('make clean', shell=True) |
71
|
|
|
|
72
|
|
|
|
73
|
|
|
class Test(TestCommand): |
74
|
|
|
"""Run all tests.""" |
75
|
|
|
|
76
|
|
|
description = "run tests and display results" |
77
|
|
|
|
78
|
|
|
def run(self): |
79
|
|
|
"""Run tests.""" |
80
|
|
|
cmd = f"python3 -m pytest tests/ {self.get_args()}" |
81
|
|
|
try: |
82
|
|
|
check_call(cmd, shell=True) |
83
|
|
|
except CalledProcessError as exc: |
84
|
|
|
print(exc) |
85
|
|
|
print('Unit tests failed. Fix the errors above and try again.') |
86
|
|
|
sys.exit(-1) |
87
|
|
|
|
88
|
|
|
|
89
|
|
|
class TestCoverage(Test): |
90
|
|
|
"""Display test coverage.""" |
91
|
|
|
|
92
|
|
|
description = "run tests and display code coverage" |
93
|
|
|
|
94
|
|
|
def run(self): |
95
|
|
|
"""Run tests quietly and display coverage report.""" |
96
|
|
|
cmd = "python3 -m pytest --cov=. tests/ --cov-report term-missing" |
97
|
|
|
cmd += f" {self.get_args()}" |
98
|
|
|
try: |
99
|
|
|
check_call(cmd, shell=True) |
100
|
|
|
except CalledProcessError as exc: |
101
|
|
|
print(exc) |
102
|
|
|
print('Coverage tests failed. Fix the errors above and try again.') |
103
|
|
|
sys.exit(-1) |
104
|
|
|
|
105
|
|
|
|
106
|
|
|
class Linter(SimpleCommand): |
107
|
|
|
"""Code linters.""" |
108
|
|
|
|
109
|
|
|
description = 'lint Python source code' |
110
|
|
|
|
111
|
|
|
def run(self): |
112
|
|
|
"""Run yala.""" |
113
|
|
|
print('Yala is running. It may take several seconds...') |
114
|
|
|
try: |
115
|
|
|
check_call('yala setup.py tests kytos', shell=True) |
116
|
|
|
print('No linter error found.') |
117
|
|
|
except CalledProcessError: |
118
|
|
|
print('Linter check failed. Fix the error(s) above and try again.') |
119
|
|
|
sys.exit(-1) |
120
|
|
|
|
121
|
|
|
|
122
|
|
|
# We are parsing the metadata file as if it was a text file because if we |
123
|
|
|
# import it as a python module, necessarily the kytos.utils module would be |
124
|
|
|
# initialized. |
125
|
|
|
META_FILE = open("kytos/utils/metadata.py").read() |
126
|
|
|
METADATA = dict(re.findall(r"(__[a-z]+__)\s*=\s*'([^']+)'", META_FILE)) |
127
|
|
|
|
128
|
|
|
setup(name='kytos-utils', |
129
|
|
|
version=METADATA.get('__version__'), |
130
|
|
|
description=METADATA.get('__description__'), |
131
|
|
|
long_description=open("README.rst", "r").read(), |
132
|
|
|
url=METADATA.get('__url__'), |
133
|
|
|
author=METADATA.get('__author__'), |
134
|
|
|
author_email=METADATA.get('__author_email__'), |
135
|
|
|
license=METADATA.get('__license__'), |
136
|
|
|
test_suite='tests', |
137
|
|
|
include_package_data=True, |
138
|
|
|
scripts=['bin/kytos'], |
139
|
|
|
install_requires=[line.strip() |
140
|
|
|
for line in open("requirements/run.txt").readlines() |
141
|
|
|
if not line.startswith('#')], |
142
|
|
|
extras_require={'dev': [ |
143
|
|
|
'pip-tools >= 2.0', |
144
|
|
|
'pytest==8.0.1', |
145
|
|
|
'pytest-cov==4.1.0', |
146
|
|
|
'pytest-asyncio==0.23.5', |
147
|
|
|
'black==24.2.0', |
148
|
|
|
'isort==5.13.2', |
149
|
|
|
'pylint==3.1.0', |
150
|
|
|
'pycodestyle==2.11.1', |
151
|
|
|
'yala==3.2.0', |
152
|
|
|
'tox==4.13.0', |
153
|
|
|
'virtualenv==20.25.1', |
154
|
|
|
]}, |
155
|
|
|
packages=find_packages(exclude=['tests']), |
156
|
|
|
cmdclass={ |
157
|
|
|
'clean': Cleaner, |
158
|
|
|
'coverage': TestCoverage, |
159
|
|
|
'lint': Linter, |
160
|
|
|
'test': Test |
161
|
|
|
}, |
162
|
|
|
zip_safe=False) |
163
|
|
|
|