summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2015-04-24 06:52:55 -0400
committerChris McDonough <chrism@plope.com>2015-04-24 06:52:55 -0400
commitd2c49d66399e8caa0509822cab189c04d3f7ec35 (patch)
treef8f5663ca51ac47fb35732364b96990cd9cd42b4
parentbedd5f21f6f2c7660824cf901c24a31cad8f737d (diff)
parent43857c97c5c5cf437b31be013c6540ffc536d803 (diff)
downloadpyramid-d2c49d66399e8caa0509822cab189c04d3f7ec35.tar.gz
pyramid-d2c49d66399e8caa0509822cab189c04d3f7ec35.tar.bz2
pyramid-d2c49d66399e8caa0509822cab189c04d3f7ec35.zip
Merge branch 'master' of github.com:Pylons/pyramid
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml1
-rw-r--r--CHANGES.txt4
-rw-r--r--HACKING.txt25
-rw-r--r--docs/Makefile69
m---------docs/_themes0
-rw-r--r--docs/conf.py22
-rw-r--r--pyramid/authentication.py31
-rw-r--r--pyramid/authorization.py6
-rw-r--r--pyramid/config/__init__.py25
-rw-r--r--pyramid/config/adapters.py2
-rw-r--r--pyramid/config/assets.py8
-rw-r--r--pyramid/config/util.py2
-rw-r--r--pyramid/config/views.py70
-rw-r--r--pyramid/exceptions.py6
-rw-r--r--pyramid/httpexceptions.py82
-rw-r--r--pyramid/i18n.py7
-rw-r--r--pyramid/interfaces.py2
-rw-r--r--pyramid/path.py4
-rw-r--r--pyramid/registry.py8
-rw-r--r--pyramid/renderers.py9
-rw-r--r--pyramid/request.py32
-rw-r--r--pyramid/scaffolds/alchemy/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/copydir.py19
-rw-r--r--pyramid/scaffolds/starter/development.ini_tmpl2
-rw-r--r--pyramid/scaffolds/tests.py8
-rw-r--r--pyramid/scaffolds/zodb/+package+/models.py2
-rw-r--r--pyramid/scaffolds/zodb/development.ini_tmpl2
-rw-r--r--pyramid/scripts/pcreate.py2
-rw-r--r--pyramid/scripts/prequest.py14
-rw-r--r--pyramid/scripts/pserve.py32
-rw-r--r--pyramid/scripts/ptweens.py4
-rw-r--r--pyramid/scripts/pviews.py8
-rw-r--r--pyramid/static.py8
-rw-r--r--pyramid/testing.py13
-rw-r--r--pyramid/tests/test_renderers.py8
-rw-r--r--pyramid/tests/test_request.py4
-rw-r--r--pyramid/traversal.py54
-rw-r--r--pyramid/url.py19
-rw-r--r--pyramid/util.py7
-rw-r--r--pyramid/view.py8
-rw-r--r--rtd.txt1
-rw-r--r--setup.cfg4
-rw-r--r--setup.py1
-rw-r--r--tox.ini38
45 files changed, 359 insertions, 319 deletions
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 45397942b..000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "docs/_themes"]
- path = docs/_themes
- url = git://github.com/Pylons/pylons_sphinx_theme.git
diff --git a/.travis.yml b/.travis.yml
index 42b3073c7..ba077d342 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,7 @@ env:
- TOXENV=py2-docs
- TOXENV=py3-docs
- TOXENV=py2-cover,py3-cover,coverage
+ - TOXENV=pep8
install:
- travis_retry pip install tox
diff --git a/CHANGES.txt b/CHANGES.txt
index a9e159275..87e9f1f3a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -191,6 +191,10 @@ Bug Fixes
- Avoiding timing attacks against CSRF tokens.
See https://github.com/Pylons/pyramid/pull/1574
+- ``request.finished_callbacks`` and ``request.response_callbacks`` now
+ default to an iterable instead of ``None``. It may be checked for a length
+ of 0. This was the behavior in 1.5.
+
Deprecations
------------
diff --git a/HACKING.txt b/HACKING.txt
index e31dd8714..b82041c71 100644
--- a/HACKING.txt
+++ b/HACKING.txt
@@ -216,29 +216,12 @@ documentation in this package which references that API or behavior must be
changed to reflect the bug fix, ideally in the same commit that fixes the bug
or adds the feature. To build and review docs, use the following steps.
-1. After following the steps above in "Using a Development Checkout", update
- all git submodules from the top-level of your Pyramid checkout, like so:
+1. In the main Pyramid checkout directory, run ``./builddocs.sh`` (which just
+ turns around and runs ``tox -e py2-docs,py3-docs``)::
- $ git submodule update --init --recursive
+ $ ./builddocs.sh
- This will checkout theme subrepositories and prevent error conditions when
- HTML docs are generated.
-
-2. Next change into the submodule's directory and switch to a branch so that
- the submodule repositories are no longer "headless". Then update the
- repository to ensure that we have the latest updates.
- See http://chrisjean.com/2009/04/20/git-submodules-adding-using-removing-and-updating/ ::
-
- $ cd docs/_themes
- $ git checkout master
- $ git pull
-
-3. Change back to the main Pyramid checkout dir and run ``./builddocs`` (which
- just turns around and runs ``tox -e py2-docs,py3-docs``)::
-
- $ ./builddocs
-
-4. Open the ``.tox/_build/html/index.html`` file to see the resulting HTML
+2. Open the ``docs/_build/html/index.html`` file to see the resulting HTML
rendering.
Change Log
diff --git a/docs/Makefile b/docs/Makefile
index 12dc88bf8..546deb30a 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -5,11 +5,12 @@
SPHINXOPTS = -W
SPHINXBUILD = sphinx-build
PAPER =
+BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
@@ -23,71 +24,69 @@ help:
@echo " linkcheck to check all external links for integrity"
clean:
- -rm -rf _build/*
+ -rm -rf $(BUILDDIR)/*
-html: themes
- mkdir -p _build/html _build/doctrees
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
+html:
+ mkdir -p $(BUILDDIR)/html $(BUILDDIR)/doctrees
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
- @echo "Build finished. The HTML pages are in _build/html."
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
text:
- mkdir -p _build/text _build/doctrees
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) _build/text
+ mkdir -p $(BUILDDIR)/text $(BUILDDIR)/doctrees
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
- @echo "Build finished. The HTML pages are in _build/text."
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/text."
pickle:
- mkdir -p _build/pickle _build/doctrees
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
+ mkdir -p $(BUILDDIR)/pickle $(BUILDDIR)/doctrees
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files or run"
- @echo " sphinx-web _build/pickle"
+ @echo " sphinx-web $(BUILDDIR)/pickle"
@echo "to start the sphinx-web server."
web: pickle
-htmlhelp: themes
- mkdir -p _build/htmlhelp _build/doctrees
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
+htmlhelp:
+ mkdir -p $(BUILDDIR)/htmlhelp $(BUILDDIR)/doctrees
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in _build/htmlhelp."
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
latex:
- mkdir -p _build/latex _build/doctrees
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
- cp _static/*.png _build/latex
+ mkdir -p $(BUILDDIR)/latex $(BUILDDIR)/doctrees
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ cp _static/*.png $(BUILDDIR)/latex
./convert_images.sh
- cp _static/latex-warning.png _build/latex
- cp _static/latex-note.png _build/latex
+ cp _static/latex-warning.png $(BUILDDIR)/latex
+ cp _static/latex-note.png $(BUILDDIR)/latex
@echo
- @echo "Build finished; the LaTeX files are in _build/latex."
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make latexpdf' to build a PDF file from them."
latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C _build/latex all-pdf
- @echo "pdflatex finished; the PDF file is in _build/latex."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF file is in $(BUILDDIR)/latex."
changes:
- mkdir -p _build/changes _build/doctrees
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
+ mkdir -p $(BUILDDIR)/changes $(BUILDDIR)/doctrees
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
- @echo "The overview file is in _build/changes."
+ @echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
- mkdir -p _build/linkcheck _build/doctrees
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
+ mkdir -p $(BUILDDIR)/linkcheck $(BUILDDIR)/doctrees
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
- "or in _build/linkcheck/output.txt."
+ "or in $(BUILDDIR)/linkcheck/output.txt."
epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
- @echo "Build finished. The epub file is in _build/epub."
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-themes:
- cd ..; git submodule update --init --recursive; cd docs;
diff --git a/docs/_themes b/docs/_themes
deleted file mode 160000
-Subproject 382cba80fbd6a7424818d17ec63ca520e485f10
diff --git a/docs/conf.py b/docs/conf.py
index fa4578275..11e42c5f3 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -20,6 +20,7 @@ import warnings
warnings.simplefilter('ignore', DeprecationWarning)
import pkg_resources
+import pylons_sphinx_themes
# skip raw nodes
from sphinx.writers.text import TextTranslator
@@ -136,27 +137,8 @@ if book:
# Options for HTML output
# -----------------------
-# Add and use Pylons theme
-if 'sphinx-build' in ' '.join(sys.argv): # protect against dumb importers
- from subprocess import call, Popen, PIPE
- cwd = os.getcwd()
- p = Popen('which git', shell=True, stdout=PIPE)
- here = os.path.abspath(os.path.dirname(__file__))
- parent = os.path.abspath(os.path.dirname(here))
- _themes = os.path.join(here, '_themes')
- git = p.stdout.read().strip()
- try:
- os.chdir(parent)
- if not os.listdir(_themes):
- call([git, 'submodule', '--init'])
- else:
- call([git, 'submodule', 'update'])
- sys.path.append(_themes)
- finally:
- os.chdir(cwd)
-
-html_theme_path = ['_themes']
html_theme = 'pyramid'
+html_theme_path = pylons_sphinx_themes.get_html_themes_path()
html_theme_options = dict(
github_url='https://github.com/Pylons/pyramid',
in_progress='true',
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index e0e241e52..0924b5901 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -36,6 +36,7 @@ from pyramid.util import strings_differ
VALID_TOKEN = re.compile(r"^[A-Za-z][A-Za-z0-9+_-]*$")
+
class CallbackAuthenticationPolicy(object):
""" Abstract class """
@@ -77,10 +78,10 @@ class CallbackAuthenticationPolicy(object):
debug and self._log(
('use of userid %r is disallowed by any built-in Pyramid '
'security policy, returning None' % userid),
- 'authenticated_userid' ,
+ 'authenticated_userid',
request)
return None
-
+
if self.callback is None:
debug and self._log(
'there was no groupfinder callback; returning %r' % (userid,),
@@ -146,7 +147,7 @@ class CallbackAuthenticationPolicy(object):
request
)
return effective_principals
-
+
if self.callback is None:
debug and self._log(
'groupfinder callback is None, so groups is []',
@@ -178,9 +179,10 @@ class CallbackAuthenticationPolicy(object):
effective_principals,),
'effective_principals',
request
- )
+ )
return effective_principals
+
@implementer(IAuthenticationPolicy)
class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
""" A :app:`Pyramid` :term:`authentication policy` which
@@ -248,7 +250,7 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
'authenticated_userid',
request)
return None
-
+
if self._clean_principal(userid) is None:
self.debug and self._log(
('use of userid %r is disallowed by any built-in Pyramid '
@@ -336,7 +338,7 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
def remember(self, request, userid, **kw):
""" Store the ``userid`` as ``repoze.who.userid``.
-
+
The identity to authenticated to :mod:`repoze.who`
will contain the given userid as ``userid``, and
provide all keyword arguments as additional identity
@@ -839,15 +841,15 @@ class AuthTktCookieHelper(object):
hashalg='md5', parent_domain=False, domain=None):
serializer = _SimpleSerializer()
-
+
self.cookie_profile = CookieProfile(
- cookie_name = cookie_name,
- secure = secure,
- max_age = max_age,
- httponly = http_only,
- path = path,
+ cookie_name=cookie_name,
+ secure=secure,
+ max_age=max_age,
+ httponly=http_only,
+ path=path,
serializer=serializer
- )
+ )
self.secret = secret
self.cookie_name = cookie_name
@@ -882,7 +884,7 @@ class AuthTktCookieHelper(object):
kw['domains'] = domains
if max_age is not None:
kw['max_age'] = max_age
-
+
headers = profile.get_headers(value, **kw)
return headers
@@ -1187,4 +1189,3 @@ class _SimpleSerializer(object):
def dumps(self, appstruct):
return bytes_(appstruct)
-
diff --git a/pyramid/authorization.py b/pyramid/authorization.py
index 5e7baa19d..4845762ef 100644
--- a/pyramid/authorization.py
+++ b/pyramid/authorization.py
@@ -73,7 +73,7 @@ class ACLAuthorizationPolicy(object):
:class:`pyramid.security.ACLDenied` if not."""
acl = '<No ACL found on any object in resource lineage>'
-
+
for location in lineage(context):
try:
acl = location.__acl__
@@ -121,7 +121,7 @@ class ACLAuthorizationPolicy(object):
allowed_here = set()
denied_here = set()
-
+
if acl and callable(acl):
acl = acl()
@@ -129,7 +129,7 @@ class ACLAuthorizationPolicy(object):
if not is_nonstr_iter(ace_permissions):
ace_permissions = [ace_permissions]
if (ace_action == Allow) and (permission in ace_permissions):
- if not ace_principal in denied_here:
+ if ace_principal not in denied_here:
allowed_here.add(ace_principal)
if (ace_action == Deny) and (permission in ace_permissions):
denied_here.add(ace_principal)
diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py
index 549144fab..5a1b7b122 100644
--- a/pyramid/config/__init__.py
+++ b/pyramid/config/__init__.py
@@ -436,7 +436,7 @@ class Configurator(
if session_factory is not None:
self.set_session_factory(session_factory)
- tweens = aslist(registry.settings.get('pyramid.tweens', []))
+ tweens = aslist(registry.settings.get('pyramid.tweens', []))
for factory in tweens:
self._add_tween(factory, explicit=True)
@@ -965,11 +965,11 @@ class Configurator(
if package is None: # pragma: no cover
package = caller_package()
- ctorkw = {'config':self}
+ ctorkw = {'config': self}
ctorkw.update(kw)
scanner = self.venusian.Scanner(**ctorkw)
-
+
scanner.scan(package, categories=categories, onerror=onerror,
ignore=ignore)
@@ -990,7 +990,7 @@ class Configurator(
# Push the registry onto the stack in case any code that depends on
# the registry threadlocal APIs used in listeners subscribed to the
# IApplicationCreated event.
- self.manager.push({'registry':self.registry, 'request':None})
+ self.manager.push({'registry': self.registry, 'request': None})
try:
self.registry.notify(ApplicationCreated(app))
finally:
@@ -998,6 +998,7 @@ class Configurator(
return app
+
# this class is licensed under the ZPL (stolen from Zope)
class ActionState(object):
def __init__(self):
@@ -1151,9 +1152,9 @@ class ActionState(object):
# O(n lg n).
all_actions.extend(self.actions)
pending_actions = resume(resolveConflicts(
- executed_actions
- + list(pending_actions)
- + self.actions
+ executed_actions +
+ list(pending_actions) +
+ self.actions
))
self.actions = []
@@ -1287,18 +1288,20 @@ def resolveConflicts(actions):
for _, _, action in rest:
includepath = action['includepath']
# Test whether path is a prefix of opath
- if (includepath[:len(basepath)] != basepath # not a prefix
- or includepath == basepath):
+ if (includepath[:len(basepath)] != basepath or # not a prefix
+ includepath == basepath):
L = conflicts.setdefault(discriminator, [baseinfo])
L.append(action['info'])
if conflicts:
raise ConfigurationConflictError(conflicts)
- # sort conflict-resolved actions by (order, i) and yield them one by one
+ # sort conflict-resolved actions by (order, i) and yield them one
+ # by one
for a in [x[2] for x in sorted(output, key=operator.itemgetter(0, 1))]:
yield a
-
+
+
def expand_action(discriminator, callable=None, args=(), kw=None,
includepath=(), info=None, order=0, introspectables=()):
if kw is None:
diff --git a/pyramid/config/adapters.py b/pyramid/config/adapters.py
index 3d11980da..a68070134 100644
--- a/pyramid/config/adapters.py
+++ b/pyramid/config/adapters.py
@@ -255,7 +255,7 @@ class AdaptersConfiguratorMixin(object):
See :ref:`changing_the_traverser` for more information.
"""
iface = self.maybe_dotted(iface)
- adapter= self.maybe_dotted(adapter)
+ adapter = self.maybe_dotted(adapter)
def register(iface=iface):
if iface is None:
iface = Interface
diff --git a/pyramid/config/assets.py b/pyramid/config/assets.py
index 6dabea358..bbdf18ced 100644
--- a/pyramid/config/assets.py
+++ b/pyramid/config/assets.py
@@ -20,7 +20,7 @@ class OverrideProvider(pkg_resources.DefaultProvider):
reg = get_current_registry()
overrides = reg.queryUtility(IPackageOverrides, self.module_name)
return overrides
-
+
def get_resource_filename(self, manager, resource_name):
""" Return a true filesystem path for resource_name,
co-ordinating the extraction with manager, if the resource
@@ -33,12 +33,12 @@ class OverrideProvider(pkg_resources.DefaultProvider):
return filename
return pkg_resources.DefaultProvider.get_resource_filename(
self, manager, resource_name)
-
+
def get_resource_stream(self, manager, resource_name):
""" Return a readable file-like object for resource_name."""
overrides = self._get_overrides()
if overrides is not None:
- stream = overrides.get_stream(resource_name)
+ stream = overrides.get_stream(resource_name)
if stream is not None:
return stream
return pkg_resources.DefaultProvider.get_resource_stream(
@@ -387,5 +387,3 @@ class AssetsConfiguratorMixin(object):
self.action(None, register, introspectables=(intr,))
override_resource = override_asset # bw compat
-
-
diff --git a/pyramid/config/util.py b/pyramid/config/util.py
index 23cdc6be8..0fd9ef4a7 100644
--- a/pyramid/config/util.py
+++ b/pyramid/config/util.py
@@ -151,7 +151,7 @@ class PredicateList(object):
hashes = [hashes]
for h in hashes:
phash.update(bytes_(h))
- weights.append(1 << n+1)
+ weights.append(1 << n + 1)
preds.append(pred)
if kw:
raise ConfigurationError('Unknown predicate values: %r' % (kw,))
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index e2da950be..a522880c4 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -157,7 +157,7 @@ class ViewDeriver(object):
self.decorated_view(
self.rendered_view(
self.mapped_view(
- view)))))))))
+ view)))))))))
@wraps_view
def mapped_view(self, view):
@@ -232,7 +232,11 @@ class ViewDeriver(object):
permission = None
wrapped_view = view
- if self.authn_policy and self.authz_policy and (permission is not None):
+ if (
+ self.authn_policy and
+ self.authz_policy and
+ (permission is not None)
+ ):
def _permitted(context, request):
principals = self.authn_policy.effective_principals(request)
return self.authz_policy.permits(context, principals,
@@ -270,7 +274,8 @@ class ViewDeriver(object):
else:
principals = self.authn_policy.effective_principals(
request)
- msg = str(self.authz_policy.permits(context, principals,
+ msg = str(self.authz_policy.permits(context,
+ principals,
permission))
else:
msg = 'Allowed (no authorization policy in use)'
@@ -293,14 +298,16 @@ class ViewDeriver(object):
preds = self.kw.get('predicates', ())
if not preds:
return view
+
def predicate_wrapper(context, request):
for predicate in preds:
if not predicate(context, request):
view_name = getattr(view, '__name__', view)
raise PredicateMismatch(
- 'predicate mismatch for view %s (%s)' % (
- view_name, predicate.text()))
+ 'predicate mismatch for view %s (%s)' % (
+ view_name, predicate.text()))
return view(context, request)
+
def checker(context, request):
return all((predicate(context, request) for predicate in
preds))
@@ -321,7 +328,7 @@ class ViewDeriver(object):
(accept is None) and
(order == MAX_ORDER) and
(phash == DEFAULT_PHASH)
- ):
+ ):
return view # defaults
def attr_view(context, request):
return view(context, request)
@@ -350,7 +357,7 @@ class ViewDeriver(object):
def _rendered_view(self, view, view_renderer):
def rendered_view(context, request):
result = view(context, request)
- if result.__class__ is Response: # potential common case
+ if result.__class__ is Response: # potential common case
response = result
else:
registry = self.registry
@@ -365,9 +372,10 @@ class ViewDeriver(object):
renderer = renderers.RendererHelper(
name=renderer_name,
package=self.kw.get('package'),
- registry = registry)
+ registry=registry)
else:
renderer = view_renderer.clone()
+
if '__view__' in attrs:
view_inst = attrs.pop('__view__')
else:
@@ -380,9 +388,10 @@ class ViewDeriver(object):
def _response_resolved_view(self, view):
registry = self.registry
+
def viewresult_to_response(context, request):
result = view(context, request)
- if result.__class__ is Response: # common case
+ if result.__class__ is Response: # common case
response = result
else:
response = registry.queryAdapterOrSelf(result, IResponse)
@@ -413,6 +422,7 @@ class ViewDeriver(object):
return view
return decorator(view)
+
@implementer(IViewMapper)
@provider(IViewMapperFactory)
class DefaultViewMapper(object):
@@ -631,7 +641,8 @@ class ViewsConfiguratorMixin(object):
http_cache=None,
match_param=None,
check_csrf=None,
- **predicates):
+ **predicates
+ ):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
down below into *predicate* arguments and *non-predicate*
@@ -1126,7 +1137,7 @@ class ViewsConfiguratorMixin(object):
if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
- registry = self.registry)
+ registry=self.registry)
if accept is not None:
accept = accept.lower()
@@ -1154,7 +1165,11 @@ class ViewsConfiguratorMixin(object):
# is. It can't be computed any sooner because thirdparty
# predicates may not yet exist when add_view is called.
order, preds, phash = predlist.make(self, **pvals)
- view_intr.update({'phash':phash, 'order':order, 'predicates':preds})
+ view_intr.update({
+ 'phash': phash,
+ 'order': order,
+ 'predicates': preds
+ })
return ('view', context, name, route_name, phash)
discriminator = Deferred(discrim_func)
@@ -1353,7 +1368,7 @@ class ViewsConfiguratorMixin(object):
tmpl_intr is not None and
intrspc is not None and
intrspc.get('renderer factories', renderer_type) is not None
- ):
+ ):
# allow failure of registered template factories to be deferred
# until view execution, like other bad renderer factories; if
# we tried to relate this to an existing renderer factory
@@ -1395,7 +1410,7 @@ class ViewsConfiguratorMixin(object):
permission,
permission,
'permission'
- )
+ )
perm_intr['value'] = permission
perm_intr.relate('views', discriminator)
introspectables.append(perm_intr)
@@ -1427,7 +1442,7 @@ class ViewsConfiguratorMixin(object):
factory,
weighs_more_than=weighs_more_than,
weighs_less_than=weighs_less_than
- )
+ )
def add_default_view_predicates(self):
p = pyramid.config.predicates
@@ -1445,7 +1460,7 @@ class ViewsConfiguratorMixin(object):
('physical_path', p.PhysicalPathPredicate),
('effective_principals', p.EffectivePrincipalsPredicate),
('custom', p.CustomPredicate),
- ):
+ ):
self.add_view_predicate(name, factory)
def derive_view(self, view, attr=None, renderer=None):
@@ -1537,7 +1552,7 @@ class ViewsConfiguratorMixin(object):
if isinstance(renderer, string_types):
renderer = renderers.RendererHelper(
name=renderer, package=self.package,
- registry = self.registry)
+ registry=self.registry)
if renderer is None:
# use default renderer if one exists
if self.registry.queryUtility(IRendererFactory) is not None:
@@ -1585,7 +1600,7 @@ class ViewsConfiguratorMixin(object):
mapper=None,
match_param=None,
**predicates
- ):
+ ):
""" Add a forbidden view to the current configuration state. The
view will be called when Pyramid or application code raises a
:exc:`pyramid.httpexceptions.HTTPForbidden` exception and the set of
@@ -1618,7 +1633,7 @@ class ViewsConfiguratorMixin(object):
raise ConfigurationError(
'%s may not be used as an argument to add_forbidden_view'
% arg
- )
+ )
if view is None:
view = default_exceptionresponse_view
@@ -1643,7 +1658,7 @@ class ViewsConfiguratorMixin(object):
permission=NO_PERMISSION_REQUIRED,
attr=attr,
renderer=renderer,
- )
+ )
settings.update(predicates)
return self.add_view(**settings)
@@ -1672,7 +1687,7 @@ class ViewsConfiguratorMixin(object):
match_param=None,
append_slash=False,
**predicates
- ):
+ ):
""" Add a default Not Found View to the current configuration state.
The view will be called when Pyramid or application code raises an
:exc:`pyramid.httpexceptions.HTTPNotFound` exception (e.g. when a
@@ -1730,7 +1745,7 @@ class ViewsConfiguratorMixin(object):
raise ConfigurationError(
'%s may not be used as an argument to add_notfound_view'
% arg
- )
+ )
if view is None:
view = default_exceptionresponse_view
@@ -1753,7 +1768,7 @@ class ViewsConfiguratorMixin(object):
match_param=match_param,
route_name=route_name,
permission=NO_PERMISSION_REQUIRED,
- )
+ )
settings.update(predicates)
if append_slash:
view = self._derive_view(view, attr=attr, renderer=renderer)
@@ -1931,7 +1946,7 @@ def isexception(o):
return (
isinstance(o, Exception) or
(inspect.isclass(o) and (issubclass(o, Exception)))
- )
+ )
@implementer(IStaticURLInfo)
@@ -2045,7 +2060,7 @@ class StaticURLInfo(object):
# register a route using the computed view, permission, and
# pattern, plus any extras passed to us via add_static_view
- pattern = "%s*subpath" % name # name already ends with slash
+ pattern = "%s*subpath" % name # name already ends with slash
if config.route_prefix:
route_name = '__%s/%s' % (config.route_prefix, name)
else:
@@ -2057,12 +2072,12 @@ class StaticURLInfo(object):
permission=permission,
context=context,
renderer=renderer,
- )
+ )
def register():
registrations = self._get_registrations(config.registry)
- names = [ t[0] for t in registrations ]
+ names = [t[0] for t in registrations]
if name in names:
idx = names.index(name)
@@ -2079,4 +2094,3 @@ class StaticURLInfo(object):
intr['spec'] = spec
config.action(None, callable=register, introspectables=(intr,))
-
diff --git a/pyramid/exceptions.py b/pyramid/exceptions.py
index c59d109df..c1481ce9c 100644
--- a/pyramid/exceptions.py
+++ b/pyramid/exceptions.py
@@ -33,7 +33,7 @@ class PredicateMismatch(HTTPNotFound):
be treated as :class:`HTTPNotFound`` by any exception view
registrations. Thus, typically, this exception will not be seen
publicly.
-
+
However, this exception will be raised if the predicates of all
views configured to handle another exception context cannot be
successfully matched. For instance, if a view is configured to
@@ -79,7 +79,7 @@ class ConfigurationConflictError(ConfigurationError):
r.append(" For: %s" % (discriminator, ))
for info in infos:
for line in str(info).rstrip().split(CR):
- r.append(" "+line)
+ r.append(" " + line)
return CR.join(r)
@@ -109,5 +109,3 @@ class CyclicDependencyError(Exception):
L.append('%r sorts before %r' % (dependent, dependees))
msg = 'Implicit ordering cycle:' + '; '.join(L)
return msg
-
-
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index a30129e16..465769834 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -116,10 +116,10 @@ Substitution of response headers into template values is always performed.
Substitution of WSGI environment values is performed if a ``request`` is
passed to the exception's constructor.
-The subclasses of :class:`~_HTTPMove`
+The subclasses of :class:`~_HTTPMove`
(:class:`~HTTPMultipleChoices`, :class:`~HTTPMovedPermanently`,
:class:`~HTTPFound`, :class:`~HTTPSeeOther`, :class:`~HTTPUseProxy` and
-:class:`~HTTPTemporaryRedirect`) are redirections that require a ``Location``
+:class:`~HTTPTemporaryRedirect`) are redirections that require a ``Location``
field. Reflecting this, these subclasses have one additional keyword argument:
``location``, which indicates the location to which to redirect.
"""
@@ -296,7 +296,7 @@ class HTTPError(HTTPException):
base class for exceptions with status codes in the 400s and 500s
This is an exception which indicates that an error has occurred,
- and that any work in progress should not be committed.
+ and that any work in progress should not be committed.
"""
class HTTPRedirection(HTTPException):
@@ -324,7 +324,7 @@ class HTTPOk(HTTPSuccessful):
subclass of :class:`~HTTPSuccessful`
Indicates that the request has succeeded.
-
+
code: 200, title: OK
"""
code = 200
@@ -336,7 +336,7 @@ class HTTPCreated(HTTPSuccessful):
This indicates that request has been fulfilled and resulted in a new
resource being created.
-
+
code: 201, title: Created
"""
code = 201
@@ -375,7 +375,7 @@ class HTTPNoContent(HTTPSuccessful):
This indicates that the server has fulfilled the request but does
not need to return an entity-body, and might want to return updated
metainformation.
-
+
code: 204, title: No Content
"""
code = 204
@@ -389,7 +389,7 @@ class HTTPResetContent(HTTPSuccessful):
This indicates that the server has fulfilled the request and
the user agent SHOULD reset the document view which caused the
request to be sent.
-
+
code: 205, title: Reset Content
"""
code = 205
@@ -402,7 +402,7 @@ class HTTPPartialContent(HTTPSuccessful):
This indicates that the server has fulfilled the partial GET
request for the resource.
-
+
code: 206, title: Partial Content
"""
code = 206
@@ -460,7 +460,7 @@ class HTTPMultipleChoices(_HTTPMove):
and agent-driven negotiation information is being provided so that
the user can select a preferred representation and redirect its
request to that location.
-
+
code: 300, title: Multiple Choices
"""
code = 300
@@ -485,7 +485,7 @@ class HTTPFound(_HTTPMove):
This indicates that the requested resource resides temporarily under
a different URI.
-
+
code: 302, title: Found
"""
code = 302
@@ -501,7 +501,7 @@ class HTTPSeeOther(_HTTPMove):
This indicates that the response to the request can be found under
a different URI and SHOULD be retrieved using a GET method on that
resource.
-
+
code: 303, title: See Other
"""
code = 303
@@ -528,7 +528,7 @@ class HTTPUseProxy(_HTTPMove):
This indicates that the requested resource MUST be accessed through
the proxy given by the Location field.
-
+
code: 305, title: Use Proxy
"""
# Not a move, but looks a little like one
@@ -543,7 +543,7 @@ class HTTPTemporaryRedirect(_HTTPMove):
This indicates that the requested resource resides temporarily
under a different URI.
-
+
code: 307, title: Temporary Redirect
"""
code = 307
@@ -583,7 +583,7 @@ class HTTPUnauthorized(HTTPClientError):
subclass of :class:`~HTTPClientError`
This indicates that the request requires user authentication.
-
+
code: 401, title: Unauthorized
"""
code = 401
@@ -597,7 +597,7 @@ class HTTPUnauthorized(HTTPClientError):
class HTTPPaymentRequired(HTTPClientError):
"""
subclass of :class:`~HTTPClientError`
-
+
code: 402, title: Payment Required
"""
code = 402
@@ -636,7 +636,7 @@ class HTTPForbidden(HTTPClientError):
# differences from webob.exc.HTTPForbidden:
#
# - accepts a ``result`` keyword argument
- #
+ #
# - overrides constructor to set ``self.result``
#
# differences from older ``pyramid.exceptions.Forbidden``:
@@ -659,7 +659,7 @@ class HTTPNotFound(HTTPClientError):
This indicates that the server did not find anything matching the
Request-URI.
-
+
code: 404, title: Not Found
Raise this exception within :term:`view` code to immediately
@@ -702,7 +702,7 @@ class HTTPNotAcceptable(HTTPClientError):
capable of generating response entities which have content
characteristics not acceptable according to the accept headers
sent in the request.
-
+
code: 406, title: Not Acceptable
"""
# differences from webob.exc.HTTPNotAcceptable:
@@ -717,7 +717,7 @@ class HTTPProxyAuthenticationRequired(HTTPClientError):
This is similar to 401, but indicates that the client must first
authenticate itself with the proxy.
-
+
code: 407, title: Proxy Authentication Required
"""
code = 407
@@ -730,7 +730,7 @@ class HTTPRequestTimeout(HTTPClientError):
This indicates that the client did not produce a request within
the time that the server was prepared to wait.
-
+
code: 408, title: Request Timeout
"""
code = 408
@@ -744,7 +744,7 @@ class HTTPConflict(HTTPClientError):
This indicates that the request could not be completed due to a
conflict with the current state of the resource.
-
+
code: 409, title: Conflict
"""
code = 409
@@ -758,7 +758,7 @@ class HTTPGone(HTTPClientError):
This indicates that the requested resource is no longer available
at the server and no forwarding address is known.
-
+
code: 410, title: Gone
"""
code = 410
@@ -772,7 +772,7 @@ class HTTPLengthRequired(HTTPClientError):
This indicates that the server refuses to accept the request
without a defined Content-Length.
-
+
code: 411, title: Length Required
"""
code = 411
@@ -786,7 +786,7 @@ class HTTPPreconditionFailed(HTTPClientError):
This indicates that the precondition given in one or more of the
request-header fields evaluated to false when it was tested on the
server.
-
+
code: 412, title: Precondition Failed
"""
code = 412
@@ -814,7 +814,7 @@ class HTTPRequestURITooLong(HTTPClientError):
This indicates that the server is refusing to service the request
because the Request-URI is longer than the server is willing to
interpret.
-
+
code: 414, title: Request-URI Too Long
"""
code = 414
@@ -828,7 +828,7 @@ class HTTPUnsupportedMediaType(HTTPClientError):
This indicates that the server is refusing to service the request
because the entity of the request is in a format not supported by
the requested resource for the requested method.
-
+
code: 415, title: Unsupported Media Type
"""
# differences from webob.exc.HTTPUnsupportedMediaType:
@@ -846,7 +846,7 @@ class HTTPRequestRangeNotSatisfiable(HTTPClientError):
range-specifier values in this field overlap the current extent
of the selected resource, and the request did not include an
If-Range request-header field.
-
+
code: 416, title: Request Range Not Satisfiable
"""
code = 416
@@ -859,7 +859,7 @@ class HTTPExpectationFailed(HTTPClientError):
This indidcates that the expectation given in an Expect
request-header field could not be met by this server.
-
+
code: 417, title: Expectation Failed
"""
code = 417
@@ -871,13 +871,13 @@ class HTTPUnprocessableEntity(HTTPClientError):
subclass of :class:`~HTTPClientError`
This indicates that the server is unable to process the contained
- instructions.
+ instructions.
May be used to notify the client that their JSON/XML is well formed, but
not correct for the current request.
See RFC4918 section 11 for more information.
-
+
code: 422, title: Unprocessable Entity
"""
## Note: from WebDAV
@@ -890,7 +890,7 @@ class HTTPLocked(HTTPClientError):
subclass of :class:`~HTTPClientError`
This indicates that the resource is locked.
-
+
code: 423, title: Locked
"""
## Note: from WebDAV
@@ -904,7 +904,7 @@ class HTTPFailedDependency(HTTPClientError):
This indicates that the method could not be performed because the
requested action depended on another action and that action failed.
-
+
code: 424, title: Failed Dependency
"""
## Note: from WebDAV
@@ -1003,7 +1003,7 @@ class HTTPNotImplemented(HTTPServerError):
This indicates that the server does not support the functionality
required to fulfill the request.
-
+
code: 501, title: Not Implemented
"""
# differences from webob.exc.HTTPNotAcceptable:
@@ -1019,7 +1019,7 @@ class HTTPBadGateway(HTTPServerError):
This indicates that the server, while acting as a gateway or proxy,
received an invalid response from the upstream server it accessed
in attempting to fulfill the request.
-
+
code: 502, title: Bad Gateway
"""
code = 502
@@ -1032,7 +1032,7 @@ class HTTPServiceUnavailable(HTTPServerError):
This indicates that the server is currently unable to handle the
request due to a temporary overloading or maintenance of the server.
-
+
code: 503, title: Service Unavailable
"""
code = 503
@@ -1075,7 +1075,7 @@ class HTTPInsufficientStorage(HTTPServerError):
This indicates that the server does not have enough space to save
the resource.
-
+
code: 507, title: Insufficient Storage
"""
code = 507
@@ -1100,12 +1100,14 @@ def default_exceptionresponse_view(context, request):
context = request.exception or context
return context # assumed to be an IResponse
-status_map={}
+status_map = {}
code = None
for name, value in list(globals().items()):
- if (isinstance(value, class_types) and
- issubclass(value, HTTPException)
- and not name.startswith('_')):
+ if (
+ isinstance(value, class_types) and
+ issubclass(value, HTTPException) and
+ not name.startswith('_')
+ ):
code = getattr(value, 'code', None)
if code:
status_map[code] = value
diff --git a/pyramid/i18n.py b/pyramid/i18n.py
index c30351f7a..458f6168d 100644
--- a/pyramid/i18n.py
+++ b/pyramid/i18n.py
@@ -8,9 +8,6 @@ from translationstring import (
TranslationStringFactory, # API
)
-TranslationString = TranslationString # PyFlakes
-TranslationStringFactory = TranslationStringFactory # PyFlakes
-
from pyramid.compat import PY3
from pyramid.decorator import reify
@@ -22,6 +19,10 @@ from pyramid.interfaces import (
from pyramid.threadlocal import get_current_registry
+TranslationString = TranslationString # PyFlakes
+TranslationStringFactory = TranslationStringFactory # PyFlakes
+
+
class Localizer(object):
"""
An object providing translation and pluralizations related to
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index bab91b0ee..7b9a850d1 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -149,7 +149,7 @@ class IResponse(Interface):
"""Gets and sets and deletes the Content-MD5 header. For more
information on Content-MD5 see RFC 2616 section 14.14.""")
- content_range = Attribute(
+ content_range = Attribute(
"""Gets and sets and deletes the Content-Range header. For more
information on Content-Range see section 14.16. Converts using
ContentRange object.""")
diff --git a/pyramid/path.py b/pyramid/path.py
index f2d8fff55..b79c5a6ac 100644
--- a/pyramid/path.py
+++ b/pyramid/path.py
@@ -15,7 +15,7 @@ init_names = [ '__init__%s' % x[0] for x in imp.get_suffixes() if
def caller_path(path, level=2):
if not os.path.isabs(path):
- module = caller_module(level+1)
+ module = caller_module(level + 1)
prefix = package_path(module)
path = os.path.join(prefix, path)
return path
@@ -53,7 +53,7 @@ def package_of(pkg_or_module):
def caller_package(level=2, caller_module=caller_module):
# caller_module in arglist for tests
- module = caller_module(level+1)
+ module = caller_module(level + 1)
f = getattr(module, '__file__', '')
if (('__init__.py' in f) or ('__init__$py' in f)): # empty at >>>
# Module is a package
diff --git a/pyramid/registry.py b/pyramid/registry.py
index fb4ec52e2..1073134ff 100644
--- a/pyramid/registry.py
+++ b/pyramid/registry.py
@@ -129,10 +129,10 @@ class Introspector(object):
values = category.values()
values = sorted(set(values), key=sort_key)
return [
- {'introspectable':intr,
- 'related':self.related(intr)}
- for intr in values
- ]
+ {'introspectable': intr,
+ 'related': self.related(intr)}
+ for intr in values
+ ]
def categorized(self, sort_key=None):
L = []
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index de0b1d27f..42296bad1 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -1,6 +1,7 @@
import contextlib
import json
import os
+import re
from zope.interface import (
implementer,
@@ -23,6 +24,8 @@ from pyramid.decorator import reify
from pyramid.events import BeforeRender
+from pyramid.httpexceptions import HTTPBadRequest
+
from pyramid.path import caller_package
from pyramid.response import _get_response_factory
@@ -305,6 +308,8 @@ class JSON(object):
json_renderer_factory = JSON() # bw compat
+JSONP_VALID_CALLBACK = re.compile(r"^[a-zA-Z_$][0-9a-zA-Z_$]+$")
+
class JSONP(JSON):
""" `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper
which implements a hybrid json/jsonp renderer. JSONP is useful for
@@ -385,7 +390,11 @@ class JSONP(JSON):
body = val
if request is not None:
callback = request.GET.get(self.param_name)
+
if callback is not None:
+ if not JSONP_VALID_CALLBACK.match(callback):
+ raise HTTPBadRequest('Invalid JSONP callback function name.')
+
ct = 'application/javascript'
body = '%s(%s);' % (callback, val)
response = request.response
diff --git a/pyramid/request.py b/pyramid/request.py
index 13b8cd339..45d936cef 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -22,7 +22,7 @@ from pyramid.compat import (
from pyramid.decorator import reify
from pyramid.i18n import LocalizerRequestMixin
-from pyramid.response import Response, _get_response_factory
+from pyramid.response import Response, _get_response_factory
from pyramid.security import (
AuthenticationAPIMixin,
AuthorizationAPIMixin,
@@ -37,8 +37,14 @@ class TemplateContext(object):
pass
class CallbackMethodsMixin(object):
- response_callbacks = None
- finished_callbacks = None
+ @reify
+ def finished_callbacks(self):
+ return deque()
+
+ @reify
+ def response_callbacks(self):
+ return deque()
+
def add_response_callback(self, callback):
"""
Add a callback to the set of callbacks to be called by the
@@ -64,7 +70,7 @@ class CallbackMethodsMixin(object):
called if an exception happens in application code, or if the
response object returned by :term:`view` code is invalid.
- All response callbacks are called *after* the tweens and
+ All response callbacks are called *after* the tweens and
*before* the :class:`pyramid.events.NewResponse` event is sent.
Errors raised by callbacks are not handled specially. They
@@ -76,11 +82,7 @@ class CallbackMethodsMixin(object):
See also :ref:`using_response_callbacks`.
"""
- callbacks = self.response_callbacks
- if callbacks is None:
- callbacks = deque()
- callbacks.append(callback)
- self.response_callbacks = callbacks
+ self.response_callbacks.append(callback)
def _process_response_callbacks(self, response):
callbacks = self.response_callbacks
@@ -135,12 +137,7 @@ class CallbackMethodsMixin(object):
See also :ref:`using_finished_callbacks`.
"""
-
- callbacks = self.finished_callbacks
- if callbacks is None:
- callbacks = deque()
- callbacks.append(callback)
- self.finished_callbacks = callbacks
+ self.finished_callbacks.append(callback)
def _process_finished_callbacks(self):
callbacks = self.finished_callbacks
@@ -237,7 +234,7 @@ class Request(
def json_body(self):
return json.loads(text_(self.body, self.charset))
-
+
def route_request_iface(name, bases=()):
# zope.interface treats the __name__ as the __doc__ and changes __name__
# to None for interfaces that contain spaces if you do not pass a
@@ -251,9 +248,10 @@ def route_request_iface(name, bases=()):
iface.combined = InterfaceClass(
'%s_combined_IRequest' % name,
bases=(iface, IRequest),
- __doc__ = 'route_request_iface-generated combined interface')
+ __doc__='route_request_iface-generated combined interface')
return iface
+
def add_global_response_headers(request, headerlist):
def add_headers(request, response):
for k, v in headerlist:
diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl
index 448803c8f..07811f7c3 100644
--- a/pyramid/scaffolds/alchemy/development.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
+host = 127.0.0.1
port = 6543
###
diff --git a/pyramid/scaffolds/copydir.py b/pyramid/scaffolds/copydir.py
index 3b871dc19..4471777f2 100644
--- a/pyramid/scaffolds/copydir.py
+++ b/pyramid/scaffolds/copydir.py
@@ -15,6 +15,7 @@ from pyramid.compat import (
fsenc = sys.getfilesystemencoding()
+
class SkipTemplate(Exception):
"""
Raised to indicate that the template should not be copied over.
@@ -61,7 +62,7 @@ def copy_dir(source, dest, vars, verbosity, simulate, indent=0,
names = sorted(pkg_resources.resource_listdir(source[0], source[1]))
else:
names = sorted(os.listdir(source))
- pad = ' '*(indent*2)
+ pad = ' ' * (indent * 2)
if not os.path.exists(dest):
if verbosity >= 1:
out('%sCreating %s/' % (pad, dest))
@@ -90,7 +91,7 @@ def copy_dir(source, dest, vars, verbosity, simulate, indent=0,
if verbosity:
out('%sRecursing into %s' % (pad, os.path.basename(full)))
copy_dir((source[0], full), dest_full, vars, verbosity, simulate,
- indent=indent+1, sub_vars=sub_vars,
+ indent=indent + 1, sub_vars=sub_vars,
interactive=interactive, overwrite=overwrite,
template_renderer=template_renderer, out_=out_)
continue
@@ -98,7 +99,7 @@ def copy_dir(source, dest, vars, verbosity, simulate, indent=0,
if verbosity:
out('%sRecursing into %s' % (pad, os.path.basename(full)))
copy_dir(full, dest_full, vars, verbosity, simulate,
- indent=indent+1, sub_vars=sub_vars,
+ indent=indent + 1, sub_vars=sub_vars,
interactive=interactive, overwrite=overwrite,
template_renderer=template_renderer, out_=out_)
continue
@@ -185,14 +186,14 @@ def query_interactive(src_fn, dest_fn, src_content, dest_content,
dest_content.splitlines(),
src_content.splitlines(),
dest_fn, src_fn))
- added = len([l for l in u_diff if l.startswith('+')
- and not l.startswith('+++')])
- removed = len([l for l in u_diff if l.startswith('-')
- and not l.startswith('---')])
+ added = len([l for l in u_diff if l.startswith('+') and
+ not l.startswith('+++')])
+ removed = len([l for l in u_diff if l.startswith('-') and
+ not l.startswith('---')])
if added > removed:
- msg = '; %i lines added' % (added-removed)
+ msg = '; %i lines added' % (added - removed)
elif removed > added:
- msg = '; %i lines removed' % (removed-added)
+ msg = '; %i lines removed' % (removed - added)
else:
msg = ''
out('Replace %i bytes with %i bytes (%i/%i lines changed%s)' % (
diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl
index c2a28e178..ae9460b11 100644
--- a/pyramid/scaffolds/starter/development.ini_tmpl
+++ b/pyramid/scaffolds/starter/development.ini_tmpl
@@ -24,7 +24,7 @@ pyramid.includes =
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
+host = 127.0.0.1
port = 6543
###
diff --git a/pyramid/scaffolds/tests.py b/pyramid/scaffolds/tests.py
index db828759e..49358c1cf 100644
--- a/pyramid/scaffolds/tests.py
+++ b/pyramid/scaffolds/tests.py
@@ -10,8 +10,9 @@ try:
except ImportError:
import httplib
+
class TemplateTest(object):
- def make_venv(self, directory): # pragma: no cover
+ def make_venv(self, directory): # pragma: no cover
import virtualenv
from virtualenv import Logger
logger = Logger([(Logger.level_for_integer(2), sys.stdout)])
@@ -20,7 +21,8 @@ class TemplateTest(object):
site_packages=False,
clear=False,
unzip_setuptools=True)
- def install(self, tmpl_name): # pragma: no cover
+
+ def install(self, tmpl_name): # pragma: no cover
try:
self.old_cwd = os.getcwd()
self.directory = tempfile.mkdtemp()
@@ -58,7 +60,7 @@ class TemplateTest(object):
if hastoolbar:
assert toolbarchunk in data, ininame
else:
- assert not toolbarchunk in data, ininame
+ assert toolbarchunk not in data, ininame
finally:
proc.terminate()
finally:
diff --git a/pyramid/scaffolds/zodb/+package+/models.py b/pyramid/scaffolds/zodb/+package+/models.py
index a94b36ef4..e5aa3e9f7 100644
--- a/pyramid/scaffolds/zodb/+package+/models.py
+++ b/pyramid/scaffolds/zodb/+package+/models.py
@@ -6,7 +6,7 @@ class MyModel(PersistentMapping):
def appmaker(zodb_root):
- if not 'app_root' in zodb_root:
+ if 'app_root' not in zodb_root:
app_root = MyModel()
zodb_root['app_root'] = app_root
import transaction
diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl
index 199ddfab4..a44b61686 100644
--- a/pyramid/scaffolds/zodb/development.ini_tmpl
+++ b/pyramid/scaffolds/zodb/development.ini_tmpl
@@ -29,7 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
[server:main]
use = egg:waitress#main
-host = 0.0.0.0
+host = 127.0.0.1
port = 6543
###
diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py
index d2c5f8c27..f6376f575 100644
--- a/pyramid/scripts/pcreate.py
+++ b/pyramid/scripts/pcreate.py
@@ -132,7 +132,7 @@ class PCreateCommand(object):
for scaffold in scaffolds:
self.out(' %s:%s %s' % (
scaffold.name,
- ' '*(max_name-len(scaffold.name)), scaffold.summary))
+ ' ' * (max_name - len(scaffold.name)), scaffold.summary))
else:
self.out('No scaffolds available')
return 0
diff --git a/pyramid/scripts/prequest.py b/pyramid/scripts/prequest.py
index 34eeadf32..61e422c64 100644
--- a/pyramid/scripts/prequest.py
+++ b/pyramid/scripts/prequest.py
@@ -52,8 +52,10 @@ class PRequestCommand(object):
parser.add_option(
'-n', '--app-name',
dest='app_name',
- metavar= 'NAME',
- help="Load the named application from the config file (default 'main')",
+ metavar='NAME',
+ help=(
+ "Load the named application from the config file (default 'main')"
+ ),
type="string",
)
parser.add_option(
@@ -62,8 +64,10 @@ class PRequestCommand(object):
metavar='NAME:VALUE',
type='string',
action='append',
- help="Header to add to request (you can use this option multiple times)"
- )
+ help=(
+ "Header to add to request (you can use this option multiple times)"
+ ),
+ )
parser.add_option(
'-d', '--display-headers',
dest='display_headers',
@@ -167,7 +171,7 @@ class PRequestCommand(object):
if name.lower() == 'content-type':
name = 'CONTENT_TYPE'
else:
- name = 'HTTP_'+name.upper().replace('-', '_')
+ name = 'HTTP_' + name.upper().replace('-', '_')
environ[name] = value
request = Request.blank(path, environ=environ)
diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py
index 57e4ab012..e97bdcd48 100644
--- a/pyramid/scripts/pserve.py
+++ b/pyramid/scripts/pserve.py
@@ -143,7 +143,7 @@ class PServeCommand(object):
default=default_verbosity,
dest='verbose',
action='count',
- help="Set verbose level (default "+str(default_verbosity)+")")
+ help="Set verbose level (default " + str(default_verbosity) + ")")
parser.add_option(
'-q', '--quiet',
action='store_const',
@@ -188,15 +188,17 @@ class PServeCommand(object):
print(msg)
def get_options(self):
- if (len(self.args) > 1
- and self.args[1] in self.possible_subcommands):
+ if (
+ len(self.args) > 1 and
+ self.args[1] in self.possible_subcommands
+ ):
restvars = self.args[2:]
else:
restvars = self.args[1:]
return parse_vars(restvars)
- def run(self): # pragma: no cover
+ def run(self): # pragma: no cover
if self.options.stop_daemon:
return self.stop_daemon()
@@ -213,8 +215,10 @@ class PServeCommand(object):
return 2
app_spec = self.args[0]
- if (len(self.args) > 1
- and self.args[1] in self.possible_subcommands):
+ if (
+ len(self.args) > 1 and
+ self.args[1] in self.possible_subcommands
+ ):
cmd = self.args[1]
else:
cmd = None
@@ -299,8 +303,10 @@ class PServeCommand(object):
self.out(str(ex))
return 2
- if (self.options.monitor_restart
- and not os.environ.get(self._monitor_environ_key)):
+ if (
+ self.options.monitor_restart and
+ not os.environ.get(self._monitor_environ_key)
+ ):
return self.restart_with_monitor()
if self.options.pid_file:
@@ -568,7 +574,9 @@ class PServeCommand(object):
def change_user_group(self, user, group): # pragma: no cover
if not user and not group:
return
- import pwd, grp
+ import pwd
+ import grp
+
uid = gid = None
if group:
try:
@@ -602,6 +610,7 @@ class PServeCommand(object):
if uid:
os.setuid(uid)
+
class LazyWriter(object):
"""
@@ -762,11 +771,12 @@ class _methodwrapper(object):
self.type = type
def __call__(self, *args, **kw):
- assert not 'self' in kw and not 'cls' in kw, (
+ assert 'self' not in kw and 'cls' not in kw, (
"You cannot use 'self' or 'cls' arguments to a "
"classinstancemethod")
return self.func(*((self.obj, self.type) + args), **kw)
+
class Monitor(object): # pragma: no cover
"""
A file monitor and server restarter.
@@ -860,7 +870,7 @@ class Monitor(object): # pragma: no cover
continue
if filename.endswith('.pyc') and os.path.exists(filename[:-1]):
mtime = max(os.stat(filename[:-1]).st_mtime, mtime)
- if not filename in self.module_mtimes:
+ if filename not in self.module_mtimes:
self.module_mtimes[filename] = mtime
elif self.module_mtimes[filename] < mtime:
print("%s changed; reloading..." % filename)
diff --git a/pyramid/scripts/ptweens.py b/pyramid/scripts/ptweens.py
index ad52d5d8f..a7aa009da 100644
--- a/pyramid/scripts/ptweens.py
+++ b/pyramid/scripts/ptweens.py
@@ -42,7 +42,7 @@ class PTweensCommand(object):
def _get_tweens(self, registry):
from pyramid.config import Configurator
- config = Configurator(registry = registry)
+ config = Configurator(registry=registry)
return config.registry.queryUtility(ITweens)
def out(self, msg): # pragma: no cover
@@ -52,7 +52,7 @@ class PTweensCommand(object):
def show_chain(self, chain):
fmt = '%-10s %-65s'
self.out(fmt % ('Position', 'Name'))
- self.out(fmt % ('-'*len('Position'), '-'*len('Name')))
+ self.out(fmt % ('-' * len('Position'), '-' * len('Name')))
self.out(fmt % ('-', INGRESS))
for pos, (name, _) in enumerate(chain):
self.out(fmt % (pos, name))
diff --git a/pyramid/scripts/pviews.py b/pyramid/scripts/pviews.py
index 4922c3a70..9018eddb4 100644
--- a/pyramid/scripts/pviews.py
+++ b/pyramid/scripts/pviews.py
@@ -143,10 +143,8 @@ class PViewsCommand(object):
if traverser is None:
traverser = ResourceTreeTraverser(root)
tdict = traverser(request)
- context, view_name, subpath, traversed, vroot, vroot_path =(
- tdict['context'], tdict['view_name'], tdict['subpath'],
- tdict['traversed'], tdict['virtual_root'],
- tdict['virtual_root_path'])
+ context, view_name = (tdict['context'], tdict['view_name'])
+
attrs.update(tdict)
# find a view callable
@@ -218,7 +216,7 @@ class PViewsCommand(object):
if not IMultiView.providedBy(view_wrapper):
# single view for this route, so repeat call without route data
del request_attrs['matched_route']
- self.output_view_info(view_wrapper, level+1)
+ self.output_view_info(view_wrapper, level + 1)
else:
self.out("%sView:" % indent)
self.out("%s-----" % indent)
diff --git a/pyramid/static.py b/pyramid/static.py
index 4ff02f798..cb78feb9b 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -107,12 +107,14 @@ class static_view(object):
raise HTTPNotFound('Out of bounds: %s' % request.url)
if self.package_name: # package resource
-
- resource_path ='%s/%s' % (self.docroot.rstrip('/'), path)
+ resource_path = '%s/%s' % (self.docroot.rstrip('/'), path)
if resource_isdir(self.package_name, resource_path):
if not request.path_url.endswith('/'):
self.add_slash_redirect(request)
- resource_path = '%s/%s' % (resource_path.rstrip('/'),self.index)
+ resource_path = '%s/%s' % (
+ resource_path.rstrip('/'), self.index
+ )
+
if not resource_exists(self.package_name, resource_path):
raise HTTPNotFound(request.url)
filepath = resource_filename(self.package_name, resource_path)
diff --git a/pyramid/testing.py b/pyramid/testing.py
index 772914f3b..58dcb0b59 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -154,9 +154,10 @@ class DummyTemplateRenderer(object):
if myval != v:
raise AssertionError(
'\nasserted value for %s: %r\nactual value: %r' % (
- k, v, myval))
+ k, v, myval))
return True
+
class DummyResource:
""" A dummy :app:`Pyramid` :term:`resource` object."""
def __init__(self, __name__=None, __parent__=None, __provides__=None,
@@ -384,11 +385,12 @@ class DummyRequest(
@reify
def response(self):
- f = _get_response_factory(self.registry)
+ f = _get_response_factory(self.registry)
return f(self)
have_zca = True
+
def setUp(registry=None, request=None, hook_zca=True, autocommit=True,
settings=None, package=None):
"""
@@ -578,10 +580,13 @@ def skip_on(*platforms): # pragma: no cover
skip = True
if platform == 'py3' and PY3:
skip = True
+
def decorator(func):
if isinstance(func, class_types):
- if skip: return None
- else: return func
+ if skip:
+ return None
+ else:
+ return func
else:
def wrapper(*args, **kw):
if skip:
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 542eea9aa..61a798ad1 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -688,6 +688,14 @@ class TestJSONP(unittest.TestCase):
result = renderer({'a':'1'}, {})
self.assertEqual(result, '{"a": "1"}')
+ def test_render_to_jsonp_invalid_callback(self):
+ from pyramid.httpexceptions import HTTPBadRequest
+ renderer_factory = self._makeOne()
+ renderer = renderer_factory(None)
+ request = testing.DummyRequest()
+ request.GET['callback'] = '78mycallback'
+ self.assertRaises(HTTPBadRequest, renderer, {'a':'1'}, {'request':request})
+
class Dummy:
pass
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index 2c2298f26..c528b9174 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -120,7 +120,7 @@ class TestRequest(unittest.TestCase):
def test_add_response_callback(self):
inst = self._makeOne()
- self.assertEqual(inst.response_callbacks, None)
+ self.assertEqual(len(inst.response_callbacks), 0)
def callback(request, response):
""" """
inst.add_response_callback(callback)
@@ -171,7 +171,7 @@ class TestRequest(unittest.TestCase):
def test_add_finished_callback(self):
inst = self._makeOne()
- self.assertEqual(inst.finished_callbacks, None)
+ self.assertEqual(len(inst.finished_callbacks), 0)
def callback(request):
""" """
inst.add_finished_callback(callback)
diff --git a/pyramid/traversal.py b/pyramid/traversal.py
index a38cf271e..db73d13fc 100644
--- a/pyramid/traversal.py
+++ b/pyramid/traversal.py
@@ -14,10 +14,6 @@ from pyramid.interfaces import (
VH_ROOT_KEY,
)
-with warnings.catch_warnings():
- warnings.filterwarnings('ignore')
- from pyramid.interfaces import IContextURL
-
from pyramid.compat import (
PY3,
native_,
@@ -35,6 +31,10 @@ from pyramid.exceptions import URLDecodeError
from pyramid.location import lineage
from pyramid.threadlocal import get_current_registry
+with warnings.catch_warnings():
+ warnings.filterwarnings('ignore')
+ from pyramid.interfaces import IContextURL
+
empty = text_('')
def find_root(resource):
@@ -664,10 +664,10 @@ class ResourceTreeTraverser(object):
if VH_ROOT_KEY in environ:
# HTTP_X_VHM_ROOT
- vroot_path = decode_path_info(environ[VH_ROOT_KEY])
+ vroot_path = decode_path_info(environ[VH_ROOT_KEY])
vroot_tuple = split_path_info(vroot_path)
vpath = vroot_path + path # both will (must) be unicode or asciistr
- vroot_idx = len(vroot_tuple) -1
+ vroot_idx = len(vroot_tuple) - 1
else:
vroot_tuple = ()
vpath = path
@@ -689,34 +689,34 @@ class ResourceTreeTraverser(object):
vpath_tuple = split_path_info(vpath)
for segment in vpath_tuple:
if segment[:2] == view_selector:
- return {'context':ob,
- 'view_name':segment[2:],
- 'subpath':vpath_tuple[i+1:],
- 'traversed':vpath_tuple[:vroot_idx+i+1],
- 'virtual_root':vroot,
- 'virtual_root_path':vroot_tuple,
- 'root':root}
+ return {'context': ob,
+ 'view_name': segment[2:],
+ 'subpath': vpath_tuple[i + 1:],
+ 'traversed': vpath_tuple[:vroot_idx + i + 1],
+ 'virtual_root': vroot,
+ 'virtual_root_path': vroot_tuple,
+ 'root': root}
try:
getitem = ob.__getitem__
except AttributeError:
- return {'context':ob,
- 'view_name':segment,
- 'subpath':vpath_tuple[i+1:],
- 'traversed':vpath_tuple[:vroot_idx+i+1],
- 'virtual_root':vroot,
- 'virtual_root_path':vroot_tuple,
- 'root':root}
+ return {'context': ob,
+ 'view_name': segment,
+ 'subpath': vpath_tuple[i + 1:],
+ 'traversed': vpath_tuple[:vroot_idx + i + 1],
+ 'virtual_root': vroot,
+ 'virtual_root_path': vroot_tuple,
+ 'root': root}
try:
next = getitem(segment)
except KeyError:
- return {'context':ob,
- 'view_name':segment,
- 'subpath':vpath_tuple[i+1:],
- 'traversed':vpath_tuple[:vroot_idx+i+1],
- 'virtual_root':vroot,
- 'virtual_root_path':vroot_tuple,
- 'root':root}
+ return {'context': ob,
+ 'view_name': segment,
+ 'subpath': vpath_tuple[i + 1:],
+ 'traversed': vpath_tuple[:vroot_idx + i + 1],
+ 'virtual_root': vroot,
+ 'virtual_root_path': vroot_tuple,
+ 'root': root}
if i == vroot_idx:
vroot = next
ob = next
diff --git a/pyramid/url.py b/pyramid/url.py
index a0f3d7f2f..b004c40ec 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -606,10 +606,11 @@ class URLMethodsMixin(object):
if local_url is not None:
# the resource handles its own url generation
d = dict(
- virtual_path = virtual_path,
- physical_path = url_adapter.physical_path,
- app_url = app_url,
- )
+ virtual_path=virtual_path,
+ physical_path=url_adapter.physical_path,
+ app_url=app_url,
+ )
+
# allow __resource_url__ to punt by returning None
resource_url = local_url(self, d)
@@ -698,7 +699,7 @@ class URLMethodsMixin(object):
"""
if not os.path.isabs(path):
- if not ':' in path:
+ if ':' not in path:
# if it's not a package:relative/name and it's not an
# /absolute/path it's a relative/path; this means its relative
# to the package in which the caller's module is defined.
@@ -743,7 +744,7 @@ class URLMethodsMixin(object):
to ``static_path`` will be ignored.
"""
if not os.path.isabs(path):
- if not ':' in path:
+ if ':' not in path:
# if it's not a package:relative/name and it's not an
# /absolute/path it's a relative/path; this means its relative
# to the package in which the caller's module is defined.
@@ -884,6 +885,7 @@ def resource_url(resource, request, *elements, **kw):
model_url = resource_url # b/w compat (forever)
+
def static_url(path, request, **kw):
"""
This is a backwards compatibility function. Its result is the same as
@@ -894,7 +896,7 @@ def static_url(path, request, **kw):
See :meth:`pyramid.request.Request.static_url` for more information.
"""
if not os.path.isabs(path):
- if not ':' in path:
+ if ':' not in path:
# if it's not a package:relative/name and it's not an
# /absolute/path it's a relative/path; this means its relative
# to the package in which the caller's module is defined.
@@ -902,6 +904,7 @@ def static_url(path, request, **kw):
path = '%s:%s' % (package.__name__, path)
return request.static_url(path, **kw)
+
def static_path(path, request, **kw):
"""
This is a backwards compatibility function. Its result is the same as
@@ -912,7 +915,7 @@ def static_path(path, request, **kw):
See :meth:`pyramid.request.Request.static_path` for more information.
"""
if not os.path.isabs(path):
- if not ':' in path:
+ if ':' not in path:
# if it's not a package:relative/name and it's not an
# /absolute/path it's a relative/path; this means its relative
# to the package in which the caller's module is defined.
diff --git a/pyramid/util.py b/pyramid/util.py
index 4a722b381..1ae7e6afc 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -16,7 +16,6 @@ from pyramid.exceptions import (
)
from pyramid.compat import (
- iteritems_,
is_nonstr_iter,
integer_types,
string_types,
@@ -28,12 +27,14 @@ from pyramid.compat import (
from pyramid.interfaces import IActionInfo
from pyramid.path import DottedNameResolver as _DottedNameResolver
+
class DottedNameResolver(_DottedNameResolver):
def __init__(self, package=None): # default to package = None for bw compat
return _DottedNameResolver.__init__(self, package)
_marker = object()
+
class InstancePropertyHelper(object):
"""A helper object for assigning properties and descriptors to instances.
It is not normally possible to do this because descriptors must be
@@ -446,7 +447,7 @@ class TopologicalSorter(object):
order.append((a, b))
def add_node(node):
- if not node in graph:
+ if node not in graph:
roots.append(node)
graph[node] = [0] # 0 = number of arcs coming into this node
@@ -519,7 +520,7 @@ def viewdefaults(wrapped):
view = self.maybe_dotted(view)
if inspect.isclass(view):
defaults = getattr(view, '__view_defaults__', {}).copy()
- if not '_backframes' in kw:
+ if '_backframes' not in kw:
kw['_backframes'] = 1 # for action_method
defaults.update(kw)
return wrapped(self, *arg, **defaults)
diff --git a/pyramid/view.py b/pyramid/view.py
index fd020c7ea..005e81148 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -56,7 +56,7 @@ def render_view_to_response(context, request, name='', secure=True):
context,
context_iface,
name,
- secure = secure,
+ secure=secure,
)
return response # NB: might be None
@@ -272,7 +272,7 @@ class AppendSlashNotFoundViewFactory(object):
qs = request.query_string
if qs:
qs = '?' + qs
- return self.redirect_class(location=request.path+'/'+qs)
+ return self.redirect_class(location=request.path + '/' + qs)
return self.notfound_view(context, request)
append_slash_notfound_view = AppendSlashNotFoundViewFactory()
@@ -451,9 +451,9 @@ def _find_views(
view_types=None,
view_classifier=None,
):
- if view_types is None:
+ if view_types is None:
view_types = (IView, ISecuredView, IMultiView)
- if view_classifier is None:
+ if view_classifier is None:
view_classifier = IViewClassifier
registered = registry.adapters.registered
cache = registry._view_lookup_cache
diff --git a/rtd.txt b/rtd.txt
index 4aecd9933..45be10175 100644
--- a/rtd.txt
+++ b/rtd.txt
@@ -2,3 +2,4 @@ Sphinx >= 1.2.3
repoze.sphinx.autointerface
repoze.lru
pylons_sphinx_latesturl
+pylons-sphinx-themes \ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index 875480594..b52d24bdb 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -13,3 +13,7 @@ docs = develop easy_install pyramid[docs]
[bdist_wheel]
universal = 1
+[flake8]
+ignore = E301,E302,E731,E261,E123,E121,E128,E129,E125,W291,E501,W293,E303,W391,E266,E231,E201,E202,E127,E262,E265
+exclude = pyramid/tests/,pyramid/compat.py,pyramid/resource.py
+show-source = True
diff --git a/setup.py b/setup.py
index e91e0ee21..ac03da9f5 100644
--- a/setup.py
+++ b/setup.py
@@ -59,6 +59,7 @@ docs_extras = [
'Sphinx >= 1.2.3',
'docutils',
'repoze.sphinx.autointerface',
+ 'pylons-sphinx-themes >= 0.3',
]
testing_extras = tests_require + [
diff --git a/tox.ini b/tox.ini
index 2b974b07e..f3b21561f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
envlist =
- py26,py27,py32,py33,py34,pypy,pypy3,
+ py26,py27,py32,py33,py34,pypy,pypy3,pep8,
{py2,py3}-docs,
{py2,py3}-cover,coverage,
@@ -22,6 +22,9 @@ commands =
pip install pyramid[testing]
nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:}
+# we separate coverage into its own testenv because a) "last run wins" wrt
+# cobertura jenkins reporting and b) pypy and jython can't handle any
+# combination of versions of coverage and nosexcover that i can find.
[testenv:py2-cover]
commands =
pip install pyramid[testing]
@@ -38,21 +41,9 @@ commands =
setenv =
COVERAGE_FILE=.coverage.py3
-[testenv:py2-docs]
-whitelist_externals = make
-commands =
- pip install pyramid[docs]
- make -C docs html
-
-[testenv:py3-docs]
-whitelist_externals = make
-commands =
- pip install pyramid[docs]
- make -C docs html
-
[testenv:coverage]
basepython = python3.4
-commands =
+commands =
coverage erase
coverage combine
coverage xml
@@ -62,6 +53,18 @@ deps =
setenv =
COVERAGE_FILE=.coverage
+[testenv:py2-docs]
+whitelist_externals = make
+commands =
+ pip install pyramid[docs]
+ make -C docs html epub BUILDDIR={envdir}
+
+[testenv:py3-docs]
+whitelist_externals = make
+commands =
+ pip install pyramid[docs]
+ make -C docs html epub BUILDDIR={envdir}
+
[testenv:py26-scaffolds]
basepython = python2.6
commands =
@@ -103,3 +106,10 @@ basepython = pypy3
commands =
python pyramid/scaffolds/tests.py
deps = virtualenv
+
+[testenv:pep8]
+basepython = python3.4
+commands =
+ flake8 pyramid/
+deps =
+ flake8