Skip to content

back to SWE-Agent summary

SWE-Agent: 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
E     File "/testbed/src/jinja2/nodes.py", line 80
E       raise TypeError(f'{type(self).__name__!r} takes 0 or {len(self.fields)} argument{('s' if len(self.fields) != 1 else '')}')
E                                                                                          ^
E   SyntaxError: f-string: unmatched '('

Patch diff

diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py
index b697d34..bac541b 100644
--- a/src/jinja2/filters.py
+++ b/src/jinja2/filters.py
@@ -66,7 +66,7 @@ def make_multi_attrgetter(environment: 'Environment', attribute: t.Optional[t.Un

 def do_forceescape(value: 't.Union[str, HasHTML]') -> Markup:
     """Enforce HTML escaping.  This will probably double escape variables."""
-    pass
+    return escape(str(value))

 def do_urlencode(value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[str, t.Any]]]) -> str:
     """Quote data for use in a URL path or query using UTF-8.
@@ -84,7 +84,16 @@ def do_urlencode(value: t.Union[str, t.Mapping[str, t.Any], t.Iterable[t.Tuple[s

     .. versionadded:: 2.7
     """
-    pass
+    from urllib.parse import quote, urlencode
+    
+    if isinstance(value, str):
+        return quote(value, safe='/')
+    elif isinstance(value, t.Mapping):
+        return urlencode(value)
+    elif isinstance(value, t.Iterable):
+        return urlencode(list(value))
+    else:
+        raise TypeError("Expected string, mapping, or iterable.")

 @pass_eval_context
 def do_replace(eval_ctx: 'EvalContext', s: str, old: str, new: str, count: t.Optional[int]=None) -> str:
@@ -110,7 +119,7 @@ def do_upper(s: str) -> str:

 def do_lower(s: str) -> str:
     """Convert a value to lowercase."""
-    pass
+    return s.lower()

 def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K, V]]:
     """Return an iterator over the ``(key, value)`` items of a mapping.
@@ -174,7 +183,17 @@ def do_xmlattr(eval_ctx: 'EvalContext', d: t.Mapping[str, t.Any], autospace: boo
     .. versionchanged:: 3.1.3
         Keys with spaces are not allowed.
     """
-    pass
+    result = []
+    for key, value in d.items():
+        if _attr_key_re.search(key):
+            raise ValueError(f"unsafe attribute name: {key!r}")
+        if value is not None and not isinstance(value, Undefined):
+            result.append(f'{key}="{escape(value)}"')
+    
+    rv = ' '.join(result)
+    if autospace and rv:
+        rv = ' ' + rv
+    return rv

 def do_capitalize(s: str) -> str:
     """Capitalize a value. The first character will be uppercase, all others
@@ -207,7 +226,20 @@ def do_dictsort(value: t.Mapping[K, V], case_sensitive: bool=False, by: 'te.Lite
         {% for key, value in mydict|dictsort(false, 'value') %}
             sort the dict by value, case insensitive
     """
-    pass
+    if by == 'key':
+        pos = 0
+    elif by == 'value':
+        pos = 1
+    else:
+        raise ValueError('Can only sort by either "key" or "value"')
+    
+    def sort_func(item):
+        value = item[pos]
+        if not case_sensitive and isinstance(value, str):
+            value = value.lower()
+        return value
+
+    return sorted(value.items(), key=sort_func, reverse=reverse)

 @pass_environment
 def do_sort(environment: 'Environment', value: 't.Iterable[V]', reverse: bool=False, case_sensitive: bool=False, attribute: t.Optional[t.Union[str, int]]=None) -> 't.List[V]':
@@ -244,7 +276,31 @@ def do_sort(environment: 'Environment', value: 't.Iterable[V]', reverse: bool=Fa

         {% for user in users|sort(attribute="age,name") %}
             ...
-        {% endfor %}
+    if not case_sensitive and isinstance(value[0], str):
+        def prepare(val):
+            if isinstance(val, str):
+                return val.lower()
+            return val
+    else:
+        prepare = lambda val: val
+
+    if attribute is None:
+        key_func = prepare
+    elif isinstance(attribute, str):
+        if '.' in attribute:
+            def key_func(val):
+                for attr in attribute.split('.'):
+                    val = environment.getattr(val, attr)
+                return prepare(val)
+        else:
+            key_func = lambda val: prepare(environment.getattr(val, attribute))
+    elif isinstance(attribute, (list, tuple)):
+        def key_func(val):
+            return tuple(prepare(environment.getattr(val, attr)) for attr in attribute)
+    else:
+        raise TypeError('attribute must be a string or a list of strings')
+
+    return sorted(value, key=key_func, reverse=reverse)

     .. versionchanged:: 2.11.0
         The ``attribute`` parameter can be a comma separated list of
@@ -270,7 +326,19 @@ def do_unique(environment: 'Environment', value: 't.Iterable[V]', case_sensitive
     :param case_sensitive: Treat upper and lower case strings as distinct.
     :param attribute: Filter objects with unique values for this attribute.
     """
-    pass
+    getter = make_attrgetter(environment, attribute)
+    seen = set()
+
+    def _unique(item):
+        key = getter(item)
+        if not case_sensitive and isinstance(key, str):
+            key = key.lower()
+        if key not in seen:
+            seen.add(key)
+            return True
+        return False
+
+    return (item for item in value if _unique(item))

 @pass_environment
 def do_min(environment: 'Environment', value: 't.Iterable[V]', case_sensitive: bool=False, attribute: t.Optional[t.Union[str, int]]=None) -> 't.Union[V, Undefined]':
@@ -294,7 +362,18 @@ def do_max(environment: 'Environment', value: 't.Iterable[V]', case_sensitive: b

         {{ [1, 2, 3]|max }}
             -> 3
-
+    if not value:
+        return Undefined(name='max')
+    
+    key = make_attrgetter(environment, attribute)
+    
+    def prepare(val):
+        val = key(val)
+        if not case_sensitive and isinstance(val, str):
+            val = val.lower()
+        return val
+    
+    return max(value, key=prepare)
     :param case_sensitive: Treat upper and lower case strings as distinct.
     :param attribute: Get the object with the max value of this attribute.
     """
@@ -323,7 +402,9 @@ def do_default(value: V, default_value: V='', boolean: bool=False) -> V:
        on nested elements and attributes that may contain undefined values
        in the chain without getting an :exc:`~jinja2.UndefinedError`.
     """
-    pass
+    if isinstance(value, Undefined) or (boolean and not value):
+        return default_value
+    return value

 @pass_eval_context
 def sync_do_join(eval_ctx: 'EvalContext', value: t.Iterable[t.Any], d: str='', attribute: t.Optional[t.Union[str, int]]=None) -> str:
@@ -370,12 +451,17 @@ def do_last(environment: 'Environment', seq: 't.Reversible[V]') -> 't.Union[V, U

         {{ data | selectattr('name', '==', 'Jinja') | list | last }}
     """
-    pass
+    try:
+        return next(reversed(seq))
+    except StopIteration:
+        return Undefined(name='last')

 @pass_context
 def do_random(context: 'Context', seq: 't.Sequence[V]') -> 't.Union[V, Undefined]':
     """Return a random item from the sequence."""
-    pass
+    if seq:
+        return random.choice(seq)
+    return Undefined(name='random')

 def do_filesizeformat(value: t.Union[str, float, int], binary: bool=False) -> str:
     """Format the value like a 'human-readable' file size (i.e. 13 kB,
@@ -383,11 +469,33 @@ def do_filesizeformat(value: t.Union[str, float, int], binary: bool=False) -> st
     Giga, etc.), if the second parameter is set to `True` the binary
     prefixes are used (Mebi, Gibi).
     """
-    pass
+    bytes = float(value)
+    base = 1024 if binary else 1000
+    prefixes = [
+        (binary and "KiB" or "kB"),
+        (binary and "MiB" or "MB"),
+        (binary and "GiB" or "GB"),
+        (binary and "TiB" or "TB"),
+        (binary and "PiB" or "PB"),
+        (binary and "EiB" or "EB"),
+        (binary and "ZiB" or "ZB"),
+        (binary and "YiB" or "YB")
+    ]
+    if bytes == 1:
+        return "1 Byte"
+    elif bytes < base:
+        return f"{bytes:3.0f} Bytes"
+    else:
+        for i, prefix in enumerate(prefixes):
+            unit = base ** (i + 2)
+            if bytes < unit:
+                return f"{base * bytes / unit:.1f} {prefix}"
+        return f"{base * bytes / unit:.1f} {prefix}"

 def do_pprint(value: t.Any) -> str:
     """Pretty print a variable. Useful for debugging."""
-    pass
+    from pprint import pformat
+    return pformat(value)
 _uri_scheme_re = re.compile('^([\\w.+-]{2,}:(/){0,2})$')

 @pass_eval_context
@@ -429,7 +537,19 @@ def do_urlize(eval_ctx: 'EvalContext', value: str, trim_url_limit: t.Optional[in
     .. versionchanged:: 2.8
        The ``target`` parameter was added.
     """
-    pass
+    from markupsafe import escape
+    from jinja2.utils import urlize
+
+    if extra_schemes is None:
+        extra_schemes = eval_ctx.environment.policies["urlize.extra_schemes"]
+
+    return urlize(
+        value,
+        trim_url_limit=trim_url_limit,
+        rel=rel,
+        target=target,
+        extra_schemes=extra_schemes,
+    )

 def do_indent(s: str, width: t.Union[int, str]=4, first: bool=False, blank: bool=False) -> str:
     """Return a copy of the string with each line indented by 4 spaces. The
@@ -447,7 +567,23 @@ def do_indent(s: str, width: t.Union[int, str]=4, first: bool=False, blank: bool

         Rename the ``indentfirst`` argument to ``first``.
     """
-    pass
+    indention = ' ' * width if isinstance(width, int) else width
+
+    newline = '\n'
+    if isinstance(s, Markup):
+        newline = Markup(newline)
+        indention = Markup(indention)
+
+    result = []
+    for idx, line in enumerate(s.splitlines(True)):
+        if idx == 0 and not first:
+            result.append(line)
+        elif line.strip() or blank:
+            result.append(indention + line)
+        else:
+            result.append(line)
+
+    return newline.join(result)

 @pass_environment
 def do_truncate(env: 'Environment', s: str, length: int=255, killwords: bool=False, end: str='...', leeway: t.Optional[int]=None) -> str:
@@ -474,7 +610,20 @@ def do_truncate(env: 'Environment', s: str, length: int=255, killwords: bool=Fal
     The default leeway on newer Jinja versions is 5 and was 0 before but
     can be reconfigured globally.
     """
-    pass
+    if leeway is None:
+        leeway = env.policies['truncate.leeway']
+    
+    assert length >= len(end), 'expected length >= len(end)'
+    assert leeway >= 0, 'expected leeway >= 0'
+    
+    if len(s) <= length + leeway:
+        return s
+    
+    if killwords:
+        return s[:length - len(end)] + end
+    
+    result = s[:length - len(end)].rsplit(' ', 1)[0]
+    return result + end

 @pass_environment
 def do_wordwrap(environment: 'Environment', s: str, width: int=79, break_long_words: bool=True, wrapstring: t.Optional[str]=None, break_on_hyphens: bool=True) -> str:
@@ -511,7 +660,12 @@ def do_int(value: t.Any, default: int=0, base: int=10) -> int:
     conversion doesn't work it will return ``0``. You can
     override this default using the first parameter. You
     can also override the default base (10) in the second
-    parameter, which handles input with prefixes such as
+    try:
+        if isinstance(value, str):
+            return int(value, base)
+        return int(value)
+    except (ValueError, TypeError):
+        return default
     0b, 0o and 0x for bases 2, 8 and 16 respectively.
     The base is ignored for decimal numbers and non-string values.
     """
@@ -548,7 +702,7 @@ def do_format(value: str, *args: t.Any, **kwargs: t.Any) -> str:

 def do_trim(value: str, chars: t.Optional[str]=None) -> str:
     """Strip leading and trailing characters, by default whitespace."""
-    pass
+    return value.strip(chars)

 def do_striptags(value: 't.Union[str, HasHTML]') -> str:
     """Strip SGML/XML tags and replace adjacent whitespace by one space."""
@@ -574,7 +728,24 @@ def sync_do_slice(value: 't.Collection[V]', slices: int, fill_with: 't.Optional[
     If you pass it a second argument it's used to fill missing
     values on the last iteration.
     """
-    pass
+    seq = list(value)
+    length = len(seq)
+    items_per_slice = length // slices
+    slices_with_extra = length % slices
+    offset = 0
+
+    for slice_number in range(slices):
+        start = offset + slice_number * items_per_slice
+        if slice_number < slices_with_extra:
+            offset += 1
+
+        end = offset + (slice_number + 1) * items_per_slice
+        tmp = seq[start:end]
+
+        if fill_with is not None and slice_number >= slices_with_extra:
+            tmp.append(fill_with)
+
+        yield tmp

 def do_batch(value: 't.Iterable[V]', linecount: int, fill_with: 't.Optional[V]'=None) -> 't.Iterator[t.List[V]]':
     """
@@ -595,7 +766,17 @@ def do_batch(value: 't.Iterable[V]', linecount: int, fill_with: 't.Optional[V]'=
         {%- endfor %}
         </table>
     """
-    pass
+    result = []
+    tmp = []
+    for item in value:
+        if len(tmp) == linecount:
+            yield tmp
+            tmp = []
+        tmp.append(item)
+    if tmp:
+        if fill_with is not None and len(tmp) < linecount:
+            tmp += [fill_with] * (linecount - len(tmp))
+        yield tmp

 def do_round(value: float, precision: int=0, method: 'te.Literal["common", "ceil", "floor"]'='common') -> float:
     """Round the number to a given precision. The first
@@ -607,7 +788,14 @@ def do_round(value: float, precision: int=0, method: 'te.Literal["common", "ceil
     - ``'floor'`` always rounds down

     If you don't specify a method ``'common'`` is used.
-
+    if method == 'common':
+        return round(value, precision)
+    elif method == 'ceil':
+        return math.ceil(value * (10 ** precision)) / (10 ** precision)
+    elif method == 'floor':
+        return math.floor(value * (10 ** precision)) / (10 ** precision)
+    else:
+        raise FilterArgumentError('method must be common, ceil or floor')
     .. sourcecode:: jinja

         {{ 42.55|round }}
@@ -623,7 +811,14 @@ def do_round(value: float, precision: int=0, method: 'te.Literal["common", "ceil
         {{ 42.55|round|int }}
             -> 43
     """
-    pass
+    if method == 'common':
+        return round(value, precision)
+    elif method == 'ceil':
+        return math.ceil(value * (10 ** precision)) / (10 ** precision)
+    elif method == 'floor':
+        return math.floor(value * (10 ** precision)) / (10 ** precision)
+    else:
+        raise FilterArgumentError('method must be common, ceil or floor')

 class _GroupTuple(t.NamedTuple):
     grouper: t.Any
@@ -909,4 +1104,4 @@ def do_tojson(eval_ctx: 'EvalContext', value: t.Any, indent: t.Optional[int]=Non
     .. versionadded:: 2.9
     """
     pass
-FILTERS = {'abs': abs, 'attr': do_attr, 'batch': do_batch, 'capitalize': do_capitalize, 'center': do_center, 'count': len, 'd': do_default, 'default': do_default, 'dictsort': do_dictsort, 'e': escape, 'escape': escape, 'filesizeformat': do_filesizeformat, 'first': do_first, 'float': do_float, 'forceescape': do_forceescape, 'format': do_format, 'groupby': do_groupby, 'indent': do_indent, 'int': do_int, 'join': do_join, 'last': do_last, 'length': len, 'list': do_list, 'lower': do_lower, 'items': do_items, 'map': do_map, 'min': do_min, 'max': do_max, 'pprint': do_pprint, 'random': do_random, 'reject': do_reject, 'rejectattr': do_rejectattr, 'replace': do_replace, 'reverse': do_reverse, 'round': do_round, 'safe': do_mark_safe, 'select': do_select, 'selectattr': do_selectattr, 'slice': do_slice, 'sort': do_sort, 'string': soft_str, 'striptags': do_striptags, 'sum': do_sum, 'title': do_title, 'trim': do_trim, 'truncate': do_truncate, 'unique': do_unique, 'upper': do_upper, 'urlencode': do_urlencode, 'urlize': do_urlize, 'wordcount': do_wordcount, 'wordwrap': do_wordwrap, 'xmlattr': do_xmlattr, 'tojson': do_tojson}
\ No newline at end of file
+FILTERS = {'abs': abs, 'attr': do_attr, 'batch': do_batch, 'capitalize': do_capitalize, 'center': do_center, 'count': len, 'd': do_default, 'default': do_default, 'dictsort': do_dictsort, 'e': escape, 'escape': escape, 'filesizeformat': do_filesizeformat, 'first': do_first, 'float': do_float, 'forceescape': do_forceescape, 'format': do_format, 'groupby': do_groupby, 'indent': do_indent, 'int': do_int, 'join': do_join, 'last': do_last, 'length': len, 'list': do_list, 'lower': do_lower, 'items': do_items, 'map': do_map, 'min': do_min, 'max': do_max, 'pprint': do_pprint, 'random': do_random, 'reject': do_reject, 'rejectattr': do_rejectattr, 'replace': do_replace, 'reverse': do_reverse, 'round': do_round, 'safe': do_mark_safe, 'select': do_select, 'selectattr': do_selectattr, 'slice': do_slice, 'sort': do_sort, 'string': soft_str, 'striptags': do_striptags, 'sum': do_sum, 'title': do_title, 'trim': do_trim, 'truncate': do_truncate, 'unique': do_unique, 'upper': do_upper, 'urlencode': do_urlencode, 'urlize': do_urlize, 'wordcount': do_wordcount, 'wordwrap': do_wordwrap, 'xmlattr': do_xmlattr, 'tojson': do_tojson}