setup.py 11.6 KB
Newer Older
1
# pygolang | pythonic package setup
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# Copyright (C) 2018-2019  Nexedi SA and Contributors.
#                          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
Kirill Smelkov's avatar
Kirill Smelkov committed
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

Kirill Smelkov's avatar
Kirill Smelkov committed
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.2'},
160 161 162 163 164 165 166 167 168 169 170 171 172 173
    '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

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

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

193
    packages    = find_packages(),
194

195 196
    x_dsos      = [DSO('golang.runtime.libgolang',
                        ['golang/runtime/libgolang.cpp',
197
                         'golang/context.cpp',
Kirill Smelkov's avatar
Kirill Smelkov committed
198
                         'golang/errors.cpp',
199 200 201 202
                         'golang/sync.cpp',
                         'golang/time.cpp'],
                        depends = [
                            'golang/libgolang.h',
203
                            'golang/context.h',
Kirill Smelkov's avatar
Kirill Smelkov committed
204
                            'golang/cxx.h',
Kirill Smelkov's avatar
Kirill Smelkov committed
205
                            'golang/errors.h',
206 207
                            'golang/sync.h',
                            'golang/time.h'],
Kirill Smelkov's avatar
Kirill Smelkov committed
208
                        include_dirs    = ['.', '3rdparty/include'],
209
                        define_macros   = [('BUILDING_LIBGOLANG', None)],
Kirill Smelkov's avatar
Kirill Smelkov committed
210
                        extra_compile_args = ['-std=gnu++11'], # not c++11 as linux/list.h uses typeof
211 212 213 214 215 216 217 218
                        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'],
219
                        soversion       = '0.1')],
220

221 222 223 224
    ext_modules = [
                    Ext('golang._golang',
                        ['golang/_golang.pyx']),

225 226 227 228 229 230 231
                    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
232

233 234 235 236
                    Ext('golang.pyx.runtime',
                        ['golang/pyx/runtime.pyx'],
                        dsos = ['golang.runtime.libpyxruntime']),

Kirill Smelkov's avatar
Kirill Smelkov committed
237 238 239 240 241
                    Ext('golang._golang_test',
                        ['golang/_golang_test.pyx',
                         'golang/runtime/libgolang_test_c.c',
                         'golang/runtime/libgolang_test.cpp']),

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

Kirill Smelkov's avatar
Kirill Smelkov committed
246 247 248
                    Ext('golang._context',
                        ['golang/_context.pyx']),

249 250 251 252 253
                    Ext('golang._sync',
                        ['golang/_sync.pyx']),
                    Ext('golang._sync_test',
                        ['golang/_sync_test.pyx']),

Kirill Smelkov's avatar
Kirill Smelkov committed
254
                    Ext('golang._time',
255 256
                        ['golang/_time.pyx'],
                        dsos = ['golang.runtime.libpyxruntime']),
257
                  ],
258
    include_package_data = True,
Kirill Smelkov's avatar
Kirill Smelkov committed
259

260
    install_requires = ['gevent', 'six', 'decorator', 'Importing;python_version<="2.7"'],
261
    extras_require   = extras_require,
Kirill Smelkov's avatar
Kirill Smelkov committed
262

263
    entry_points= {'console_scripts': [
264 265 266
                        # NOTE gpython is handled specially - see XInstallGPython.
                        'gpython  = gpython:main',

267 268 269 270
                        'py.bench = golang.cmd.pybench:main',
                      ]
                  },

271 272 273 274 275
    cmdclass    = {
        'install_scripts':  install_scripts,
        'develop':          develop,
    },

Kirill Smelkov's avatar
Kirill Smelkov committed
276 277
    classifiers = [_.strip() for _ in """\
        Development Status :: 3 - Alpha
278
        Intended Audience :: Developers
Kirill Smelkov's avatar
Kirill Smelkov committed
279 280
        Programming Language :: Python
        Programming Language :: Cython
281
        Programming Language :: Python :: 2
Kirill Smelkov's avatar
Kirill Smelkov committed
282 283 284 285 286 287 288 289 290
        Programming Language :: Python :: 2.7
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3.5
        Programming Language :: Python :: 3.6
        Programming Language :: Python :: 3.7
        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
291 292
    """.splitlines()]
)