-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstat.py
More file actions
executable file
·130 lines (98 loc) · 3.96 KB
/
stat.py
File metadata and controls
executable file
·130 lines (98 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python3
import datetime as dt
from io import StringIO
import osc.conf
import json
from dateutil.relativedelta import relativedelta
import requests
import xml.etree.ElementTree as ET
from osc.core import http_GET, makeurl
def _month(yyyy_mm: str) -> tuple[str, str]:
"""Helper to return start_date and end_date of a month as yyyy-mm-dd"""
year, month = map(int, yyyy_mm.split("-"))
first = dt.date(year, month, 1)
last = first + relativedelta(months=1) - relativedelta(days=1)
return str(first), str(last)
def _last_month() -> tuple[str, str]:
"""Helper to return start_date and end_date of the previous month as yyyy-mm-dd"""
today = dt.date.today()
d = today - relativedelta(months=1)
return _month(d.isoformat()[:7])
def _this_month() -> str:
"""Helper to return start_date of the current month as yyyy-mm-dd.
No end_date needed."""
today = dt.date.today()
return _month(today.isoformat()[:7])[0]
def download_monthly(package) -> int:
import pypistats
results = json.loads(pypistats.overall(package, mirrors=True,
start_date=_this_month(),
format="json", total="monthly", color="no", verbose=False))
return results["data"][0]["downloads"]
def get_pkg_list(project) -> list[str]:
pkglistxml = http_GET(
makeurl(
"https://api.opensuse.org/",
['source', project],
query={'view': 'info', 'parse': 1},
)
)
ET_root = ET.fromstring(pkglistxml.read())
pkgs = {}
for elem in ET_root.iterfind("sourceinfo"):
pkg = elem.attrib["package"]
if ':' in pkg or not pkg.startswith("python-"):
continue
if (l := elem.find("linked")) is None:
# not submitted to Factory, ignore
continue
if (v := elem.find("version")) is not None:
pkg_version = v.text
pkgs[pkg] = pkg_version
return pkgs
def lookup_pypi(pypi_name, pkg_version):
r = requests.get(f"https://pypi.io/pypi/{pypi_name}/json")
if r.status_code >= 400:
return None, None
r = json.loads(r.text)
classifiers = r["info"].get("classifiers", [])
if "Programming Language :: Python :: 3.12" not in classifiers:
pass # print(f"WARN: {pypi_name} not compatible with 3.12!")
last_version = r["info"]["version"]
last_version_date = None
version_is_yanked = True
for release in r["releases"][last_version]:
if release["packagetype"] == "sdist":
last_version_date = dt.datetime.fromisoformat(release["upload_time"])
version_is_yanked = release["yanked"]
if not last_version_date:
return None, None
if version_is_yanked:
print(f"ERROR: version package {pypi_name} {last_version} is yanked!!")
pkg_version_date = None
if pkg_version in r["releases"]:
for release in r["releases"][pkg_version]:
if release["packagetype"] == "sdist":
pkg_version_date = dt.datetime.fromisoformat(release["upload_time"])
if release["yanked"]:
print(f"ERROR: packaged version for {pypi_name} - {pkg_version} is marked as yanked!")
if last_version_date and dt.datetime.now() - last_version_date < dt.timedelta(weeks=4):
return None, None
if pkg_version == last_version:
return None, None
return pkg_version_date, last_version
def main() -> None:
osc.conf.get_config()
pkgs = get_pkg_list("devel:languages:python")
for pkg in pkgs:
if pkg.startswith("python-"):
pypi_name = pkg.partition("-")[2]
pkg_version = pkgs[pkg]
r, latest_version = lookup_pypi(pypi_name, pkg_version)
if not r:
continue
age = dt.datetime.now() - r
if age > dt.timedelta(days=5 * 365):
print(f"{pypi_name} (latest is {latest_version}): packaged ver {pkg_version} is {age.days} days old")
if __name__ == "__main__":
main()