Skip to content

Utilities

markup

alias: m, document

def markup(s: str) -> Safe

Wrapper for the markup. Triggers the HTML-syntax highlight and linting. Strips the whitespaces and marks the string as safe

>>> ht.m(f" <div>{ ht.m('  <span>Content</span>') }</div>  ")
'<div><span>Content</span></div>'

Note

Linter treats document as the root HTML document with the <!DOCTYPE html><html>...</html> top-level tags present.

m is treated as the fragment and may consist of any markup.


text

alias: t

def text(*args: Arg | Iterable[Arg], sep="") -> Safe

Basic building block for HTML texts and fragments.

The arguments may be str | bool | int | float | None or iterables of such types. Strings are escaped unless marked as Safe. Numbers are stringified. Everything else is discarded.

Returns the single string of concatenated arguments

>>> ht.t('123', '456', '789', ['a', 'b', 'c']):
'123456789abc'

>>> ht.t(True, False, None, [0, '1', False, '2'])
'012'

>>> ht.t('<div>')
'&lt;div&gt;'

>>> ht.t(Safe('<div>'))
'<div>'

Using with actual markup

>>> ht.t(
        ht.m("<h1> H1 </h1>"),
        truthy_func() and [
            0 == 1 and ht.m("<h2> H2 </h2>"),
            "foo" if 1 == 1 else "bar",
        ],
    )
'<h1> H1 </h1>foo'

Common patterns

  • JSX-like conditionals

    >>> ht.t(
            1 == 1 and 'a',
            2 == 3 or 'b',
            'c' if 4 == 5 else 'd'
        )
    'abd'

  • Conditional groups

    >>> ht.t(
            1 == 1 and ['a', 'b'],
            2 != 2 and ['c', 'd'],
            ['e', 'f'] if 3 == 3 else ['g', 'h'],
            4 == 4 and ['i', 5 == 5 and 'j']
        )
    'abefij'

  • Looping

    >>> ht.t(
            [c for c in 'abcd'],
            [c for c in 'efgh' if c == 'f']
        )
    'abcdf'

  • Looping with generator expression (less braces)

    ht.t(thing for thing in things if thing.visible)


attr

def attr(arg: Attrs | None = None, /, **kwargs: Arg) -> Safe

Formats arguments as the quoted HTML-attributes suitable for direct inclusion into the tags. Accepts the dictionary of name-value pairs and/or name-value keywords. Keywords overrides the dictionary.

  • True values are rendered as just the name, e.g hidden
  • False and None values are discarded
  • strings are rendered as name-value pairs, e.g. type="checkbox"
  • numbers are interpolated, e.g. tabindex="-1"

Returns the single string of sorted whitespace-separated pairs.

>>> ht.attr(id=12, hidden=True, tabindex=-1)
'hidden id="12" tabindex="-1"'

>>> ht.attr({'data-user': 'Joe', 'data-user-id': 3}, contenteditable=False)
'data-user="Joe" data-user-id="3"'

>>> ht.attr({'data-tag': '<div>'} )
'data-tag="&lt;div&gt;"'

>>> ht.m(f"<div { ht.attr(hidden=True, id=1) }> Text </div>")
'<div hidden id="1"> Text </div>'

classname

alias: c

def classname(*args: CnArg | Iterable[CnArg], sep=" ") -> Safe

htmf's variation of a classic classnames. The supplied arguments may be str | bool | None or iterables of such values. Class names are flattened and joined into the single (unquoted!) string suitable for inclusion into the class attribute. The usage patterns are the same as of t.

>>> ht.classname(
        'text-blue-400',
        1 == 0 and 'flex',
        None,
        False,
        1 == 1 and ['mb-1', 'mr-1', 2 == 2 and 'ml-1']
    )
'text-blue-400 mb-1 mr-1 ml-1'

>>> ht.m(f'''
    <div class="{ ht.classname('px-2', 'flex' if 1 == 1 else 'grid') }">
    </div>''')
'<div class="px-2 flex"></div>'

style

def style(s: str) -> Safe

Wrapper for inline styles intended to be included into the style attribute. HTML-escapes the string. Triggers the CSS-syntax highlight.


handler

def handler(s: str) -> Safe

Wrapper for inline javascript event handlers (onlick etc). HTML-escapes the string. Triggers the JS-syntax highlight.


stylesheet

def stylesheet(s: str) -> Safe

Wrapper for css stylesheet for inclusion into the <style> tag. Doing almost nothing. Triggers the CSS-syntax highlight and formatting.


script

def script(s: str) -> Safe

Wrapper for javascript for inclusion into the <script> tag. Escapes </ characters. Triggers the JS-syntax highlight and formatting


json_attr

def json_attr(val: Mapping[str, Any]) -> Safe

json.dumps the value and HTML-escape it.


csv_attr

def csv_attr(*args: CnArg | Iterable[CnArg]) -> Safe

Same as the classname but joins string with commas instead of the whitespaces.


mark_as_safe

def mark_as_safe(s: str) -> Safe

Mark the string as safe, promoting it to the Safe class. It's the escape hatch if one really need to include some not-to-be escaped string. This is the same as calling the Safe() class constructor, but recognized by linter.


Safe

class Safe(str)

Noop subclass of str. Used at runtime to determine if string is already escaped. Used in linting to prove the f-expressions are safe. Not intended to be instantiated directly.

Warning

String methods of Safe strings, e.g. strip, join, removeprefix, etc. returns the str (at least in current version of htmf). The result (even if actually escaped) is no longer marked as safe. One should be aware of it to avoid double-escaping. The Safe string may be unescaped by calling the Safe.unescape() method.


SafeOf

Generic annotation to help the linter recognize new subtypes of Safe.