diff --git a/CHANGES.txt b/CHANGES.txt index 9394b0560cd..e0c67f94629 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ + +* Add warning when python version classifiers do not match the running version. + **8.1.1 (2016-03-17)** * Fix regression with non-ascii requirement files on Python 2 and add support diff --git a/pip/commands/show.py b/pip/commands/show.py index 52a673ac9ae..28c2b690555 100644 --- a/pip/commands/show.py +++ b/pip/commands/show.py @@ -6,6 +6,7 @@ from pip.basecommand import Command from pip.status_codes import SUCCESS, ERROR +from pip.utils.packaging import get_classifiers from pip._vendor import pkg_resources @@ -102,15 +103,7 @@ def search_packages_info(query): 'home-page', 'author', 'author-email', 'license'): package[key] = pkg_info_dict.get(key) - # It looks like FeedParser can not deal with repeated headers - classifiers = [] - for line in metadata.splitlines(): - if not line: - break - # Classifier: License :: OSI Approved :: MIT License - if line.startswith('Classifier: '): - classifiers.append(line[len('Classifier: '):]) - package['classifiers'] = classifiers + package['classifiers'] = get_classifiers(metadata) if file_list: package['files'] = sorted(file_list) diff --git a/pip/req/req_set.py b/pip/req/req_set.py index e7a8d8789f9..416f199671c 100644 --- a/pip/req/req_set.py +++ b/pip/req/req_set.py @@ -20,6 +20,7 @@ display_path, dist_in_usersite, ensure_dir, normalize_path) from pip.utils.hashes import MissingHashes from pip.utils.logging import indent_log +from pip.utils.packaging import check_python_classifiers from pip.vcs import vcs @@ -619,6 +620,7 @@ def _prepare_file(self, # # parse dependencies # # # ###################### # dist = abstract_dist.dist(finder) + check_python_classifiers(dist) more_reqs = [] def add_req(subreq): diff --git a/pip/utils/packaging.py b/pip/utils/packaging.py new file mode 100644 index 00000000000..d7ece11c236 --- /dev/null +++ b/pip/utils/packaging.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import + +import logging +import sys + +from pip._vendor import pkg_resources + + +logger = logging.getLogger(__name__) + + +def get_metadata(dist): + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata('METADATA')): + return dist.get_metadata('METADATA') + elif dist.has_metadata('PKG-INFO'): + return dist.get_metadata('PKG-INFO') + + +def get_classifiers(metadata): + # It looks like FeedParser can not deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if not line: + break + # Classifier: License :: OSI Approved :: MIT License + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + return classifiers + + +def check_python_classifiers(dist): + classifiers = get_classifiers(get_metadata(dist)) + # Catch: + # Programming Language :: Python :: 3.6 + # but not: + # Programming Language :: Python :: Implementation :: PyPy + supported_versions = [ + # 34 == len('Programming Language :: Python :: ') + classifier[34:] for classifier in classifiers + if ( + classifier.startswith('Programming Language :: Python :: ') and + classifier[34:35].isdigit() + ) + ] + if not supported_versions: + # The package provides no information + return + major_versions, minor_versions = [], [] + for version in supported_versions: + if '.' in version: + minor_versions.append(tuple(map(int, version.split('.')))) + else: + major_versions.append(int(version)) + if major_versions and sys.version_info[0] not in major_versions: + logger.warning( + "%s is advertised as supporting %s but not Python %s", + dist.project_name, + ", ".join(["Python %s" % (version,) + for version in major_versions]), + sys.version_info[0]) + if (minor_versions and sys.version_info[0:2] not in minor_versions and + sys.version_info[0:2] < max(minor_versions)): + logger.warning( + "%s is advertised as supporting %s but not Python %s", + dist.project_name, + ", ".join(["Python %s" % ('.'.join(map(str, version)),) + for version in minor_versions]), + "Python %s" % ('.'.join(map(str, sys.version_info[0:2])),))