forked from wikimedia/pywikibot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmake_dist.py
executable file
·236 lines (186 loc) · 7.15 KB
/
make_dist.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#!/usr/bin/env python3
"""Script to create a new distribution. Requires Python 3.7+.
The following options are supported:
-help Print documentation of this file and of setup.py
-local Install the distribution as a local site-package. If a
Pywikibot package is already there, it will be uninstalled
first. Clears old dist folders first.
-remote Upload the package to pypi. This cannot be done if the
Pywikibot version is a development release. Clears old dist
folders first.
-clear Clear old dist folders and leave. Does not create a
distribution.
-upgrade Upgrade pip first; upgrade or install distribution packages
build and twine first.
Usage::
[pwb] make_dist [options]
.. versionadded:: 7.3
.. versionchanged:: 7.4
- updates pip, setuptools, wheel and twine packages first
- installs pre-releases over stable versions
- also creates built distribution together with source distribution
- *-upgrade* option was added
.. versionchanged:: 7.5
- *clear* option was added
- *nodist* option was added
.. versionchanged:: 8.1
Python 3.7+ required because *dataclasses* module is used.
*nodist* option was removed, *clear* option does not create a
distribution. *local* and *remote* option clears old distributions
first.
.. versionchanged:: 8.2
Build frontend was changed from setuptools to build. ``-upgrade``
option also installs packages if necessary.
"""
#
# (C) Pywikibot team, 2022-2023
#
# Distributed under the terms of the MIT license.
#
import abc
import shutil
import sys
from dataclasses import dataclass, field
from importlib import import_module
from pathlib import Path
from subprocess import check_call, run
from pywikibot import __version__, error, info, input_yn, warning
from pywikibot.backports import Tuple
@dataclass
class SetupBase(abc.ABC):
"""Setup distribution base class.
.. versionadded:: 8.0
.. versionchanged:: 8.1
*dataclass* is used.
"""
local: bool
remote: bool
clear: bool
upgrade: bool
folder: Path = field(init=False)
def __post_init__(self) -> None:
"""Post-init initializer."""
self.folder = Path().resolve()
def clear_old_dist(self) -> None: # pragma: no cover
"""Delete old dist folders.
.. versionadded:: 7.5
"""
info('<<lightyellow>>Removing old dist folders... ', newline=False)
shutil.rmtree(self.folder / 'build', ignore_errors=True)
shutil.rmtree(self.folder / 'dist', ignore_errors=True)
shutil.rmtree(self.folder / 'pywikibot.egg-info', ignore_errors=True)
info('<<lightyellow>>done')
@abc.abstractmethod
def copy_files(self) -> None:
"""Copy files."""
@abc.abstractmethod
def cleanup(self) -> None:
"""Cleanup copied files."""
def run(self) -> bool:
"""Run the installer script.
:return: True if no error occurs, else False
"""
if self.local or self.remote or self.clear:
self.clear_old_dist()
if self.clear:
return True # pragma: no cover
if self.upgrade: # pragma: no cover
check_call('python -m pip install --upgrade pip', shell=True)
for module in ('build', 'twine'):
info(f'<<lightyellow>>Install or upgrade {module}')
try:
import_module(module)
except ModuleNotFoundError:
check_call(f'pip install {module}', shell=True)
else:
check_call(f'pip install --upgrade {module}', shell=True)
else:
for module in ('build', 'twine'):
try:
import_module(module)
except ModuleNotFoundError as e: # pragma: no cover
error(f'<<lightred>>{e}')
info('<<lightblue>>You may use -upgrade option to install')
return False
self.copy_files()
info('<<lightyellow>>Build package')
try:
check_call('python -m build')
except Exception as e: # pragma: no cover
error(e)
return False
finally:
self.cleanup()
info('<<lightyellow>>Check package and description')
if run('twine check dist/*', shell=True).returncode:
return False # pragma: no cover
if self.local:
info('<<lightyellow>>Install locally')
check_call('pip uninstall pywikibot -y', shell=True)
check_call(
'pip install --no-index --pre --find-links=dist pywikibot',
shell=True)
if self.remote and input_yn(
'<<lightblue>>Upload dist to pypi', automatic_quit=False):
check_call('twine upload dist/*', shell=True) # pragma: no cover
return True
class SetupPywikibot(SetupBase):
"""Setup for Pywikibot distribution.
.. versionadded:: 8.0
"""
def __init__(self, *args) -> None:
"""Set source and target directories."""
super().__init__(*args)
source = self.folder / 'scripts' / 'i18n' / 'pywikibot'
target = self.folder / 'pywikibot' / 'scripts' / 'i18n' / 'pywikibot'
self.target = target
self.source = source
def copy_files(self) -> None:
"""Copy i18n files to pywikibot.scripts folder.
Pywikibot i18n files are used for some translations. They are copied
to the pywikibot scripts folder.
"""
info('<<lightyellow>>Copy files')
info(f'directory is {self.folder}')
info(f'clear {self.target} directory')
shutil.rmtree(self.target, ignore_errors=True)
info('copy i18n files ... ', newline=False)
shutil.copytree(self.source, self.target)
info('done')
def cleanup(self) -> None:
"""Remove all copied files from pywikibot scripts folder."""
info('<<lightyellow>>Remove copied files... ', newline=False)
shutil.rmtree(self.target)
# restore pywikibot en.json file
filename = 'en.json'
self.target.mkdir()
shutil.copy(self.source / filename, self.target / filename)
info('<<lightyellow>>done')
def handle_args() -> Tuple[bool, bool, bool, bool]:
"""Handle arguments and print documentation if requested.
Read arguments from `sys.argv` and adjust it passing `sdist` to
`setuptools.setup`.
:return: Return whether dist is to be installed locally or to be
uploaded
"""
if '-help' in sys.argv:
import setup
info(__doc__)
info(setup.__doc__)
sys.exit()
local = '-local' in sys.argv
remote = '-remote' in sys.argv
clear = '-clear' in sys.argv
upgrade = '-upgrade' in sys.argv
if remote and 'dev' in __version__: # pragma: no cover
warning('Distribution must not be a developmental release to upload.')
remote = False
sys.argv = [sys.argv[0]]
return local, remote, clear, upgrade
def main() -> None:
"""Script entry point."""
args = handle_args()
return SetupPywikibot(*args).run()
if __name__ == '__main__':
if not main():
sys.exit(1) # pragma: no cover