diff --git a/plug/pip_resolve.py b/plug/pip_resolve.py index ec8b0da0..fdfc725c 100644 --- a/plug/pip_resolve.py +++ b/plug/pip_resolve.py @@ -7,6 +7,7 @@ import utils import requirements import pipfile +from operator import le, lt, gt, ge, eq, ne try: import pkg_resources @@ -99,6 +100,48 @@ def create_package_as_root(package, dir_as_root): sys_platform_re = re.compile('sys_platform\s*==\s*[\'"](.+)[\'"]') sys_platform = sys.platform.lower() +def satisfies_python_requirement(parsed_operator, parsed_python_version ): + # TODO: use python semver library to compare versions + operator_func = { + ">": gt, + "==": eq, + "<": lt, + "<=": le, + ">=": ge, + '!=': ne, + }[parsed_operator] + system_python = str(sys.version_info[0]) + '.' + str(sys.version_info[1]) + satisfies = operator_func(float(system_python), float(parsed_python_version)) + return satisfies + +def matches_python_version(requirement): + """Filter out requirements that should not be installed + in this Python version. + See: https://www.python.org/dev/peps/pep-0508/#environment-markers + """ + python_version_re = re.compile('python_version\s*(==|<=|=>|>|<)\s*\'(.+)\'') + if isinstance(requirement, pipfile.PipfileRequirement): + markers_text = requirement.markers + else: + markers_text = requirement.line + + if markers_text is None: + return True + + if 'python_version' not in markers_text: + return True + + match = python_version_re.findall(markers_text) + if len(match) > 0: + parsed_operator = match[0][0] + parsed_python_version = match[0][1] + + if parsed_operator and parsed_python_version: + return satisfies_python_requirement(parsed_operator, parsed_python_version) + else: + return True + + def matches_environment(requirement): """Filter out requirements that should not be installed in this environment. Only sys_platform is inspected right now. @@ -137,6 +180,7 @@ def get_requirements_list(requirements_file_path, dev_deps=False): req_list = filter(matches_environment, req_list) req_list = filter(is_testable, req_list) + req_list = filter(matches_python_version, req_list) required = [req.name.replace('_', '-') for req in req_list] return required diff --git a/test/inspect.test.js b/test/inspect.test.js index 82a1808c..2bc6fb72 100644 --- a/test/inspect.test.js +++ b/test/inspect.test.js @@ -370,6 +370,27 @@ test('package installed conditionally based on python version', function (t) { }); }); +test('Pipfile package found conditionally based on python version', function (t) { + return Promise.resolve().then(function () { + chdirWorkspaces('pipfile-markers'); + var venvCreated = testUtils.ensureVirtualenv('pipfile-markers'); + t.teardown(testUtils.activateVirtualenv('pipfile-markers')); + if (venvCreated) { + testUtils.pipInstall(); + } + }) + .then(function () { + return plugin.inspect('.', 'Pipfile'); + }) + .then(function (result) { + var pkg = result.package; + t.notOk(pkg.dependencies.black, 'black dep ignored'); + t.notOk(pkg.dependencies.stdeb, 'stdeb dep ignored'); + + t.end(); + }); +}); + test('package depends on platform', function (t) { return Promise.resolve().then(function () { chdirWorkspaces('pip-app-deps-conditional'); @@ -446,7 +467,6 @@ test('deps with options', function (t) { t.equal(pkg.version, '0.0.0', 'version'); t.end(); }); - t.test('package dependencies', function (t) { t.match(pkg.dependencies.markupsafe, { name: 'markupsafe', diff --git a/test/workspaces/pipfile-markers/Pipfile b/test/workspaces/pipfile-markers/Pipfile new file mode 100644 index 00000000..8df5a7c9 --- /dev/null +++ b/test/workspaces/pipfile-markers/Pipfile @@ -0,0 +1,26 @@ +[dev-packages] +"flake8" = ">=3.3.0,<4" +sphinx = "<=1.5.5" +twine = "*" +sphinx-click = "*" +click = "*" +pytest_pypi = {path = "./tests/pytest-pypi", editable = true} +stdeb = {version="*", markers="sys_platform == 'linux' ; python_version != '3.4"} +black = {version="*", markers="python_version > '3.6'"} +pytz = "*" +towncrier = {git = "https://github.com/hawkowl/towncrier.git", editable = true, ref = "master"} +parver = "*" +invoke = "*" +jedi = "*" +isort = "*" +rope = "*" +passa = {editable = true, git = "https://github.com/sarugaku/passa.git"} +bs4 = "*" + +[packages] + +[scripts] +tests = "bash ./run-tests.sh" + +[pipenv] +allow_prereleases = true