setup.py 13.6 KB
Newer Older
1
# pygolang | pythonic package setup
2
# Copyright (C) 2018-2020  Nexedi SA and Contributors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#                          Kirill Smelkov <kirr@nexedi.com>
#
# 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.
20 21 22 23 24 25

from setuptools import find_packages
# setuptools has Library but this days it is not well supported and test for it
# has been killed https://github.com/pypa/setuptools/commit/654c26f78a30
# -> use setuptools_dso instead.
from setuptools_dso import DSO
26 27
from setuptools.command.install_scripts import install_scripts as _install_scripts
from setuptools.command.develop import develop as _develop
28
from distutils import sysconfig
29
from os.path import dirname, join
30
import sys, re
Kirill Smelkov's avatar
Kirill Smelkov committed
31

32 33 34 35 36
# read file content
def readfile(path):
    with open(path, 'r') as f:
        return f.read()

37 38 39
# reuse golang.pyx.build to build pygolang 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.
40 41 42
trun = {}
exec(readfile('trun'), trun)
trun['ximport_empty_golangmod']()
43 44
from golang.pyx.build import setup, Extension as Ext

Kirill Smelkov's avatar
Kirill Smelkov committed
45

46 47 48 49 50 51 52 53 54 55 56 57 58 59
# 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)

60 61 62 63 64 65 66 67 68 69 70 71 72
# 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
Kirill Smelkov's avatar
Kirill Smelkov committed
73
# to be generated not via XInstallGPython - because in those cases pkg_resources
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
# 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


156 157 158
# requirements of packages under "golang." namespace
R = {
    'cmd.pybench':      {'pytest'},
159
    'pyx.build':        {'setuptools', 'wheel', 'cython', 'setuptools_dso >= 1.7'},
160 161 162 163 164 165 166 167
    '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

168 169 170
# ipython/pytest are required to test py2 integration patches
R['all_test'] = Rall.union(['ipython', 'pytest']) # pip does not like "+" in all+test

171 172 173 174 175 176
# extras_require <- R
extras_require = {}
for k in sorted(R.keys()):
    extras_require[k] = list(sorted(R[k]))


Kirill Smelkov's avatar
Kirill Smelkov committed
177
setup(
178
    name        = 'pygolang',
179
    version     = version,
180
    description = 'Go-like features for Python and Cython',
Kirill Smelkov's avatar
Kirill Smelkov committed
181 182
    long_description = '%s\n----\n\n%s' % (
                            readfile('README.rst'), readfile('CHANGELOG.rst')),
Kirill Smelkov's avatar
Kirill Smelkov committed
183
    long_description_content_type  = 'text/x-rst',
Kirill Smelkov's avatar
Kirill Smelkov committed
184
    url         = 'https://pygolang.nexedi.com',
Kirill Smelkov's avatar
Kirill Smelkov committed
185
    project_urls= {
186 187
        'Bug Tracker':   'https://lab.nexedi.com/nexedi/pygolang/issues',
        'Source Code':   'https://lab.nexedi.com/nexedi/pygolang',
Kirill Smelkov's avatar
Kirill Smelkov committed
188 189
        'Documentation': 'https://pypi.org/project/pygolang',
    },
Kirill Smelkov's avatar
Kirill Smelkov committed
190 191 192 193
    license     = 'GPLv3+ with wide exception for Open-Source',
    author      = 'Kirill Smelkov',
    author_email= 'kirr@nexedi.com',

194
    keywords    = 'golang go channel goroutine concurrency GOPATH python import gpython gevent cython nogil GIL',
Kirill Smelkov's avatar
Kirill Smelkov committed
195

196
    packages    = find_packages(),
197

198 199
    x_dsos      = [DSO('golang.runtime.libgolang',
                        ['golang/runtime/libgolang.cpp',
200
                         'golang/context.cpp',
Kirill Smelkov's avatar
Kirill Smelkov committed
201
                         'golang/errors.cpp',
Kirill Smelkov's avatar
Kirill Smelkov committed
202
                         'golang/fmt.cpp',
203
                         'golang/io.cpp',
Kirill Smelkov's avatar
Kirill Smelkov committed
204
                         'golang/strings.cpp',
205 206 207 208
                         'golang/sync.cpp',
                         'golang/time.cpp'],
                        depends = [
                            'golang/libgolang.h',
209
                            'golang/context.h',
Kirill Smelkov's avatar
Kirill Smelkov committed
210
                            'golang/cxx.h',
Kirill Smelkov's avatar
Kirill Smelkov committed
211
                            'golang/errors.h',
Kirill Smelkov's avatar
Kirill Smelkov committed
212
                            'golang/fmt.h',
213
                            'golang/io.h',
Kirill Smelkov's avatar
Kirill Smelkov committed
214
                            'golang/strings.h',
215 216
                            'golang/sync.h',
                            'golang/time.h'],
217
                        include_dirs    = ['.', '3rdparty/include'],
218
                        define_macros   = [('BUILDING_LIBGOLANG', None)],
219
                        extra_compile_args = ['-std=gnu++11'], # not c++11 as linux/list.h uses typeof
220 221 222 223 224 225 226 227
                        soversion       = '0.1'),

                    DSO('golang.runtime.libpyxruntime',
                        ['golang/runtime/libpyxruntime.cpp'],
                        depends = ['golang/pyx/runtime.h'],
                        include_dirs    = ['.', sysconfig.get_python_inc()],
                        define_macros   = [('BUILDING_LIBPYXRUNTIME', None)],
                        extra_compile_args = ['-std=c++11'],
228 229
                        soversion       = '0.1',
                        dsos = ['golang.runtime.libgolang'])],
230

231 232 233 234
    ext_modules = [
                    Ext('golang._golang',
                        ['golang/_golang.pyx']),

235 236 237 238 239 240 241
                    Ext('golang.runtime._runtime_thread',
                        ['golang/runtime/_runtime_thread.pyx'],
                        language = "c"),

                    Ext('golang.runtime._runtime_gevent',
                        ['golang/runtime/_runtime_gevent.pyx'],
                        language = 'c'),
Kirill Smelkov's avatar
Kirill Smelkov committed
242

243 244 245 246
                    Ext('golang.pyx.runtime',
                        ['golang/pyx/runtime.pyx'],
                        dsos = ['golang.runtime.libpyxruntime']),

Kirill Smelkov's avatar
Kirill Smelkov committed
247 248 249 250 251
                    Ext('golang._golang_test',
                        ['golang/_golang_test.pyx',
                         'golang/runtime/libgolang_test_c.c',
                         'golang/runtime/libgolang_test.cpp']),

252 253 254 255
                    Ext('golang.pyx._runtime_test',
                        ['golang/pyx/_runtime_test.pyx'],
                        dsos = ['golang.runtime.libpyxruntime']),

256 257 258
                    Ext('golang._context',
                        ['golang/_context.pyx']),

259 260 261 262
                    Ext('golang._cxx_test',
                        ['golang/_cxx_test.pyx',
                         'golang/cxx_test.cpp']),

263 264
                    Ext('golang._errors',
                        ['golang/_errors.pyx']),
265 266 267 268
                    Ext('golang._errors_test',
                        ['golang/_errors_test.pyx',
                         'golang/errors_test.cpp']),

269 270
                    Ext('golang._fmt',
                        ['golang/_fmt.pyx']),
271 272 273 274
                    Ext('golang._fmt_test',
                        ['golang/_fmt_test.pyx',
                         'golang/fmt_test.cpp']),

275 276 277
                    Ext('golang._io',
                        ['golang/_io.pyx']),

278 279 280 281
                    Ext('golang._strings_test',
                        ['golang/_strings_test.pyx',
                         'golang/strings_test.cpp']),

282
                    Ext('golang._sync',
283 284 285
                        ['golang/_sync.pyx'],
                        dsos = ['golang.runtime.libpyxruntime'],
                        define_macros = [('_LIBGOLANG_SYNC_INTERNAL_API', None)]),
286
                    Ext('golang._sync_test',
287 288
                        ['golang/_sync_test.pyx',
                         'golang/sync_test.cpp']),
289

Kirill Smelkov's avatar
Kirill Smelkov committed
290
                    Ext('golang._time',
291 292
                        ['golang/_time.pyx'],
                        dsos = ['golang.runtime.libpyxruntime']),
293
                  ],
294
    include_package_data = True,
Kirill Smelkov's avatar
Kirill Smelkov committed
295

296 297 298 299 300 301 302
    install_requires = ['gevent', 'six', 'decorator', 'Importing;python_version<="2.7"',
                        # pyx.build -> setuptools_dso uses multiprocessing
                        # FIXME geventmp fails on python2, but setuptools_dso
                        # uses multiprocessing only on Python3, so for now we
                        # are ok. https://github.com/karellen/geventmp/pull/2
                        'geventmp;python_version>="3"',
                       ],
303
    extras_require   = extras_require,
Kirill Smelkov's avatar
Kirill Smelkov committed
304

305
    entry_points= {'console_scripts': [
306 307 308
                        # NOTE gpython is handled specially - see XInstallGPython.
                        'gpython  = gpython:main',

309 310 311 312
                        'py.bench = golang.cmd.pybench:main',
                      ]
                  },

313 314 315 316 317
    cmdclass    = {
        'install_scripts':  install_scripts,
        'develop':          develop,
    },

Kirill Smelkov's avatar
Kirill Smelkov committed
318
    classifiers = [_.strip() for _ in """\
Kirill Smelkov's avatar
Kirill Smelkov committed
319
        Development Status :: 4 - Beta
320
        Intended Audience :: Developers
Kirill Smelkov's avatar
Kirill Smelkov committed
321 322
        Programming Language :: Python
        Programming Language :: Cython
323
        Programming Language :: Python :: 2
Kirill Smelkov's avatar
Kirill Smelkov committed
324 325 326 327 328
        Programming Language :: Python :: 2.7
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3.5
        Programming Language :: Python :: 3.6
        Programming Language :: Python :: 3.7
Kirill Smelkov's avatar
Kirill Smelkov committed
329
        Programming Language :: Python :: 3.8
Kirill Smelkov's avatar
Kirill Smelkov committed
330 331 332 333
        Programming Language :: Python :: Implementation :: CPython
        Programming Language :: Python :: Implementation :: PyPy
        Topic :: Software Development :: Interpreters
        Topic :: Software Development :: Libraries :: Python Modules\
Kirill Smelkov's avatar
Kirill Smelkov committed
334 335
    """.splitlines()]
)