PyPI Package Versions

Seeing what wheels exist on PyPI, and what python versions they support.

Note that this doesn’t look at OS support.

pandas, numpy

Out:

/home/kevin/projects/kevbase/sphinx/examples/misc/pypi-versions.py:86: DeprecationWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  s = pd.Series(d, name=version)
/home/kevin/projects/kevbase/sphinx/examples/misc/pypi-versions.py:81: DeprecationWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  s = pd.Series([], name=version)

import requests
import bs4
import pandas as pd
import matplotlib.pyplot as plt
from packaging.version import parse as parse_version


def get_version_history(project):
    url = f'https://pypi.org/project/{project}'
    response = requests.get(url)
    soup = bs4.BeautifulSoup(response.content, parser='lxml')
    hist_div = soup.find(attrs={'class': 'release-timeline'})
    release_divs = hist_div.find_all(attrs={'class': 'release'})
    metadata = []
    for div in release_divs:
        version = div.find(attrs={'class': 'release__version'}).text.strip()
        version = version.replace('pre-release', '').strip()
        date = div.find(attrs={'class': 'release__version-date'}).text.strip()
        metadata.append({
            'version': version,
            'release_date': date,
        })
    return metadata


def get_files(project, release):
    url = f'https://pypi.org/project/{project}/{release}'
    response = requests.get(url)
    soup = bs4.BeautifulSoup(response.content, parser='lxml')
    files_div = soup.find(id='files')
    if files_div is None:
        return []
    file_divs = files_div.find_all('tr')
    # skip header row
    file_divs = file_divs[1:]
    metadata = []
    for div in file_divs:
        splits = div.find('th').text.split()
        file_name = splits[2]
        file_size = splits[3][1:] + ' ' + splits[4][:-1]
        cells = div.find_all('td')
        file_type = cells[0].text.replace('File type', '').strip()
        py_version = cells[1].text.replace('Python version', '').strip()
        # convert "cp37" to "3.7"
        mapping = {
            "cp26": "2.6", "cp27": "2.7", "cp32": "3.2", "cp33": "3.3", "cp34": "3.4",
            "cp35": "3.5", "cp36": "3.6", "cp37": "3.7", "cp38": "3.8", "cp39": "3.9",
            "cp310": "3.10", "cp311": "3.11", "cp312": "3.12",
        }
        py_version = mapping.get(py_version, py_version)

        metadata.append({
            'file_name': file_name,
            'file_size': file_size,
            'file_type': file_type,
            'python_version': py_version,
        })
    return metadata


projects = ['pandas', 'numpy']
fig, axes = plt.subplots(len(projects), 1, figsize=(14, 4*len(projects)))

for ax, project in zip(axes, projects):
    versions = get_version_history(project)

    version_support_info = []
    for version_info in versions:
        version = version_info['version']
        version_files = pd.DataFrame(get_files(project, version))
        if version_files.empty:
            s = pd.Series([], name=version)
            version_support_info.append(s)
            continue
        supported_python_versions = version_files['python_version'].unique()
        d = {ver: True for ver in supported_python_versions if ver != 'None'}
        s = pd.Series(d, name=version)
        version_support_info.append(s)

    support = pd.DataFrame({s.name: s for s in version_support_info})

    df = support.fillna(False).astype(int).iloc[:, ::-1]
    # sort so that 3.10 > 3.9
    df = df.sort_index(key=lambda idx: list(map(parse_version, idx)))

    ax.pcolormesh(df.columns, df.index, df, shading='auto')
    ax.set_ylabel('Python Version')
    ax.set_xlabel('Package Version')
    plt.setp(ax.get_xticklabels(), rotation=90)
    ax.set_title(project)

fig.tight_layout()
fig.show()

Total running time of the script: ( 1 minutes 6.383 seconds)

Gallery generated by Sphinx-Gallery