#13244 closed defect (fixed)
Passing Jinja2 context to callable instance as first argument when the callable instance has __getattr__ method since Jinja2 2.11.0
Reported by: | Jun Omae | Owned by: | Jun Omae |
---|---|---|---|
Priority: | normal | Milestone: | 1.4.1 |
Component: | rendering | Version: | |
Severity: | normal | Keywords: | jinja2 |
Cc: | Branch: | ||
Release Notes: | |||
API Changes: | |||
Internal Changes: |
Skip Jinja2 context instance in |
Description (last modified by )
The callable instance is Href
instance.
See also
- https://github.com/pallets/jinja/blob/2.11.0/src/jinja2/runtime.py#L261
- https://github.com/pallets/jinja/blob/2.10.3/jinja2/runtime.py#L235
Jinja2 2.11.0
>>> import jinja2, sys >>> jinja2.__version__ '2.11.0' >>> from jinja2 import Template >>> from trac.web.href import Href >>> href = Href('/trac.cgi') >>> Template('{{ href() }}').render(href=href) "/trac.cgi/%3CContext%20%7B'range'%3A%20%3Cclass%20'range'%3E%2C%20'dict'%3A%20%3Cclass%20'dict'%3E%2C%20'lipsum'%3A%20%3Cfunction%20generate_lorem_ipsum%20at%200x7f0386031310%3E%2C%20'cycler'%3A%20%3Cclass%20'jinja2.utils.Cycler'%3E%2C%20'joiner'%3A%20%3Cclass%20'jinja2.utils.Joiner'%3E%2C%20'namespace'%3A%20%3Cclass%20'jinja2.utils.Namespace'%3E%2C%20'href'%3A%20%3CHref%20'/trac.cgi'%3E%7D%20of%20None%3E" >>> class Foo(object): ... def __getattr__(self, name): ... sys.stderr.write('__getattr__({!r})\n'.format(name)) ... return self ... def __call__(self, *args, **kwargs): ... sys.stderr.write('__call__(args={!r}, kwargs={!r})\n'.format(args, kwargs)) ... >>> Template('{{ val() }}').render(val=Foo()) __getattr__('contextfunction') __call__(args=(<Context {'range': <class 'range'>, 'dict': <class 'dict'>, 'lipsum': <function generate_lorem_ipsum at 0x7f0386031310>, 'cycler': <class 'jinja2.utils.Cycler'>, 'joiner': <class 'jinja2.utils.Joiner'>, 'namespace': <class 'jinja2.utils.Namespace'>, 'val': <__main__.Foo object at 0x7f038a15d160>} of None>,), kwargs={}) 'None'
Jinja2 2.10.3
>>> import jinja2, sys >>> jinja2.__version__ '2.10.3' >>> from jinja2 import Template >>> from trac.web.href import Href >>> href = Href('/trac.cgi') >>> Template('{{ href() }}').render(href=href) '/trac.cgi' >>> class Foo(object): ... def __getattr__(self, name): ... sys.stderr.write('__getattr__({!r})\n'.format(name)) ... return self ... def __call__(self, *args, **kwargs): ... sys.stderr.write('__call__(args={!r}, kwargs={!r})\n'.format(args, kwargs)) ... >>> Template('{{ val() }}').render(val=Foo()) __call__(args=(), kwargs={}) 'None'
Attachments (0)
Change History (15)
comment:1 by , 5 years ago
comment:2 by , 5 years ago
Description: | modified (diff) |
---|
comment:3 by , 5 years ago
Looks similar to Jinja2 issue #1139. Relevant change is 70ea1d3e.
It wouldn't be an issue if Jinja2 tested the value: getattr(__obj, "contextfunction", 0) is True
rather than just getattr(__obj, "contextfunction", 0)
.
comment:6 by , 5 years ago
Release Notes: | modified (diff) |
---|---|
Resolution: | → fixed |
Status: | new → closed |
comment:8 by , 5 years ago
Replying to Ryan J Ollos:
I posted a related question in Jinja2 issue #1139.
They may address the issue in Jinja2 issue #1145, targeted to Jinja2 2.11.2. We can probably remove the Href
attributes (contextfunction
, …) if we ever drop support for Jinja < 2.11.2.
comment:9 by , 5 years ago
Another work around without contextfunction
attribute: we could simply ignore Jinja2 context instance because the arguments of Href.__call__()
are typically instances of basestring, dict, list and tuple.
-
trac/web/href.py
diff --git a/trac/web/href.py b/trac/web/href.py index 02f3cbe76..d27d2879c 100644
a b 16 16 # Author: Jonas Borgström <jonas@edgewall.com> 17 17 # Christopher Lenz <cmlenz@gmx.de> 18 18 19 from jinja2.runtime import Context as Jinja2Context 19 20 import re 20 21 21 22 from trac.util.text import unicode_quote, unicode_urlencode … … class Href(object): 140 141 '/milestone/<look,here%3E?param=%3Chere,too>' 141 142 """ 142 143 143 # Avoid passing Jinja2 context to __call__ (#13244)144 contextfunction = 0145 evalcontextfunction = 0146 environmentfunction = 0147 148 144 def __init__(self, base, path_safe="/!~*'()", query_safe="!~*'()"): 149 145 self.base = base.rstrip('/') 150 146 self.path_safe = path_safe … … class Href(object): 165 161 elif value is not None: 166 162 params.append((name, value)) 167 163 164 # Skip Jinja2 context (#13244) 165 if args and isinstance(args[0], Jinja2Context): 166 args = args[1:] 168 167 if args: 169 168 lastp = args[-1] 170 169 if isinstance(lastp, dict):
comment:10 by , 5 years ago
Owner: | set to |
---|
comment:11 by , 5 years ago
The comment:9 patch seems like a better solution because it doesn't prevent a path like /contextfunction
.
One minor tweak is that the third-party import should be placed after the stdlib import:
import re from jinja2.runtime import Context as Jinja2Context from trac.util.text import unicode_quote, unicode_urlencode
comment:12 by , 5 years ago
Release Notes: | modified (diff) |
---|
comment:13 by , 5 years ago
comment:15 by , 5 years ago
Moving change notes to Internal Changes since Jinja2 2.11 compatibility is announced in #13242.
Ad hoc patch:
trac/web/href.py