Formatted plugins_index files to use a 80 char width
- Formatted code to follow pep-8 more closely; - Got rid of header comments to better follow py.test coding style;
This commit is contained in:
parent
53a9ee21d4
commit
2058f11931
|
@ -1,9 +1,10 @@
|
||||||
'''
|
"""
|
||||||
Script to generate the file `plugins_index.txt` with information about pytest plugins taken directly
|
Script to generate the file `plugins_index.txt` with information about
|
||||||
from a live PyPI server.
|
pytest plugins taken directly from a live PyPI server.
|
||||||
|
|
||||||
This will evolve to include test compatibility (pythons and pytest versions) information also.
|
This will evolve to include test compatibility (pythons and pytest versions)
|
||||||
'''
|
information also.
|
||||||
|
"""
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import datetime
|
import datetime
|
||||||
from distutils.version import LooseVersion
|
from distutils.version import LooseVersion
|
||||||
|
@ -15,107 +16,108 @@ import xmlrpclib
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
#===================================================================================================
|
|
||||||
# iter_plugins
|
|
||||||
#===================================================================================================
|
|
||||||
def iter_plugins(client, search='pytest-'):
|
def iter_plugins(client, search='pytest-'):
|
||||||
'''
|
"""
|
||||||
Returns an iterator of (name, version) from PyPI.
|
Returns an iterator of (name, version) from PyPI.
|
||||||
|
|
||||||
:param client: xmlrpclib.ServerProxy
|
:param client: xmlrpclib.ServerProxy
|
||||||
:param search: package names to search for
|
:param search: package names to search for
|
||||||
'''
|
"""
|
||||||
for plug_data in client.search({'name' : search}):
|
for plug_data in client.search({'name': search}):
|
||||||
yield plug_data['name'], plug_data['version']
|
yield plug_data['name'], plug_data['version']
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
|
||||||
# get_latest_versions
|
|
||||||
#===================================================================================================
|
|
||||||
def get_latest_versions(plugins):
|
def get_latest_versions(plugins):
|
||||||
'''
|
"""
|
||||||
Returns an iterator of (name, version) from the given list of (name, version), but returning
|
Returns an iterator of (name, version) from the given list of (name,
|
||||||
only the latest version of the package. Uses distutils.LooseVersion to ensure compatibility
|
version), but returning only the latest version of the package. Uses
|
||||||
with PEP386.
|
distutils.LooseVersion to ensure compatibility with PEP386.
|
||||||
'''
|
"""
|
||||||
plugins = [(name, LooseVersion(version)) for (name, version) in plugins]
|
plugins = [(name, LooseVersion(version)) for (name, version) in plugins]
|
||||||
for name, grouped_plugins in itertools.groupby(plugins, key=lambda x: x[0]):
|
for name, grouped_plugins in itertools.groupby(plugins, key=lambda x: x[0]):
|
||||||
name, loose_version = list(grouped_plugins)[-1]
|
name, loose_version = list(grouped_plugins)[-1]
|
||||||
yield name, str(loose_version)
|
yield name, str(loose_version)
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
def obtain_plugins_table(plugins, client):
|
||||||
# obtain_plugins_table
|
"""
|
||||||
#===================================================================================================
|
Returns information to populate a table of plugins, their versions,
|
||||||
def obtain_plugins_table(plugins, client):
|
authors, etc.
|
||||||
'''
|
|
||||||
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
|
The returned information is a list of columns of `ColumnData`
|
||||||
can be None if the text for that column should not be linked to anything.
|
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 plugins: list of (name, version)
|
||||||
:param client: xmlrpclib.ServerProxy
|
:param client: xmlrpclib.ServerProxy
|
||||||
'''
|
"""
|
||||||
rows = []
|
rows = []
|
||||||
ColumnData = namedtuple('ColumnData', 'text link')
|
ColumnData = namedtuple('ColumnData', 'text link')
|
||||||
headers = ['Name', 'Author', 'Downloads', 'Python 2.7', 'Python 3.3', 'Summary']
|
headers = ['Name', 'Author', 'Downloads', 'Python 2.7', 'Python 3.3',
|
||||||
|
'Summary']
|
||||||
pytest_version = pytest.__version__
|
pytest_version = pytest.__version__
|
||||||
print '*** pytest-{0} ***'.format(pytest_version)
|
print '*** pytest-{0} ***'.format(pytest_version)
|
||||||
plugins = list(plugins)
|
plugins = list(plugins)
|
||||||
for index, (package_name, version) in enumerate(plugins):
|
for index, (package_name, version) in enumerate(plugins):
|
||||||
print package_name, version, '...',
|
print package_name, version, '...',
|
||||||
|
|
||||||
release_data = client.release_data(package_name, version)
|
release_data = client.release_data(package_name, version)
|
||||||
download_count = release_data['downloads']['last_month']
|
download_count = release_data['downloads']['last_month']
|
||||||
image_url = '.. image:: http://pytest-plugs.herokuapp.com/status/{name}-{version}'.format(name=package_name,
|
url = '.. image:: {site}/status/{name}-{version}'
|
||||||
version=version)
|
image_url = url.format(
|
||||||
|
site='http://pytest-plugs.herokuapp.com',
|
||||||
|
name=package_name,
|
||||||
|
version=version)
|
||||||
image_url += '?py={py}&pytest={pytest}'
|
image_url += '?py={py}&pytest={pytest}'
|
||||||
row = (
|
row = (
|
||||||
ColumnData(package_name + '-' + version, release_data['release_url']),
|
ColumnData(package_name + '-' + version,
|
||||||
|
release_data['release_url']),
|
||||||
ColumnData(release_data['author'], release_data['author_email']),
|
ColumnData(release_data['author'], release_data['author_email']),
|
||||||
ColumnData(str(download_count), None),
|
ColumnData(str(download_count), None),
|
||||||
ColumnData(image_url.format(py='py27', pytest=pytest_version), None),
|
ColumnData(image_url.format(py='py27', pytest=pytest_version),
|
||||||
ColumnData(image_url.format(py='py33', pytest=pytest_version), None),
|
None),
|
||||||
|
ColumnData(image_url.format(py='py33', pytest=pytest_version),
|
||||||
|
None),
|
||||||
ColumnData(release_data['summary'], None),
|
ColumnData(release_data['summary'], None),
|
||||||
)
|
)
|
||||||
assert len(row) == len(headers)
|
assert len(row) == len(headers)
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
|
|
||||||
print 'OK (%d%%)' % ((index + 1) * 100 / len(plugins))
|
print 'OK (%d%%)' % ((index + 1) * 100 / len(plugins))
|
||||||
|
|
||||||
return headers, rows
|
return headers, rows
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
|
||||||
# generate_plugins_index_from_table
|
|
||||||
#===================================================================================================
|
|
||||||
def generate_plugins_index_from_table(filename, headers, rows):
|
def generate_plugins_index_from_table(filename, headers, rows):
|
||||||
'''
|
"""
|
||||||
Generates a RST file with the table data given.
|
Generates a RST file with the table data given.
|
||||||
|
|
||||||
:param filename: output filename
|
:param filename: output filename
|
||||||
:param headers: see `obtain_plugins_table`
|
:param headers: see `obtain_plugins_table`
|
||||||
:param rows: 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
|
# creates a list of rows, each being a str containing appropriate column
|
||||||
|
# text and link
|
||||||
table_texts = []
|
table_texts = []
|
||||||
for row in rows:
|
for row in rows:
|
||||||
column_texts = []
|
column_texts = []
|
||||||
for i, col_data in enumerate(row):
|
for i, col_data in enumerate(row):
|
||||||
text = '`%s <%s>`_' % (col_data.text, col_data.link) if col_data.link else col_data.text
|
text = '`%s <%s>`_' % (
|
||||||
|
col_data.text,
|
||||||
|
col_data.link) if col_data.link else col_data.text
|
||||||
column_texts.append(text)
|
column_texts.append(text)
|
||||||
table_texts.append(column_texts)
|
table_texts.append(column_texts)
|
||||||
|
|
||||||
# compute max length of each column so we can build the rst table
|
# compute max length of each column so we can build the rst table
|
||||||
column_lengths = [len(x) for x in headers]
|
column_lengths = [len(x) for x in headers]
|
||||||
for column_texts in table_texts:
|
for column_texts in table_texts:
|
||||||
for i, row_text in enumerate(column_texts):
|
for i, row_text in enumerate(column_texts):
|
||||||
column_lengths[i] = max(column_lengths[i], len(row_text) + 2)
|
column_lengths[i] = max(column_lengths[i], len(row_text) + 2)
|
||||||
|
|
||||||
def get_row_limiter(char):
|
def get_row_limiter(char):
|
||||||
return ' '.join(char * length for length in column_lengths)
|
return ' '.join(char * length for length in column_lengths)
|
||||||
|
|
||||||
with file(filename, 'w') as f:
|
with file(filename, 'w') as f:
|
||||||
# write welcome
|
# write welcome
|
||||||
print >> f, '.. _plugins_index:'
|
print >> f, '.. _plugins_index:'
|
||||||
|
@ -123,17 +125,18 @@ def generate_plugins_index_from_table(filename, headers, rows):
|
||||||
print >> f, 'List of Third-Party Plugins'
|
print >> f, 'List of Third-Party Plugins'
|
||||||
print >> f, '==========================='
|
print >> f, '==========================='
|
||||||
print >> f
|
print >> f
|
||||||
|
|
||||||
# table
|
# table
|
||||||
print >> f, get_row_limiter('=')
|
print >> f, get_row_limiter('=')
|
||||||
for i, header in enumerate(headers):
|
for i, header in enumerate(headers):
|
||||||
print >> f, '{0:^{fill}}'.format(header, fill=column_lengths[i]),
|
print >> f, '{0:^{fill}}'.format(header, fill=column_lengths[i]),
|
||||||
print >> f
|
print >> f
|
||||||
print >> f, get_row_limiter('=')
|
print >> f, get_row_limiter('=')
|
||||||
|
|
||||||
for column_texts in table_texts:
|
for column_texts in table_texts:
|
||||||
for i, row_text in enumerate(column_texts):
|
for i, row_text in enumerate(column_texts):
|
||||||
print >> f, '{0:^{fill}}'.format(row_text, fill=column_lengths[i]),
|
print >> f, '{0:^{fill}}'.format(row_text,
|
||||||
|
fill=column_lengths[i]),
|
||||||
print >> f
|
print >> f
|
||||||
print >> f
|
print >> f
|
||||||
print >> f, get_row_limiter('=')
|
print >> f, get_row_limiter('=')
|
||||||
|
@ -141,54 +144,50 @@ def generate_plugins_index_from_table(filename, headers, rows):
|
||||||
print >> f, '*(Downloads are given from last month only)*'
|
print >> f, '*(Downloads are given from last month only)*'
|
||||||
print >> f
|
print >> f
|
||||||
print >> f, '*(Updated on %s)*' % _get_today_as_str()
|
print >> f, '*(Updated on %s)*' % _get_today_as_str()
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
|
||||||
# _get_today_as_str
|
|
||||||
#===================================================================================================
|
|
||||||
def _get_today_as_str():
|
def _get_today_as_str():
|
||||||
'''
|
"""
|
||||||
internal. only exists so we can patch it in testing.
|
internal. only exists so we can patch it in testing.
|
||||||
'''
|
"""
|
||||||
return datetime.date.today().strftime('%Y-%m-%d')
|
return datetime.date.today().strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
|
||||||
# generate_plugins_index
|
|
||||||
#===================================================================================================
|
|
||||||
def generate_plugins_index(client, filename):
|
def generate_plugins_index(client, filename):
|
||||||
'''
|
"""
|
||||||
Generates an RST file with a table of the latest pytest plugins found in PyPI.
|
Generates an RST file with a table of the latest pytest plugins found in
|
||||||
|
PyPI.
|
||||||
|
|
||||||
:param client: xmlrpclib.ServerProxy
|
:param client: xmlrpclib.ServerProxy
|
||||||
:param filename: output filename
|
:param filename: output filename
|
||||||
'''
|
"""
|
||||||
plugins = get_latest_versions(iter_plugins(client))
|
plugins = get_latest_versions(iter_plugins(client))
|
||||||
headers, rows = obtain_plugins_table(plugins, client)
|
headers, rows = obtain_plugins_table(plugins, client)
|
||||||
generate_plugins_index_from_table(filename, headers, rows)
|
generate_plugins_index_from_table(filename, headers, rows)
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
|
||||||
# main
|
|
||||||
#===================================================================================================
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
|
"""
|
||||||
|
Script entry point. Configures an option parser and calls the appropriate
|
||||||
|
internal function.
|
||||||
|
"""
|
||||||
filename = os.path.join(os.path.dirname(__file__), 'plugins_index.txt')
|
filename = os.path.join(os.path.dirname(__file__), 'plugins_index.txt')
|
||||||
url = 'http://pypi.python.org/pypi'
|
url = 'http://pypi.python.org/pypi'
|
||||||
|
|
||||||
parser = OptionParser(description='Generates a restructured document of pytest plugins from PyPI')
|
parser = OptionParser(
|
||||||
parser.add_option('-f', '--filename', default=filename, help='output filename [default: %default]')
|
description='Generates a restructured document of pytest plugins from PyPI')
|
||||||
parser.add_option('-u', '--url', default=url, help='url of PyPI server to obtain data from [default: %default]')
|
parser.add_option('-f', '--filename', default=filename,
|
||||||
|
help='output filename [default: %default]')
|
||||||
|
parser.add_option('-u', '--url', default=url,
|
||||||
|
help='url of PyPI server to obtain data from [default: %default]')
|
||||||
(options, _) = parser.parse_args(argv[1:])
|
(options, _) = parser.parse_args(argv[1:])
|
||||||
|
|
||||||
client = xmlrpclib.ServerProxy(options.url)
|
client = xmlrpclib.ServerProxy(options.url)
|
||||||
generate_plugins_index(client, options.filename)
|
generate_plugins_index(client, options.filename)
|
||||||
|
|
||||||
print
|
print
|
||||||
print '%s Updated.' % options.filename
|
print '%s Updated.' % options.filename
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
#===================================================================================================
|
|
||||||
# main
|
|
||||||
#===================================================================================================
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(main(sys.argv))
|
sys.exit(main(sys.argv))
|
||||||
|
|
|
@ -4,64 +4,67 @@ import xmlrpclib
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
@pytest.mark.xfail(
|
||||||
# test_plugins_index
|
reason="issue405 fails, not py33 ready, not a core pytest test")
|
||||||
#===================================================================================================
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="issue405 fails, not py33 ready, not a core pytest test")
|
|
||||||
def test_plugins_index(tmpdir, monkeypatch):
|
def test_plugins_index(tmpdir, monkeypatch):
|
||||||
'''
|
"""
|
||||||
Blackbox testing for plugins_index script. Calls main() generating a file and compares produced
|
Blackbox testing for plugins_index script. Calls main() generating a file
|
||||||
output to expected.
|
and compares produced output to expected.
|
||||||
|
|
||||||
.. note:: if the test fails, a file named `test_plugins_index.obtained.rst` will be generated in
|
.. note:: if the test fails, a file named
|
||||||
the same directory as this test file. Ensure the contents are correct and overwrite
|
`test_plugins_index.obtained.rst` will be generated in the same directory
|
||||||
|
as this test file. Ensure the contents are correct and overwrite
|
||||||
`test_plugins_index.expected.rst` with that file.
|
`test_plugins_index.expected.rst` with that file.
|
||||||
'''
|
"""
|
||||||
import plugins_index
|
import plugins_index
|
||||||
|
|
||||||
# dummy interface to xmlrpclib.ServerProxy
|
# dummy interface to xmlrpclib.ServerProxy
|
||||||
class DummyProxy(object):
|
class DummyProxy(object):
|
||||||
|
|
||||||
expected_url = 'http://dummy.pypi'
|
expected_url = 'http://dummy.pypi'
|
||||||
|
|
||||||
def __init__(self, url):
|
def __init__(self, url):
|
||||||
assert url == self.expected_url
|
assert url == self.expected_url
|
||||||
|
|
||||||
def search(self, query):
|
def search(self, query):
|
||||||
assert query == {'name' : 'pytest-'}
|
assert query == {'name': 'pytest-'}
|
||||||
return [
|
return [
|
||||||
{'name': 'pytest-plugin1', 'version' : '0.8'},
|
{'name': 'pytest-plugin1', 'version': '0.8'},
|
||||||
{'name': 'pytest-plugin1', 'version' : '1.0'},
|
{'name': 'pytest-plugin1', 'version': '1.0'},
|
||||||
{'name': 'pytest-plugin2', 'version' : '1.2'},
|
{'name': 'pytest-plugin2', 'version': '1.2'},
|
||||||
]
|
]
|
||||||
|
|
||||||
def release_data(self, package_name, version):
|
def release_data(self, package_name, version):
|
||||||
results = {
|
results = {
|
||||||
('pytest-plugin1', '1.0') : {
|
('pytest-plugin1', '1.0'): {
|
||||||
'package_url' : 'http://plugin1',
|
'package_url': 'http://plugin1',
|
||||||
'release_url' : 'http://plugin1/1.0',
|
'release_url': 'http://plugin1/1.0',
|
||||||
'author' : 'someone',
|
'author': 'someone',
|
||||||
'author_email' : 'someone@py.com',
|
'author_email': 'someone@py.com',
|
||||||
'summary' : 'some plugin',
|
'summary': 'some plugin',
|
||||||
'downloads': {'last_day': 1, 'last_month': 4, 'last_week': 2},
|
'downloads': {'last_day': 1, 'last_month': 4,
|
||||||
|
'last_week': 2},
|
||||||
},
|
},
|
||||||
|
|
||||||
('pytest-plugin2', '1.2') : {
|
('pytest-plugin2', '1.2'): {
|
||||||
'package_url' : 'http://plugin2',
|
'package_url': 'http://plugin2',
|
||||||
'release_url' : 'http://plugin2/1.2',
|
'release_url': 'http://plugin2/1.2',
|
||||||
'author' : 'other',
|
'author': 'other',
|
||||||
'author_email' : 'other@py.com',
|
'author_email': 'other@py.com',
|
||||||
'summary' : 'some other plugin',
|
'summary': 'some other plugin',
|
||||||
'downloads': {'last_day': 10, 'last_month': 40, 'last_week': 20},
|
'downloads': {'last_day': 10, 'last_month': 40,
|
||||||
|
'last_week': 20},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return results[(package_name, version)]
|
return results[(package_name, version)]
|
||||||
|
|
||||||
monkeypatch.setattr(xmlrpclib, 'ServerProxy', DummyProxy, 'foo')
|
monkeypatch.setattr(xmlrpclib, 'ServerProxy', DummyProxy, 'foo')
|
||||||
monkeypatch.setattr(plugins_index, '_get_today_as_str', lambda: '2013-10-20')
|
monkeypatch.setattr(plugins_index, '_get_today_as_str',
|
||||||
|
lambda: '2013-10-20')
|
||||||
|
|
||||||
output_file = str(tmpdir.join('output.rst'))
|
output_file = str(tmpdir.join('output.rst'))
|
||||||
assert plugins_index.main(['', '-f', output_file, '-u', DummyProxy.expected_url]) == 0
|
assert plugins_index.main(
|
||||||
|
['', '-f', output_file, '-u', DummyProxy.expected_url]) == 0
|
||||||
|
|
||||||
with file(output_file, 'rU') as f:
|
with file(output_file, 'rU') as f:
|
||||||
obtained_output = f.read()
|
obtained_output = f.read()
|
||||||
|
@ -79,13 +82,15 @@ def get_expected_output():
|
||||||
"""
|
"""
|
||||||
:return: string with expected rst output from the plugins_index.py script.
|
:return: string with expected rst output from the plugins_index.py script.
|
||||||
"""
|
"""
|
||||||
expected_filename = os.path.join(os.path.dirname(__file__), 'test_plugins_index.expected.rst')
|
expected_filename = os.path.join(os.path.dirname(__file__),
|
||||||
|
'test_plugins_index.expected.rst')
|
||||||
expected_output = open(expected_filename, 'rU').read()
|
expected_output = open(expected_filename, 'rU').read()
|
||||||
return expected_output.replace('pytest=2.X.Y', 'pytest={0}'.format(pytest.__version__))
|
return expected_output.replace('pytest=2.X.Y',
|
||||||
|
'pytest={0}'.format(pytest.__version__))
|
||||||
|
|
||||||
|
|
||||||
#===================================================================================================
|
#===============================================================================
|
||||||
# main
|
# main
|
||||||
#===================================================================================================
|
#===============================================================================
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
pytest.main()
|
pytest.main()
|
||||||
|
|
Loading…
Reference in New Issue