Skip to content

filters ¤

This module contains Jinja filters.

Functions:

console_width ¤

console_width(default: int = 80) -> int

Return current console width.

Parameters:

  • default (int, default: 80 ) –

    The default value if width cannot be retrieved.

Returns:

  • int

    The console width.

Source code in src/shellman/templates/filters.py
148
149
150
151
152
153
154
155
156
157
158
159
def console_width(default: int = 80) -> int:
    """Return current console width.

    Parameters:
        default: The default value if width cannot be retrieved.

    Returns:
        The console width.
    """
    # only solution that works with stdin redirected from file
    # https://stackoverflow.com/questions/566746
    return get_terminal_size((default, 20)).columns

do_body ¤

do_body(
    string_or_list: str | Sequence[str],
    delimiter: str = " ",
) -> str | None

Get the body of a text.

Parameters:

Returns:

  • str | None

    The text's body.

Source code in src/shellman/templates/filters.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def do_body(string_or_list: str | Sequence[str], delimiter: str = " ") -> str | None:
    """Get the body of a text.

    Parameters:
        string_or_list: Given text.


    Returns:
        The text's body.
    """
    if isinstance(string_or_list, str):
        return string_or_list.split(delimiter, 1)[1]
    if isinstance(string_or_list, list):
        return "\n".join(string_or_list[1:])
    return None

do_escape ¤

do_escape(
    value: str, except_starts_with: list[str] | None = None
) -> str

Escape (HTML) given text.

Parameters:

  • except_starts_with (list[str] | None, default: None ) –

    Each line starting with at least one of the prefixes listed in this parameter will not be escaped.

Returns:

  • str

    The escaped text.

Source code in src/shellman/templates/filters.py
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
def do_escape(value: str, except_starts_with: list[str] | None = None) -> str:
    """Escape (HTML) given text.

    Parameters:
        except_starts_with: Each line starting with at least one of the prefixes
            listed in this parameter will not be escaped.

    Returns:
        The escaped text.
    """
    predicate = (
        (lambda line: any(line.startswith(string) for string in except_starts_with))
        if except_starts_with is not None
        else lambda line: False
    )
    return "\n".join(line if line == "" or predicate(line) else escape(line) for line in value.split("\n"))

do_firstline ¤

do_firstline(
    string_or_list: str | Sequence[str],
) -> str | None

Get the first line of a text.

Parameters:

Returns:

  • str | None

    The text's first line.

Source code in src/shellman/templates/filters.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def do_firstline(string_or_list: str | Sequence[str]) -> str | None:
    """Get the first line of a text.

    Parameters:
        string_or_list: Given text.


    Returns:
        The text's first line.
    """
    if isinstance(string_or_list, str):
        return string_or_list.split("\n", 1)[0]
    if isinstance(string_or_list, list):
        return string_or_list[0]
    return None

do_firstword ¤

do_firstword(string: str, delimiters: str = ' ') -> str

Get the first word of a string.

Parameters:

  • string (str) –

    The string.

  • delimiters (str, default: ' ' ) –

    The delimiter characters.

Returns:

  • str

    The string's first word.

Source code in src/shellman/templates/filters.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def do_firstword(string: str, delimiters: str = " ") -> str:
    """Get the first word of a string.

    Parameters:
        string: The string.
        delimiters: The delimiter characters.


    Returns:
        The string's first word.
    """
    # FIXME: maybe use a regex instead: ^[\w_]+
    for i, char in enumerate(string):
        if char in delimiters:
            return string[:i]
    return string

do_format ¤

do_format(string: str, *args: Any, **kwargs: Any) -> str

Override Jinja's format filter to use format method instead of % operator.

Parameters:

  • string (str) –

    The string to format.

  • *args (Any, default: () ) –

    Arguments passed to str.format.

  • **kwargs (Any, default: {} ) –

    Keyword arguments passed to str.format.

Returns:

  • str

    The formatted string.

Source code in src/shellman/templates/filters.py
227
228
229
230
231
232
233
234
235
236
237
238
239
def do_format(string: str, *args: Any, **kwargs: Any) -> str:
    """Override Jinja's format filter to use format method instead of % operator.

    Parameters:
        string: The string to format.
        *args: Arguments passed to `str.format`.
        **kwargs: Keyword arguments passed to `str.format`.


    Returns:
        The formatted string.
    """
    return string.format(*args, **kwargs)

do_groffauto ¤

do_groffauto(string: str, *, escape: bool = True) -> str

Convert a string to the Groff format.

Parameters:

  • string (str) –

    The string to convert.

  • escape (bool, default: True ) –

    Whether to escape the result.

Returns:

  • str

    A Groff string.

Source code in src/shellman/templates/filters.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def do_groffauto(string: str, *, escape: bool = True) -> str:
    """Convert a string to the Groff format.

    Parameters:
        string: The string to convert.
        escape: Whether to escape the result.

    Returns:
        A Groff string.
    """
    string = do_groffautoemphasis(string)
    string = do_groffautostrong(string)
    if escape:
        string = do_groffautoescape(string)
    return string

do_groffautoemphasis ¤

do_groffautoemphasis(string: str) -> str

Automatically mark uppercase words as Groff emphasis.

Parameters:

  • string (str) –

    The string to convert.

Returns:

  • str

    The updated string.

Source code in src/shellman/templates/filters.py
55
56
57
58
59
60
61
62
63
64
def do_groffautoemphasis(string: str) -> str:
    """Automatically mark uppercase words as Groff emphasis.

    Parameters:
        string: The string to convert.

    Returns:
        The updated string.
    """
    return re.sub(r"(\b[A-Z_0-9]{2,}\b)", r"\\fI\1\\fR", string)

do_groffautoescape ¤

do_groffautoescape(string: str) -> str

Automatically Groff-escape dashes, single/double quotes, dots and dollar signs in a string.

Parameters:

  • string (str) –

    The string to escape.

Returns:

  • str

    The escaped string.

Source code in src/shellman/templates/filters.py
19
20
21
22
23
24
25
26
27
28
def do_groffautoescape(string: str) -> str:
    """Automatically Groff-escape dashes, single/double quotes, dots and dollar signs in a string.

    Parameters:
        string: The string to escape.

    Returns:
        The escaped string.
    """
    return string.replace("-", "\\-").replace("'", "\\'").replace('"', '\\"').replace(".", "\\.").replace("$", "\\f$")

do_groffautostrong ¤

do_groffautostrong(string: str) -> str

Automatically mark words starting with - or -- as Groff strong.

Parameters:

  • string (str) –

    The string to convert.

Returns:

  • str

    The updated string.

Source code in src/shellman/templates/filters.py
67
68
69
70
71
72
73
74
75
76
def do_groffautostrong(string: str) -> str:
    """Automatically mark words starting with `-` or `--` as Groff strong.

    Parameters:
        string: The string to convert.

    Returns:
        The updated string.
    """
    return re.sub(r"(--?[\w-]+=?)", r"\\fB\1\\fR", string)

do_groffemphasis ¤

do_groffemphasis(string: str) -> str

Mark a string as Groff emphasis.

Parameters:

  • string (str) –

    The string to convert

Returns:

  • str

    The updated string.

Source code in src/shellman/templates/filters.py
43
44
45
46
47
48
49
50
51
52
def do_groffemphasis(string: str) -> str:
    """Mark a string as Groff emphasis.

    Parameters:
        string: The string to convert

    Returns:
        The updated string.
    """
    return "\\fI" + string + "\\fR"

do_groffstrong ¤

do_groffstrong(string: str) -> str

Mark a string as Groff strong.

Parameters:

  • string (str) –

    The string to convert.

Returns:

  • str

    The updated string.

Source code in src/shellman/templates/filters.py
31
32
33
34
35
36
37
38
39
40
def do_groffstrong(string: str) -> str:
    """Mark a string as Groff strong.

    Parameters:
        string: The string to convert.

    Returns:
        The updated string.
    """
    return "\\fB" + string + "\\fR"

do_groupby ¤

do_groupby(
    environment: Environment,
    value: Sequence[Any],
    attribute: str,
    *,
    sort: bool = True
) -> list[tuple[str, list[Any]]]

Override Jinja's groupby filter to add un(sort) option.

Parameters:

  • environment (Environment) –

    Passed by Jinja.

  • value (Sequence[Any]) –

    The value to group.

  • attribute (str) –

    The attribute to use for grouping/sorting.

Returns:

Source code in src/shellman/templates/filters.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
@pass_environment
def do_groupby(
    environment: Environment,
    value: Sequence[Any],
    attribute: str,
    *,
    sort: bool = True,
) -> list[tuple[str, list[Any]]]:
    """Override Jinja's groupby filter to add un(sort) option.

    Parameters:
        environment: Passed by Jinja.
        value: The value to group.
        attribute: The attribute to use for grouping/sorting.

    Returns:
        The value grouped by the given attribute.
    """
    expr = make_attrgetter(environment, attribute)

    # Original behavior: groups are sorted
    if sort:
        return [_GroupTuple(key, list(values)) for key, values in groupby(sorted(value, key=expr), expr)]

    # Added behavior: original order of appearance is kept
    all_groups = [expr(_) for _ in value]
    group_set = set()
    unique_groups = []
    for group in all_groups:
        if group not in group_set:
            unique_groups.append(group)
            group_set.add(group)
    grouped = {k: list(v) for k, v in groupby(sorted(value, key=expr), expr)}
    return [_GroupTuple(group, grouped[group]) for group in unique_groups]

do_smartwrap ¤

do_smartwrap(
    text: str,
    indent: int = 4,
    width: int | None = None,
    *,
    indentfirst: bool = True
) -> str

Smartly wrap the given text.

Parameters:

  • text (str) –

    The text to wrap.

  • indent (int, default: 4 ) –

    The indentation to use (number of spaces).

  • width (int | None, default: None ) –

    The desired text width.

  • indentfirst (bool, default: True ) –

    Whether to indent the first line too.

Returns:

  • str

    The wrapped text.

Source code in src/shellman/templates/filters.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def do_smartwrap(text: str, indent: int = 4, width: int | None = None, *, indentfirst: bool = True) -> str:
    """Smartly wrap the given text.

    Parameters:
        text: The text to wrap.
        indent: The indentation to use (number of spaces).
        width: The desired text width.
        indentfirst: Whether to indent the first line too.

    Returns:
        The wrapped text.
    """
    if width is None or width < 0:
        c_width = console_width(default=79)
        if width is None:
            width = c_width or 79
        else:
            width += c_width

    indent_str = indent * " "
    to_join = defaultdict(lambda: False)
    lines = text.split("\n")
    previous = True
    for i, line in enumerate(lines):
        if not (line == "" or line[0] in (" ", "\t")):
            if previous:
                to_join[i] = True
            previous = True
        else:
            previous = False
    joined_lines = [lines[0]]
    for i in range(1, len(lines)):
        if to_join[i]:
            joined_lines.append(" " + lines[i])
        else:
            joined_lines.append("\n" + lines[i])
    new_text = "".join(joined_lines)
    new_text_lines = new_text.split("\n")
    wrapper = textwrap.TextWrapper(subsequent_indent=indent_str)
    wrap_indented_text_lines = []
    first_line = new_text_lines[0]
    if not (first_line == "" or first_line[0] in (" ", "\t")):
        if indentfirst:
            wrapper.width = width
            wrapper.initial_indent = indent_str
        else:
            wrapper.width = width - indent
            wrapper.initial_indent = ""
        wrap_indented_text_lines.append(wrapper.fill(first_line))
    elif first_line:
        wrap_indented_text_lines.append(indent_str + first_line)
    else:
        wrap_indented_text_lines.append("")
    wrapper.width = width
    wrapper.initial_indent = indent_str
    for line in new_text_lines[1:]:
        if not (line == "" or line[0] in (" ", "\t")):
            wrap_indented_text_lines.append(wrapper.fill(line))
        elif line:
            wrap_indented_text_lines.append(indent_str + line)
        else:
            wrap_indented_text_lines.append("")
    return "\n".join(wrap_indented_text_lines)