[Cython] Sources list handling in Build/Dependencies.py:create_extension_list

Robert Bradshaw robertwb at math.washington.edu
Wed Mar 9 19:42:26 CET 2011


On Wed, Mar 9, 2011 at 3:13 AM, Oleksandr Kreshchenko
<cross at ueh0.bank.gov.ua> wrote:
> Hello!
>
> Following the "CEP 201 - Distutils Preprocessing"
> (http://wiki.cython.org/enhancements/distutils_preprocessing)
> there are two possibilities to build a module with cythonize function in
> setup.py.
>
> Second one uses list of distutils.extension.Extension class which accept all
> necessary compiler and
> linker options along with list of non-pyx sources to compile and link with
> for a particular extension module.
> As this way appears more natural for me I do::
>
>    ext_modules = cythonize([Extension(
>        'CyCont', ['CyCont.pyx', 'Cont.c', 'ContCet.c', 'Clash.c',
> 'ContInit.c', 'MemMan.c', '../Log/Log.c'],
>        define_macros = [('CONT_API', '')], # None value expands to 1,
> whereas '' to empty
>        include_dirs = dirs, #libraries = ['libgcov'],
>        extra_compile_args = sys_comp_opts[sys.platform],
>        extra_link_args = sys_link_opts[sys.platform])],
>      show_version = 1, language_level = 3, verbose = 1, include_path = dirs)
>
> But, only CyCont.c is created from CyCont.pyx then compiled and linked into
> the module CyCont.so,
> but the rest of sources to be wrapped are completely ignored in the build
> process.
> So, doing import CyCont I've got undefined symbol error.
>
> However when I move 'CyCont.pyx' to the end of the sources list i.e.::
>
>    ext_modules = cythonize([Extension(
>        'CyCont', ['Cont.c', 'ContCet.c', 'Clash.c', 'ContInit.c',
> 'MemMan.c', '../Log/Log.c', 'CyCont.pyx'],
>
>    ...
>
> the module is built properly.
>
> Execution flow goes though Cython/Build/Dependencies.py (latest revision)::
>
> 393 def create_extension_list(patterns, exclude=[], ctx=None, aliases=None)
> ...
> 404     for pattern in patterns:
> 405         if isinstance(pattern, str):
> 406             filepattern = pattern
> 407             template = None
> 408             name = '*'
> 409             base = None
> 410             exn_type = Extension
> 411         elif isinstance(pattern, Extension):
> 412             filepattern = pattern.sources[0]
> 413             if os.path.splitext(filepattern)[1] not in ('.py', '.pyx'):
> 414                 # ignore non-cython modules
> 415                 module_list.append(pattern)
> 416                 continue
> 417             template = pattern
> 418             name = template.name
> 419             base = DistutilsInfo(exn=template)
> 420             exn_type = template.__class__
> ...
> 423         for file in glob(filepattern):
> ...
> 437                 module_list.append(exn_type(
> 438                         name=module_name,
> 439                         sources=[file],
> 440                         **kwds))
> ...
> 443     return module_list
>
> If the first list member (sources[0]) is *.pyx or *.py then the only list
> member is CyCont.pyx (see line 439).
>
> Otherwise if CyCont.pyx is placed at the end of the list or, generally, the
> first list member is
> neither *.pyx nor *.py than the loop over patterns/extensions (on line 404)
> breaks at the line 416
> so sources list is left untouched and cythonize function (line 446) does the
> job.
>
> This difference is a bug. Isn't it?

Yes, for sure, thanks for the report.
https://github.com/cython/cython/commit/0fefeb25858ac943a39c4f9ca7e0a493052517a1

> Have "sources[0]" on line 412 and "sources=[file]" on line 439 to be
> revised?
>
>
> BTW, I catch the idea of twiking an extension in setup.py like this::
>
> for ext in ext_modules:
>    ext.define_macros = [('CONT_API', '')]
>    ext.extra_compile_args = sys_comp_opts[sys.platform]
>    ext.extra_link_args = sys_link_opts[sys.platform]
>    ext.sources.extend(['Cont.c', 'ContCet.c', 'Clash.c', 'ContInit.c',
> 'MemMan.c', '../Log/Log.c'])
>
> but it seams **better** to handle this stuff in the first place within
> Extension(...),
> not to mention using module-level distutils directives.

Yep. The build code is set to handle subclasses of Extension, but
there's only so much you can do, otherwise it becomes difficult to use
two projects that need different distutils hacks.

- Robert


More information about the cython-devel mailing list