-
Notifications
You must be signed in to change notification settings - Fork 7
/
setup.py
371 lines (323 loc) · 15.2 KB
/
setup.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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# -*- coding: utf-8 -*-
# pygolang | pythonic package setup
# Copyright (C) 2018-2024 Nexedi SA and Contributors.
# Kirill Smelkov <[email protected]>
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
from setuptools import find_packages
from setuptools.command.install_scripts import install_scripts as _install_scripts
from setuptools.command.develop import develop as _develop
from distutils import sysconfig
from os.path import dirname, join
import sys, os, re
# read file content
def readfile(path): # -> str
with open(path, 'rb') as f:
data = f.read()
if not isinstance(data, str): # py3
data = data.decode('utf-8')
return data
# reuse golang.pyx.build to build pygolang dso and extensions.
# we have to be careful and inject synthetic golang package in order to be
# able to import golang.pyx.build without built/working golang.
trun = {}
exec(readfile('trun'), trun)
trun['ximport_empty_golangmod']()
from golang.pyx.build import setup, DSO, Extension as Ext
# grep searches text for pattern.
# return re.Match object or raises if pattern was not found.
def grep1(pattern, text):
rex = re.compile(pattern, re.MULTILINE)
m = rex.search(text)
if m is None:
raise RuntimeError('%r not found' % pattern)
return m
# find our version
_ = readfile(join(dirname(__file__), 'golang/__init__.py'))
_ = grep1('^__version__ = "(.*)"$', _)
version = _.group(1)
# XInstallGPython customly installs bin/gpython.
#
# console_scripts generated by setuptools do lots of imports. However we need
# gevent.monkey.patch_all() to be done first - before all other imports. We
# could use plain scripts for gpython, however even for plain scripts
# setuptools wants to inject pkg_resources import for develop install, and
# pkg_resources does import lots of modules.
#
# -> generate the script via our custom install, but keep gpython listed as
# console_scripts entry point, so that pip knows to remove the file on develop
# uninstall.
#
# NOTE in some cases (see below e.g. about bdist_wheel) we accept for gpython
# to be generated not via XInstallGPython - because in those cases pkg_resources
# and entry points are not used - just plain `import gpython`.
class XInstallGPython:
gpython_installed = 0
# NOTE cannot override write_script, because base class - _install_scripts
# or _develop, is old-style and super does not work with it.
#def write_script(self, script_name, script, mode="t", blockers=()):
# script_name, script = self.transform_script(script_name, script)
# super(XInstallGPython, self).write_script(script_name, script, mode, blockers)
# transform_script transform to-be installed script to override installed gpython content.
#
# (script_name, script) -> (script_name, script)
def transform_script(self, script_name, script):
# on windows setuptools installs 3 files:
# gpython-script.py
# gpython.exe
# gpython.exe.manifest
# we want to override .py only.
#
# for-windows build could be cross - e.g. from linux via bdist_wininst -
# -> we can't rely on os.name. Rely on just script name.
if script_name in ('gpython', 'gpython-script.py'):
script = '#!%s\n' % sys.executable
script += '\nfrom gpython import main; main()\n'
self.gpython_installed += 1
return script_name, script
# install_scripts is custom scripts installer that takes gpython into account.
class install_scripts(XInstallGPython, _install_scripts):
def write_script(self, script_name, script, mode="t", blockers=()):
script_name, script = self.transform_script(script_name, script)
_install_scripts.write_script(self, script_name, script, mode, blockers)
def run(self):
_install_scripts.run(self)
# bdist_wheel disables generation of scripts for entry-points[1]
# and pip/setuptools regenerate them when installing the wheel[2].
#
# [1] https://github.com/pypa/wheel/commit/0d7f398b
# [2] https://github.com/pypa/wheel/commit/9aaa6628
#
# since setup.py is not included into the wheel, we cannot control
# entry-point installation when the wheel is installed. However,
# console script generated when installing the wheel looks like:
#
# #!/path/to/python
# # -*- coding: utf-8 -*-
# import re
# import sys
#
# from gpython import main
#
# if __name__ == '__main__':
# sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
# sys.exit(main())
#
# which does not import pkg_resources. Since we also double-check in
# gpython itself that pkg_resources and other modules are not imported,
# we are ok with this.
if not self.no_ep:
# regular install
assert self.gpython_installed == 1
else:
# bdist_wheel
assert self.gpython_installed == 0
assert len(self.outfiles) == 0
# develop, similarly to install_scripts, is used to handle gpython in `pip install -e` mode.
class develop(XInstallGPython, _develop):
def write_script(self, script_name, script, mode="t", blockers=()):
script_name, script = self.transform_script(script_name, script)
_develop.write_script(self, script_name, script, mode, blockers)
def install_egg_scripts(self, dist):
_develop.install_egg_scripts(self, dist)
assert self.gpython_installed == 1
# requirements of packages under "golang." namespace
R = {
'cmd.pybench': {'pytest', 'py ; python_version >= "3"'},
'pyx.build': {'setuptools', 'wheel', 'cython < 3', 'setuptools_dso >= 2.8'},
'x.perf.benchlib': {'numpy'},
}
# TODO generate `a.b -> a`, e.g. x.perf = join(x.perf.*); x = join(x.*)
Rall = set()
for pkg in R:
Rall.update(R[pkg])
R['all'] = Rall
# ipython/pytest are required to test py2 integration patches
R['all_test'] = Rall.union(['ipython', 'pytest']) # pip does not like "+" in all+test
# extras_require <- R
extras_require = {}
for k in sorted(R.keys()):
extras_require[k] = list(sorted(R[k]))
# get_python_libdir() returns path where libpython is located
def get_python_libdir():
# mimic what distutils.command.build_ext does
if os.name == 'nt':
return join(sysconfig.get_config_var('installed_platbase'), 'libs')
else:
return sysconfig.get_config_var('LIBDIR')
setup(
name = 'pygolang',
version = version,
description = 'Go-like features for Python and Cython',
long_description = '%s\n----\n\n%s' % (
readfile('README.rst'), readfile('CHANGELOG.rst')),
long_description_content_type = 'text/x-rst',
url = 'https://pygolang.nexedi.com',
project_urls= {
'Bug Tracker': 'https://lab.nexedi.com/nexedi/pygolang/issues',
'Source Code': 'https://lab.nexedi.com/nexedi/pygolang',
'Documentation': 'https://pypi.org/project/pygolang',
},
license = 'GPLv3+ with wide exception for Open-Source',
author = 'Kirill Smelkov',
author_email= '[email protected]',
keywords = 'golang go channel goroutine concurrency GOPATH python import gpython gevent cython nogil GIL',
packages = find_packages(),
x_dsos = [DSO('golang.runtime.libgolang',
['golang/runtime/libgolang.cpp',
'golang/runtime/internal/atomic.cpp',
'golang/runtime/internal/syscall.cpp',
'golang/context.cpp',
'golang/errors.cpp',
'golang/fmt.cpp',
'golang/io.cpp',
'golang/os.cpp',
'golang/os/signal.cpp',
'golang/strings.cpp',
'golang/sync.cpp',
'golang/time.cpp'],
depends = [
'golang/libgolang.h',
'golang/runtime/internal.h',
'golang/runtime/internal/atomic.h',
'golang/runtime/internal/syscall.h',
'golang/context.h',
'golang/cxx.h',
'golang/errors.h',
'golang/fmt.h',
'golang/io.h',
'golang/os.h',
'golang/os/signal.h',
'golang/strings.h',
'golang/sync.h',
'golang/time.h',
'3rdparty/ratas/src/timer-wheel.h'],
include_dirs = [
'3rdparty/include',
'3rdparty/ratas/src'],
define_macros = [('BUILDING_LIBGOLANG', None)],
soversion = '0.1'),
DSO('golang.runtime.libpyxruntime',
['golang/runtime/libpyxruntime.cpp'],
depends = ['golang/pyx/runtime.h'],
include_dirs = [sysconfig.get_python_inc()],
library_dirs = [get_python_libdir()],
define_macros = [('BUILDING_LIBPYXRUNTIME', None)],
soversion = '0.1')],
ext_modules = [
Ext('golang._golang',
['golang/_golang.pyx'],
depends = ['golang/_golang_str.pyx']),
Ext('golang.runtime._runtime_thread',
['golang/runtime/_runtime_thread.pyx']),
Ext('golang.runtime._runtime_gevent',
['golang/runtime/_runtime_gevent.pyx']),
Ext('golang.pyx.runtime',
['golang/pyx/runtime.pyx'],
dsos = ['golang.runtime.libpyxruntime']),
Ext('golang._golang_test',
['golang/_golang_test.pyx',
'golang/runtime/libgolang_test_c.c',
'golang/runtime/libgolang_test.cpp']),
Ext('golang.pyx._runtime_test',
['golang/pyx/_runtime_test.pyx'],
dsos = ['golang.runtime.libpyxruntime']),
Ext('golang._context',
['golang/_context.pyx']),
Ext('golang._cxx_test',
['golang/_cxx_test.pyx',
'golang/cxx_test.cpp']),
Ext('golang._errors',
['golang/_errors.pyx']),
Ext('golang._errors_test',
['golang/_errors_test.pyx',
'golang/errors_test.cpp']),
Ext('golang._fmt',
['golang/_fmt.pyx']),
Ext('golang._fmt_test',
['golang/_fmt_test.pyx',
'golang/fmt_test.cpp']),
Ext('golang._io',
['golang/_io.pyx']),
Ext('golang._os',
['golang/_os.pyx']),
Ext('golang._os_test',
['golang/_os_test.pyx',
'golang/os_test.cpp']),
Ext('golang.os._signal',
['golang/os/_signal.pyx']),
Ext('golang._strings_test',
['golang/_strings_test.pyx',
'golang/strings_test.cpp']),
Ext('golang._sync',
['golang/_sync.pyx'],
dsos = ['golang.runtime.libpyxruntime'],
define_macros = [('_LIBGOLANG_SYNC_INTERNAL_API', None)]),
Ext('golang._sync_test',
['golang/_sync_test.pyx',
'golang/sync_test.cpp']),
Ext('golang._time',
['golang/_time.pyx'],
dsos = ['golang.runtime.libpyxruntime']),
],
include_package_data = True,
install_requires = ['gevent', 'six', 'decorator', 'Importing;python_version<="2.7"',
# only runtime part: for dylink_prepare_dso
# also need to pin setuptools ≥ 60.2 because else wheel configures logging
# to go to stdout and so dylink_prepare_dso garbles program output
'setuptools_dso >= 2.8',
'setuptools >= 60.2 ; python_version>="3"',
# pyx.build -> setuptools_dso uses multiprocessing
# setuptools_dso uses multiprocessing only on Python3, and only on systems where
# mp.get_start_method()!='fork', while geventmp does not work on windows.
'geventmp ; python_version>="3" and platform_system != "Windows" ',
],
extras_require = extras_require,
entry_points= {'console_scripts': [
# NOTE gpython is handled specially - see XInstallGPython.
'gpython = gpython:main',
'py.bench = golang.cmd.pybench:main',
]
},
cmdclass = {
'install_scripts': install_scripts,
'develop': develop,
},
classifiers = [_.strip() for _ in """\
Development Status :: 4 - Beta
Intended Audience :: Developers
Programming Language :: Python
Programming Language :: Cython
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
Operating System :: POSIX
Operating System :: POSIX :: Linux
Operating System :: Unix
Operating System :: MacOS
Operating System :: Microsoft :: Windows
Topic :: Software Development :: Interpreters
Topic :: Software Development :: Libraries :: Python Modules\
""".splitlines()]
)