Skip to content

Commit 6781179

Browse files
authored
Use python3's venv instead of virtualenv (kivy#2152)
* 👽 Migrate to `toml` and apply `isort` to `pythonpackage.py` Because `pytoml` is deprecated See also: https://pypi.org/project/pytoml/ * 🐛 Avoid copying `.tox` folder when extracting metadata from package Because when running local tests for `test_pythonpackages` and we have some virtual envs created with tox the function also copies the hidden tox folder causing error on our tests * 🔥 Use python3's venv instead of `virtualenv` * 💚 Add new dependency to docker `libssl-dev` So this way we will get hostpython's _ssl module build, and we will be able to use hostpython's pip without security errors. * 📝 Update outdated inline comment * 🍎 Don't run p4a inside virtualenv/venv because it seems that hostpython's build scripts gets confused on about locating the `pyconfig.h` file **Note:** This only happens to macOS platform
1 parent 088077f commit 6781179

10 files changed

+34
-129
lines changed

.travis.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ jobs:
2323
# See also: https://github.com/travis-ci/travis-ci/issues/8589
2424
- type -t deactivate && deactivate || true
2525
- export PATH=/opt/python/3.7/bin:$PATH
26-
# Install tox & virtualenv
27-
# Note: venv/virtualenv are both used by tests/test_pythonpackage.py
28-
- pip3.7 install -U virtualenv
26+
# Install tox
2927
- pip3.7 install tox>=2.0
3028
# Install coveralls & dependencies
3129
# Note: pyOpenSSL needed to send the coveralls reports
@@ -54,7 +52,7 @@ jobs:
5452
# installs java 1.8, android's SDK/NDK and p4a
5553
- make -f ci/makefiles/osx.mk
5654
- export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
57-
script: make testapps/armeabi-v7a PYTHON_WITH_VERSION=python3
55+
script: make testapps-no-venv/armeabi-v7a
5856
- <<: *testapps
5957
name: Rebuild updated recipes
6058
script: travis_wait 30 make docker/run/make/rebuild_updated_recipes

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ RUN dpkg --add-architecture i386 \
6666
libncurses5:i386 \
6767
libpangox-1.0-0:i386 \
6868
libpangoxft-1.0-0:i386 \
69+
libssl-dev \
6970
libstdc++6:i386 \
7071
libtool \
7172
openjdk-8-jdk \
@@ -77,7 +78,6 @@ RUN dpkg --add-architecture i386 \
7778
python3-venv \
7879
sudo \
7980
unzip \
80-
virtualenv \
8181
wget \
8282
zip \
8383
zlib1g-dev \

Makefile

+9-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ ANDROID_NDK_HOME ?= $(HOME)/.android/android-ndk
1919
all: virtualenv
2020

2121
$(VIRTUAL_ENV):
22-
virtualenv --python=$(PYTHON_WITH_VERSION) $(VIRTUAL_ENV)
22+
python3 -m venv $(VIRTUAL_ENV)
2323
$(PIP) install Cython==0.28.6
2424
$(PIP) install -e .
2525

@@ -47,6 +47,14 @@ testapps/%: virtualenv
4747
python setup.py apk --sdk-dir $(ANDROID_SDK_HOME) --ndk-dir $(ANDROID_NDK_HOME) \
4848
--arch=$($@_APP_ARCH)
4949

50+
testapps-no-venv/%:
51+
pip3 install Cython==0.28.6
52+
pip3 install -e .
53+
$(eval $@_APP_ARCH := $(shell basename $*))
54+
cd testapps/on_device_unit_tests/ && \
55+
python3 setup.py apk --sdk-dir $(ANDROID_SDK_HOME) --ndk-dir $(ANDROID_NDK_HOME) \
56+
--arch=$($@_APP_ARCH)
57+
5058
clean:
5159
find . -type d -name "__pycache__" -exec rm -r {} +
5260
find . -type d -name "*.egg-info" -exec rm -r {} +

pythonforandroid/build.py

+5-17
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
import subprocess
1414

1515
from pythonforandroid.util import (
16-
current_directory, ensure_dir, get_virtualenv_executable,
17-
BuildInterruptingException
16+
current_directory, ensure_dir,
17+
BuildInterruptingException,
1818
)
1919
from pythonforandroid.logger import (info, warning, info_notify, info_main, shprint)
2020
from pythonforandroid.archs import ArchARM, ArchARMv7_a, ArchAarch_64, Archx86, Archx86_64
@@ -357,13 +357,6 @@ def prepare_build_environment(self,
357357

358358
check_ndk_api(ndk_api, self.android_api)
359359

360-
virtualenv = get_virtualenv_executable()
361-
if virtualenv is None:
362-
raise IOError('Couldn\'t find a virtualenv executable, '
363-
'you must install this to use p4a.')
364-
self.virtualenv = virtualenv
365-
info('Found virtualenv at {}'.format(virtualenv))
366-
367360
# path to some tools
368361
self.ccache = sh.which("ccache")
369362
if not self.ccache:
@@ -765,15 +758,10 @@ def run_pymodules_install(ctx, modules, project_dir=None,
765758
info('Will process project install, if it fails then the '
766759
'project may not be compatible for Android install.')
767760

768-
venv = sh.Command(ctx.virtualenv)
761+
# Use our hostpython to create the virtualenv
762+
host_python = sh.Command(ctx.hostpython)
769763
with current_directory(join(ctx.build_dir)):
770-
shprint(venv,
771-
'--python=python{}'.format(
772-
ctx.python_recipe.major_minor_version_string.
773-
partition(".")[0]
774-
),
775-
'venv'
776-
)
764+
shprint(host_python, '-m', 'venv', 'venv')
777765

778766
# Prepare base environment and upgrade pip:
779767
base_env = copy.copy(os.environ)

pythonforandroid/pythonpackage.py

+11-13
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,23 @@
3333
"""
3434

3535

36-
from io import open # needed for python 2
3736
import functools
3837
import os
39-
from pep517.envbuild import BuildEnvironment
40-
from pep517.wrappers import Pep517HookCaller
41-
import pytoml
4238
import shutil
4339
import subprocess
4440
import sys
4541
import tarfile
4642
import tempfile
4743
import textwrap
4844
import time
49-
try:
50-
from urllib.parse import urlparse
51-
from urllib.parse import unquote as urlunquote
52-
except ImportError: # Python 2...
53-
from urlparse import urlparse
54-
from urlparse import unquote as urlunquote
5545
import zipfile
46+
from io import open # needed for python 2
47+
from urllib.parse import unquote as urlunquote
48+
from urllib.parse import urlparse
49+
50+
import toml
51+
from pep517.envbuild import BuildEnvironment
52+
from pep517.wrappers import Pep517HookCaller
5653

5754

5855
def transform_dep_for_pip(dependency):
@@ -111,7 +108,8 @@ def extract_metainfo_files_from_package(
111108
if is_filesystem_path(package):
112109
shutil.copytree(
113110
parse_as_folder_reference(package),
114-
os.path.join(temp_folder, "package")
111+
os.path.join(temp_folder, "package"),
112+
ignore=shutil.ignore_patterns(".tox")
115113
)
116114
package = os.path.join(temp_folder, "package")
117115

@@ -486,7 +484,7 @@ def _extract_metainfo_files_from_package_unsafe(
486484

487485
# Get build backend and requirements from pyproject.toml:
488486
with open(os.path.join(path, 'pyproject.toml')) as f:
489-
build_sys = pytoml.load(f)['build-system']
487+
build_sys = toml.load(f)['build-system']
490488
backend = build_sys["build-backend"]
491489
build_requires.extend(build_sys["requires"])
492490

@@ -630,7 +628,7 @@ def _extract_info_from_package(dependency,
630628
) and include_build_requirements:
631629
# Read build system from pyproject.toml file: (PEP518)
632630
with open(os.path.join(output_folder, 'pyproject.toml')) as f:
633-
build_sys = pytoml.load(f)['build-system']
631+
build_sys = toml.load(f)['build-system']
634632
if "requires" in build_sys:
635633
requirements += build_sys["requires"]
636634
elif include_build_requirements:

pythonforandroid/util.py

-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import contextlib
22
from os.path import exists, join
33
from os import getcwd, chdir, makedirs, walk, uname
4-
import sh
54
import shutil
65
from fnmatch import fnmatch
76
from tempfile import mkdtemp
@@ -55,17 +54,6 @@ def ensure_dir(filename):
5554
makedirs(filename)
5655

5756

58-
def get_virtualenv_executable():
59-
virtualenv = None
60-
if virtualenv is None:
61-
virtualenv = sh.which('virtualenv2')
62-
if virtualenv is None:
63-
virtualenv = sh.which('virtualenv-2.7')
64-
if virtualenv is None:
65-
virtualenv = sh.which('virtualenv')
66-
return virtualenv
67-
68-
6957
def walk_valid_filens(base_dir, invalid_dir_names, invalid_file_patterns):
7058
"""Recursively walks all the files and directories in ``dirn``,
7159
ignoring directories that match any pattern in ``invalid_dirns``

setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
install_reqs = [
2424
'appdirs', 'colorama>=0.3.3', 'jinja2', 'six',
2525
'enum34; python_version<"3.4"', 'sh>=1.10; sys_platform!="nt"',
26-
'pep517<0.7.0"', 'pytoml', 'virtualenv<20'
26+
'pep517<0.7.0"', 'toml',
2727
]
28-
# (pep517, pytoml and virtualenv are used by pythonpackage.py)
28+
# (pep517 and toml are used by pythonpackage.py)
2929

3030
# By specifying every file manually, package_data will be able to
3131
# include them in binary distributions. Note that we have to add

tests/test_pythonpackage_basic.py

+4-38
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"""
77

88
import os
9-
import pytest
109
import shutil
1110
import sys
1211
import subprocess
@@ -279,43 +278,6 @@ def test_systemwide_python(self):
279278
else:
280279
raise
281280

282-
def test_virtualenv(self):
283-
""" Verifies that _get_system_python_executable() works correctly
284-
if called with a python binary as found inside a virtualenv.
285-
"""
286-
287-
# Get system-wide python bin seen from here first:
288-
pybin = _get_system_python_executable()
289-
# (this call was not a test, we really just need the path here)
290-
291-
test_dir = tempfile.mkdtemp()
292-
try:
293-
# Check that in a virtualenv, the system-wide python is returned:
294-
subprocess.check_output([
295-
pybin, "-m", "virtualenv",
296-
"--python=" + str(sys.executable),
297-
"--",
298-
os.path.join(test_dir, "virtualenv")
299-
])
300-
subprocess.check_output([
301-
os.path.join(test_dir, "virtualenv", "bin", "pip"),
302-
"install", "-U", "pip"
303-
])
304-
subprocess.check_output([
305-
os.path.join(test_dir, "virtualenv", "bin", "pip"),
306-
"install", "-U", "pep517<0.7.0"
307-
])
308-
sys_python_path = self.run__get_system_python_executable(
309-
os.path.join(test_dir, "virtualenv", "bin", "python")
310-
)
311-
assert os.path.normpath(sys_python_path).startswith(
312-
os.path.normpath(pybin)
313-
)
314-
finally:
315-
shutil.rmtree(test_dir)
316-
317-
@pytest.mark.skipif(int(sys.version.partition(".")[0]) < 3,
318-
reason="venv is python 3 only")
319281
def test_venv(self):
320282
""" Verifies that _get_system_python_executable() works correctly
321283
in a 'venv' (Python 3 only feature).
@@ -340,6 +302,10 @@ def test_venv(self):
340302
os.path.join(test_dir, "venv", "bin", "pip"),
341303
"install", "-U", "pep517<0.7.0"
342304
])
305+
subprocess.check_output([
306+
os.path.join(test_dir, "venv", "bin", "pip"),
307+
"install", "-U", "toml"
308+
])
343309
sys_python_path = self.run__get_system_python_executable(
344310
os.path.join(test_dir, "venv", "bin", "python")
345311
)

tests/test_util.py

-40
Original file line numberDiff line numberDiff line change
@@ -70,46 +70,6 @@ def test_current_directory_exception(self):
7070
):
7171
pass
7272

73-
@mock.patch("pythonforandroid.util.sh.which")
74-
def test_get_virtualenv_executable(self, mock_sh_which):
75-
"""
76-
Test method :meth:`~pythonforandroid.util.get_virtualenv_executable`.
77-
In here we test:
78-
79-
- that all calls to `sh.which` are performed, so we expect the
80-
first two `sh.which` calls should be None and the last one should
81-
return the expected virtualenv (the python3 one)
82-
- that we don't have virtualenv installed, so all calls to
83-
`sh.which` should return None
84-
"""
85-
expected_venv = os.path.join(
86-
os.path.expanduser("~"), ".local/bin/virtualenv"
87-
)
88-
mock_sh_which.side_effect = [None, None, expected_venv]
89-
self.assertEqual(util.get_virtualenv_executable(), expected_venv)
90-
mock_sh_which.assert_has_calls(
91-
[
92-
mock.call("virtualenv2"),
93-
mock.call("virtualenv-2.7"),
94-
mock.call("virtualenv"),
95-
]
96-
)
97-
self.assertEqual(mock_sh_which.call_count, 3)
98-
mock_sh_which.reset_mock()
99-
100-
# Now test that we don't have virtualenv installed, so all calls to
101-
# `sh.which` should return None
102-
mock_sh_which.side_effect = [None, None, None]
103-
self.assertIsNone(util.get_virtualenv_executable())
104-
self.assertEqual(mock_sh_which.call_count, 3)
105-
mock_sh_which.assert_has_calls(
106-
[
107-
mock.call("virtualenv2"),
108-
mock.call("virtualenv-2.7"),
109-
mock.call("virtualenv"),
110-
]
111-
)
112-
11373
@mock.patch("pythonforandroid.util.walk")
11474
def test_walk_valid_filens(self, mock_walk):
11575
"""

tox.ini

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ basepython = python3
55
[testenv]
66
deps =
77
pytest
8-
virtualenv
98
py3: coveralls
109
backports.tempfile
1110
# posargs will be replaced by the tox args, so you can override pytest

0 commit comments

Comments
 (0)