back to Claude Sonnet 3.5 - Fill-in summary
Claude Sonnet 3.5 - Fill-in: jinja
Failed to run pytests for test tests
ImportError while loading conftest '/testbed/tests/conftest.py'.
tests/conftest.py:5: in <module>
from jinja2 import loaders
src/jinja2/__init__.py:9: in <module>
from .environment import Environment as Environment
src/jinja2/environment.py:14: in <module>
from . import nodes
src/jinja2/nodes.py:798: in <module>
NodeType.__new__ = staticmethod(_failing_new)
E NameError: name '_failing_new' is not defined
Patch diff
diff --git a/src/jinja2/bccache.py b/src/jinja2/bccache.py
index ae575a3..f289af9 100644
--- a/src/jinja2/bccache.py
+++ b/src/jinja2/bccache.py
@@ -47,23 +47,28 @@ class Bucket:
def reset(self) ->None:
"""Resets the bucket (unloads the bytecode)."""
- pass
+ self.code = None
def load_bytecode(self, f: t.BinaryIO) ->None:
"""Loads bytecode from a file or file like object."""
- pass
+ code = marshal.load(f)
+ if isinstance(code, CodeType):
+ self.code = code
def write_bytecode(self, f: t.IO[bytes]) ->None:
"""Dump the bytecode into the file or file like object passed."""
- pass
+ if self.code is not None:
+ marshal.dump(self.code, f)
def bytecode_from_string(self, string: bytes) ->None:
"""Load bytecode from bytes."""
- pass
+ self.load_bytecode(BytesIO(string))
def bytecode_to_string(self) ->bytes:
"""Return the bytecode as bytes."""
- pass
+ out = BytesIO()
+ self.write_bytecode(out)
+ return out.getvalue()
class BytecodeCache:
@@ -100,44 +105,75 @@ class BytecodeCache:
bucket. If they are not able to find code in the cache for the
bucket, it must not do anything.
"""
- pass
+ raise NotImplementedError()
def dump_bytecode(self, bucket: Bucket) ->None:
"""Subclasses have to override this method to write the bytecode
from a bucket back to the cache. If it unable to do so it must not
fail silently but raise an exception.
"""
- pass
+ raise NotImplementedError()
def clear(self) ->None:
"""Clears the cache. This method is not used by Jinja but should be
implemented to allow applications to clear the bytecode cache used
by a particular environment.
"""
- pass
+ raise NotImplementedError()
def get_cache_key(self, name: str, filename: t.Optional[t.Union[str]]=None
) ->str:
"""Returns the unique hash key for this template name."""
- pass
+ return sha1(f"{name}|{filename}".encode("utf-8")).hexdigest()
def get_source_checksum(self, source: str) ->str:
"""Returns a checksum for the source."""
- pass
+ return sha1(source.encode("utf-8")).hexdigest()
def get_bucket(self, environment: 'Environment', name: str, filename: t
.Optional[str], source: str) ->Bucket:
"""Return a cache bucket for the given template. All arguments are
mandatory but filename may be `None`.
"""
- pass
+ key = self.get_cache_key(name, filename)
+ checksum = self.get_source_checksum(source)
+ bucket = Bucket(environment, key, checksum)
+ self.load_bytecode(bucket)
+ return bucket
def set_bucket(self, bucket: Bucket) ->None:
"""Put the bucket into the cache."""
- pass
+ self.dump_bytecode(bucket)
class FileSystemBytecodeCache(BytecodeCache):
+
+ def _get_cache_filename(self, bucket: Bucket) ->str:
+ return os.path.join(self.directory, self.pattern % bucket.key)
+
+ def load_bytecode(self, bucket: Bucket) ->None:
+ filename = self._get_cache_filename(bucket)
+ try:
+ with open(filename, 'rb') as f:
+ bucket.load_bytecode(f)
+ except OSError:
+ return
+
+ def dump_bytecode(self, bucket: Bucket) ->None:
+ filename = self._get_cache_filename(bucket)
+ try:
+ with open(filename, 'wb') as f:
+ bucket.write_bytecode(f)
+ except OSError as e:
+ raise RuntimeError(f"Unable to write bytecode file: {e}")
+
+ def clear(self) ->None:
+ for filename in os.listdir(self.directory):
+ if fnmatch.fnmatch(filename, self.pattern % '*'):
+ try:
+ os.remove(os.path.join(self.directory, filename))
+ except OSError:
+ pass
"""A bytecode cache that stores bytecode on the filesystem. It accepts
two arguments: The directory where the cache items are stored and a
pattern string that is used to build the filename.
@@ -161,6 +197,18 @@ class FileSystemBytecodeCache(BytecodeCache):
directory = self._get_default_cache_dir()
self.directory = directory
self.pattern = pattern
+
+ try:
+ os.makedirs(self.directory)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ def _get_default_cache_dir(self) ->str:
+ if sys.platform == 'win32':
+ return os.path.join(tempfile.gettempdir(), 'jinja2_cache')
+ else:
+ return os.path.join(tempfile.gettempdir(), f'jinja2_cache_{os.getuid()}')
class MemcachedBytecodeCache(BytecodeCache):
diff --git a/src/jinja2/compiler.py b/src/jinja2/compiler.py
index 32df45a..7b0a4c3 100644
--- a/src/jinja2/compiler.py
+++ b/src/jinja2/compiler.py
@@ -31,12 +31,16 @@ def generate(node: nodes.Template, environment: 'Environment', name: t.
Optional[str], filename: t.Optional[str], stream: t.Optional[t.TextIO]=
None, defer_init: bool=False, optimized: bool=True) ->t.Optional[str]:
"""Generate the python source for a node tree."""
- pass
+ codegen = CodeGenerator(environment, name, filename, stream, defer_init, optimized)
+ codegen.visit(node)
+ if stream is None:
+ return codegen.stream.getvalue()
+ return None
def has_safe_repr(value: t.Any) ->bool:
"""Does the node have a safe representation?"""
- pass
+ return isinstance(value, (bool, int, float, str, tuple, frozenset))
def find_undeclared(nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
@@ -44,7 +48,13 @@ def find_undeclared(nodes: t.Iterable[nodes.Node], names: t.Iterable[str]
"""Check if the names passed are accessed undeclared. The return value
is a set of all the undeclared names from the sequence of names found.
"""
- pass
+ visitor = UndeclaredNameVisitor(names)
+ try:
+ for node in nodes:
+ visitor.visit(node)
+ except VisitorExit:
+ pass
+ return visitor.undeclared
class MacroRef:
@@ -81,11 +91,21 @@ class Frame:
def copy(self) ->'Frame':
"""Create a copy of the current one."""
- pass
+ rv = object.__new__(self.__class__)
+ rv.__dict__.update(self.__dict__)
+ rv.symbols = self.symbols.copy()
+ return rv
def inner(self, isolated: bool=False) ->'Frame':
"""Return an inner frame."""
- pass
+ rv = self.copy()
+ if isolated:
+ rv.symbols = Symbols(parent=rv.symbols)
+ rv.block_frame = False
+ rv.loop_frame = False
+ rv.toplevel = False
+ rv.rootlevel = False
+ return rv
def soft(self) ->'Frame':
"""Return a soft frame. A soft frame may not be modified as
@@ -95,7 +115,11 @@ class Frame:
This is only used to implement if-statements and conditional
expressions.
"""
- pass
+ rv = self.copy()
+ rv.toplevel = False
+ rv.rootlevel = False
+ rv.soft_frame = True
+ return rv
__copy__ = copy
@@ -112,8 +136,7 @@ class DependencyFinderVisitor(NodeVisitor):
def visit_Block(self, node: nodes.Block) ->None:
"""Stop visiting at blocks."""
- pass
-
+ return
class UndeclaredNameVisitor(NodeVisitor):
"""A visitor that checks if a name is accessed without being
@@ -125,9 +148,15 @@ class UndeclaredNameVisitor(NodeVisitor):
self.names = set(names)
self.undeclared: t.Set[str] = set()
+ def visit_Name(self, node: nodes.Name) ->None:
+ if node.name in self.names:
+ self.undeclared.add(node.name)
+ if len(self.undeclared) == len(self.names):
+ raise VisitorExit()
+
def visit_Block(self, node: nodes.Block) ->None:
"""Stop visiting a blocks."""
- pass
+ return
class CompilerExit(Exception):
diff --git a/src/jinja2/debug.py b/src/jinja2/debug.py
index 412f2c2..25c184f 100644
--- a/src/jinja2/debug.py
+++ b/src/jinja2/debug.py
@@ -20,7 +20,37 @@ def rewrite_traceback_stack(source: t.Optional[str]=None) ->BaseException:
known.
:return: The original exception with the rewritten traceback.
"""
- pass
+ exc_type, exc_value, tb = sys.exc_info()
+ if exc_type is None:
+ raise RuntimeError("No exception is currently being handled")
+
+ if isinstance(exc_value, TemplateSyntaxError) and source is not None:
+ exc_value.source = source
+
+ new_tb = None
+ template_frame = None
+
+ for frame in reversed(list(iter_tb(tb))):
+ if internal_code(frame.tb_frame.f_code):
+ continue
+
+ if template_frame is None:
+ template_frame = frame
+
+ new_tb = fake_traceback(
+ exc_value,
+ frame,
+ frame.tb_frame.f_code.co_filename,
+ frame.tb_frame.f_lineno,
+ )
+
+ if new_tb is None:
+ return exc_value
+
+ if template_frame is not None:
+ exc_value.__traceback__ = new_tb
+
+ return exc_value
def fake_traceback(exc_value: BaseException, tb: t.Optional[TracebackType],
@@ -37,7 +67,43 @@ def fake_traceback(exc_value: BaseException, tb: t.Optional[TracebackType],
:param filename: The template filename.
:param lineno: The line number in the template source.
"""
- pass
+ if tb is None:
+ raise exc_value
+
+ code = tb.tb_frame.f_code
+ locals = get_template_locals(tb.tb_frame.f_locals)
+
+ # Create a new code object with the template filename and line number
+ new_code = CodeType(
+ code.co_argcount,
+ code.co_kwonlyargcount,
+ code.co_nlocals,
+ code.co_stacksize,
+ code.co_flags,
+ code.co_code,
+ code.co_consts,
+ code.co_names,
+ code.co_varnames,
+ filename,
+ code.co_name,
+ lineno,
+ code.co_lnotab,
+ code.co_freevars,
+ code.co_cellvars
+ )
+
+ # Create a new frame with the new code object and locals
+ fake_frame = tb.tb_frame.__class__(new_code, tb.tb_frame.f_globals, locals)
+ fake_frame.f_lineno = lineno
+
+ # Create a new traceback object
+ return TracebackType(
+ tb=None,
+ tb_next=tb.tb_next,
+ tb_frame=fake_frame,
+ tb_lasti=tb.tb_lasti,
+ tb_lineno=lineno
+ )
def get_template_locals(real_locals: t.Mapping[str, t.Any]) ->t.Dict[str, t.Any
@@ -45,4 +111,13 @@ def get_template_locals(real_locals: t.Mapping[str, t.Any]) ->t.Dict[str, t.Any
"""Based on the runtime locals, get the context that would be
available at that point in the template.
"""
- pass
+ ctx = real_locals.get('context')
+ if ctx is None or not isinstance(ctx, Context):
+ return {}
+
+ locals = {}
+ for key, value in ctx.items():
+ if not key.startswith('_'):
+ locals[key] = value
+
+ return locals
diff --git a/src/jinja2/environment.py b/src/jinja2/environment.py
index aae9f98..f21e599 100644
--- a/src/jinja2/environment.py
+++ b/src/jinja2/environment.py
@@ -68,19 +68,28 @@ def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any
:param cls: Environment class to create.
:param args: Positional arguments passed to environment.
"""
- pass
+ return cls(*args)
def create_cache(size: int) ->t.Optional[t.MutableMapping[t.Tuple[
'weakref.ref[t.Any]', str], 'Template']]:
"""Return the cache class for the given size."""
- pass
+ if size == 0:
+ return None
+ if size < 0:
+ return {}
+ return LRUCache(size)
def copy_cache(cache: t.Optional[t.MutableMapping[t.Any, t.Any]]) ->t.Optional[
t.MutableMapping[t.Tuple['weakref.ref[t.Any]', str], 'Template']]:
"""Create an empty copy of the given cache."""
- pass
+ if cache is None:
+ return None
+ elif isinstance(cache, LRUCache):
+ return LRUCache(cache.capacity)
+ else:
+ return {}
def load_extensions(environment: 'Environment', extensions: t.Sequence[t.
@@ -88,12 +97,39 @@ def load_extensions(environment: 'Environment', extensions: t.Sequence[t.
"""Load the extensions from the list and bind it to the environment.
Returns a dict of instantiated extensions.
"""
- pass
+ result = {}
+ for extension in extensions:
+ if isinstance(extension, str):
+ extension = import_string(extension)
+ if isinstance(extension, type):
+ extension = extension(environment)
+ result[extension.identifier] = extension
+ return result
def _environment_config_check(environment: 'Environment') ->'Environment':
"""Perform a sanity check on the environment."""
- pass
+ if not isinstance(environment.block_start_string, str):
+ raise TypeError('block_start_string must be a string')
+ if not isinstance(environment.block_end_string, str):
+ raise TypeError('block_end_string must be a string')
+ if not isinstance(environment.variable_start_string, str):
+ raise TypeError('variable_start_string must be a string')
+ if not isinstance(environment.variable_end_string, str):
+ raise TypeError('variable_end_string must be a string')
+ if not isinstance(environment.comment_start_string, str):
+ raise TypeError('comment_start_string must be a string')
+ if not isinstance(environment.comment_end_string, str):
+ raise TypeError('comment_end_string must be a string')
+ if not isinstance(environment.line_statement_prefix, (str, type(None))):
+ raise TypeError('line_statement_prefix must be a string or None')
+ if not isinstance(environment.line_comment_prefix, (str, type(None))):
+ raise TypeError('line_comment_prefix must be a string or None')
+ if not isinstance(environment.trim_blocks, bool):
+ raise TypeError('trim_blocks must be a boolean')
+ if not isinstance(environment.lstrip_blocks, bool):
+ raise TypeError('lstrip_blocks must be a boolean')
+ return environment
class Environment:
diff --git a/src/jinja2/ext.py b/src/jinja2/ext.py
index 337f30c..b196010 100644
--- a/src/jinja2/ext.py
+++ b/src/jinja2/ext.py
@@ -62,7 +62,9 @@ class Extension:
def bind(self, environment: Environment) ->'Extension':
"""Create a copy of this extension bound to another environment."""
- pass
+ new_ext = type(self)(environment)
+ new_ext.__dict__.update(self.__dict__)
+ return new_ext
def preprocess(self, source: str, name: t.Optional[str], filename: t.
Optional[str]=None) ->str:
@@ -70,7 +72,7 @@ class Extension:
preprocess the source. The `filename` is optional. The return value
must be the preprocessed source.
"""
- pass
+ return source
def filter_stream(self, stream: 'TokenStream') ->t.Union['TokenStream',
t.Iterable['Token']]:
@@ -79,7 +81,7 @@ class Extension:
:class:`~jinja2.lexer.Token`\\s, but it doesn't have to return a
:class:`~jinja2.lexer.TokenStream`.
"""
- pass
+ return stream
def parse(self, parser: 'Parser') ->t.Union[nodes.Node, t.List[nodes.Node]
]:
@@ -88,7 +90,7 @@ class Extension:
is the name token that matched. This method has to return one or a
list of multiple nodes.
"""
- pass
+ raise NotImplementedError(f"parse method not implemented for {self.__class__.__name__}")
def attr(self, name: str, lineno: t.Optional[int]=None
) ->nodes.ExtensionAttribute:
@@ -99,7 +101,7 @@ class Extension:
self.attr('_my_attribute', lineno=lineno)
"""
- pass
+ return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
def call_method(self, name: str, args: t.Optional[t.List[nodes.Expr]]=
None, kwargs: t.Optional[t.List[nodes.Keyword]]=None, dyn_args: t.
@@ -108,7 +110,12 @@ class Extension:
"""Call a method of the extension. This is a shortcut for
:meth:`attr` + :class:`jinja2.nodes.Call`.
"""
- pass
+ if args is None:
+ args = []
+ if kwargs is None:
+ kwargs = []
+ return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
+ dyn_args, dyn_kwargs, lineno=lineno)
class InternationalizationExtension(Extension):
@@ -132,14 +139,61 @@ class InternationalizationExtension(Extension):
def _parse_block(self, parser: 'Parser', allow_pluralize: bool) ->t.Tuple[
t.List[str], str]:
"""Parse until the next block tag with a given name."""
- pass
+ plural = None
+ variables = []
+ while parser.stream.current.type != 'block_end':
+ if parser.stream.current.type == 'name' and \
+ parser.stream.current.value in ('plural', 'context'):
+ next(parser.stream)
+ if parser.stream.current.value == 'plural':
+ if allow_pluralize:
+ plural = parser.parse_expression()
+ else:
+ parser.fail('plural not allowed here', parser.stream.current.lineno)
+ else:
+ variables.append(parser.parse_expression())
+ else:
+ variables.append(parser.parse_expression())
+ return variables, plural
def _make_node(self, singular: str, plural: t.Optional[str], context: t
.Optional[str], variables: t.Dict[str, nodes.Expr], plural_expr: t.
Optional[nodes.Expr], vars_referenced: bool, num_called_num: bool
) ->nodes.Output:
"""Generates a useful node from the data provided."""
- pass
+ func_name = 'gettext' if plural is None else 'ngettext'
+ if context is not None:
+ func_name = 'p' + func_name
+
+ gettext_node = nodes.Call(
+ nodes.Name(func_name, 'load'),
+ [nodes.Const(singular)],
+ [],
+ None,
+ None
+ )
+
+ if plural is not None:
+ gettext_node.args.append(nodes.Const(plural))
+ if context is not None:
+ gettext_node.args.insert(0, nodes.Const(context))
+ if plural_expr is not None:
+ gettext_node.args.append(plural_expr)
+
+ if not vars_referenced:
+ return nodes.Output([gettext_node])
+
+ result = []
+ for key, value in variables.items():
+ result.append(nodes.Assign(nodes.Name(key, 'store'), value))
+
+ if plural_expr is not None:
+ result.append(nodes.Assign(nodes.Name('num', 'store'), plural_expr))
+ else:
+ result.append(nodes.Assign(nodes.Name('num', 'store'), nodes.Const(1)))
+
+ result.append(nodes.Output([gettext_node]))
+ return nodes.Node(result)
class ExprStmtExtension(Extension):
diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py
index 9498dc3..65e08df 100644
--- a/src/jinja2/filters.py
+++ b/src/jinja2/filters.py
@@ -43,7 +43,9 @@ V = t.TypeVar('V')
def ignore_case(value: V) ->V:
"""For use as a postprocessor for :func:`make_attrgetter`. Converts strings
to lowercase and returns other types as-is."""
- pass
+ if isinstance(value, str):
+ return value.lower()
+ return value
def make_attrgetter(environment: 'Environment', attribute: t.Optional[t.
@@ -54,7 +56,21 @@ def make_attrgetter(environment: 'Environment', attribute: t.Optional[t.
to access attributes of attributes. Integer parts in paths are
looked up as integers.
"""
- pass
+ if attribute is None:
+ return lambda x: x
+ if isinstance(attribute, int):
+ return lambda x: environment.getitem(x, attribute)
+ if '.' not in attribute:
+ return lambda x: environment.getattr(x, attribute, default)
+
+ def getter(x):
+ for part in attribute.split('.'):
+ if part.isdigit():
+ x = environment.getitem(x, int(part))
+ else:
+ x = environment.getattr(x, part, default)
+ return x if postprocess is None else postprocess(x)
+ return getter
def make_multi_attrgetter(environment: 'Environment', attribute: t.Optional
@@ -70,12 +86,22 @@ def make_multi_attrgetter(environment: 'Environment', attribute: t.Optional
Examples of attribute: "attr1,attr2", "attr1.inner1.0,attr2.inner2.0", etc.
"""
- pass
+ if attribute is None:
+ return lambda x: [x]
+
+ getters = [make_attrgetter(environment, attr.strip(), postprocess)
+ for attr in attribute.split(',')]
+
+ def getter(x):
+ return [g(x) for g in getters]
+ return getter
def do_forceescape(value: 't.Union[str, HasHTML]') ->Markup:
"""Enforce HTML escaping. This will probably double escape variables."""
- pass
+ if hasattr(value, '__html__'):
+ value = value.__html__()
+ return Markup(escape(str(value)))
def do_urlencode(value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.
@@ -95,7 +121,16 @@ def do_urlencode(value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.
.. versionadded:: 2.7
"""
- pass
+ import urllib.parse
+
+ if isinstance(value, str):
+ return urllib.parse.quote(value, safe='/')
+ elif isinstance(value, t.Mapping):
+ return urllib.parse.urlencode(value)
+ elif isinstance(value, t.Iterable):
+ return urllib.parse.urlencode(list(value))
+ else:
+ raise TypeError("Expected string, mapping, or iterable")
@pass_eval_context
diff --git a/src/jinja2/idtracking.py b/src/jinja2/idtracking.py
index a1d69ca..0f1a9ff 100644
--- a/src/jinja2/idtracking.py
+++ b/src/jinja2/idtracking.py
@@ -45,32 +45,40 @@ class FrameSymbolVisitor(NodeVisitor):
def visit_Name(self, node: nodes.Name, store_as_param: bool=False, **
kwargs: t.Any) ->None:
"""All assignments to names go through this function."""
- pass
+ if node.ctx == 'store':
+ if store_as_param:
+ self.symbols.loads[node.name] = VAR_LOAD_PARAMETER
+ self.symbols.stores.add(node.name)
+ elif node.ctx == 'load':
+ self.symbols.refs[node.name] = self.symbols.loads.get(node.name, VAR_LOAD_UNDEFINED)
def visit_Assign(self, node: nodes.Assign, **kwargs: t.Any) ->None:
"""Visit assignments in the correct order."""
- pass
+ self.visit(node.node, **kwargs)
+ for target in node.target:
+ self.visit(target, **kwargs)
def visit_For(self, node: nodes.For, **kwargs: t.Any) ->None:
"""Visiting stops at for blocks. However the block sequence
is visited as part of the outer scope.
"""
- pass
+ self.visit(node.iter, **kwargs)
+ self.visit(node.target, store_as_param=True, **kwargs)
def visit_AssignBlock(self, node: nodes.AssignBlock, **kwargs: t.Any
) ->None:
"""Stop visiting at block assigns."""
- pass
+ self.visit(node.target, **kwargs)
def visit_Scope(self, node: nodes.Scope, **kwargs: t.Any) ->None:
"""Stop visiting at scopes."""
- pass
+ # We don't need to do anything here, as we're stopping at scopes
def visit_Block(self, node: nodes.Block, **kwargs: t.Any) ->None:
"""Stop visiting at blocks."""
- pass
+ # We don't need to do anything here, as we're stopping at blocks
def visit_OverlayScope(self, node: nodes.OverlayScope, **kwargs: t.Any
) ->None:
"""Do not visit into overlay scopes."""
- pass
+ # We don't need to do anything here, as we're not visiting into overlay scopes
diff --git a/src/jinja2/lexer.py b/src/jinja2/lexer.py
index 2281b7e..1d7e812 100644
--- a/src/jinja2/lexer.py
+++ b/src/jinja2/lexer.py
@@ -117,24 +117,39 @@ ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA, TOKEN_COMMENT,
def describe_token(token: 'Token') ->str:
"""Returns a description of the token."""
- pass
+ if token.type == 'name':
+ return token.value
+ return f'{token.type}'
def describe_token_expr(expr: str) ->str:
"""Like `describe_token` but for token expressions."""
- pass
+ if ':' in expr:
+ type, value = expr.split(':', 1)
+ if type == 'name':
+ return value
+ return f'{type}({value})'
+ return expr
def count_newlines(value: str) ->int:
"""Count the number of newline characters in the string. This is
useful for extensions that filter a stream.
"""
- pass
+ return len(newline_re.findall(value))
def compile_rules(environment: 'Environment') ->t.List[t.Tuple[str, str]]:
"""Compiles all the rules from the environment into a list of rules."""
- pass
+ e = re.escape
+ rules = [
+ ('comment', e(environment.comment_start_string)),
+ ('block', e(environment.block_start_string)),
+ ('variable', e(environment.variable_start_string)),
+ ('linestatement', e(environment.line_statement_prefix) if environment.line_statement_prefix else ''),
+ ('linecomment', e(environment.line_comment_prefix) if environment.line_comment_prefix else ''),
+ ]
+ return [(k, v) for k, v in rules if v]
class Failure:
diff --git a/src/jinja2/meta.py b/src/jinja2/meta.py
index 37016c7..6f69091 100644
--- a/src/jinja2/meta.py
+++ b/src/jinja2/meta.py
@@ -22,7 +22,7 @@ class TrackingCodeGenerator(CodeGenerator):
def enter_frame(self, frame: Frame) ->None:
"""Remember all undeclared identifiers."""
- pass
+ self.undeclared_identifiers.update(frame.identifiers.undeclared)
def find_undeclared_variables(ast: nodes.Template) ->t.Set[str]:
@@ -44,7 +44,9 @@ def find_undeclared_variables(ast: nodes.Template) ->t.Set[str]:
:exc:`TemplateAssertionError` during compilation and as a matter of
fact this function can currently raise that exception as well.
"""
- pass
+ codegen = TrackingCodeGenerator(ast.environment)
+ codegen.visit(ast)
+ return codegen.undeclared_identifiers
_ref_types = nodes.Extends, nodes.FromImport, nodes.Import, nodes.Include
@@ -68,4 +70,13 @@ def find_referenced_templates(ast: nodes.Template) ->t.Iterator[t.Optional[str]
This function is useful for dependency tracking. For example if you want
to rebuild parts of the website after a layout template has changed.
"""
- pass
+ def _visit(node):
+ if isinstance(node, _ref_types):
+ if isinstance(node.template, nodes.Const):
+ yield node.template.value
+ else:
+ yield None
+ for child in node.iter_child_nodes():
+ yield from _visit(child)
+
+ return _visit(ast)
diff --git a/src/jinja2/nativetypes.py b/src/jinja2/nativetypes.py
index 9eae726..6d65c49 100644
--- a/src/jinja2/nativetypes.py
+++ b/src/jinja2/nativetypes.py
@@ -1,4 +1,5 @@
import typing as t
+import sys
from ast import literal_eval
from ast import parse
from itertools import chain
@@ -21,7 +22,16 @@ def native_concat(values: t.Iterable[t.Any]) ->t.Optional[t.Any]:
:param values: Iterable of outputs to concatenate.
"""
- pass
+ result = list(values)
+ if not result:
+ return None
+ if len(result) == 1:
+ return result[0]
+
+ try:
+ return literal_eval(''.join(str(v) for v in result))
+ except (ValueError, SyntaxError):
+ return ''.join(str(v) for v in result)
class NativeCodeGenerator(CodeGenerator):
@@ -46,7 +56,12 @@ class NativeTemplate(Template):
with :func:`ast.literal_eval`, the parsed value is returned.
Otherwise, the string is returned.
"""
- pass
+ ctx = self.new_context(dict(*args, **kwargs))
+ try:
+ return self.environment.concat(self.root_render_func(ctx))
+ except Exception:
+ exc_info = sys.exc_info()
+ return self.environment.handle_exception(exc_info, True)
NativeEnvironment.template_class = NativeTemplate
diff --git a/src/jinja2/nodes.py b/src/jinja2/nodes.py
index 4ec1d17..b444e6f 100644
--- a/src/jinja2/nodes.py
+++ b/src/jinja2/nodes.py
@@ -107,7 +107,9 @@ class Node(metaclass=NodeType):
parameter or to exclude some using the `exclude` parameter. Both
should be sets or tuples of field names.
"""
- pass
+ for name in self.fields:
+ if (exclude is None or name not in exclude) and (only is None or name in only):
+ yield name, getattr(self, name)
def iter_child_nodes(self, exclude: t.Optional[t.Container[str]]=None,
only: t.Optional[t.Container[str]]=None) ->t.Iterator['Node']:
@@ -115,20 +117,35 @@ class Node(metaclass=NodeType):
over all fields and yields the values of they are nodes. If the value
of a field is a list all the nodes in that list are returned.
"""
- pass
+ for field, item in self.iter_fields(exclude, only):
+ if isinstance(item, Node):
+ yield item
+ elif isinstance(item, list):
+ for n in item:
+ if isinstance(n, Node):
+ yield n
def find(self, node_type: t.Type[_NodeBound]) ->t.Optional[_NodeBound]:
"""Find the first node of a given type. If no such node exists the
return value is `None`.
"""
- pass
+ for child in self.iter_child_nodes():
+ if isinstance(child, node_type):
+ return child
+ result = child.find(node_type)
+ if result is not None:
+ return result
+ return None
def find_all(self, node_type: t.Union[t.Type[_NodeBound], t.Tuple[t.
Type[_NodeBound], ...]]) ->t.Iterator[_NodeBound]:
"""Find all the nodes of a given type. If the type is a tuple,
the check is performed for any of the tuple items.
"""
- pass
+ for child in self.iter_child_nodes():
+ if isinstance(child, node_type):
+ yield child
+ yield from child.find_all(node_type)
def set_ctx(self, ctx: str) ->'Node':
"""Reset the context of a node and all child nodes. Per default the
@@ -136,15 +153,26 @@ class Node(metaclass=NodeType):
most common one. This method is used in the parser to set assignment
targets and other nodes to a store context.
"""
- pass
+ if hasattr(self, 'ctx'):
+ self.ctx = ctx
+ for child in self.iter_child_nodes():
+ child.set_ctx(ctx)
+ return self
def set_lineno(self, lineno: int, override: bool=False) ->'Node':
"""Set the line numbers of the node and children."""
- pass
+ if not self.lineno or override:
+ self.lineno = lineno
+ for child in self.iter_child_nodes():
+ child.set_lineno(lineno, override)
+ return self
def set_environment(self, environment: 'Environment') ->'Node':
"""Set the environment for all nodes."""
- pass
+ self.environment = environment
+ for child in self.iter_child_nodes():
+ child.set_environment(environment)
+ return self
def __eq__(self, other: t.Any) ->bool:
if type(self) is not type(other):
diff --git a/src/jinja2/optimizer.py b/src/jinja2/optimizer.py
index 53d50e4..ab86a53 100644
--- a/src/jinja2/optimizer.py
+++ b/src/jinja2/optimizer.py
@@ -17,10 +17,83 @@ if t.TYPE_CHECKING:
def optimize(node: nodes.Node, environment: 'Environment') ->nodes.Node:
"""The context hint can be used to perform an static optimization
based on the context given."""
- pass
+ optimizer = Optimizer(environment)
+ return optimizer.visit(node)
class Optimizer(NodeTransformer):
def __init__(self, environment: 't.Optional[Environment]') ->None:
self.environment = environment
+
+ def visit_Const(self, node: nodes.Const) ->nodes.Node:
+ return node
+
+ def visit_List(self, node: nodes.List) ->nodes.Node:
+ for idx, item in enumerate(node.items):
+ node.items[idx] = self.visit(item)
+ return node
+
+ def visit_Dict(self, node: nodes.Dict) ->nodes.Node:
+ for idx, (key, value) in enumerate(node.items):
+ node.items[idx] = (self.visit(key), self.visit(value))
+ return node
+
+ def visit_BinExpr(self, node: nodes.BinExpr) ->nodes.Node:
+ node.left = self.visit(node.left)
+ node.right = self.visit(node.right)
+ if isinstance(node.left, nodes.Const) and isinstance(node.right, nodes.Const):
+ try:
+ if node.op == 'add':
+ return nodes.Const(node.left.value + node.right.value)
+ elif node.op == 'sub':
+ return nodes.Const(node.left.value - node.right.value)
+ elif node.op == 'mul':
+ return nodes.Const(node.left.value * node.right.value)
+ elif node.op == 'div':
+ return nodes.Const(node.left.value / node.right.value)
+ elif node.op == 'floordiv':
+ return nodes.Const(node.left.value // node.right.value)
+ elif node.op == 'mod':
+ return nodes.Const(node.left.value % node.right.value)
+ elif node.op == 'pow':
+ return nodes.Const(node.left.value ** node.right.value)
+ except:
+ pass
+ return node
+
+ def visit_UnaryExpr(self, node: nodes.UnaryExpr) ->nodes.Node:
+ node.node = self.visit(node.node)
+ if isinstance(node.node, nodes.Const):
+ try:
+ if node.op == 'not':
+ return nodes.Const(not node.node.value)
+ elif node.op == 'neg':
+ return nodes.Const(-node.node.value)
+ elif node.op == 'pos':
+ return nodes.Const(+node.node.value)
+ except:
+ pass
+ return node
+
+ def visit_CondExpr(self, node: nodes.CondExpr) ->nodes.Node:
+ node.test = self.visit(node.test)
+ node.expr1 = self.visit(node.expr1)
+ node.expr2 = self.visit(node.expr2)
+ if isinstance(node.test, nodes.Const):
+ if node.test.value:
+ return node.expr1
+ else:
+ return node.expr2
+ return node
+
+ def visit_Filter(self, node: nodes.Filter) ->nodes.Node:
+ node.node = self.visit(node.node)
+ for idx, arg in enumerate(node.args):
+ node.args[idx] = self.visit(arg)
+ for idx, (key, value) in enumerate(node.kwargs):
+ node.kwargs[idx] = (key, self.visit(value))
+ return node
+
+ def generic_visit(self, node: nodes.Node) ->nodes.Node:
+ return super().generic_visit(node)
diff --git a/src/jinja2/parser.py b/src/jinja2/parser.py
index 05ce33d..79113a2 100644
--- a/src/jinja2/parser.py
+++ b/src/jinja2/parser.py
@@ -47,7 +47,9 @@ class Parser:
line number or last line number as well as the current name and
filename.
"""
- pass
+ if lineno is None:
+ lineno = self.stream.current.lineno
+ raise exc(msg, lineno, self.name, self.filename)
def fail_unknown_tag(self, name: str, lineno: t.Optional[int]=None
) ->'te.NoReturn':
@@ -55,26 +57,83 @@ class Parser:
with a human readable error message that could help to identify
the problem.
"""
- pass
+ if lineno is None:
+ lineno = self.stream.current.lineno
+ if self._tag_stack:
+ expected = f'Encountered unknown tag {name!r}. Jinja was looking for the following tags: {", ".join(reversed(self._tag_stack))}.'
+ else:
+ expected = f'Encountered unknown tag {name!r}.'
+ self.fail(expected, lineno)
def fail_eof(self, end_tokens: t.Optional[t.Tuple[str, ...]]=None,
lineno: t.Optional[int]=None) ->'te.NoReturn':
"""Like fail_unknown_tag but for end of template situations."""
- pass
+ if end_tokens is None:
+ end_tokens = tuple()
+ if lineno is None:
+ lineno = self.stream.current.lineno
+ if self._tag_stack:
+ expected = f'Unexpected end of template. Jinja was looking for the following tags: {", ".join(reversed(self._tag_stack))}.'
+ elif end_tokens:
+ expected = f'Unexpected end of template. Jinja was looking for one of the following tokens: {", ".join(repr(x) for x in end_tokens)}.'
+ else:
+ expected = 'Unexpected end of template.'
+ self.fail(expected, lineno)
def is_tuple_end(self, extra_end_rules: t.Optional[t.Tuple[str, ...]]=None
) ->bool:
"""Are we at the end of a tuple?"""
- pass
+ if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
+ return True
+ elif extra_end_rules is not None:
+ return self.stream.current.test_any(*extra_end_rules)
+ return False
def free_identifier(self, lineno: t.Optional[int]=None
) ->nodes.InternalName:
"""Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
- pass
+ self._last_identifier += 1
+ rv = object.__new__(nodes.InternalName)
+ rv.name = f'fi{self._last_identifier}'
+ rv.lineno = lineno or self.stream.current.lineno
+ return rv
def parse_statement(self) ->t.Union[nodes.Node, t.List[nodes.Node]]:
"""Parse a single statement."""
- pass
+ token = self.stream.current
+ if token.type != 'name':
+ return self.parse_expression()
+
+ if token.value in _statement_keywords:
+ if token.value == 'for':
+ return self.parse_for()
+ elif token.value == 'if':
+ return self.parse_if()
+ elif token.value == 'block':
+ return self.parse_block()
+ elif token.value == 'extends':
+ return self.parse_extends()
+ elif token.value == 'print':
+ return self.parse_print()
+ elif token.value == 'macro':
+ return self.parse_macro()
+ elif token.value == 'include':
+ return self.parse_include()
+ elif token.value == 'from':
+ return self.parse_from()
+ elif token.value == 'import':
+ return self.parse_import()
+ elif token.value == 'set':
+ return self.parse_set()
+ elif token.value == 'with':
+ return self.parse_with()
+ elif token.value == 'autoescape':
+ return self.parse_autoescape()
+
+ if token.value in self.extensions:
+ return self.extensions[token.value](self)
+
+ self.fail_unknown_tag(token.value)
def parse_statements(self, end_tokens: t.Tuple[str, ...], drop_needle:
bool=False) ->t.List[nodes.Node]:
@@ -87,7 +146,29 @@ class Parser:
the call is the matched end token. If this is not wanted `drop_needle`
can be set to `True` and the end token is removed.
"""
- pass
+ result = []
+
+ # skip colon if present
+ if self.stream.current.type == 'colon':
+ self.stream.next()
+
+ # check for block end
+ if self.stream.current.type == 'block_end':
+ self.stream.next()
+
+ while self.stream.current.type != 'eof':
+ if self.stream.current.test_any(*end_tokens):
+ if drop_needle:
+ self.stream.next()
+ break
+
+ if self.stream.current.type == 'data':
+ result.append(nodes.Output([nodes.TemplateData(self.stream.current.value)]))
+ self.stream.next()
+ else:
+ result.append(self.parse_statement())
+
+ return result
def parse_set(self) ->t.Union[nodes.Assign, nodes.AssignBlock]:
"""Parse an assign statement."""
diff --git a/src/jinja2/runtime.py b/src/jinja2/runtime.py
index c88211d..b7d315c 100644
--- a/src/jinja2/runtime.py
+++ b/src/jinja2/runtime.py
@@ -43,17 +43,17 @@ def identity(x: V) ->V:
"""Returns its argument. Useful for certain things in the
environment.
"""
- pass
+ return x
def markup_join(seq: t.Iterable[t.Any]) ->str:
"""Concatenation that escapes if necessary and converts to string."""
- pass
+ return Markup('').join(escape(soft_str(v)) for v in seq)
def str_join(seq: t.Iterable[t.Any]) ->str:
"""Simple args to string conversion and concatenation."""
- pass
+ return ''.join(map(str, seq))
def new_context(environment: 'Environment', template_name: t.Optional[str],
@@ -62,7 +62,14 @@ def new_context(environment: 'Environment', template_name: t.Optional[str],
Optional[t.MutableMapping[str, t.Any]]=None, locals: t.Optional[t.
Mapping[str, t.Any]]=None) ->'Context':
"""Internal helper for context creation."""
- pass
+ parent = environment.make_globals(globals)
+ if vars is not None:
+ parent.update(vars)
+ if shared:
+ parent = parent.copy()
+ if locals:
+ parent.update(locals)
+ return Context(environment, parent, template_name, blocks)
class TemplateReference:
@@ -116,7 +123,14 @@ class Context:
def super(self, name: str, current: t.Callable[['Context'], t.Iterator[
str]]) ->t.Union['BlockReference', 'Undefined']:
"""Render a parent block."""
- pass
+ try:
+ blocks = self.blocks[name]
+ index = blocks.index(current) + 1
+ if index < len(blocks):
+ return BlockReference(name, self, blocks, index)
+ except (LookupError, ValueError):
+ pass
+ return self.environment.undefined(f'there is no parent block called {name!r}.', name='super')
def get(self, key: str, default: t.Any=None) ->t.Any:
"""Look up a variable by name, or return a default if the key is
@@ -125,7 +139,10 @@ class Context:
:param key: The variable name to look up.
:param default: The value to return if the key is not found.
"""
- pass
+ try:
+ return self[key]
+ except KeyError:
+ return default
def resolve(self, key: str) ->t.Union[t.Any, 'Undefined']:
"""Look up a variable by name, or return an :class:`Undefined`
@@ -137,7 +154,10 @@ class Context:
:param key: The variable name to look up.
"""
- pass
+ rv = self.resolve_or_missing(key)
+ if rv is missing:
+ return self.environment.undefined(name=key)
+ return rv
def resolve_or_missing(self, key: str) ->t.Any:
"""Look up a variable by name, or return a ``missing`` sentinel
@@ -149,18 +169,22 @@ class Context:
:param key: The variable name to look up.
"""
- pass
+ if key in self.vars:
+ return self.vars[key]
+ if key in self.parent:
+ return self.parent[key]
+ return missing
def get_exported(self) ->t.Dict[str, t.Any]:
"""Get a new dict with the exported variables."""
- pass
+ return {k: self.vars[k] for k in self.exported_vars}
def get_all(self) ->t.Dict[str, t.Any]:
"""Return the complete context as dict including the exported
variables. For optimizations reasons this might not return an
actual copy so be careful with using it.
"""
- pass
+ return {**self.parent, **self.vars}
@internalcode
def call(__self, __obj: t.Callable[..., t.Any], *args: t.Any, **kwargs:
@@ -170,14 +194,25 @@ class Context:
argument if the callable has :func:`pass_context` or
:func:`pass_environment`.
"""
- pass
+ if isinstance(__obj, _PassArg):
+ if __obj._type == 'context':
+ args = (__self,) + args
+ elif __obj._type == 'environment':
+ args = (__self.environment,) + args
+ return __obj._func(*args, **kwargs)
+ return __obj(*args, **kwargs)
def derived(self, locals: t.Optional[t.Dict[str, t.Any]]=None) ->'Context':
"""Internal helper function to create a derived context. This is
used in situations where the system needs a new context in the same
template that is independent.
"""
- pass
+ context = new_context(self.environment, self.name, self.blocks,
+ {**self.vars, **(locals or {})},
+ True, self.globals_keys)
+ context.eval_ctx = self.eval_ctx
+ context.exported_vars = set(self.exported_vars)
+ return context
keys = _dict_method_all(dict.keys)
values = _dict_method_all(dict.values)
items = _dict_method_all(dict.items)
diff --git a/src/jinja2/sandbox.py b/src/jinja2/sandbox.py
index b73a983..6803d85 100644
--- a/src/jinja2/sandbox.py
+++ b/src/jinja2/sandbox.py
@@ -35,7 +35,22 @@ def safe_range(*args: int) ->range:
"""A range that can't generate ranges with a length of more than
MAX_RANGE items.
"""
- pass
+ if len(args) == 1:
+ start, stop, step = 0, args[0], 1
+ elif len(args) == 2:
+ start, stop, step = args[0], args[1], 1
+ elif len(args) == 3:
+ start, stop, step = args
+ else:
+ raise TypeError('range() requires 1-3 integer arguments')
+
+ # Calculate the length of the range
+ length = (stop - start + step - 1) // step
+
+ if length > MAX_RANGE:
+ raise OverflowError(f'range() result has too many items (maximum is {MAX_RANGE})')
+
+ return range(start, stop, step)
def unsafe(f: F) ->F:
@@ -47,7 +62,8 @@ def unsafe(f: F) ->F:
def delete(self):
pass
"""
- pass
+ f.unsafe_callable = True
+ return f
def is_internal_attribute(obj: t.Any, attr: str) ->bool:
@@ -62,7 +78,14 @@ def is_internal_attribute(obj: t.Any, attr: str) ->bool:
>>> is_internal_attribute(str, "upper")
False
"""
- pass
+ return attr.startswith('__') and attr.endswith('__') or \
+ attr.startswith('func_') or \
+ attr in UNSAFE_FUNCTION_ATTRIBUTES or \
+ attr in UNSAFE_METHOD_ATTRIBUTES or \
+ (isinstance(obj, (types.FunctionType, types.MethodType)) and attr in ('__globals__', '__closure__')) or \
+ (isinstance(obj, types.GeneratorType) and attr in UNSAFE_GENERATOR_ATTRIBUTES) or \
+ (isinstance(obj, types.CoroutineType) and attr in UNSAFE_COROUTINE_ATTRIBUTES) or \
+ (isinstance(obj, types.AsyncGeneratorType) and attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES)
def modifies_known_mutable(obj: t.Any, attr: str) ->bool:
@@ -84,7 +107,10 @@ def modifies_known_mutable(obj: t.Any, attr: str) ->bool:
>>> modifies_known_mutable("foo", "upper")
False
"""
- pass
+ for typ, mutable_attrs in _mutable_spec:
+ if isinstance(obj, typ):
+ return attr in mutable_attrs
+ return False
class SandboxedEnvironment(Environment):
@@ -120,7 +146,7 @@ class SandboxedEnvironment(Environment):
special attributes of internal python objects as returned by the
:func:`is_internal_attribute` function.
"""
- pass
+ return not (attr.startswith('_') or is_internal_attribute(obj, attr))
def is_safe_callable(self, obj: t.Any) ->bool:
"""Check if an object is safely callable. By default callables
@@ -129,7 +155,8 @@ class SandboxedEnvironment(Environment):
This also recognizes the Django convention of setting
``func.alters_data = True``.
"""
- pass
+ return not (getattr(obj, 'unsafe_callable', False) or
+ getattr(obj, 'alters_data', False))
def call_binop(self, context: Context, operator: str, left: t.Any,
right: t.Any) ->t.Any:
@@ -139,7 +166,9 @@ class SandboxedEnvironment(Environment):
.. versionadded:: 2.6
"""
- pass
+ if operator not in self.binop_table:
+ raise SecurityError(f'unsupported binary operator: {operator}')
+ return self.binop_table[operator](left, right)
def call_unop(self, context: Context, operator: str, arg: t.Any) ->t.Any:
"""For intercepted unary operator calls (:meth:`intercepted_unops`)
@@ -148,22 +177,40 @@ class SandboxedEnvironment(Environment):
.. versionadded:: 2.6
"""
- pass
+ if operator not in self.unop_table:
+ raise SecurityError(f'unsupported unary operator: {operator}')
+ return self.unop_table[operator](arg)
def getitem(self, obj: t.Any, argument: t.Union[str, t.Any]) ->t.Union[
t.Any, Undefined]:
"""Subscribe an object from sandboxed code."""
- pass
+ try:
+ return obj[argument]
+ except (TypeError, LookupError):
+ if isinstance(argument, str):
+ try:
+ return self.getattr(obj, argument)
+ except AttributeError:
+ pass
+ return self.undefined(obj=obj, name=argument)
def getattr(self, obj: t.Any, attribute: str) ->t.Union[t.Any, Undefined]:
"""Subscribe an object from sandboxed code and prefer the
attribute. The attribute passed *must* be a bytestring.
"""
- pass
+ try:
+ value = getattr(obj, attribute)
+ except AttributeError:
+ return self.undefined(obj=obj, name=attribute)
+ else:
+ if self.is_safe_attribute(obj, attribute, value):
+ return value
+ return self.unsafe_undefined(obj, attribute)
def unsafe_undefined(self, obj: t.Any, attribute: str) ->Undefined:
"""Return an undefined object for unsafe attributes."""
- pass
+ return self.undefined(obj=obj, name=attribute, exc=SecurityError(
+ f'{attribute!r} is an unsafe attribute'))
def format_string(self, s: str, args: t.Tuple[t.Any, ...], kwargs: t.
Dict[str, t.Any], format_func: t.Optional[t.Callable[..., t.Any]]=None
@@ -171,12 +218,20 @@ class SandboxedEnvironment(Environment):
"""If a format call is detected, then this is routed through this
method so that our safety sandbox can be used for it.
"""
- pass
+ if format_func is None:
+ format_func = self.format_string
+ if isinstance(s, Markup):
+ formatter = SandboxedEscapeFormatter(self, format_func=format_func)
+ else:
+ formatter = SandboxedFormatter(self, format_func=format_func)
+ return formatter.vformat(s, args, kwargs)
def call(__self, __context: Context, __obj: t.Any, *args: t.Any, **
kwargs: t.Any) ->t.Any:
"""Call an object from sandboxed code."""
- pass
+ if not __self.is_safe_callable(__obj):
+ raise SecurityError(f'{__obj!r} is not safely callable')
+ return __obj(*args, **kwargs)
class ImmutableSandboxedEnvironment(SandboxedEnvironment):
diff --git a/src/jinja2/tests.py b/src/jinja2/tests.py
index 2823a4b..914a56d 100644
--- a/src/jinja2/tests.py
+++ b/src/jinja2/tests.py
@@ -11,17 +11,17 @@ if t.TYPE_CHECKING:
def test_odd(value: int) ->bool:
"""Return true if the variable is odd."""
- pass
+ return value % 2 != 0
def test_even(value: int) ->bool:
"""Return true if the variable is even."""
- pass
+ return value % 2 == 0
def test_divisibleby(value: int, num: int) ->bool:
"""Check if a variable is divisible by a number."""
- pass
+ return value % num == 0
def test_defined(value: t.Any) ->bool:
@@ -38,12 +38,12 @@ def test_defined(value: t.Any) ->bool:
See the :func:`default` filter for a simple way to set undefined
variables.
"""
- pass
+ return not isinstance(value, Undefined)
def test_undefined(value: t.Any) ->bool:
"""Like :func:`defined` but the other way round."""
- pass
+ return isinstance(value, Undefined)
@pass_environment
@@ -61,7 +61,7 @@ def test_filter(env: 'Environment', value: str) ->bool:
.. versionadded:: 3.0
"""
- pass
+ return value in env.filters
@pass_environment
@@ -83,12 +83,12 @@ def test_test(env: 'Environment', value: str) ->bool:
.. versionadded:: 3.0
"""
- pass
+ return value in env.tests
def test_none(value: t.Any) ->bool:
"""Return true if the variable is none."""
- pass
+ return value is None
def test_boolean(value: t.Any) ->bool:
@@ -96,7 +96,7 @@ def test_boolean(value: t.Any) ->bool:
.. versionadded:: 2.11
"""
- pass
+ return isinstance(value, bool)
def test_false(value: t.Any) ->bool:
@@ -104,7 +104,7 @@ def test_false(value: t.Any) ->bool:
.. versionadded:: 2.11
"""
- pass
+ return value is False
def test_true(value: t.Any) ->bool:
@@ -112,7 +112,7 @@ def test_true(value: t.Any) ->bool:
.. versionadded:: 2.11
"""
- pass
+ return value is True
def test_integer(value: t.Any) ->bool:
@@ -120,7 +120,7 @@ def test_integer(value: t.Any) ->bool:
.. versionadded:: 2.11
"""
- pass
+ return isinstance(value, int)
def test_float(value: t.Any) ->bool:
@@ -128,22 +128,22 @@ def test_float(value: t.Any) ->bool:
.. versionadded:: 2.11
"""
- pass
+ return isinstance(value, float)
def test_lower(value: str) ->bool:
"""Return true if the variable is lowercased."""
- pass
+ return isinstance(value, str) and value.islower()
def test_upper(value: str) ->bool:
"""Return true if the variable is uppercased."""
- pass
+ return isinstance(value, str) and value.isupper()
def test_string(value: t.Any) ->bool:
"""Return true if the object is a string."""
- pass
+ return isinstance(value, str)
def test_mapping(value: t.Any) ->bool:
@@ -151,19 +151,19 @@ def test_mapping(value: t.Any) ->bool:
.. versionadded:: 2.6
"""
- pass
+ return isinstance(value, abc.Mapping)
def test_number(value: t.Any) ->bool:
"""Return true if the variable is a number."""
- pass
+ return isinstance(value, Number)
def test_sequence(value: t.Any) ->bool:
"""Return true if the variable is a sequence. Sequences are variables
that are iterable.
"""
- pass
+ return isinstance(value, abc.Sequence) and not isinstance(value, str)
def test_sameas(value: t.Any, other: t.Any) ->bool:
@@ -176,17 +176,21 @@ def test_sameas(value: t.Any, other: t.Any) ->bool:
the foo attribute really is the `False` singleton
{% endif %}
"""
- pass
+ return value is other
def test_iterable(value: t.Any) ->bool:
"""Check if it's possible to iterate over an object."""
- pass
+ try:
+ iter(value)
+ return True
+ except TypeError:
+ return False
def test_escaped(value: t.Any) ->bool:
"""Check if the value is escaped."""
- pass
+ return hasattr(value, '__html__')
def test_in(value: t.Any, seq: t.Container[t.Any]) ->bool:
@@ -194,7 +198,7 @@ def test_in(value: t.Any, seq: t.Container[t.Any]) ->bool:
.. versionadded:: 2.10
"""
- pass
+ return value in seq
TESTS = {'odd': test_odd, 'even': test_even, 'divisibleby':
diff --git a/src/jinja2/utils.py b/src/jinja2/utils.py
index 7563812..9a4ac9c 100644
--- a/src/jinja2/utils.py
+++ b/src/jinja2/utils.py
@@ -32,7 +32,8 @@ def pass_context(f: F) ->F:
.. versionadded:: 3.0.0
Replaces ``contextfunction`` and ``contextfilter``.
"""
- pass
+ f.jinja_pass_arg = _PassArg.context
+ return f
def pass_eval_context(f: F) ->F:
@@ -48,7 +49,8 @@ def pass_eval_context(f: F) ->F:
.. versionadded:: 3.0.0
Replaces ``evalcontextfunction`` and ``evalcontextfilter``.
"""
- pass
+ f.jinja_pass_arg = _PassArg.eval_context
+ return f
def pass_environment(f: F) ->F:
@@ -60,7 +62,8 @@ def pass_environment(f: F) ->F:
.. versionadded:: 3.0.0
Replaces ``environmentfunction`` and ``environmentfilter``.
"""
- pass
+ f.jinja_pass_arg = _PassArg.environment
+ return f
class _PassArg(enum.Enum):
@@ -71,7 +74,8 @@ class _PassArg(enum.Enum):
def internalcode(f: F) ->F:
"""Marks the function as internally used"""
- pass
+ internal_code.add(f.__code__)
+ return f
def is_undefined(obj: t.Any) ->bool:
@@ -86,7 +90,8 @@ def is_undefined(obj: t.Any) ->bool:
return default
return var
"""
- pass
+ from .runtime import Undefined
+ return isinstance(obj, Undefined)
def consume(iterable: t.Iterable[t.Any]) ->None:
diff --git a/src/jinja2/visitor.py b/src/jinja2/visitor.py
index ebb34c6..54d7091 100644
--- a/src/jinja2/visitor.py
+++ b/src/jinja2/visitor.py
@@ -30,15 +30,19 @@ class NodeVisitor:
exists for this node. In that case the generic visit function is
used instead.
"""
- pass
+ method = 'visit_' + node.__class__.__name__
+ return getattr(self, method, None)
def visit(self, node: Node, *args: t.Any, **kwargs: t.Any) ->t.Any:
"""Visit a node."""
- pass
+ f = self.get_visitor(node)
+ if f is None:
+ return self.generic_visit(node, *args, **kwargs)
+ return f(node, *args, **kwargs)
def generic_visit(self, node: Node, *args: t.Any, **kwargs: t.Any) ->t.Any:
"""Called if no explicit visitor function exists for a node."""
- pass
+ return node
class NodeTransformer(NodeVisitor):
@@ -57,4 +61,12 @@ class NodeTransformer(NodeVisitor):
"""As transformers may return lists in some places this method
can be used to enforce a list as return value.
"""
- pass
+ result = []
+ for child in node:
+ new_node = self.visit(child, *args, **kwargs)
+ if new_node is not None:
+ if isinstance(new_node, list):
+ result.extend(new_node)
+ else:
+ result.append(new_node)
+ return result