Skip to content

Commit

Permalink
Merge pull request #25 from arcivanov/issue_21
Browse files Browse the repository at this point in the history
Switch to PEP 691 for version and release information
  • Loading branch information
arcivanov authored Feb 27, 2024
2 parents dfac33a + 0485f88 commit e8ba878
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 22 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/pypi-cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ jobs:
os:
- ubuntu-latest
python-version:
- '3.11.0-rc.1'
- '3.12'
- '3.11'
- '3.10'
- '3.9'
- '3.8'
- '3.7'
- '3.6'
env:
DEPLOY_PYTHONS: "3.10"
DEPLOY_PYTHONS: "3.12"
DEPLOY_OSES: "Linux"
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![PyPI Cleanup Version](https://img.shields.io/pypi/v/pypi-cleanup?logo=pypi)](https://pypi.org/project/pypi-cleanup/)
[![PyPI Cleanup Python Versions](https://img.shields.io/pypi/pyversions/pypi-cleanup?logo=pypi)](https://pypi.org/project/pypi-cleanup/)
[![Build Status](https://img.shields.io/github/workflow/status/arcivanov/pypi-cleanup/pypi-cleanup/master)](https://github.com/arcivanov/pypi-cleanup/actions/workflows/pypi-cleanup.yml)
[![Build Status](https://img.shields.io/github/actions/workflow/status/arcivanov/pypi-cleanup/pypi-cleanup.yml?branch=master)](https://github.com/arcivanov/pypi-cleanup/actions/workflows/pypi-cleanup.yml)
[![PyPI Cleanup Downloads Per Day](https://img.shields.io/pypi/dd/pypi-cleanup?logo=pypi)](https://pypi.org/project/pypi-cleanup/)
[![PyPI Cleanup Downloads Per Week](https://img.shields.io/pypi/dw/pypi-cleanup?logo=pypi)](https://pypi.org/project/pypi-cleanup/)
[![PyPI Cleanup Downloads Per Month](https://img.shields.io/pypi/dm/pypi-cleanup?logo=pypi)](https://pypi.org/project/pypi-cleanup/)
Expand Down
5 changes: 3 additions & 2 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"Documentation": "https://github.com/arcivanov/pypi-cleanup"
}

requires_python = ">=3.6"
requires_python = ">=3.7"

default_task = ["analyze", "publish"]

Expand Down Expand Up @@ -67,11 +67,12 @@ def set_properties(project):
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Operating System :: MacOS :: MacOS X",
"Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows",
Expand Down
44 changes: 28 additions & 16 deletions src/main/python/pypi_cleanup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def __init__(self, url, username, package, do_it, patterns, verbose, days, **_):
self.package = package
self.patterns = patterns or DEFAULT_PATTERNS
self.verbose = verbose
self.date = datetime.datetime.now() - datetime.timedelta(days=days)
self.date = datetime.datetime.now(datetime.UTC) - datetime.timedelta(days=days)

def run(self):
csrf = None
Expand All @@ -89,29 +89,31 @@ def run(self):
logging.root.setLevel(logging.DEBUG)

if self.do_it:
logging.warning("!!! WILL ACTUALLY DELETE THINGS !!!")
logging.warning("Will sleep for 3 seconds - Ctrl-C to abort!")
time.sleep(3.0)
logging.warning("!!! POSSIBLE DESTRUCTIVE OPERATION !!!")
else:
logging.info("Running in DRY RUN mode")

logging.info(f"Will use the following patterns {self.patterns} on package {self.package}")
logging.info(f"Will use the following patterns {self.patterns} on package {self.package!r}")

with requests.Session() as s:
with s.get(f"{self.url}/pypi/{self.package}/json") as r:
with s.get(f"{self.url}/simple/{self.package}/",
headers={"Accept": "application/vnd.pypi.simple.v1+json"}) as r:
try:
r.raise_for_status()
except RequestException as e:
logging.error(f"Unable to find package {repr(self.package)}", exc_info=e)
logging.error(f"Unable to find package {self.package!r}", exc_info=e)
return 1

project_info = r.json()
releases_by_date = {}
for release, files in r.json()["releases"].items():
releases_by_date[release] = max([datetime.datetime.strptime(f["upload_time"],
'%Y-%m-%dT%H:%M:%S') for f in files])
for version in project_info["versions"]:
releases_by_date[version] = max(
[datetime.datetime.strptime(f["upload-time"], '%Y-%m-%dT%H:%M:%S.%f%z')
for f in project_info["files"]
if f["filename"].lower().startswith(f"{self.package}-{version}")])

if not releases_by_date:
logging.info(f"No releases for package {self.package} have been found")
logging.info(f"No releases for package {self.package!r} have been found")
return

pkg_vers = list(filter(lambda k:
Expand All @@ -120,14 +122,15 @@ def run(self):
releases_by_date.keys()))

if not pkg_vers:
logging.info(f"No releases were found matching specified patterns and dates in package {self.package}")
logging.info(f"No releases were found matching specified patterns "
f"and dates in package {self.package!r}")
return

if set(pkg_vers) == set(releases_by_date.keys()):
print(dedent(f"""
WARNING:
\tYou have selected the following patterns: {self.patterns}
\tThese patterns would delete all available released versions of `{self.package}`.
\tThese patterns would delete all available released versions of {self.package!r}.
\tThis will render your project/package permanently inaccessible.
\tSince the costs of an error are too high I'm refusing to do this.
\tGoodbye.
Expand All @@ -136,6 +139,10 @@ def run(self):
if not self.do_it:
return 3

logging.info("Found the following releases to delete:")
for pkg_ver in pkg_vers:
logging.info(f" {pkg_ver}")

password = os.getenv("PYPI_CLEANUP_PASSWORD")

if self.username is None:
Expand Down Expand Up @@ -201,9 +208,14 @@ def run(self):
logging.error(f"Authentication code {auth_code} is invalid")
return 1

if self.do_it:
logging.warning("!!! WILL ACTUALLY DELETE THINGS - LAST CHANCE TO CHANGE YOUR MIND !!!")
logging.warning("Sleeping for 5 seconds - Ctrl-C to abort!")
time.sleep(5.0)

for pkg_ver in pkg_vers:
if self.do_it:
logging.info(f"Deleting {self.package} version {pkg_ver}")
logging.info(f"Deleting {self.package!r} version {pkg_ver}")
form_action = f"/manage/project/{self.package}/release/{pkg_ver}/"
form_url = f"{self.url}{form_action}"
with s.get(form_url) as r:
Expand All @@ -222,9 +234,9 @@ def run(self):
headers={"referer": referer}) as r:
r.raise_for_status()

logging.info(f"Deleted {self.package} version {pkg_ver}")
logging.info(f"Deleted {self.package!r} version {pkg_ver}")
else:
logging.info(f"Would be deleting {self.package} version {pkg_ver}, but not doing it!")
logging.info(f"Would be deleting {self.package!r} version {pkg_ver}, but not doing it!")


def main():
Expand Down

0 comments on commit e8ba878

Please sign in to comment.