-
Notifications
You must be signed in to change notification settings - Fork 4
/
pypi_stats.py
137 lines (104 loc) · 3.97 KB
/
pypi_stats.py
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
131
132
133
134
135
136
137
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Calculates the total number of downloads that a particular PyPI package has
received across all versions tracked by PyPI
"""
from datetime import datetime
import locale
import sys
import xmlrpclib
locale.setlocale(locale.LC_ALL, '')
class PyPIDownloadAggregator(object):
def __init__(self, package_name, include_hidden=True):
self.package_name = package_name
self.include_hidden = include_hidden
self.proxy = xmlrpclib.Server('http://pypi.python.org/pypi')
self._downloads = {}
self.first_upload = None
self.first_upload_rel = None
self.last_upload = None
self.last_upload_rel = None
@property
def releases(self):
"""Retrieves the release number for each uploaded release"""
result = self.proxy.package_releases(self.package_name, self.include_hidden)
if len(result) == 0:
# no matching package--search for possibles, and limit to 15 results
results = self.proxy.search({
'name': self.package_name,
'description': self.package_name
}, 'or')[:15]
# make sure we only get unique package names
matches = []
for match in results:
name = match['name']
if name not in matches:
matches.append(name)
# if only one package was found, return it
if len(matches) == 1:
self.package_name = matches[0]
return self.releases
error = """No such package found: %s
Possible matches include:
%s
""" % (self.package_name, '\n'.join('\t- %s' % n for n in matches))
sys.exit(error)
return result
@property
def downloads(self, force=False):
"""Calculate the total number of downloads for the package"""
if len(self._downloads) == 0 or force:
for release in self.releases:
urls = self.proxy.release_urls(self.package_name, release)
self._downloads[release] = 0
for url in urls:
# upload times
uptime = datetime.strptime(url['upload_time'].value, "%Y%m%dT%H:%M:%S")
if self.first_upload is None or uptime < self.first_upload:
self.first_upload = uptime
self.first_upload_rel = release
if self.last_upload is None or uptime > self.last_upload:
self.last_upload = uptime
self.last_upload_rel = release
self._downloads[release] += url['downloads']
return self._downloads
def total(self):
return sum(self.downloads.values())
def average(self):
return self.total() / len(self.downloads)
def max(self):
return max(self.downloads.values())
def min(self):
return min(self.downloads.values())
def stats(self):
"""Prints a nicely formatted list of statistics about the package"""
self.downloads # explicitly call, so we have first/last upload data
fmt = locale.nl_langinfo(locale.D_T_FMT)
sep = lambda s: locale.format('%d', s, 3)
val = lambda dt: dt and dt.strftime(fmt) or '--'
params = (
self.package_name,
val(self.first_upload),
self.first_upload_rel,
val(self.last_upload),
self.last_upload_rel,
sep(len(self.releases)),
sep(self.max()),
sep(self.min()),
sep(self.average()),
sep(self.total()),
)
print """PyPI Package statistics for: %s
First Upload: %40s (%s)
Last Upload: %40s (%s)
Number of releases: %34s
Most downloads: %35s
Fewest downloads: %35s
Average downloads: %35s
Total downloads: %35s
""" % params
def main():
PyPIDownloadAggregator('pg_fabrep').stats()
if __name__ == '__main__':
main()