# # This source file is part of the EdgeDB open source project. # # Copyright 2008-present MagicStack Inc. and the EdgeDB authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import sys if sys.version_info < (3, 6): # edgedb.NamedTuple() relies on ordered **kwargs. raise RuntimeError('edgedb requires Python 3.6 or greater') import os import os.path import pathlib import re import subprocess # We use vanilla build_ext, to avoid importing Cython via # the setuptools version. from distutils import extension as distutils_extension from distutils.command import build_ext as distutils_build_ext import setuptools from setuptools.command import build_py as setuptools_build_py from setuptools.command import sdist as setuptools_sdist CYTHON_DEPENDENCY = 'Cython==0.29.14' # Minimal dependencies required to test edgedb. TEST_DEPENDENCIES = [ # pycodestyle is a dependency of flake8, but it must be frozen because # their combination breaks too often # (example breakage: https://gitlab.com/pycqa/flake8/issues/427) 'pycodestyle~=2.5.0', 'flake8~=3.7.9', 'uvloop>=0.14.0;platform_system!="Windows"', ] # Dependencies required to build documentation. DOC_DEPENDENCIES = [ 'sphinx~=2.3.1', 'sphinxcontrib-asyncio~=0.2.0', 'sphinx_rtd_theme~=0.4.3', ] EXTRA_DEPENDENCIES = { 'docs': DOC_DEPENDENCIES, 'test': TEST_DEPENDENCIES, # Dependencies required to develop edgedb. 'dev': [ CYTHON_DEPENDENCY, 'pytest>=3.6.0', ] + DOC_DEPENDENCIES + TEST_DEPENDENCIES } CFLAGS = ['-O2'] LDFLAGS = [] SYSTEM = sys.platform if SYSTEM != 'win32': CFLAGS.extend(['-std=gnu99', '-fsigned-char', '-Wall', '-Wsign-compare', '-Wconversion']) if SYSTEM == 'darwin': # Lots of warnings from the standard library on macOS 10.14 CFLAGS.extend(['-Wno-nullability-completeness']) _ROOT = pathlib.Path(__file__).parent with open(str(_ROOT / 'README.rst')) as f: readme = f.read() with open(str(_ROOT / 'edgedb' / '_version.py')) as f: for line in f: if line.startswith('__version__ ='): _, _, version = line.partition('=') VERSION = version.strip(" \n'\"") break else: raise RuntimeError( 'unable to read the version from edgedb/_version.py') if (_ROOT / '.git').is_dir() and 'dev' in VERSION: # This is a git checkout, use git to # generate a precise version. def git_commitish(): env = {} v = os.environ.get('PATH') if v is not None: env['PATH'] = v git = subprocess.run(['git', 'rev-parse', 'HEAD'], env=env, cwd=str(_ROOT), stdout=subprocess.PIPE) if git.returncode == 0: commitish = git.stdout.strip().decode('ascii') else: commitish = 'unknown' return commitish VERSION += '+' + git_commitish()[:7] class VersionMixin: def _fix_version(self, filename): # Replace edgedb.__version__ with the actual version # of the distribution (possibly inferred from git). with open(str(filename)) as f: content = f.read() version_re = r"(.*__version__\s*=\s*)'[^']+'(.*)" repl = r"\1'{}'\2".format(self.distribution.metadata.version) content = re.sub(version_re, repl, content) with open(str(filename), 'w') as f: f.write(content) class sdist(setuptools_sdist.sdist, VersionMixin): def make_release_tree(self, base_dir, files): super().make_release_tree(base_dir, files) self._fix_version(pathlib.Path(base_dir) / 'edgedb' / '_version.py') class build_py(setuptools_build_py.build_py, VersionMixin): def build_module(self, module, module_file, package): outfile, copied = super().build_module(module, module_file, package) if module == '_version' and package == 'edgedb': self._fix_version(outfile) return outfile, copied class build_ext(distutils_build_ext.build_ext): user_options = distutils_build_ext.build_ext.user_options + [ ('cython-always', None, 'run cythonize() even if .c files are present'), ('cython-annotate', None, 'Produce a colorized HTML version of the Cython source.'), ('cython-directives=', None, 'Cython compiler directives'), ] def initialize_options(self): # initialize_options() may be called multiple times on the # same command object, so make sure not to override previously # set options. if getattr(self, '_initialized', False): return super(build_ext, self).initialize_options() if os.environ.get('EDGEDB_DEBUG'): self.cython_always = True self.cython_annotate = True self.cython_directives = "linetrace=True" self.define = 'PG_DEBUG,CYTHON_TRACE,CYTHON_TRACE_NOGIL' self.debug = True else: self.cython_always = False self.cython_annotate = None self.cython_directives = None self.debug = False def finalize_options(self): # finalize_options() may be called multiple times on the # same command object, so make sure not to override previously # set options. if getattr(self, '_initialized', False): return need_cythonize = self.cython_always cfiles = {} for extension in self.distribution.ext_modules: for i, sfile in enumerate(extension.sources): if sfile.endswith('.pyx'): prefix, ext = os.path.splitext(sfile) cfile = prefix + '.c' if os.path.exists(cfile) and not self.cython_always: extension.sources[i] = cfile else: if os.path.exists(cfile): cfiles[cfile] = os.path.getmtime(cfile) else: cfiles[cfile] = 0 need_cythonize = True if need_cythonize: import pkg_resources # Double check Cython presence in case setup_requires # didn't go into effect (most likely because someone # imported Cython before setup_requires injected the # correct egg into sys.path. try: import Cython except ImportError: raise RuntimeError( 'please install {} to compile edgedb from source'.format( CYTHON_DEPENDENCY)) cython_dep = pkg_resources.Requirement.parse(CYTHON_DEPENDENCY) if Cython.__version__ not in cython_dep: raise RuntimeError( 'edgedb requires {}, got Cython=={}'.format( CYTHON_DEPENDENCY, Cython.__version__ )) from Cython.Build import cythonize directives = { 'language_level': '3' } if self.cython_directives: for directive in self.cython_directives.split(','): k, _, v = directive.partition('=') if v.lower() == 'false': v = False if v.lower() == 'true': v = True directives[k] = v self.distribution.ext_modules[:] = cythonize( self.distribution.ext_modules, compiler_directives=directives, annotate=self.cython_annotate) super(build_ext, self).finalize_options() INCLUDE_DIRS = [ 'edgedb/pgproto/', 'edgedb/datatypes', ] setup_requires = [] if (not (_ROOT / 'edgedb' / 'protocol' / 'protocol.c').exists() or '--cython-always' in sys.argv): # No Cython output, require Cython to build. setup_requires.append(CYTHON_DEPENDENCY) with open(str(_ROOT / 'README.rst')) as f: readme = f.read() setuptools.setup( name='edgedb', version=VERSION, description='EdgeDB Python driver', long_description=readme, platforms=['macOS', 'POSIX', 'Windows'], author='MagicStack Inc', author_email='hello@magic.io', url='https://github.com/edgedb/edgedb-python', license='Apache License, Version 2.0', packages=['edgedb'], provides=['edgedb'], zip_safe=False, include_package_data=True, package_data={'edgedb': ['py.typed']}, ext_modules=[ distutils_extension.Extension( "edgedb.pgproto.pgproto", ["edgedb/pgproto/pgproto.pyx"], extra_compile_args=CFLAGS, extra_link_args=LDFLAGS), distutils_extension.Extension( "edgedb.datatypes.datatypes", ["edgedb/datatypes/args.c", "edgedb/datatypes/record_desc.c", "edgedb/datatypes/tuple.c", "edgedb/datatypes/namedtuple.c", "edgedb/datatypes/object.c", "edgedb/datatypes/set.c", "edgedb/datatypes/hash.c", "edgedb/datatypes/array.c", "edgedb/datatypes/link.c", "edgedb/datatypes/linkset.c", "edgedb/datatypes/repr.c", "edgedb/datatypes/comp.c", "edgedb/datatypes/datatypes.pyx"], extra_compile_args=CFLAGS, extra_link_args=LDFLAGS), distutils_extension.Extension( "edgedb.protocol.protocol", ["edgedb/protocol/protocol.pyx"], extra_compile_args=CFLAGS, extra_link_args=LDFLAGS, include_dirs=INCLUDE_DIRS), distutils_extension.Extension( "edgedb.protocol.asyncio_proto", ["edgedb/protocol/asyncio_proto.pyx"], extra_compile_args=CFLAGS, extra_link_args=LDFLAGS, include_dirs=INCLUDE_DIRS), distutils_extension.Extension( "edgedb.protocol.blocking_proto", ["edgedb/protocol/blocking_proto.pyx"], extra_compile_args=CFLAGS, extra_link_args=LDFLAGS, include_dirs=INCLUDE_DIRS), ], cmdclass={'build_ext': build_ext}, test_suite='tests.suite', install_requires=[ 'typing-extensions~=3.7.4', ], extras_require=EXTRA_DEPENDENCIES, setup_requires=setup_requires, )