Source code

Revision control

Copy as Markdown

Other Tools

# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
import itertools
import json
import os.path
import xmlrpc.client
import invoke
import pkg_resources
import progress.bar
from packaging.version import Version
from .paths import CACHE
def _parse_version(value):
try:
return Version(value)
except ValueError:
return None
@invoke.task
def pep440(cached=False):
cache_path = os.path.join(CACHE, "pep440.json")
# If we were given --cached, then we want to attempt to use cached data if
# possible
if cached:
try:
with open(cache_path) as fp:
data = json.load(fp)
except Exception:
data = None
else:
data = None
# If we don't have data, then let's go fetch it from PyPI
if data is None:
bar = progress.bar.ShadyBar("Fetching Versions")
client = xmlrpc.client.Server("https://pypi.python.org/pypi")
data = {
project: client.package_releases(project, True)
for project in bar.iter(client.list_packages())
}
os.makedirs(os.path.dirname(cache_path), exist_ok=True)
with open(cache_path, "w") as fp:
json.dump(data, fp)
# Get a list of all of the version numbers on PyPI
all_versions = list(itertools.chain.from_iterable(data.values()))
# Determine the total number of versions which are compatible with the
# current routine
parsed_versions = [
_parse_version(v) for v in all_versions if _parse_version(v) is not None
]
# Determine a list of projects that sort exactly the same between
# pkg_resources and PEP 440
compatible_sorting = [
project
for project, versions in data.items()
if (
sorted(versions, key=pkg_resources.parse_version)
== sorted((x for x in versions if _parse_version(x)), key=Version)
)
]
# Determine a list of projects that sort exactly the same between
# pkg_resources and PEP 440 when invalid versions are filtered out
filtered_compatible_sorting = [
project
for project, versions in (
(p, [v for v in vs if _parse_version(v) is not None])
for p, vs in data.items()
)
if (
sorted(versions, key=pkg_resources.parse_version)
== sorted(versions, key=Version)
)
]
# Determine a list of projects which do not have any versions that are
# valid with PEP 440 and which have any versions registered
only_invalid_versions = [
project
for project, versions in data.items()
if (versions and not [v for v in versions if _parse_version(v) is not None])
]
# Determine a list of projects which have matching latest versions between
# pkg_resources and PEP 440
differing_latest_versions = [
project
for project, versions in data.items()
if (
sorted(versions, key=pkg_resources.parse_version)[-1:]
!= sorted((x for x in versions if _parse_version(x)), key=Version)[-1:]
)
]
# Print out our findings
print(
"Total Version Compatibility: {}/{} ({:.2%})".format(
len(parsed_versions),
len(all_versions),
len(parsed_versions) / len(all_versions),
)
)
print(
"Total Sorting Compatibility (Unfiltered): {}/{} ({:.2%})".format(
len(compatible_sorting), len(data), len(compatible_sorting) / len(data)
)
)
print(
"Total Sorting Compatibility (Filtered): {}/{} ({:.2%})".format(
len(filtered_compatible_sorting),
len(data),
len(filtered_compatible_sorting) / len(data),
)
)
print(
"Projects with No Compatible Versions: {}/{} ({:.2%})".format(
len(only_invalid_versions),
len(data),
len(only_invalid_versions) / len(data),
)
)
print(
"Projects with Differing Latest Version: {}/{} ({:.2%})".format(
len(differing_latest_versions),
len(data),
len(differing_latest_versions) / len(data),
)
)