Skip to content

Commit f91d080

Browse files
authored
✨ Add pandas recipe (kivy#2100)
* ✨ 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.
1 parent 4d9652a commit f91d080

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from os.path import join
2+
3+
from pythonforandroid.recipe import CppCompiledComponentsPythonRecipe
4+
5+
6+
class PandasRecipe(CppCompiledComponentsPythonRecipe):
7+
version = '1.0.3'
8+
url = 'https://github.com/pandas-dev/pandas/releases/download/v{version}/pandas-{version}.tar.gz' # noqa
9+
10+
depends = ['cython', 'numpy', 'pytz', 'libbz2', 'liblzma']
11+
conflicts = ['python2']
12+
13+
python_depends = ['python-dateutil']
14+
patches = ['fix_numpy_includes.patch']
15+
16+
call_hostpython_via_targetpython = False
17+
18+
def get_recipe_env(self, arch):
19+
env = super(PandasRecipe, self).get_recipe_env(arch)
20+
# we need the includes from our installed numpy at site packages
21+
# because we need some includes generated at numpy's compile time
22+
env['NUMPY_INCLUDES'] = join(
23+
self.ctx.get_python_install_dir(), "numpy/core/include",
24+
)
25+
26+
# this flag below is to fix a runtime error:
27+
# ImportError: dlopen failed: cannot locate symbol
28+
# "_ZTVSt12length_error" referenced by
29+
# "/data/data/org.test.matplotlib_testapp/files/app/_python_bundle
30+
# /site-packages/pandas/_libs/window/aggregations.so"...
31+
env['LDFLAGS'] += ' -landroid'
32+
return env
33+
34+
35+
recipe = PandasRecipe()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--- pandas-1.0.1/setup.py.orig 2020-02-05 17:15:24.000000000 +0100
2+
+++ pandas-1.0.1/setup.py 2020-03-15 13:47:57.612237225 +0100
3+
@@ -37,11 +37,12 @@ min_cython_ver = "0.29.13" # note: sync
4+
5+
setuptools_kwargs = {
6+
"install_requires": [
7+
- "python-dateutil >= 2.6.1",
8+
- "pytz >= 2017.2",
9+
- f"numpy >= {min_numpy_ver}",
10+
+ # dependencies managed via p4a's recipe
11+
+ # "python-dateutil >= 2.6.1",
12+
+ # "pytz >= 2017.2",
13+
+ # f"numpy >= {min_numpy_ver}",
14+
],
15+
- "setup_requires": [f"numpy >= {min_numpy_ver}"],
16+
+ "setup_requires": [],
17+
"zip_safe": False,
18+
}
19+
20+
@@ -514,7 +515,10 @@ def maybe_cythonize(extensions, *args, *
21+
)
22+
raise RuntimeError("Cannot cythonize without Cython installed.")
23+
24+
- numpy_incl = pkg_resources.resource_filename("numpy", "core/include")
25+
+ if 'NUMPY_INCLUDES' in os.environ:
26+
+ numpy_incl = os.environ['NUMPY_INCLUDES']
27+
+ else:
28+
+ numpy_incl = pkg_resources.resource_filename("numpy", "core/include")
29+
# TODO: Is this really necessary here?
30+
for ext in extensions:
31+
if hasattr(ext, "include_dirs") and numpy_incl not in ext.include_dirs:

tests/recipes/test_pandas.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import unittest
2+
3+
from os.path import join
4+
from unittest import mock
5+
6+
from tests.recipes.recipe_lib_test import RecipeCtx
7+
8+
9+
class TestPandasRecipe(RecipeCtx, unittest.TestCase):
10+
"""
11+
TestCase for recipe :mod:`~pythonforandroid.recipes.pandas`
12+
"""
13+
recipe_name = "pandas"
14+
15+
@mock.patch("pythonforandroid.recipe.Recipe.check_recipe_choices")
16+
@mock.patch("pythonforandroid.build.ensure_dir")
17+
@mock.patch("pythonforandroid.archs.glob")
18+
@mock.patch("pythonforandroid.archs.find_executable")
19+
def test_get_recipe_env(
20+
self,
21+
mock_find_executable,
22+
mock_glob,
23+
mock_ensure_dir,
24+
mock_check_recipe_choices,
25+
):
26+
"""
27+
Test that method
28+
:meth:`~pythonforandroid.recipes.pandas.PandasRecipe.get_recipe_env`
29+
returns the expected flags
30+
"""
31+
32+
mock_find_executable.return_value = (
33+
"/opt/android/android-ndk/toolchains/"
34+
"llvm/prebuilt/linux-x86_64/bin/clang"
35+
)
36+
mock_glob.return_value = ["llvm"]
37+
mock_check_recipe_choices.return_value = sorted(
38+
self.ctx.recipe_build_order
39+
)
40+
numpy_includes = join(
41+
self.ctx.get_python_install_dir(), "numpy/core/include",
42+
)
43+
env = self.recipe.get_recipe_env(self.arch)
44+
self.assertIn(numpy_includes, env["NUMPY_INCLUDES"])
45+
self.assertIn(" -landroid", env["LDFLAGS"])
46+
47+
# make sure that the mocked methods are actually called
48+
mock_glob.assert_called()
49+
mock_ensure_dir.assert_called()
50+
mock_find_executable.assert_called()
51+
mock_check_recipe_choices.assert_called()

0 commit comments

Comments
 (0)