Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define parse_version for broad compatibility with packaging #32

Merged
merged 6 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def get_version(filename):
requirements = [
'click >= 6.7',
'jinja2',
'packaging < 22.0',
'packaging',
'pyparsing >= 2.0.2',
'setuptools',
'sphinx',
Expand Down
3 changes: 2 additions & 1 deletion src/docs_versions_menu/folder_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from collections import OrderedDict
from functools import partial

from packaging.version import parse as parse_version
from pyparsing import (
Forward,
Group,
Expand All @@ -17,6 +16,8 @@
oneOf,
)

from .parse_version import parse_version


def _parse_folder_spec(spec, groups, sort_key):
"""Parse the folder specification into a nested list.
Expand Down
6 changes: 3 additions & 3 deletions src/docs_versions_menu/groups.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Classification of folders into groups according to :pep:`440`."""
from packaging.version import LegacyVersion
from packaging.version import parse as parse_version

from .parse_version import NonVersionFolderName, parse_version


def get_groups(folders, default_branches=None):
Expand Down Expand Up @@ -51,7 +51,7 @@ def get_groups(folders, default_branches=None):
version = parse_version(folder)
if folder in default_branches:
groups['default-branch'].add(folder)
if isinstance(version, LegacyVersion):
if isinstance(version, NonVersionFolderName):
groups['branches'].add(folder)
else:
groups['releases'].add(folder)
Expand Down
43 changes: 43 additions & 0 deletions src/docs_versions_menu/parse_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Parse a version-based foldeer name.

The ``parse_version`` function provided here is functionally equivalent to
``packaging.version.parse`` in ``packaging < 22.0``.
"""
import packaging.version


class NonVersionFolderName(packaging.version._BaseVersion):
"""A "version" that is just an arbitrary folder name"""

def __init__(self, name):
name = str(name)
self.name = name
self._key = (-1, (f'*{name}', '*final'))
# The _key mimics the _key of a LegacyVersion in packaging < 22.0.
# The "-1" is a hard-coded "epoch" here. A PEP 440 version can only
# have a epoch greater than or equal to 0. This will sort
# NonVersionFolderName before any PEP 440 version.
#
# Sorting behavior is inherited from _BaseVersion

def __str__(self):
return self.name

def __repr__(self):
return f"<NonVersionFolderName('{self}')>"


class VersionFolderName(packaging.version.Version):
"""A PEP440-compatible version name."""

def __repr__(self):
return f"<VersionFolderName('{self}')>"


def parse_version(name):
"""Parse `name` string into either a `VersionFolderName` or a
`NonVersionFolderName` object."""
try:
return VersionFolderName(name)
except packaging.version.InvalidVersion:
return NonVersionFolderName(name)
4 changes: 0 additions & 4 deletions src/docs_versions_menu/version_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
from pathlib import Path

import jinja2
from packaging.version import parse as parse_version

from .folder_spec import resolve_folder_spec
from .groups import get_groups


def get_version_data(
*,
sort_key=None,
suffix_latest,
default_branch_spec,
versions_spec,
Expand All @@ -23,8 +21,6 @@ def get_version_data(
):
"""Get the versions data, to be serialized to json."""
logger = logging.getLogger(__name__)
if sort_key is None:
sort_key = parse_version

folders = sorted(
[
Expand Down
75 changes: 75 additions & 0 deletions tests/test_parse_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Test the parse_version function"""
from docs_versions_menu.parse_version import parse_version


def test_parse_version():
"""Test that :func:`parse_version` behaves like ``packaging.version.parse`
in ``packaging < 22.0``.
"""
versions = [
"1.0",
"0.8.3",
"v0.1.0",
"v0.2.1",
"v0.2.1+dev",
"v0.2.1-dev",
"v0.2.1-rc1",
"v0.2.1-rc2",
"main",
"master",
"fix-bug",
]
for version in versions:
assert parse_version(version) is not None # should not error
sorted_versions = sorted(versions, key=parse_version)
assert sorted_versions == [
'fix-bug',
'main',
'master',
'v0.1.0',
'v0.2.1-dev',
'v0.2.1-rc1',
'v0.2.1-rc2',
'v0.2.1',
'v0.2.1+dev',
'0.8.3',
'1.0',
]


def test_non_version_folder_name():
"""Test the behavior of NonVersionFolderName inherited from _BaseVersion"""
v1 = parse_version("branch-a")
v1_copy = parse_version("branch-a")
v2 = parse_version("branch_b")
v3 = parse_version("0.0.1-dev")

assert repr(v1) == "<NonVersionFolderName('branch-a')>"

assert repr(v3) == "<VersionFolderName('0.0.1.dev0')>"
# Note the normalization!

# __lt__
assert v1 < v2
assert v1 < v3
assert not (v3 < v1)
# __le__
assert v1 <= v2
assert v1 <= v1_copy
assert not (v3 <= v1)
# __eq__
assert v1 == v1_copy
assert not (v1 == v3)
assert not (v3 == v1)
# __ge__
assert v2 >= v1
assert v3 >= v1
assert v1_copy >= v1
# __gt__
assert v2 > v1
assert v3 > v1
assert not (v1 > v3)
# __ne__
assert v1 != v2
assert v1 != v3
assert v3 != v1
4 changes: 4 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ envdir =
py38: {toxworkdir}/py38
py37: {toxworkdir}/py37
deps =
py37: packaging<22.0
py38: packaging>=22.0
py39: packaging>=22.0
py310: packaging>=22.0
usedevelop = true
extras=
dev
Expand Down