summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt10
-rw-r--r--CONTRIBUTORS.txt4
-rw-r--r--README.rst2
-rw-r--r--contributing.md2
-rw-r--r--docs/api/url.rst2
-rw-r--r--docs/index.rst6
-rw-r--r--docs/narr/introduction.rst2
-rw-r--r--docs/narr/myproject/myproject/templates/layout.jinja22
-rw-r--r--docs/quick_tour/logging/hello_world/templates/layout.jinja22
-rw-r--r--docs/quick_tour/package/hello_world/templates/layout.jinja22
-rw-r--r--docs/quick_tour/sessions/hello_world/templates/layout.jinja22
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/templates/layout.jinja22
-rw-r--r--docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja22
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt2
-rw-r--r--docs/tutorials/wiki/src/installation/tutorial/templates/mytemplate.pt2
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt2
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt2
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja22
-rw-r--r--docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja22
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja22
-rw-r--r--docs/whatsnew-1.9.rst2
-rw-r--r--pyramid/config/views.py3
-rw-r--r--pyramid/encode.py45
-rw-r--r--pyramid/scaffolds/alchemy/+package+/templates/layout.jinja2_tmpl2
-rw-r--r--pyramid/scaffolds/starter/+package+/templates/layout.jinja2_tmpl2
-rw-r--r--pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl2
-rw-r--r--pyramid/tests/test_config/test_views.py1
-rw-r--r--pyramid/tests/test_encode.py11
-rw-r--r--pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/mytemplate.pt_tmpl2
-rw-r--r--pyramid/tests/test_url.py32
-rw-r--r--pyramid/url.py180
31 files changed, 178 insertions, 158 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index fdd9dd884..d695599a5 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -27,6 +27,16 @@ unreleased
subclass of ``pyramid.security.Denied``.
See https://github.com/Pylons/pyramid/pull/3084
+- Add a ``quote_via`` argument to ``pyramid.encode.urlencode`` to follow
+ the stdlib's version and enable custom quoting functions.
+ See https://github.com/Pylons/pyramid/pull/3088
+
+- Support `_query=None` and `_anchor=None` in ``request.route_url`` as well
+ as ``query=None`` and ``anchor=None`` in ``request.resource_url``.
+ Previously this would cause an `?` and a `#`, respectively, in the url
+ with nothing after it.
+ See https://github.com/Pylons/pyramid/pull/3034
+
1.9a2 (2017-05-09)
==================
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 445536e9e..242fbbcda 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -301,6 +301,10 @@ Contributors
- Jeremy(Ching-Rui) Chen, 2017/04/19
+- Russell Ballestrini, 2017/05/06
+
- Fang-Pen Lin, 2017/05/22
- Volker Diels-Grabsch, 2017/06/09
+
+- Denis Rykov, 2017/06/15
diff --git a/README.rst b/README.rst
index 0429c36b5..2b2535bfb 100644
--- a/README.rst
+++ b/README.rst
@@ -34,7 +34,7 @@ and deployment more fun, more predictable, and more productive.
server = make_server('0.0.0.0', 8080, app)
server.serve_forever()
-Pyramid is a project of the `Pylons Project <http://pylonsproject.org/>`_.
+Pyramid is a project of the `Pylons Project <https://pylonsproject.org>`_.
Support and Documentation
-------------------------
diff --git a/contributing.md b/contributing.md
index 82f60e7b8..5e0ac53bf 100644
--- a/contributing.md
+++ b/contributing.md
@@ -3,7 +3,7 @@ Contributing
All projects under the Pylons Projects, including this one, follow the
guidelines established at [How to
-Contribute](http://www.pylonsproject.org/community/how-to-contribute) and
+Contribute](https://pylonsproject.org/community-how-to-contribute.html) and
[Coding Style and
Standards](http://docs.pylonsproject.org/en/latest/community/codestyle.html).
diff --git a/docs/api/url.rst b/docs/api/url.rst
index 131d85806..8aaabc352 100644
--- a/docs/api/url.rst
+++ b/docs/api/url.rst
@@ -5,7 +5,7 @@
.. automodule:: pyramid.url
- .. autofunction:: pyramid.url.resource_url(context, request, *elements, query=None, anchor=None)
+ .. autofunction:: resource_url
.. autofunction:: route_url
diff --git a/docs/index.rst b/docs/index.rst
index 7d3393548..4b739d23f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -5,7 +5,7 @@ The Pyramid Web Framework
=========================
:app:`Pyramid` is a small, fast, down-to-earth Python web framework. It is
-developed as part of the `Pylons Project <http://pylonsproject.org/>`_.
+developed as part of the `Pylons Project <https://pylonsproject.org>`_.
It is licensed under a `BSD-like license <http://repoze.org/license.html>`_.
Here is one of the simplest :app:`Pyramid` applications you can make:
@@ -95,9 +95,7 @@ the trunk via ``git``, use either command:
# Otherwise, HTTPS will work, using your GitHub login:
git clone https://github.com/Pylons/pyramid.git
-To find out how to become a contributor to :app:`Pyramid`, please see the
-`contributor's section of the documentation
-<http://docs.pylonsproject.org/en/latest/#contributing>`_.
+To find out how to become a contributor to :app:`Pyramid`, please see `How to Contribute Source Code and Documentation <https://pylonsproject.org/community-how-to-contribute.html>`_.
.. _html_narrative_documentation:
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index 3dd0cc464..4efc35d25 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -364,7 +364,7 @@ And much, much more...
What Is The Pylons Project?
---------------------------
-:app:`Pyramid` is a member of the collection of software published under the Pylons Project. Pylons software is written by a loose-knit community of contributors. The `Pylons Project website <http://www.pylonsproject.org>`_ includes details about how :app:`Pyramid` relates to the Pylons Project.
+:app:`Pyramid` is a member of the collection of software published under the Pylons Project. Pylons software is written by a loose-knit community of contributors. The `Pylons Project website <https://pylonsproject.org>`_ includes details about how :app:`Pyramid` relates to the Pylons Project.
.. index::
single: pyramid and other frameworks
diff --git a/docs/narr/myproject/myproject/templates/layout.jinja2 b/docs/narr/myproject/myproject/templates/layout.jinja2
index 820758fea..2b3c26628 100644
--- a/docs/narr/myproject/myproject/templates/layout.jinja2
+++ b/docs/narr/myproject/myproject/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/quick_tour/logging/hello_world/templates/layout.jinja2 b/docs/quick_tour/logging/hello_world/templates/layout.jinja2
index c82cac915..8473e8b5d 100644
--- a/docs/quick_tour/logging/hello_world/templates/layout.jinja2
+++ b/docs/quick_tour/logging/hello_world/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/quick_tour/package/hello_world/templates/layout.jinja2 b/docs/quick_tour/package/hello_world/templates/layout.jinja2
index c82cac915..8473e8b5d 100644
--- a/docs/quick_tour/package/hello_world/templates/layout.jinja2
+++ b/docs/quick_tour/package/hello_world/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/quick_tour/sessions/hello_world/templates/layout.jinja2 b/docs/quick_tour/sessions/hello_world/templates/layout.jinja2
index c82cac915..8473e8b5d 100644
--- a/docs/quick_tour/sessions/hello_world/templates/layout.jinja2
+++ b/docs/quick_tour/sessions/hello_world/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/templates/layout.jinja2 b/docs/quick_tour/sqla_demo/sqla_demo/templates/layout.jinja2
index b84b3ec0e..03f07524a 100644
--- a/docs/quick_tour/sqla_demo/sqla_demo/templates/layout.jinja2
+++ b/docs/quick_tour/sqla_demo/sqla_demo/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja2 b/docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja2
index 3aed0a123..2d8a341d9 100644
--- a/docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja2
+++ b/docs/quick_tutorial/cookiecutters/cc_starter/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
index 4ffc0eb22..26c9a2f21 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -43,7 +43,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/installation/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/installation/tutorial/templates/mytemplate.pt
index 4ffc0eb22..26c9a2f21 100644
--- a/docs/tutorials/wiki/src/installation/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/installation/tutorial/templates/mytemplate.pt
@@ -43,7 +43,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
index 4ffc0eb22..26c9a2f21 100644
--- a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
@@ -43,7 +43,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
index 6c3809250..2d967f83a 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
@@ -45,7 +45,7 @@
<li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/">Docs</a></li>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2
index e29413cf9..36b1fa374 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2
index e29413cf9..36b1fa374 100644
--- a/docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2
+++ b/docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2
index e29413cf9..36b1fa374 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2
@@ -42,7 +42,7 @@
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/docs/whatsnew-1.9.rst b/docs/whatsnew-1.9.rst
index 0c3385a66..9e9c1614d 100644
--- a/docs/whatsnew-1.9.rst
+++ b/docs/whatsnew-1.9.rst
@@ -41,6 +41,8 @@ Minor Feature Additions
- Normalize the permission results to a proper class hierarchy. :class:`pyramid.security.ACLAllowed` is now a subclass of :class:`pyramid.security.Allowed` and :class:`pyramid.security.ACLDenied` is now a subclass of :class:`pyramid.security.Denied`. See https://github.com/Pylons/pyramid/pull/3084
+- Add a ``quote_via`` argument to :func:`pyramid.encode.urlencode` to follow the stdlib's version and enable custom quoting functions. See https://github.com/Pylons/pyramid/pull/3088
+
Deprecations
------------
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 48c4e3437..e5ebc8e07 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -1950,8 +1950,7 @@ class StaticURLInfo(object):
kw['subpath'] = subpath
return request.route_url(route_name, **kw)
else:
- app_url, scheme, host, port, qs, anchor = \
- parse_url_overrides(kw)
+ app_url, qs, anchor = parse_url_overrides(request, kw)
parsed = url_parse(url)
if not parsed.scheme:
url = urlparse.urlunparse(parsed._replace(
diff --git a/pyramid/encode.py b/pyramid/encode.py
index 0be0107b3..73ff14e62 100644
--- a/pyramid/encode.py
+++ b/pyramid/encode.py
@@ -14,13 +14,21 @@ def url_quote(val, safe=''): # bw compat api
val = str(val).encode('utf-8')
return _url_quote(val, safe=safe)
-def urlencode(query, doseq=True):
+# bw compat api (dnr)
+def quote_plus(val, safe=''):
+ cls = val.__class__
+ if cls is text_type:
+ val = val.encode('utf-8')
+ elif cls is not binary_type:
+ val = str(val).encode('utf-8')
+ return _quote_plus(val, safe=safe)
+
+def urlencode(query, doseq=True, quote_via=quote_plus):
"""
- An alternate implementation of Python's stdlib `urllib.urlencode
- function <http://docs.python.org/library/urllib.html>`_ which
- accepts unicode keys and values within the ``query``
- dict/sequence; all Unicode keys and values are first converted to
- UTF-8 before being used to compose the query string.
+ An alternate implementation of Python's stdlib
+ :func:`urllib.parse.urlencode` function which accepts unicode keys and
+ values within the ``query`` dict/sequence; all Unicode keys and values are
+ first converted to UTF-8 before being used to compose the query string.
The value of ``query`` must be a sequence of two-tuples
representing key/value pairs *or* an object (often a dictionary)
@@ -35,12 +43,18 @@ def urlencode(query, doseq=True):
the ``doseq=True`` mode, no matter what the value of the second
argument.
- See the Python stdlib documentation for ``urllib.urlencode`` for
- more information.
+ Both the key and value are encoded using the ``quote_via`` function which
+ by default is using a similar algorithm to :func:`urllib.parse.quote_plus`
+ which converts spaces into '+' characters and '/' into '%2F'.
.. versionchanged:: 1.5
In a key/value pair, if the value is ``None`` then it will be
dropped from the resulting output.
+
+ .. versionchanged:: 1.9
+ Added the ``quote_via`` argument to allow alternate quoting algorithms
+ to be used.
+
"""
try:
# presumed to be a dictionary
@@ -52,28 +66,19 @@ def urlencode(query, doseq=True):
prefix = ''
for (k, v) in query:
- k = quote_plus(k)
+ k = quote_via(k)
if is_nonstr_iter(v):
for x in v:
- x = quote_plus(x)
+ x = quote_via(x)
result += '%s%s=%s' % (prefix, k, x)
prefix = '&'
elif v is None:
result += '%s%s=' % (prefix, k)
else:
- v = quote_plus(v)
+ v = quote_via(v)
result += '%s%s=%s' % (prefix, k, v)
prefix = '&'
return result
-
-# bw compat api (dnr)
-def quote_plus(val, safe=''):
- cls = val.__class__
- if cls is text_type:
- val = val.encode('utf-8')
- elif cls is not binary_type:
- val = str(val).encode('utf-8')
- return _quote_plus(val, safe=safe)
diff --git a/pyramid/scaffolds/alchemy/+package+/templates/layout.jinja2_tmpl b/pyramid/scaffolds/alchemy/+package+/templates/layout.jinja2_tmpl
index 6f6946ba2..485d6efa4 100644
--- a/pyramid/scaffolds/alchemy/+package+/templates/layout.jinja2_tmpl
+++ b/pyramid/scaffolds/alchemy/+package+/templates/layout.jinja2_tmpl
@@ -44,7 +44,7 @@
<li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/{{pyramid_docs_branch}}/">Docs</a></li>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/pyramid/scaffolds/starter/+package+/templates/layout.jinja2_tmpl b/pyramid/scaffolds/starter/+package+/templates/layout.jinja2_tmpl
index f3c27e3f0..679ba25ea 100644
--- a/pyramid/scaffolds/starter/+package+/templates/layout.jinja2_tmpl
+++ b/pyramid/scaffolds/starter/+package+/templates/layout.jinja2_tmpl
@@ -44,7 +44,7 @@
<li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/{{pyramid_docs_branch}}/">Docs</a></li>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl b/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl
index 4b24ee2df..04f5260e3 100644
--- a/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl
+++ b/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl
@@ -45,7 +45,7 @@
<li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/{{pyramid_docs_branch}}/">Docs</a></li>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
- <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index 0816d9958..860254385 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -3411,6 +3411,7 @@ class DummyRequest:
subpath = ()
matchdict = None
request_iface = IRequest
+ application_url = 'http://example.com/foo'
def __init__(self, environ=None):
if environ is None:
diff --git a/pyramid/tests/test_encode.py b/pyramid/tests/test_encode.py
index 8fb766d88..d3a9f7095 100644
--- a/pyramid/tests/test_encode.py
+++ b/pyramid/tests/test_encode.py
@@ -5,9 +5,9 @@ from pyramid.compat import (
)
class UrlEncodeTests(unittest.TestCase):
- def _callFUT(self, query, doseq=False):
+ def _callFUT(self, query, doseq=False, **kw):
from pyramid.encode import urlencode
- return urlencode(query, doseq)
+ return urlencode(query, doseq, **kw)
def test_ascii_only(self):
result = self._callFUT([('a',1), ('b',2)])
@@ -53,6 +53,13 @@ class UrlEncodeTests(unittest.TestCase):
result = self._callFUT([('a', '1'), ('b', None), ('c', None)])
self.assertEqual(result, 'a=1&b=&c=')
+ def test_quote_via(self):
+ def my_quoter(value):
+ return 'xxx' + value
+ result = self._callFUT([('a', '1'), ('b', None), ('c', None)],
+ quote_via=my_quoter)
+ self.assertEqual(result, 'xxxa=xxx1&xxxb=&xxxc=')
+
class URLQuoteTests(unittest.TestCase):
def _callFUT(self, val, safe=''):
from pyramid.encode import url_quote
diff --git a/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/mytemplate.pt_tmpl b/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/mytemplate.pt_tmpl
index 856bc22e7..f4d98ec29 100644
--- a/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/mytemplate.pt_tmpl
+++ b/pyramid/tests/test_scaffolds/fixture_scaffold/+package+/templates/mytemplate.pt_tmpl
@@ -41,7 +41,7 @@
<h2>Pyramid links</h2>
<ul class="links">
<li>
- <a href="http://pylonsproject.org">Pylons Website</a>
+ <a href="https://pylonsproject.org">Pylons Website</a>
</li>
<li>
<a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py
index ddf28e0b0..af2e5405c 100644
--- a/pyramid/tests/test_url.py
+++ b/pyramid/tests/test_url.py
@@ -115,6 +115,14 @@ class TestURLMethodsMixin(unittest.TestCase):
self.assertEqual(result,
'http://example.com:5432/context/a')
+ def test_resource_url_with_query_None(self):
+ request = self._makeOne()
+ self._registerResourceURL(request.registry)
+ context = DummyContext()
+ result = request.resource_url(context, 'a', query=None)
+ self.assertEqual(result,
+ 'http://example.com:5432/context/a')
+
def test_resource_url_anchor_is_after_root_when_no_elements(self):
request = self._makeOne()
self._registerResourceURL(request.registry)
@@ -157,6 +165,13 @@ class TestURLMethodsMixin(unittest.TestCase):
self.assertEqual(result,
'http://example.com:5432/context/#%20/%23?&+')
+ def test_resource_url_anchor_is_None(self):
+ request = self._makeOne()
+ self._registerResourceURL(request.registry)
+ context = DummyContext()
+ result = request.resource_url(context, anchor=None)
+ self.assertEqual(result, 'http://example.com:5432/context/')
+
def test_resource_url_no_IResourceURL_registered(self):
# falls back to ResourceURL
root = DummyContext()
@@ -421,6 +436,14 @@ class TestURLMethodsMixin(unittest.TestCase):
self.assertEqual(result,
'http://example.com:5432/1/2/3?a=1#foo')
+ def test_route_url_with_query_None(self):
+ from pyramid.interfaces import IRoutesMapper
+ request = self._makeOne()
+ mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
+ request.registry.registerUtility(mapper, IRoutesMapper)
+ result = request.route_url('flub', a=1, b=2, c=3, _query=None)
+ self.assertEqual(result, 'http://example.com:5432/1/2/3')
+
def test_route_url_with_anchor_binary(self):
from pyramid.interfaces import IRoutesMapper
request = self._makeOne()
@@ -442,6 +465,15 @@ class TestURLMethodsMixin(unittest.TestCase):
self.assertEqual(result,
'http://example.com:5432/1/2/3#La%20Pe%C3%B1a')
+ def test_route_url_with_anchor_None(self):
+ from pyramid.interfaces import IRoutesMapper
+ request = self._makeOne()
+ mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
+ request.registry.registerUtility(mapper, IRoutesMapper)
+ result = request.route_url('flub', _anchor=None)
+
+ self.assertEqual(result, 'http://example.com:5432/1/2/3')
+
def test_route_url_with_query(self):
from pyramid.interfaces import IRoutesMapper
request = self._makeOne()
diff --git a/pyramid/url.py b/pyramid/url.py
index 9634f61da..2e964dc7e 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -31,45 +31,41 @@ from pyramid.traversal import (
QUERY_SAFE = "/?:@!$&'()*+,;=" # RFC 3986
ANCHOR_SAFE = QUERY_SAFE
-def parse_url_overrides(kw):
- """Parse special arguments passed when generating urls.
+def parse_url_overrides(request, kw):
+ """
+ Parse special arguments passed when generating urls.
+
+ The supplied dictionary is mutated when we pop arguments.
+ Returns a 3-tuple of the format:
+
+ ``(app_url, qs, anchor)``.
- The supplied dictionary is mutated, popping arguments as necessary.
- Returns a 6-tuple of the format ``(app_url, scheme, host, port,
- qs, anchor)``.
"""
- anchor = ''
- qs = ''
- app_url = None
- host = None
- scheme = None
- port = None
+ app_url = kw.pop('_app_url', None)
+ scheme = kw.pop('_scheme', None)
+ host = kw.pop('_host', None)
+ port = kw.pop('_port', None)
+ query = kw.pop('_query', '')
+ anchor = kw.pop('_anchor', '')
+
+ if app_url is None:
+ if (scheme is not None or host is not None or port is not None):
+ app_url = request._partial_application_url(scheme, host, port)
+ else:
+ app_url = request.application_url
- if '_query' in kw:
- query = kw.pop('_query')
+ qs = ''
+ if query:
if isinstance(query, string_types):
qs = '?' + url_quote(query, QUERY_SAFE)
- elif query:
+ else:
qs = '?' + urlencode(query, doseq=True)
- if '_anchor' in kw:
- anchor = kw.pop('_anchor')
- anchor = url_quote(anchor, ANCHOR_SAFE)
- anchor = '#' + anchor
-
- if '_app_url' in kw:
- app_url = kw.pop('_app_url')
+ frag = ''
+ if anchor:
+ frag = '#' + url_quote(anchor, ANCHOR_SAFE)
- if '_host' in kw:
- host = kw.pop('_host')
-
- if '_scheme' in kw:
- scheme = kw.pop('_scheme')
-
- if '_port' in kw:
- port = kw.pop('_port')
-
- return app_url, scheme, host, port, qs, anchor
+ return app_url, qs, frag
class URLMethodsMixin(object):
""" Request methods mixin for BaseRequest having to do with URL
@@ -85,6 +81,7 @@ class URLMethodsMixin(object):
passed, the ``port`` value is assumed to ``443``. Likewise, if
``scheme`` is passed as ``http`` and ``port`` is not passed, the
``port`` value is assumed to be ``80``.
+
"""
e = self.environ
if scheme is None:
@@ -191,10 +188,6 @@ class URLMethodsMixin(object):
as values, and a k=v pair will be placed into the query string for
each value.
- .. versionchanged:: 1.5
- Allow the ``_query`` option to be a string to enable alternative
- encodings.
-
If a keyword argument ``_anchor`` is present, its string
representation will be quoted per :rfc:`3986#section-3.5` and used as
a named anchor in the generated URL
@@ -208,10 +201,6 @@ class URLMethodsMixin(object):
``_anchor`` is passed as a Unicode object, it will be converted to
UTF-8 before being appended to the URL.
- .. versionchanged:: 1.5
- The ``_anchor`` option will be escaped instead of using
- its raw string representation.
-
If both ``_anchor`` and ``_query`` are specified, the anchor
element will always follow the query element,
e.g. ``http://example.com?foo=1#bar``.
@@ -252,6 +241,18 @@ class URLMethodsMixin(object):
If the route object which matches the ``route_name`` argument has
a :term:`pregenerator`, the ``*elements`` and ``**kw``
arguments passed to this function might be augmented or changed.
+
+ .. versionchanged:: 1.5
+ Allow the ``_query`` option to be a string to enable alternative
+ encodings.
+
+ The ``_anchor`` option will be escaped instead of using
+ its raw string representation.
+
+ .. versionchanged:: 1.9
+ If ``_query`` or ``_anchor`` are falsey (such as ``None`` or an
+ empty string) they will not be included in the generated url.
+
"""
try:
reg = self.registry
@@ -266,13 +267,7 @@ class URLMethodsMixin(object):
if route.pregenerator is not None:
elements, kw = route.pregenerator(self, elements, kw)
- app_url, scheme, host, port, qs, anchor = parse_url_overrides(kw)
-
- if app_url is None:
- if (scheme is not None or host is not None or port is not None):
- app_url = self._partial_application_url(scheme, host, port)
- else:
- app_url = self.application_url
+ app_url, qs, anchor = parse_url_overrides(self, kw)
path = route.generate(kw) # raises KeyError if generate fails
@@ -311,13 +306,13 @@ class URLMethodsMixin(object):
implemented in terms of :meth:`pyramid.request.Request.route_url`
in just this way. As a result, any ``_app_url`` passed within the
``**kw`` values to ``route_path`` will be ignored.
+
"""
kw['_app_url'] = self.script_name
return self.route_url(route_name, *elements, **kw)
def resource_url(self, resource, *elements, **kw):
"""
-
Generate a string representing the absolute URL of the
:term:`resource` object based on the ``wsgi.url_scheme``,
``HTTP_HOST`` or ``SERVER_NAME`` in the request, plus any
@@ -383,10 +378,6 @@ class URLMethodsMixin(object):
as values, and a k=v pair will be placed into the query string for
each value.
- .. versionchanged:: 1.5
- Allow the ``query`` option to be a string to enable alternative
- encodings.
-
If a keyword argument ``anchor`` is present, its string
representation will be used as a named anchor in the generated URL
(e.g. if ``anchor`` is passed as ``foo`` and the resource URL is
@@ -399,10 +390,6 @@ class URLMethodsMixin(object):
``anchor`` is passed as a Unicode object, it will be converted to
UTF-8 before being appended to the URL.
- .. versionchanged:: 1.5
- The ``anchor`` option will be escaped instead of using
- its raw string representation.
-
If both ``anchor`` and ``query`` are specified, the anchor element
will always follow the query element,
e.g. ``http://example.com?foo=1#bar``.
@@ -431,9 +418,6 @@ class URLMethodsMixin(object):
pass ``app_url=''``. Passing ``app_url=''`` when the resource path is
``/baz/bar`` will return ``/baz/bar``.
- .. versionadded:: 1.3
- ``app_url``
-
If ``app_url`` is passed and any of ``scheme``, ``port``, or ``host``
are also passed, ``app_url`` will take precedence and the values
passed for ``scheme``, ``host``, and/or ``port`` will be ignored.
@@ -445,9 +429,6 @@ class URLMethodsMixin(object):
.. seealso::
See also :ref:`overriding_resource_url_generation`.
-
- .. versionadded:: 1.5
- ``route_name``, ``route_kw``, and ``route_remainder_name``
If ``route_name`` is passed, this function will delegate its URL
production to the ``route_url`` function. Calling
@@ -521,6 +502,23 @@ class URLMethodsMixin(object):
For backwards compatibility purposes, this method is also
aliased as the ``model_url`` method of request.
+
+ .. versionchanged:: 1.3
+ Added the ``app_url`` keyword argument.
+
+ .. versionchanged:: 1.5
+ Allow the ``query`` option to be a string to enable alternative
+ encodings.
+
+ The ``anchor`` option will be escaped instead of using
+ its raw string representation.
+
+ Added the ``route_name``, ``route_kw``, and
+ ``route_remainder_name`` keyword arguments.
+
+ .. versionchanged:: 1.9
+ If ``query`` or ``anchor`` are falsey (such as ``None`` or an
+ empty string) they will not be included in the generated url.
"""
try:
reg = self.registry
@@ -533,13 +531,15 @@ class URLMethodsMixin(object):
virtual_path = getattr(url_adapter, 'virtual_path', None)
- app_url = None
- scheme = None
- host = None
- port = None
+ urlkw = {}
+ for name in (
+ 'app_url', 'scheme', 'host', 'port', 'query', 'anchor'
+ ):
+ val = kw.get(name, None)
+ if val is not None:
+ urlkw['_' + name] = val
if 'route_name' in kw:
- newkw = {}
route_name = kw['route_name']
remainder = getattr(url_adapter, 'virtual_path_tuple', None)
if remainder is None:
@@ -547,39 +547,16 @@ class URLMethodsMixin(object):
# virtual_path_tuple
remainder = tuple(url_adapter.virtual_path.split('/'))
remainder_name = kw.get('route_remainder_name', 'traverse')
- newkw[remainder_name] = remainder
-
- for name in (
- 'app_url', 'scheme', 'host', 'port', 'query', 'anchor'
- ):
- val = kw.get(name, None)
- if val is not None:
- newkw['_' + name] = val
-
+ urlkw[remainder_name] = remainder
+
if 'route_kw' in kw:
route_kw = kw.get('route_kw')
if route_kw is not None:
- newkw.update(route_kw)
-
- return self.route_url(route_name, *elements, **newkw)
+ urlkw.update(route_kw)
- if 'app_url' in kw:
- app_url = kw['app_url']
+ return self.route_url(route_name, *elements, **urlkw)
- if 'scheme' in kw:
- scheme = kw['scheme']
-
- if 'host' in kw:
- host = kw['host']
-
- if 'port' in kw:
- port = kw['port']
-
- if app_url is None:
- if scheme or host or port:
- app_url = self._partial_application_url(scheme, host, port)
- else:
- app_url = self.application_url
+ app_url, qs, anchor = parse_url_overrides(self, urlkw)
resource_url = None
local_url = getattr(resource, '__resource_url__', None)
@@ -600,21 +577,6 @@ class URLMethodsMixin(object):
# __resource_url__ function returned None
resource_url = app_url + virtual_path
- qs = ''
- anchor = ''
-
- if 'query' in kw:
- query = kw['query']
- if isinstance(query, string_types):
- qs = '?' + url_quote(query, QUERY_SAFE)
- elif query:
- qs = '?' + urlencode(query, doseq=True)
-
- if 'anchor' in kw:
- anchor = kw['anchor']
- anchor = url_quote(anchor, ANCHOR_SAFE)
- anchor = '#' + anchor
-
if elements:
suffix = _join_elements(elements)
else: