2013-10-12 06:47:40 +08:00
|
|
|
'''
|
|
|
|
Script to generate the file `plugins_index.txt` with information about pytest plugins taken directly
|
2013-10-12 07:57:35 +08:00
|
|
|
from a live PyPI server.
|
2013-10-12 06:47:40 +08:00
|
|
|
|
|
|
|
This will evolve to include test compatibility (pythons and pytest versions) information also.
|
|
|
|
'''
|
2013-10-12 06:14:22 +08:00
|
|
|
from collections import namedtuple
|
2013-10-12 06:47:40 +08:00
|
|
|
import datetime
|
2013-10-12 06:14:22 +08:00
|
|
|
from distutils.version import LooseVersion
|
|
|
|
import itertools
|
2013-10-12 07:53:03 +08:00
|
|
|
from optparse import OptionParser
|
2013-10-12 06:14:22 +08:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import xmlrpclib
|
|
|
|
|
|
|
|
|
|
|
|
#===================================================================================================
|
2013-10-12 06:47:40 +08:00
|
|
|
# iter_plugins
|
2013-10-12 06:14:22 +08:00
|
|
|
#===================================================================================================
|
2013-10-12 06:47:40 +08:00
|
|
|
def iter_plugins(client, search='pytest-'):
|
|
|
|
'''
|
2013-10-12 07:57:35 +08:00
|
|
|
Returns an iterator of (name, version) from PyPI.
|
2013-10-12 06:47:40 +08:00
|
|
|
|
|
|
|
:param client: xmlrpclib.ServerProxy
|
|
|
|
:param search: package names to search for
|
|
|
|
'''
|
|
|
|
for plug_data in client.search({'name' : search}):
|
2013-10-12 06:14:22 +08:00
|
|
|
yield plug_data['name'], plug_data['version']
|
|
|
|
|
|
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
# get_latest_versions
|
|
|
|
#===================================================================================================
|
|
|
|
def get_latest_versions(plugins):
|
2013-10-12 06:47:40 +08:00
|
|
|
'''
|
|
|
|
Returns an iterator of (name, version) from the given list of (name, version), but returning
|
|
|
|
only the latest version of the package. Uses distutils.LooseVersion to ensure compatibility
|
|
|
|
with PEP386.
|
|
|
|
'''
|
2013-10-12 06:14:22 +08:00
|
|
|
plugins = [(name, LooseVersion(version)) for (name, version) in plugins]
|
|
|
|
for name, grouped_plugins in itertools.groupby(plugins, key=lambda x: x[0]):
|
|
|
|
name, loose_version = list(grouped_plugins)[-1]
|
|
|
|
yield name, str(loose_version)
|
|
|
|
|
|
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
# obtain_plugins_table
|
|
|
|
#===================================================================================================
|
|
|
|
def obtain_plugins_table(plugins, client):
|
2013-10-12 06:47:40 +08:00
|
|
|
'''
|
|
|
|
Returns information to populate a table of plugins, their versions, authors, etc.
|
|
|
|
|
|
|
|
The returned information is a list of columns of `ColumnData` namedtuples(text, link). Link
|
|
|
|
can be None if the text for that column should not be linked to anything.
|
|
|
|
|
|
|
|
:param plugins: list of (name, version)
|
|
|
|
:param client: xmlrpclib.ServerProxy
|
|
|
|
'''
|
2013-10-12 06:14:22 +08:00
|
|
|
rows = []
|
2013-10-12 06:47:40 +08:00
|
|
|
ColumnData = namedtuple('ColumnData', 'text link')
|
2013-10-15 06:14:05 +08:00
|
|
|
headers = ['Name', 'Author', 'Downloads', 'Summary']
|
2013-10-12 06:14:22 +08:00
|
|
|
|
2013-10-15 06:14:05 +08:00
|
|
|
plugins = list(plugins)
|
|
|
|
for index, (package_name, version) in enumerate(plugins):
|
|
|
|
print package_name, version, '...',
|
|
|
|
|
2013-10-12 06:14:22 +08:00
|
|
|
release_data = client.release_data(package_name, version)
|
2013-10-15 06:14:05 +08:00
|
|
|
download_count = release_data['downloads']['last_month']
|
2013-10-12 06:14:22 +08:00
|
|
|
row = (
|
2013-10-15 05:33:41 +08:00
|
|
|
ColumnData(package_name + '-' + version, release_data['release_url']),
|
2013-10-12 06:47:40 +08:00
|
|
|
ColumnData(release_data['author'], release_data['author_email']),
|
2013-10-15 06:14:05 +08:00
|
|
|
ColumnData(str(download_count), None),
|
2013-10-12 06:47:40 +08:00
|
|
|
ColumnData(release_data['summary'], None),
|
2013-10-12 06:14:22 +08:00
|
|
|
)
|
|
|
|
assert len(row) == len(headers)
|
|
|
|
rows.append(row)
|
|
|
|
|
2013-10-15 06:14:05 +08:00
|
|
|
print 'OK (%d%%)' % ((index + 1) * 100 / len(plugins))
|
|
|
|
|
2013-10-12 06:14:22 +08:00
|
|
|
return headers, rows
|
|
|
|
|
|
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
# generate_plugins_index_from_table
|
|
|
|
#===================================================================================================
|
2013-10-12 06:47:40 +08:00
|
|
|
def generate_plugins_index_from_table(filename, headers, rows):
|
|
|
|
'''
|
|
|
|
Generates a RST file with the table data given.
|
|
|
|
|
|
|
|
:param filename: output filename
|
|
|
|
:param headers: see `obtain_plugins_table`
|
|
|
|
:param rows: see `obtain_plugins_table`
|
|
|
|
'''
|
|
|
|
# creates a list of rows, each being a str containing appropriate column text and link
|
2013-10-12 06:14:22 +08:00
|
|
|
table_texts = []
|
|
|
|
for row in rows:
|
2013-10-12 06:47:40 +08:00
|
|
|
column_texts = []
|
|
|
|
for i, col_data in enumerate(row):
|
|
|
|
text = '`%s <%s>`_' % (col_data.text, col_data.link) if col_data.link else col_data.text
|
|
|
|
column_texts.append(text)
|
|
|
|
table_texts.append(column_texts)
|
2013-10-12 06:14:22 +08:00
|
|
|
|
2013-10-12 06:47:40 +08:00
|
|
|
# compute max length of each column so we can build the rst table
|
2013-10-12 06:14:22 +08:00
|
|
|
column_lengths = [len(x) for x in headers]
|
2013-10-12 06:47:40 +08:00
|
|
|
for column_texts in table_texts:
|
|
|
|
for i, row_text in enumerate(column_texts):
|
2013-10-12 06:14:22 +08:00
|
|
|
column_lengths[i] = max(column_lengths[i], len(row_text) + 2)
|
|
|
|
|
2013-10-12 06:47:40 +08:00
|
|
|
def get_row_limiter(char):
|
|
|
|
return ' '.join(char * length for length in column_lengths)
|
|
|
|
|
|
|
|
with file(filename, 'w') as f:
|
|
|
|
# write welcome
|
2013-10-12 06:14:22 +08:00
|
|
|
print >> f, '.. _plugins_index:'
|
|
|
|
print >> f
|
|
|
|
print >> f, 'List of Third-Party Plugins'
|
|
|
|
print >> f, '==========================='
|
|
|
|
print >> f
|
2013-10-12 06:47:40 +08:00
|
|
|
|
|
|
|
# table
|
2013-10-12 06:14:22 +08:00
|
|
|
print >> f, get_row_limiter('=')
|
|
|
|
for i, header in enumerate(headers):
|
|
|
|
print >> f, '{:^{fill}}'.format(header, fill=column_lengths[i]),
|
|
|
|
print >> f
|
|
|
|
print >> f, get_row_limiter('=')
|
|
|
|
|
2013-10-12 06:47:40 +08:00
|
|
|
for column_texts in table_texts:
|
|
|
|
for i, row_text in enumerate(column_texts):
|
2013-10-12 06:14:22 +08:00
|
|
|
print >> f, '{:^{fill}}'.format(row_text, fill=column_lengths[i]),
|
|
|
|
print >> f
|
|
|
|
print >> f
|
|
|
|
print >> f, get_row_limiter('=')
|
|
|
|
print >> f
|
2013-10-15 06:14:05 +08:00
|
|
|
print >> f, '*(Downloads are given from last month only)*'
|
|
|
|
print >> f
|
2013-10-12 07:53:03 +08:00
|
|
|
print >> f, '*(Updated on %s)*' % _get_today_as_str()
|
|
|
|
|
|
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
# _get_today_as_str
|
|
|
|
#===================================================================================================
|
|
|
|
def _get_today_as_str():
|
|
|
|
'''
|
|
|
|
internal. only exists so we can patch it in testing.
|
|
|
|
'''
|
|
|
|
return datetime.date.today().strftime('%Y-%m-%d')
|
2013-10-12 06:14:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
# generate_plugins_index
|
|
|
|
#===================================================================================================
|
2013-10-12 06:47:40 +08:00
|
|
|
def generate_plugins_index(client, filename):
|
|
|
|
'''
|
2013-10-12 07:57:35 +08:00
|
|
|
Generates an RST file with a table of the latest pytest plugins found in PyPI.
|
2013-10-12 06:47:40 +08:00
|
|
|
|
|
|
|
:param client: xmlrpclib.ServerProxy
|
|
|
|
:param filename: output filename
|
|
|
|
'''
|
|
|
|
plugins = get_latest_versions(iter_plugins(client))
|
2013-10-12 06:14:22 +08:00
|
|
|
headers, rows = obtain_plugins_table(plugins, client)
|
2013-10-12 06:47:40 +08:00
|
|
|
generate_plugins_index_from_table(filename, headers, rows)
|
2013-10-12 06:14:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
# main
|
|
|
|
#===================================================================================================
|
|
|
|
def main(argv):
|
2013-10-12 06:47:40 +08:00
|
|
|
filename = os.path.join(os.path.dirname(__file__), 'plugins_index.txt')
|
2013-10-12 07:53:03 +08:00
|
|
|
url = 'http://pypi.python.org/pypi'
|
|
|
|
|
2013-10-12 07:57:35 +08:00
|
|
|
parser = OptionParser(description='Generates a restructured document of pytest plugins from PyPI')
|
2013-10-12 07:53:03 +08:00
|
|
|
parser.add_option('-f', '--filename', default=filename, help='output filename [default: %default]')
|
2013-10-12 07:57:35 +08:00
|
|
|
parser.add_option('-u', '--url', default=url, help='url of PyPI server to obtain data from [default: %default]')
|
2013-10-12 07:53:03 +08:00
|
|
|
(options, _) = parser.parse_args(argv[1:])
|
|
|
|
|
|
|
|
client = xmlrpclib.ServerProxy(options.url)
|
|
|
|
generate_plugins_index(client, options.filename)
|
2013-10-15 06:14:05 +08:00
|
|
|
|
|
|
|
print
|
|
|
|
print '%s Updated.' % options.filename
|
2013-10-12 06:14:22 +08:00
|
|
|
return 0
|
|
|
|
|
|
|
|
#===================================================================================================
|
|
|
|
# main
|
|
|
|
#===================================================================================================
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main(sys.argv))
|