Skip to content

Commit

Permalink
✨ Add pandas recipe (#2100)
Browse files Browse the repository at this point in the history
* ✨ Add `pandas` recipe

* ✅ Add tests for `pandas` recipe

* ⬆️ Update `pandas` to latest version `v1.0.3`

* 💚 Remove `pandas`'s dependencies from `setup.py`

Since we manage `pandas`'s dependencies from the recipe,
we remove them from `setup.py` because it causes an error when we run
our `CI` tests.
  • Loading branch information
opacam authored Mar 25, 2020
1 parent 4d9652a commit f91d080
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
35 changes: 35 additions & 0 deletions pythonforandroid/recipes/pandas/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from os.path import join

from pythonforandroid.recipe import CppCompiledComponentsPythonRecipe


class PandasRecipe(CppCompiledComponentsPythonRecipe):
version = '1.0.3'
url = 'https://github.com/pandas-dev/pandas/releases/download/v{version}/pandas-{version}.tar.gz' # noqa

depends = ['cython', 'numpy', 'pytz', 'libbz2', 'liblzma']
conflicts = ['python2']

python_depends = ['python-dateutil']
patches = ['fix_numpy_includes.patch']

call_hostpython_via_targetpython = False

def get_recipe_env(self, arch):
env = super(PandasRecipe, self).get_recipe_env(arch)
# we need the includes from our installed numpy at site packages
# because we need some includes generated at numpy's compile time
env['NUMPY_INCLUDES'] = join(
self.ctx.get_python_install_dir(), "numpy/core/include",
)

# this flag below is to fix a runtime error:
# ImportError: dlopen failed: cannot locate symbol
# "_ZTVSt12length_error" referenced by
# "/data/data/org.test.matplotlib_testapp/files/app/_python_bundle
# /site-packages/pandas/_libs/window/aggregations.so"...
env['LDFLAGS'] += ' -landroid'
return env


recipe = PandasRecipe()
31 changes: 31 additions & 0 deletions pythonforandroid/recipes/pandas/fix_numpy_includes.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--- pandas-1.0.1/setup.py.orig 2020-02-05 17:15:24.000000000 +0100
+++ pandas-1.0.1/setup.py 2020-03-15 13:47:57.612237225 +0100
@@ -37,11 +37,12 @@ min_cython_ver = "0.29.13" # note: sync

setuptools_kwargs = {
"install_requires": [
- "python-dateutil >= 2.6.1",
- "pytz >= 2017.2",
- f"numpy >= {min_numpy_ver}",
+ # dependencies managed via p4a's recipe
+ # "python-dateutil >= 2.6.1",
+ # "pytz >= 2017.2",
+ # f"numpy >= {min_numpy_ver}",
],
- "setup_requires": [f"numpy >= {min_numpy_ver}"],
+ "setup_requires": [],
"zip_safe": False,
}

@@ -514,7 +515,10 @@ def maybe_cythonize(extensions, *args, *
)
raise RuntimeError("Cannot cythonize without Cython installed.")

- numpy_incl = pkg_resources.resource_filename("numpy", "core/include")
+ if 'NUMPY_INCLUDES' in os.environ:
+ numpy_incl = os.environ['NUMPY_INCLUDES']
+ else:
+ numpy_incl = pkg_resources.resource_filename("numpy", "core/include")
# TODO: Is this really necessary here?
for ext in extensions:
if hasattr(ext, "include_dirs") and numpy_incl not in ext.include_dirs:
51 changes: 51 additions & 0 deletions tests/recipes/test_pandas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import unittest

from os.path import join
from unittest import mock

from tests.recipes.recipe_lib_test import RecipeCtx


class TestPandasRecipe(RecipeCtx, unittest.TestCase):
"""
TestCase for recipe :mod:`~pythonforandroid.recipes.pandas`
"""
recipe_name = "pandas"

@mock.patch("pythonforandroid.recipe.Recipe.check_recipe_choices")
@mock.patch("pythonforandroid.build.ensure_dir")
@mock.patch("pythonforandroid.archs.glob")
@mock.patch("pythonforandroid.archs.find_executable")
def test_get_recipe_env(
self,
mock_find_executable,
mock_glob,
mock_ensure_dir,
mock_check_recipe_choices,
):
"""
Test that method
:meth:`~pythonforandroid.recipes.pandas.PandasRecipe.get_recipe_env`
returns the expected flags
"""

mock_find_executable.return_value = (
"/opt/android/android-ndk/toolchains/"
"llvm/prebuilt/linux-x86_64/bin/clang"
)
mock_glob.return_value = ["llvm"]
mock_check_recipe_choices.return_value = sorted(
self.ctx.recipe_build_order
)
numpy_includes = join(
self.ctx.get_python_install_dir(), "numpy/core/include",
)
env = self.recipe.get_recipe_env(self.arch)
self.assertIn(numpy_includes, env["NUMPY_INCLUDES"])
self.assertIn(" -landroid", env["LDFLAGS"])

# make sure that the mocked methods are actually called
mock_glob.assert_called()
mock_ensure_dir.assert_called()
mock_find_executable.assert_called()
mock_check_recipe_choices.assert_called()

0 comments on commit f91d080

Please sign in to comment.