This commit is contained in:
Georg Brandl 2010-07-25 18:58:42 +02:00
commit f9c9e31eb8
9 changed files with 490 additions and 37 deletions

View file

@ -1,10 +1,13 @@
.PHONY: clean-pyc test upload-docs docs
.PHONY: clean-pyc ext-test test upload-docs docs
all: clean-pyc test
test:
python setup.py test
ext-test:
python tests/flaskext_test.py --browse
release:
python setup.py release sdist upload

View file

@ -314,6 +314,8 @@ extension to be approved you have to follow these guidelines:
link to the documentation, website (if there is one) and there
must be a link to automatically install the development version
(``PackageName==dev``).
9. The ``zip_safe`` flag in the setup scrip must be set to ``False``,
even if the extension would be safe for zipping.
.. _Flask Extension Wizard:

32
extreview/approved.rst Normal file
View file

@ -0,0 +1,32 @@
Approved Extensions
===================
This document contains a list of all extensions that were approved and the
date of approval as well as notes. This should make it possible to better
track the extension approval process.
Flask-Babel
-----------
:First Approval: 2010-07-23
:Last Review: 2010-07-23
:Approved version: 0.6
:Approved license: BSD
Notes: Developed by the Flask development head
How to improve: add a better long description to the next release
Flask-SQLAlchemy
----------------
:First Approval: 2010-07-25
:Last Review: 2010-07-25
:Approved version: 0.9
:Approved license: BSD
Notes: Developed by the Flask development head
How to improve: add a better long description to the next release

183
extreview/listed.rst Normal file
View file

@ -0,0 +1,183 @@
Listed Extensions
=================
This list contains extensions that passed listing. This means the
extension is on the list of extensions on the website. It does not
contain extensions that are approved.
Flask-CouchDB
-------------
:Last-Review: 2010-07-25
:Reviewed version: 0.2
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test".
Flask-CouchDBKit
----------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.2
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test".
Flask-Creole
------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.2
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test". Furthermore the README
file is empty.
flask-csrf
----------
:Last-Review: 2010-07-25
:Reviewed Version: 0.2
Will not be approved because this is functionality that should be handled
in the form handling systems which is for Flask-WTF already the case.
Also, this implementation only supports one open tab with forms.
Name is not following Flask extension naming rules.
Considered for unlisting.
Flask-Genshi
------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.3
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test". Furthermore the long
description is empty. The zip_safe flag is not set to False which is a
requirement for approved extensions.
flask-lesscss
-------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.9.1
Broken package description, nonconforming package name, does not follow
standard API rules (init_lesscss instead of lesscss).
Considered for unlisting, improved version should release as
"Flask-LessCSS" with a conforming API and fixed packages indices, as well
as a testsuite.
flask-mail
----------
:Last-Review: 2010-07-25
:Reviewed Version: 0.3.1
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test". Furthermore the long
description in the package index is a little bit too short.
Package name should be changed to Flask-Mail with the approval to be
consistent, this might also be the change to improve the API if necessary,
but I don't see any big design problems there.
Flask-OAuth
-----------
:Last-Review: 2010-07-25
:Reviewed Version: 0.9
Short long description, missing tests.
Flask-OpenID
------------
:Last-Review: 2010-07-25
:Reviewed Version: 1.0.1
Short long description, missing tests.
Flask-Script
------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.2
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test".
The upcoming 0.3 release looks promising, could need a longer "long
description" in the package index though.
Flask-Testing
-------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.2
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test".
Flask-Themes
------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.1
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test".
Flask-Uploads
-------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.1
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test".
Flask-WTF
---------
:Last-Review: 2010-07-25
:Reviewed Version: 0.2.1
Would be fine for approval, but the test suite is not part of the sdist
package (missing entry in MANIFEST.in) and the test suite does not respond
to either "make test" or "python setup.py test".
Flask-XML-RPC
-------------
:Last-Review: 2010-07-25
:Reviewed Version: 0.2.1
Missing tests, API wise it would be fine for approval.

55
extreview/unlisted.rst Normal file
View file

@ -0,0 +1,55 @@
Unlisted Extensions
===================
This is a list of extensions that is currently rejected from listing and
with that also not approved. If an extension ends up here it should
improved to be listed.
Flask-Actions
-------------
:Last Review: 2010-07-25
:Reviewed version: 0.2
Rejected because of missing description in PyPI, formatting issues with
the documentation (missing headlines, scrollbars etc.) and a general clash
of functionality with the Flask-Script package. Latter should not be a
problem, but the documentation should improve. For listing, the extension
developer should probably discuss the extension on the mailinglist with
others.
Futhermore it also has an egg registered with an invalid filename.
Flask-Jinja2Extender
--------------------
:Last Review: 2010-07-25
:Reviewed version: 0.1
Usecase not obvious, hacky implementation, does not solve a problem that
could not be solved with Flask itself. I suppose it is to aid other
extensions, but that should be discussed on the mailinglist.
Flask-Markdown
--------------
:Last Review: 2010-07-25
:Reviewed version: 0.2
Would be great for enlisting but it should follow the API of Flask-Creole.
Besides that, the docstrings are not valid rst (run through rst2html to
see the issue) and it is missing tests. Otherwise fine :)
flask-urls
----------
:Last Review: 2010-07-25
:Reviewed version: 0.9.2
Broken PyPI index and non-conforming extension name. Due to the small
featureset this was also delisted from the list. It was there previously
before the approval process was introduced.

View file

@ -11,7 +11,6 @@
from __future__ import with_statement
import os
from threading import Lock
from datetime import timedelta, datetime
from itertools import chain
@ -20,7 +19,7 @@ from jinja2 import Environment
from werkzeug import ImmutableDict
from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, InternalServerError, NotFound
from werkzeug.exceptions import HTTPException, InternalServerError
from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
_tojson_filter, _endpoint_from_view_func

View file

@ -15,7 +15,6 @@ import posixpath
import mimetypes
from time import time
from zlib import adler32
from functools import wraps
# try to load the best simplejson implementation available. If JSON
# is not installed, we add a failing class.

View file

@ -8,7 +8,7 @@
:copyright: (c) 2010 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound
from jinja2 import BaseLoader, TemplateNotFound
from .globals import _request_ctx_stack
from .signals import template_rendered

View file

@ -11,7 +11,14 @@
from __future__ import with_statement
import tempfile, subprocess, urllib2, os
import os
import sys
import shutil
import urllib2
import tempfile
import subprocess
import argparse
from cStringIO import StringIO
from flask import json
@ -19,24 +26,136 @@ from setuptools.package_index import PackageIndex
from setuptools.archive_util import unpack_archive
flask_svc_url = 'http://flask.pocoo.org/extensions/'
tdir = tempfile.mkdtemp()
if sys.platform == 'darwin':
_tempdir = '/private/tmp'
else:
_tempdir = tempfile.gettempdir()
tdir = _tempdir + '/flaskext-test'
flaskdir = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..'))
def run_tests(checkout_dir):
cmd = ['tox']
return subprocess.call(cmd, cwd=checkout_dir,
stdout=open(os.path.join(tdir, 'tox.log'), 'w'),
stderr=subprocess.STDOUT)
RESULT_TEMPATE = u'''\
<!doctype html>
<title>Flask-Extension Test Results</title>
<style type=text/css>
body { font-family: 'Georgia', serif; font-size: 17px; color: #000; }
a { color: #004B6B; }
a:hover { color: #6D4100; }
h1, h2, h3 { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; }
h1 { font-size: 30px; margin: 15px 0 5px 0; }
h2 { font-size: 24px; margin: 15px 0 5px 0; }
h3 { font-size: 19px; margin: 15px 0 5px 0; }
textarea, code,
pre { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace!important; font-size: 15px;
background: #eee; }
pre { padding: 7px 15px; line-height: 1.3; }
p { line-height: 1.4; }
table { border: 1px solid black; border-collapse: collapse;
margin: 15px 0; }
td, th { border: 1px solid black; padding: 4px 10px;
text-align: left; }
th { background: #eee; font-weight: normal; }
tr.success { background: #D3F5CC; }
tr.failed { background: #F5D2CB; }
</style>
<h1>Flask-Extension Test Results</h1>
<p>
This page contains the detailed test results for the test run of
all {{ 'approved' if approved }} Flask extensions.
<h2>Summary</h2>
<table class=results>
<thead>
<tr>
<th>Extension
<th>Version
<th>Author
<th>License
<th>Outcome
</tr>
</thead>
<tbody>
{%- for result in results %}
{% set outcome = 'success' if result.success else 'failed' %}
<tr class={{ outcome }}>
<th>{{ result.name }}
<td>{{ result.version }}
<td>{{ result.author }}
<td>{{ result.license }}
<td>{{ outcome }}
</tr>
{%- endfor %}
</tbody>
</table>
<h2>Test Logs</h2>
<p>Detailed test logs for all tests on all platforms:
{%- for result in results %}
{%- for iptr, log in result.logs|dictsort %}
<h3>{{ result.name }} - {{ result.version }} [{{ iptr }}]</h3>
<pre>{{ log }}</pre>
{%- endfor %}
{%- endfor %}
'''
def log(msg, *args):
print '[EXTTEST]', msg % args
class TestResult(object):
def __init__(self, name, folder, statuscode, interpreters):
intrptr = os.path.join(folder, '.tox/%s/bin/python'
% interpreters[0])
self.statuscode = statuscode
self.folder = folder
self.success = statuscode == 0
def fetch(field):
try:
c = subprocess.Popen([intrptr, 'setup.py',
'--' + field], cwd=folder,
stdout=subprocess.PIPE)
return c.communicate()[0].strip()
except OSError:
return '?'
self.name = name
self.license = fetch('license')
self.author = fetch('author')
self.version = fetch('version')
self.logs = {}
for interpreter in interpreters:
logfile = os.path.join(folder, '.tox/%s/log/test.log'
% interpreter)
if os.path.isfile(logfile):
self.logs[interpreter] = open(logfile).read()
else:
self.logs[interpreter] = ''
def create_tdir():
try:
shutil.rmtree(tdir)
except Exception:
pass
os.mkdir(tdir)
def package_flask():
distfolder = tdir + '/.flask-dist'
c = subprocess.Popen(['python', 'setup.py', 'sdist', '--formats=gztar',
'--dist', distfolder], cwd=flaskdir)
c.wait()
return os.path.join(distfolder, os.listdir(distfolder)[0])
def get_test_command(checkout_dir):
files = set(os.listdir(checkout_dir))
if 'Makefile' in files:
if os.path.isfile(checkout_dir + '/Makefile'):
return 'make test'
elif 'conftest.py' in files:
return 'py.test'
else:
return 'nosetests'
return 'python setup.py test'
def fetch_extensions_list():
@ -47,50 +166,111 @@ def fetch_extensions_list():
yield ext
def checkout_extension(ext):
name = ext['name']
def checkout_extension(name):
log('Downloading extension %s to temporary folder', name)
root = os.path.join(tdir, name)
os.mkdir(root)
checkout_path = PackageIndex().download(ext['name'], root)
checkout_path = PackageIndex().download(name, root)
unpack_archive(checkout_path, root)
path = None
for fn in os.listdir(root):
path = os.path.join(root, fn)
if os.path.isdir(path):
break
log('Downloaded to %s', path)
return path
tox_template = """[tox]
envlist=py26
envlist=%(env)s
[testenv]
commands=
%s
downloadcache=
%s
deps=%(deps)s
commands=bash flaskext-runtest.sh {envlogdir}/test.log
downloadcache=%(cache)s
"""
def create_tox_ini(checkout_path):
def create_tox_ini(checkout_path, interpreters, flask_dep):
tox_path = os.path.join(checkout_path, 'tox.ini')
if not os.path.exists(tox_path):
with open(tox_path, 'w') as f:
f.write(tox_template % (get_test_command(checkout_path), tdir))
f.write(tox_template % {
'env': ','.join(interpreters),
'cache': tdir,
'deps': flask_dep
})
# XXX command line
only_approved = True
def test_all_extensions(only_approved=only_approved):
def iter_extensions(only_approved=True):
for ext in fetch_extensions_list():
if ext['approved'] or not only_approved:
checkout_path = checkout_extension(ext)
create_tox_ini(checkout_path)
ret = run_tests(checkout_path)
yield ext['name'], ret
yield ext['name']
def test_extension(name, interpreters, flask_dep):
checkout_path = checkout_extension(name)
log('Running tests with tox in %s', checkout_path)
# figure out the test command and write a wrapper script. We
# can't write that directly into the tox ini because tox does
# not invoke the command from the shell so we have no chance
# to pipe the output into a logfile
test_command = get_test_command(checkout_path)
log('Test command: %s', test_command)
f = open(checkout_path + '/flaskext-runtest.sh', 'w')
f.write(test_command + ' &> "$1"\n')
f.close()
create_tox_ini(checkout_path, interpreters, flask_dep)
rv = subprocess.call(['tox'], cwd=checkout_path)
return TestResult(name, checkout_path, rv, interpreters)
def run_tests(interpreters, only_approved=True):
results = {}
create_tdir()
log('Packaging Flask')
flask_dep = package_flask()
log('Running extension tests')
log('Temporary Environment: %s', tdir)
for name in iter_extensions(only_approved):
log('Testing %s', name)
result = test_extension(name, interpreters, flask_dep)
if result.success:
log('Extension test succeeded')
else:
log('Extension test failed')
results[name] = result
return results
def render_results(results, approved):
from jinja2 import Template
items = results.values()
items.sort(key=lambda x: x.name.lower())
rv = Template(RESULT_TEMPATE, autoescape=True).render(results=items,
approved=approved)
fd, filename = tempfile.mkstemp(suffix='.html')
os.fdopen(fd, 'w').write(rv.encode('utf-8') + '\n')
return filename
def main():
for name, ret in test_all_extensions():
print name, ret
parser = argparse.ArgumentParser(description='Runs Flask extension tests')
parser.add_argument('--all', dest='all', action='store_true',
help='run against all extensions, not just approved')
parser.add_argument('--browse', dest='browse', action='store_true',
help='show browser with the result summary')
args = parser.parse_args()
results = run_tests(['py26'], not args.all)
filename = render_results(results, not args.all)
if args.browse:
import webbrowser
webbrowser.open('file:///' + filename.lstrip('/'))
print 'Results written to', filename
if __name__ == '__main__':