• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import re
2import functools
3import distutils.core
4import distutils.errors
5import distutils.extension
6
7from setuptools.extern.six.moves import map
8
9from .monkey import get_unpatched
10
11
12def _have_cython():
13    """
14    Return True if Cython can be imported.
15    """
16    cython_impl = 'Cython.Distutils.build_ext'
17    try:
18        # from (cython_impl) import build_ext
19        __import__(cython_impl, fromlist=['build_ext']).build_ext
20        return True
21    except Exception:
22        pass
23    return False
24
25
26# for compatibility
27have_pyrex = _have_cython
28
29_Extension = get_unpatched(distutils.core.Extension)
30
31
32class Extension(_Extension):
33    """Extension that uses '.c' files in place of '.pyx' files"""
34
35    def __init__(self, name, sources, *args, **kw):
36        # The *args is needed for compatibility as calls may use positional
37        # arguments. py_limited_api may be set only via keyword.
38        self.py_limited_api = kw.pop("py_limited_api", False)
39        _Extension.__init__(self, name, sources, *args, **kw)
40
41    def _convert_pyx_sources_to_lang(self):
42        """
43        Replace sources with .pyx extensions to sources with the target
44        language extension. This mechanism allows language authors to supply
45        pre-converted sources but to prefer the .pyx sources.
46        """
47        if _have_cython():
48            # the build has Cython, so allow it to compile the .pyx files
49            return
50        lang = self.language or ''
51        target_ext = '.cpp' if lang.lower() == 'c++' else '.c'
52        sub = functools.partial(re.sub, '.pyx$', target_ext)
53        self.sources = list(map(sub, self.sources))
54
55
56class Library(Extension):
57    """Just like a regular Extension, but built as a library instead"""
58