Skip to content

Commit

Permalink
Allow cythonizing or compiling geoseries functions if possible.
Browse files Browse the repository at this point in the history
  • Loading branch information
eriknw authored and jorisvandenbossche committed Aug 5, 2017
1 parent dd8bf01 commit 5af2fcc
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 3 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ geopandas/version.py
doc/_static/world_*
examples/nybb_*.zip
.cache/
*.c
*.so
*.pyd
*.swp
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
SHELL= /bin/bash
PYTHON ?= python

inplace:
$(PYTHON) setup.py build_ext --inplace --with-cython

test: inplace
py.test geopandas

clean:
rm -f geopandas/*.c geopandas/*.so
rm -rf build/ geopandas/__pycache__/ geopandas/*/__pycache__/
2 changes: 2 additions & 0 deletions geopandas/__init__.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from geopandas._geoseries cimport fake_func

2 changes: 2 additions & 0 deletions geopandas/_geoseries.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cpdef fake_func(x, y)

3 changes: 3 additions & 0 deletions geopandas/_geoseries.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cpdef fake_func(x, y):
return x + y

5 changes: 5 additions & 0 deletions geopandas/geoseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,8 @@ def __sub__(self, other):
return self.difference(other)

GeoSeries._create_indexer('cx', _CoordinateIndexer)

try:
from ._geoseries import *
except ImportError:
pass
115 changes: 112 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
#!/usr/bin/env/python
"""Installation script
"""Build and install ``geopandas`` with or without Cython or C compiler
Building with Cython must be specified with the "--with-cython" option such as:
$ python setup.py build_ext --inplace --with-cython
Not using Cython by default makes contributing to ``geopandas`` easy,
because Cython and a C compiler are not required for development or usage.
During installation, C extension modules will be automatically built with a
C compiler if possible, but will fail gracefully if there is an error during
compilation. Use of C extensions significantly improves the performance of
``geopandas``, but a pure Python implementation will be used if the
extension modules are unavailable.
"""

import os
import sys
import warnings

try:
from setuptools import setup
except ImportError:
from distutils.core import setup

from distutils.core import Extension
from distutils.command.build_ext import build_ext
from distutils.errors import (CCompilerError, DistutilsExecError,
DistutilsPlatformError)

import versioneer

LONG_DESCRIPTION = """GeoPandas is a project to add support for geographic data to
Expand All @@ -31,8 +51,96 @@
else:
INSTALL_REQUIRES = ['pandas', 'shapely', 'fiona', 'descartes', 'pyproj']

try:
from Cython.Build import cythonize
has_cython = True
except ImportError:
has_cython = False

use_cython = False
if '--without-cython' in sys.argv:
sys.argv.remove('--without-cython')

if '--with-cython' in sys.argv:
use_cython = True
sys.argv.remove('--with-cython')
if use_cython and not has_cython:
raise RuntimeError('ERROR: Cython not found. Exiting.\n '
'Install Cython or don\'t use "--with-cython"')

suffix = '.pyx' if use_cython else '.c'
ext_modules = []
for modname in ['_geoseries']:
ext_modules.append(Extension('geopandas.' + modname,
['geopandas/' + modname + suffix]))
if use_cython:
# Set global Cython options
# http://docs.cython.org/en/latest/src/reference/compilation.html#compiler-directives
from Cython.Compiler.Options import get_directive_defaults
directive_defaults = get_directive_defaults()
# directive_defaults['...'] = True

# Cythonize all the things!
ext_modules = cythonize(ext_modules)

build_exceptions = (CCompilerError, DistutilsExecError, DistutilsPlatformError,
IOError, SystemError)


class build_ext_may_fail(build_ext):
""" Allow compilation of extensions modules to fail, but warn if they do"""

warning_message = """
*********************************************************************
WARNING: %s
could not be compiled. See the output above for details.
Compiled C extension modules are not required for `geopandas`
to run, but they do result in significant speed improvements.
Proceeding to build `geopandas` as a pure Python package.
If you are using Linux, you probably need to install GCC or the
Python development package.
Debian and Ubuntu users should issue the following command:
$ sudo apt-get install build-essential python-dev
RedHat, CentOS, and Fedora users should issue the following command:
$ sudo yum install gcc python-devel
*********************************************************************
"""

def run(self):
try:
build_ext.run(self)
except build_exceptions:
self.warn_failed()

def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except build_exceptions:
self.warn_failed(name=ext.name)

def warn_failed(self, name=None):
if name is None:
name = 'Extension modules'
else:
name = 'The "%s" extension module' % name
exc = sys.exc_info()[1]
sys.stdout.write('%s\n' % str(exc))
warnings.warn(self.warning_message % name)


cmdclass = versioneer.get_cmdclass()
if use_cython:
cmdclass['build_ext'] = build_ext_may_fail

# get all data dirs in the datasets module
data_files = []
data_files = ['*.pyx', '*.pxd']

for item in os.listdir("geopandas/datasets"):
if os.path.isdir(os.path.join("geopandas/datasets/", item)) \
Expand All @@ -51,4 +159,5 @@
'geopandas.datasets'],
package_data={'geopandas': data_files},
install_requires=INSTALL_REQUIRES,
cmdclass=versioneer.get_cmdclass())
ext_modules=ext_modules,
cmdclass=cmdclass)

0 comments on commit 5af2fcc

Please sign in to comment.