Skip to content

Commit

Permalink
#1433 improve release filtering by adding time based option (#1594)
Browse files Browse the repository at this point in the history
* #1433 - add option to sort latest releases by release date

* #1433 - add option to sort latest releases by release date: update documentation

* #1433 - shorter lines

* #1433 - add unit test for time based sorting

* #1433 - add change log entry

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* #1433 - add debug warning if sort_by is not time or version

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* #1433 - remove unnecessary except ValueError

* Update docs/filtering_configuration.md

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Cooper Lees <[email protected]>
  • Loading branch information
3 people authored Nov 12, 2023
1 parent 79e3568 commit a5faa14
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# 6.5.0

## New Features

- Add option to filter releases based on upload time `PR #1594`
- `project_requirements_pinned` with a pinned version (range) disables additional release filter for this package `PR #1601`

# 6.4.0
Expand Down
3 changes: 3 additions & 0 deletions docs/filtering_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,12 @@ enabled =

[latest_release]
keep = 3
sort_by = [version|time]
```

By default, the plugin does not filter out any release. You have to add the `keep` setting.
The default is to sort by `version` number (parsed according to semantic versioning).
As an alternative, `time` can be used to sort releases chronologically by upload time and select the last `keep` ones.

You should be aware that it can break requirements. Prereleases are also kept.

Expand Down
25 changes: 25 additions & 0 deletions src/bandersnatch/tests/plugins/test_latest_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ def test_latest_releases_keep_latest(self) -> None:

assert pkg.releases == {"1.1.3": {}, "2.0.0": {}}

def test_latest_releases_keep_latest_time(self) -> None:
mock_config(self.config_contents + "\nsort_by = time")

mirror = BandersnatchMirror(Path("."), Master(url="https://foo.bar.com"))
pkg = Package("foo", 1)
pkg._metadata = {
"info": {"name": "foo", "version": "2.0.0"},
"releases": {
"1.0.0": [{"upload_time_iso_8601": "2013-10-01T15:24:37.255645Z"}],
"1.1.0": [{"upload_time_iso_8601": "2014-10-01T15:24:37.255645Z"}],
"1.1.1": [{"upload_time_iso_8601": "2015-10-01T15:24:37.255645Z"}],
"1.1.1d": [{"upload_time_iso_8601": "2020-10-01T15:24:37.255645Z"}],
"1.1.2": [{"upload_time_iso_8601": "2016-10-01T15:24:37.255645Z"}],
"1.1.3": [{"upload_time_iso_8601": "2017-10-01T15:24:37.255645Z"}],
"2.0.0": [{"upload_time_iso_8601": "2018-10-01T15:24:37.255645Z"}],
},
}

pkg.filter_all_releases(mirror.filters.filter_release_plugins())

assert pkg.releases == {
"1.1.1d": [{"upload_time_iso_8601": "2020-10-01T15:24:37.255645Z"}],
"2.0.0": [{"upload_time_iso_8601": "2018-10-01T15:24:37.255645Z"}],
}

def test_latest_releases_keep_stable(self) -> None:
mock_config(self.config_contents)

Expand Down
38 changes: 32 additions & 6 deletions src/bandersnatch_filter_plugins/latest_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class LatestReleaseFilter(FilterReleasePlugin):

name = "latest_release"
keep = 0 # by default, keep 'em all
# by default, sort by parsed version string, time (of release) is the other option
sort_by = "version"

def initialize_plugin(self) -> None:
"""
Expand All @@ -32,6 +34,21 @@ def initialize_plugin(self) -> None:
return
if self.keep > 0:
logger.info(f"Initialized latest releases plugin with keep={self.keep}")
try:
sort_by = self.configuration["latest_release"]["sort_by"]
if sort_by in ["time", "version"]:
self.sort_by = sort_by
else:
logger.debug(
"sort_by only allows 'time' and 'version', and not '{}'".format(
sort_by
)
)
logger.info(
f"Initialized latest releases plugin with sort_by={self.sort_by}"
)
except KeyError:
return

def filter(self, metadata: dict) -> bool:
"""
Expand All @@ -45,15 +62,24 @@ def filter(self, metadata: dict) -> bool:
if self.keep == 0 or self.keep > len(releases):
return True

versions_pair: Iterator[tuple[Version, str]] = map(
lambda v: (parse(v), v), releases.keys()
)
# Sort all versions
versions_sorted = sorted(versions_pair, reverse=True)
getter_index = 1
if self.sort_by == "time":
getter_index = 0
versions_sorted = sorted(
releases.items(),
key=lambda x: x[1][0]["upload_time_iso_8601"],
reverse=True,
)
else:
versions_pair: Iterator[tuple[Version, str]] = map(
lambda v: (parse(v), v), releases.keys()
)
# Sort all versions
versions_sorted = sorted(versions_pair, reverse=True)
# Select the first few (larger) items
versions_allowed = versions_sorted[: self.keep]
# Collect string versions back into a list
version_names = list(map(itemgetter(1), versions_allowed))
version_names = list(map(itemgetter(getter_index), versions_allowed))

# Add back latest version if necessary
if info.get("version") not in version_names:
Expand Down

0 comments on commit a5faa14

Please sign in to comment.