1

I cloned the git master branch of matplotlib and I got to build it in Mac OSX. When I try to do the same in x86_64 GNU/Linux I get this output and error:

BUILDING MATPLOTLIB
        matplotlib: yes [1.3.x]
            python: yes [3.3.1 (default, May  7 2013, 17:57:31)  [GCC
                    4.8.0]]
          platform: yes [linux]

REQUIRED DEPENDENCIES AND EXTENSIONS
             numpy: yes [version 1.7.1]
          dateutil: yes [using dateutil version 2.1]
           tornado: yes [using tornado version 3.0.1]
         pyparsing: yes [using pyparsing version 2.0.0]
             pycxx: yes [Official versions of PyCXX are not compatible
                    with Python 3.x.  Using local copy]
            libagg: yes [pkg-config information for 'libagg' could not
                    be found. Using local copy.]
          freetype: yes [version 9.22.3]
Traceback (most recent call last):
  File "./setup.py", line 133, in <module>
    result = package.check()
  File "/home/khyox/matplotlib/setupext.py", line 808, in check
    min_version='1.2')
  File "/home/khyox/matplotlib/setupext.py", line 429, in _check_for_pkg_config
    ext = self.get_extension()
  File "/home/khyox/matplotlib/setupext.py", line 821, in get_extension
    CXX().add_flags(ext)
  File "/home/khyox/matplotlib/setupext.py", line 716, in add_flags
    ext.sources.extend(glob.glob('CXX/*.cxx'))
AttributeError: 'map' object has no attribute 'extend'

I traced the error to the PNG (libpng) package parsing. I tried with several versions of this library (1.2.49, 1.5.16 and 1.6.2), all supported by matplotlib (should be version 1.2 or higher for libpng), but this not solved the issue. Looking for helping differences, in Mac OSX I compiled Python3.3 with Clang 4.1 (said to be GCC 4.2 compatible) and in this x86_64 GNU/Linux machine, I used GCC 4.8.0, but I hardly believe that the problem could be related with this...

Any clue? Thank you very much in advance!


Solution: As suggested by @DSM in the comments below, a workaround is to edit setupext.py and, in the function make_extension(name, files, *args, **kwargs) just add this line:

    ext.sources = list(ext.sources) 

after the line:

    ext = Extension(name, files, *args, **kwargs)

On the other hand, @tcaswell pointed out the suspect of the issue: distutils. In short, in order to install all the dependencies for matplotlib I used distribute3 and its last release overrides the class distutils.core.Extension converting its sources member to a map instead of a list (it is expected to be a list of strings, as documented in docs.python.org).

Entering in more details, if we add some debugging stuff, the output becomes:

BUILDING MATPLOTLIB
            matplotlib: yes [1.3.x]
                python: yes [3.3.1 (default, May  7 2013, 17:57:31)  [GCC
                        4.8.0]]
              platform: yes [linux]

REQUIRED DEPENDENCIES AND EXTENSIONS
Call distutils.core.Extension constructor with name=test and files=[]
Printing (ext = distutils.core.Extension).sources : <map object at 0x7f920c869e50>
The map object is empty!
                 numpy: yes [version 1.7.1]
              dateutil: yes [using dateutil version 2.1]
               tornado: yes [using tornado version 3.0.1]
             pyparsing: yes [using pyparsing version 2.0.0]
                 pycxx: yes [Official versions of PyCXX are not compatible
                        with Python 3.x.  Using local copy]
                libagg: yes [pkg-config information for 'libagg' could not
                        be found. Using local copy.]
Call distutils.core.Extension constructor with name=test and files=[]
Printing (ext = distutils.core.Extension).sources : <map object at 0x7f9208e81490>
The map object is empty!
              freetype: yes [version 9.22.3]
Call distutils.core.Extension constructor with name=matplotlib._png and files=['src/_png.cpp', 'src/mplutils.cpp']
Printing (ext = distutils.core.Extension).sources : <map object at 0x7f9208e81510>
The map object is empty!
Traceback (most recent call last):
  File "setup.py", line 133, in <module>
    result = package.check()
  File "/home/khyox/matplotlib/setupext.py", line 817, in check
    min_version='1.2')
  File "/home/khyox/matplotlib/setupext.py", line 438, in _check_for_pkg_config
    ext = self.get_extension()
  File "/home/khyox/matplotlib/setupext.py", line 830, in get_extension
    CXX().add_flags(ext)
  File "/home/khyox/matplotlib/setupext.py", line 725, in add_flags
    ext.sources.extend(glob.glob('CXX/*.cxx'))
AttributeError: 'map' object has no attribute 'extend'

So, instead of being a list, the sources member of ext appears as a map. What is more, it fails to contain the source files when passed to the constructor, as seen for the matplotlib._png case, that is, when processing the libpng extension.

I followed the source of such strange behavior to the file extension.py of the setuptools provided by distribute3 (see last line):

class Extension(_Extension):
    """Extension that uses '.c' files in place of '.pyx' files"""

    def __init__(self, *args, **kw):
        _Extension.__init__(self, *args, **kw)
        if not have_pyrex():
            self._convert_pyx_sources_to_c()

    def _convert_pyx_sources_to_c(self):
        "convert .pyx extensions to .c"
        def pyx_to_c(source):
            if source.endswith('.pyx'):
                source = source[:-4] + '.c'
            return source
        self.sources = map(pyx_to_c, self.sources)

Nevertheless, in the last distribute commit (newer than the last one of distribute3), it seems to be solved:

class Extension(_Extension):
    """Extension that uses '.c' files in place of '.pyx' files"""

    if not have_pyrex:
        # convert .pyx extensions to .c 
        def __init__(self,*args,**kw):
            _Extension.__init__(self,*args,**kw)
            sources = []
            for s in self.sources:
                if s.endswith('.pyx'):
                    sources.append(s[:-3]+'c')
                else:
                    sources.append(s)
            self.sources = sources

So, once distribute3 will be synchronized with distribute this problem will probably disappear. Until then, the hack suggested by @DSM is working.

4

0 回答 0