diff options
| author | Chris McDonough <chrism@plope.com> | 2015-04-24 06:52:55 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2015-04-24 06:52:55 -0400 |
| commit | d2c49d66399e8caa0509822cab189c04d3f7ec35 (patch) | |
| tree | f8f5663ca51ac47fb35732364b96990cd9cd42b4 | |
| parent | bedd5f21f6f2c7660824cf901c24a31cad8f737d (diff) | |
| parent | 43857c97c5c5cf437b31be013c6540ffc536d803 (diff) | |
| download | pyramid-d2c49d66399e8caa0509822cab189c04d3f7ec35.tar.gz pyramid-d2c49d66399e8caa0509822cab189c04d3f7ec35.tar.bz2 pyramid-d2c49d66399e8caa0509822cab189c04d3f7ec35.zip | |
Merge branch 'master' of github.com:Pylons/pyramid
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 @@ -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 @@ -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 @@ -59,6 +59,7 @@ docs_extras = [ 'Sphinx >= 1.2.3', 'docutils', 'repoze.sphinx.autointerface', + 'pylons-sphinx-themes >= 0.3', ] testing_extras = tests_require + [ @@ -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 |
