module lightweight

Lightweight is a "Code over configuration" static site generator.

from lightweight import Site, markdown, paths, jinja, template, rss, atom, sass


def blog_posts(source):
    post_template = template('posts/_template.html')
    # Use globs to select files. # source = 'posts/**.md'
    return (markdown(path, post_template) for path in paths(source))


site = Site(url='https://example.org/')

# Render an index page from Jinja2 template.
site.add('index.html', jinja('pages/index.html'))

# Render markdown blog posts.
[site.add(f'posts/{post.source_path.stem}.html', post) for post in blog_posts('posts/**.md')]
site.add('posts.html', jinja('pages/posts.html'))

# Render SASS to CSS.
site.add('css/style.css', sass('styles/style.scss'))

# Include a copy of a directory.
site.add('img')
site.add('js')

# Execute all included content.
site.generate()

module lightweight.cli

A simple CLI for lightweight projects:

# website.py

def dev(host: str, port: int) -> Site:
    site = Site(url=f'http://{host}:{port}/', title='HOME')
    ...
    return site

if __name__ == '__main__':
    cli = SiteCli(build=dev, default_port=8081)
    cli.run()

This allows to build the project: ./website.py build --url https://lightweight.site/; and to run the dev server: ./website.py serve --port 8069

class SiteCli:

····def __init__(self, build: Callable[str, Site], default_host: str = 'localhost', default_port: int = 8080, default_out: str | None = None):

@param build: a module level function that receives the site’s URL str and returns a collected Site @param default_host: a string used as host when it’s not provided to CLI @param default_port: an in used as port when it’s not provided to CLI @param default_out: a directory site is outputted to; defaults to "{cwd}/out"

····def help(self):

····def run(self):

def positional_args_count(func: Callable, equals: int) -> bool:

not positional_args_count(func, equals=2): ...


module lightweight.content


module lightweight.content.content_abc

class Content(lightweight.content.content_abc.Content):

An abstract content that can be included by a Site.

····def write(self, path: GenPath, ctx: GenContext):

Write the content to the file at path.


module lightweight.content.copies

class DirectoryCopy(Content):

Site content which is a copy of a directory from the path provided as source.

····source: Path | str

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, source: Path | str) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def write(self, path: GenPath, ctx: GenContext):

class FileCopy(Content):

Site content which is a copy of a file from the path provided as source.

····source: Path | str

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, source: Path | str) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def write(self, path: GenPath, ctx: GenContext):

def copy(path: str | Path):

Copy file or directory at path, ensuring their existence.


module lightweight.content.jinja_page

Render Jinja templates in place of Lightweight Content.

class JinjaPage(Content):

Content rendered from a Jinja Template.

····template: Template

····source_path: Path

····props: dict[str, Any]

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, template: Template, source_path: Path, props: dict[str, Any]) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def render(self, ctx):

····def write(self, path: GenPath, ctx: GenContext):

def from_ctx(func: Callable[GenContext, T]) -> Callable[GenContext, T]:

Evaluate the provided function lazily from context at the point of generation.

from lightweight import jinja, from_ctx

...

def post_tasks(ctx: GenContext):
    return [task for task in ctx.tasks if task.path.parts[0] == 'posts']

...

site.add('posts', jinja('posts.html', posts=from_ctx(post_tasks)))

def jinja(template_path: str | Path, **props) -> JinjaPage:

Renders the page at path with provided parameters.

Templates are resolved from the current directory (cwd).


module lightweight.content.lwmd

Lightweight Markdown toolkit.

LwRenderer is an implementation of the mistune.Renderer adding table of contents, and overriding some elements.

class LwRenderer(TocMixin, lightweight.content.lwmd.LwRenderer):

Renders Markdown overriding the following:

  • links — allows linking to other Markdown pages by their .md file paths.
  • images — adds width=100% to <img/> tags.

Also provides a way to compile a table of contents via LwRenderer.table_of_contents.

····toc: TocBuilder

····def __init__(self, link_mapping: dict[str, str]):

Rendering a given link or email address.

:param link: link content or email address. :param is_email: whether this is an email or not.

····def block_code(self, code, lang = None):

Rendering block level code. pre > code.

:param code: text content of the code block. :param lang: language of the given code.

····def block_html(self, html):

Rendering block level pure html content.

:param html: text content of the html snippet.

····def block_quote(self, text):

Rendering <blockquote> with the given text.

:param text: text content of the blockquote.

····def codespan(self, text):

Rendering inline code text.

:param text: text content for inline code.

····def double_emphasis(self, text):

Rendering strong text.

:param text: text content for emphasis.

····def emphasis(self, text):

Rendering emphasis text.

:param text: text content for emphasis.

····def escape(self, text):

Rendering escape sequence.

:param text: text content.

····def footnote_item(self, key, text):

Rendering a footnote item.

:param key: identity key for the footnote. :param text: text content of the footnote.

····def footnote_ref(self, key, index):

Rendering the ref anchor of a footnote.

:param key: identity key for the footnote. :param index: the index count of current footnote.

····def footnotes(self, text):

Wrapper for all footnotes.

:param text: contents of all footnotes.

····def header(self, text, level, raw = None):

····def hrule(self):

Rendering method for <hr> tag.

····def image(self, src, title, text):

Rendering a image with title and text.

:param src: source link of the image. :param title: title text of the image. :param text: alt text of the image.

····def inline_html(self, html):

Rendering span level pure html content.

:param html: text content of the html snippet.

····def linebreak(self):

Rendering line break like <br>.

····def list(self, body, ordered = True):

Rendering list tags like <ul> and <ol>.

:param body: body contents of the list. :param ordered: whether this list is ordered or not.

····def list_item(self, text):

Rendering list item snippet. Like <li>.

····def newline(self):

Rendering newline element.

····def paragraph(self, text):

Rendering paragraph tags. Like <p>.

····def placeholder(self):

Returns the default, empty output value for the renderer.

All renderer methods use the '+=' operator to append to this value. Default is a string so rendering HTML can build up a result string with the rendered Markdown.

Can be overridden by Renderer subclasses to be types like an empty list, allowing the renderer to create a tree-like structure to represent the document (which can then be reprocessed later into a separate format like docx or pdf).

····def reset(self):

····def strikethrough(self, text):

Rendering ~~strikethrough~~ text.

:param text: text content for strikethrough.

····def table(self, header, body):

Rendering table element. Wrap header and body in it.

:param header: header part of the table. :param body: body part of the table.

····def table_cell(self, content, **flags):

Rendering a table cell. Like <th> <td>.

:param content: content of current table cell. :param header: whether this is header or not. :param align: align of current table cell.

····def table_of_contents(self, level) -> TableOfContents:

····def table_row(self, content):

Rendering a table row. Like <tr>.

:param content: content of current table row.

····def text(self, text):

Rendering unformatted text.

:param text: text content.

class Section(TableOfContents):

Table of contents item.

····html: str

····id: str | None

····sections: list[Section]

····title: str

····slug: str

····def __init__(self, title: str, slug: str):

····def __iter__(self):

····def __len__(self):

class TableOfContents:

Table of contents of a Markdown document.

Composed from a list of sections.

The id property is set to the root <ul> element.

····html: str

····id: str | None

····sections: list[Section]

····def __init__(self, id: str | None = None):

····def __iter__(self):

····def __len__(self):

class TocBuilder:

Table of Contents builder used by TocMixin.

····entries: list[TocEntry]

····def __init__(self):

····def append(self, entry: TocEntry):

Add an item to the table of contents.

····def compile(self, level) -> TableOfContents:

Create a new Table of contents.

class TocEntry(tuple):

An entry of the TocBuilder.

····count: method_descriptor

····index: method_descriptor

····level: int

····slug: str

····title: str

····def __getnewargs__(self):

Return self as a plain tuple. Used by copy and pickle.

····def __new__(_cls, slug: str, title: str, level: int):

Create new instance of TocEntry(slug, title, level)

····def __repr__(self):

Return a nicely formatted representation string

class TocMixin:

A mixin used by LwRenderer compiling a table of contents.

Note: requires calling reset after rendering.

····toc: TocBuilder

····def __init__(self, **kwargs):

····def header(self, text, level, raw = None):

····def reset(self):

····def table_of_contents(self, level) -> TableOfContents:


module lightweight.content.md_page

Lightweight Markdown Content.

Usage:

from lightweight import markdown, template

...

site.add('hello.html', markdown('posts/hello.md', template('templates/post.html')))

class MarkdownPage(Content):

Content generated from rendering a markdown file to a Jinja template.

····template: Template

····source_path: Path

····text: str

····renderer: type[LwRenderer]

····title: str | None

····summary: str | None

····created: datetime | None

····updated: datetime | None

····front_matter: dict[str, Any]

····props: dict[str, Any]

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, template: Template, source_path: Path, text: str, renderer: type[LwRenderer], title: str | None, summary: str | None, created: datetime | None, updated: datetime | None, front_matter: dict[str, Any], props: dict[str, Any]) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def render(self, ctx: GenContext) -> RenderedMarkdown:

Render Markdown to html, extracting the ToC.

····def write(self, path: GenPath, ctx: GenContext):

Writes a rendered Jinja template with rendered Markdown, parameters from front-matter and code to the file provided path.

class RenderedMarkdown:

The result of parsing and rendering Markdown.

····html: str

····preview_html: str

····toc: TableOfContents

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, html: str, preview_html: str, toc: TableOfContents) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

def markdown(md_path: str | Path, template: Template, renderer = <class 'lightweight.content.lwmd.LwRenderer'>, **kwargs) -> MarkdownPage:

Create a markdown page that can be included by a Site. Markdown page is compiled from a markdown file at path (*.md) and a Jinja Template.

Provided key-word arguments are passed as props to the template on render. Such props can also be lazily evaluated from GenContext by using the from_ctx(func) decorator.


module lightweight.content.sass_scss

SCSS/Sass Lightweight Content.

Allows rendering single file, style directories and corresponding sourcemaps.

Usage:

from lightweight import sass

...

site.add('css/style.css', sass('styles/style.scss', sourcemap=False))

class Sass(Content):

Content created by compiling Sass and SCSS.

····path: Path

····sourcemap: bool

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, path: Path, sourcemap: bool) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def write(self, path: GenPath, ctx: GenContext):

def sass(location: str, sourcemap: bool = True) -> Sass:

Run Sass/SCSS compiler on files at location. Can be a file name or a directory.

Sourcemaps are written under "<location>.map".

site.add('css/style.css', sass('styles/style.scss'))

Creates 2 files: css/styles.css and css/styles.css.map.


module lightweight.errors

class AbsolutePathIncluded(Exception):

····args: getset_descriptor

····with_traceback: method_descriptor

····def __init__(self):

class IncludedDuplicate(Exception):

····args: getset_descriptor

····with_traceback: method_descriptor

····def __init__(self, at: str):

class InvalidCommand(Exception):

An invalid CLI command.

····args: getset_descriptor

····with_traceback: method_descriptor

class InvalidSiteCliUsage(Exception):

····args: getset_descriptor

····with_traceback: method_descriptor


module lightweight.files

Lightweight utilities for working with files.

@contextmanager
def directory(location: str | Path):

Execute following statements using the provided location as "cwd" (current working directory).

from pathlib import Path

project_location = Path(__file__).absolute().parent

with directory(project_location):
    site.add('index.html')

def paths(pattern: str | Path) -> list[Path]:

List paths matching the provided glob pattern.

>>> print(paths('lightweight/**/__init__.py'))
[PosixPath('lightweight/__init__.py'), PosixPath('lightweight/content/__init__.py')]

>>> print(paths('lightweight/*.typed'))
[PosixPath('lightweight/py.typed')]

module lightweight.generation


module lightweight.generation.context

class GenContext:

A generation context. Contains the data useful during the generation: the site and the list of tasks to be executed in the process.

The context is created by a Site upon starting generation and provided to the Content.write(path, ctx) method as a second parameter.

····site: Site

····out: Path

····tasks: tuple[GenTask, ...]

····generated: datetime

····version: str

····def __init__(self, out: Path, site: Site):

····def path(self, p: Path | str) -> GenPath:

Create a new GenPath in this generation context from a regular path.


module lightweight.generation.path

class GenPath:

A path for writing content. It contains both, the relative path (as specified by site.add(relative_path, content)) and the real path (an absolute path which in site’s out).

File system operations performed on real_path; relative path is used for all other operations, e.g. __str__ returns a relative path representation.

Also, proper URL can be obtained from generation path

site = Site('https://example.org/')
resources = GenPath(Path('resources'), out='/tmp/out', url_factory=lambda location: site/location)

teapot: GenPath = resources / 'teapot.txt'

print(str(teapot))  # resources/teapot.txt
print(teapot.absolute())  # '/tmp/out/resources/teapot.txt'
print(teapot.url)  # https://example.org/resources/teapot.txt

print(teapot.exists())  # False

# Create a file with text.
teapot.create('I am a teapot')

print(teapot.exists())  # True

····location: str

····name: str

····parent: GenPath

····parts: tuple[str, ...]

····real_path: Path

····suffix: str

····url: str

····relative_path: Path

····out: Path

····url_factory: Callable[str, str]

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, relative_path: Path, out: Path, url_factory: Callable[str, str]) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def __str__(self) -> str:

····def __truediv__(self, other: GenPath | PurePath | str) -> GenPath:

A child generation path can be created from an existing one by using division operator.

parent = GenPath(Path('something/', 'tmp/out, ...))
child = parent / 'child'
print(repr(child))  GenPath(relative_path=PosixPath('something/child'), out='/tmp/out', ...)

····def absolute(self) -> Path:

An alias of GenPath.real_path.

····def create(self, contents: str | bytes) -> None:

Create a file with provided contents. Contents can be str or bytes.

····def exists(self) -> bool:

Checks if file exists

····def mkdir(self, mode = 511, parents = True, exist_ok = True):

Create directory at path.

Differs from the defaults in other Python mkdir signatures: creates whole parent hierarchy of directories if they do not exist.

····def open(self, mode = 'r', buffering = -1, encoding = None, errors = None, newline = None) -> Any:

Open the file. Same as Path.open(...)

····def with_name(self, name: str) -> GenPath:

Create a new GenPath which differs from the current only by file name.

····def with_suffix(self, suffix: str) -> GenPath:

Create a new GenPath with a different file suffix (extension).


module lightweight.generation.task

class GenTask:

A task executed by Site during generation.

All of site’s tasks can be accessed during generation via GenContext.

Generation Task objects differ from the Site’s IncludedContent by having the path of GenPath type (instead of regular Path). GenPath has knowledge of the generation out directory, and is passed directly to content.write(path, ctx).

Includes cwd (current working directory) in which the original content was created. Content write(...) operates from this directory.

····path: GenPath

····ctx: GenContext

····content: Content

····cwd: str

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, path: GenPath, ctx: GenContext, content: Content, cwd: str) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def execute(self):


module lightweight.included

class IncludedContent:

The content included by a lightweight.Site.

Contains the site’s location and cwd (current working directory) of the content.

Location is a string with an output path relative to generation out directory. It does not include a leading forward slash.

cwd is important for proper subsite generation.

····path: None

····location: str

····content: Content

····cwd: str

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, location: str, content: Content, cwd: str) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def make_tasks(self, ctx: GenContext) -> list[GenTask]:

class Includes:

····ics: list[IncludedContent]

····by_cwd: dict[str, list[IncludedContent]]

····by_location: dict[str, IncludedContent]

····def __contains__(self, location: str) -> bool:

····def __init__(self):

····def __iter__(self):

····def add(self, ic: IncludedContent):


module lightweight.lw

Lightweight CLI.

Access CLI help via:

lw --help

or

python -m lightweight.lw --help

Initialize project using:

lw init example_project --url https://example.org

Additional help:

lw init --help

Start a server for the project:

lw serve website:dev

Additional help:

lw serve --help

class Color:

A color from red, green and blue.

····r: int

····g: int

····b: int

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, r: int, g: int, b: int) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def bright():

Create a new bright color.

····def css(self, alpha = None) -> str:

A string representation of color which can be used in CSS.

class FailedGeneration(Exception):

····args: getset_descriptor

····with_traceback: method_descriptor

class Generator:

····url: str

····def __call__(self):

····def __init__(self, func_file: Path, func_name: str, source: Path, out: Path, host: str, port: int):

····def generate(self):

····def load_executable(self):

class Process(lightweight.lw.Process):

https://stackoverflow.com/a/33599967/8677389

····authkey: None

····daemon: None

····exception: None

····exitcode: None

····ident: None

····name: None

····pid: None

····sentinel: None

····traceback: None

····def __init__(self, *args, **kwargs):

····def __repr__(self):

····def close(self):

Close the Process object.

This method releases resources held by the Process object. It is an error to call this method if the child process is still running.

····def is_alive(self):

    Return whether process is alive

····def join(self, timeout = None):

    Wait until child process terminates

····def kill(self):

    Terminate process; sends SIGKILL signal or uses TerminateProcess()

····def run(self):

····def start(self):

    Start child process

····def terminate(self):

    Terminate process; sends SIGTERM signal or uses TerminateProcess()

def absolute_out(out: Path | None, abs_source: Path) -> Path:

def add_init_cli(subparsers):

def add_log_arguments(parser):

def add_version_cli(subparsers):

def argument_parser():

@contextmanager
def custom_jinja_tags():

def load_module(p: Path) -> Any:

def lw_version():

def main():

def parse_args():

def parse_log_level(value: str):

def positional_args_count(func: Callable, equals: int) -> bool:

not positional_args_count(func, equals=2): ...

def quickstart(location: str, title: str | None):

def set_log_level(args):

def slugify_title(title):

def start_server(func_file: Path, func_name: str, source: Path, out: Path, host: str, port: int, enable_reload: bool, loop = None):

@contextmanager
def sys_path_starting(with_: Path):


module lightweight.server

Dev HTTP server serving static files using asyncio.

Highlights:

  • Allows to drop ".html" in URLs
    • which corresponds to nginx try_files $uri $uri.html $uri/index.html =404;
  • Can inject live-reload JS to HTML.

Mostly stolen from picoweb -- web pico-framework for Pycopy 2019 MIT

class DevServer:

A server serving static files from the provided directory.

server = DevServer('app/static')
loop = asyncio.get_event_loop()
loop.create_task(server.serve('localhost', 8080))
try:
    loop.run_forever()
except KeyboardInterrupt:
    loop.stop()

····def __init__(self, location: Path):

····def find_file(self, location: str) -> File:

Override to change how path is resolved to file.

····def handle(self, writer: StreamWriter, request: HttpRequest):

Handle the request and write the response.

····def handle_static(self, writer: StreamWriter, request: HttpRequest):

Look for file and write it to the writer. In case the file not found or there are other problems -- write an error.

····def http_error(writer: StreamWriter, status: str):

····def respond(self, reader: StreamReader, writer: StreamWriter):

····def sendfile(self, writer: StreamWriter, file: File):

Override to response with file is put together.

····def serve(self, host, port, loop: AbstractEventLoop):

Creates an asyncio coroutine, that serves requests on the provided host and port.

loop = asyncio.get_event_loop()
server.serve('localhost', 8080, loop=loop)
loop.run_forever()

····def shutdown(self, loop):

····def start_response(writer: StreamWriter, content_type: str = 'text/html; charset=utf-8', status: str = '200', headers: dict[str, str] | None = None):

class File:

A file that is served via HTTP.

····path: Path

····mime_type: MimeType

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, path: Path, mime_type: MimeType) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

····def read(self) -> bytes:

class HttpRequest:

A request processed by the server.

····headers: dict[str, str]

····reader: StreamReader

····qs: str

····location: str

····method: str

····def __delattr__(self, name):

····def __eq__(self, other):

····def __hash__(self):

····def __init__(self, headers: dict[str, str], reader: StreamReader, qs: str, location: str, method: str) -> None:

····def __repr__(self):

····def __setattr__(self, name, value):

class LiveReloadServer(DevServer):

····stopped: Event

····def __init__(self, location: Path, watch: Path, regenerate: Callable[None], ignored: Collection[Path] = ()):

····def find_file(self, location: str) -> File:

Override to change how path is resolved to file.

····def handle(self, writer: StreamWriter, request: HttpRequest):

····def handle_static(self, writer: StreamWriter, request: HttpRequest):

Look for file and write it to the writer. In case the file not found or there are other problems -- write an error.

····def http_error(writer: StreamWriter, status: str):

····def on_source_changed(self):

····def respond(self, reader: StreamReader, writer: StreamWriter):

····def send_live_reload_id(self, writer: StreamWriter):

····def sendfile(self, writer: StreamWriter, file: File):

····def serve(self, host, port, loop):

····def shutdown(self, loop):

····def start_response(writer: StreamWriter, content_type: str = 'text/html; charset=utf-8', status: str = '200', headers: dict[str, str] | None = None):

····def watch_source(self):

class MimeType(lightweight.server.MimeType):

Mime-type of the file written to response Content-Type.

····css: MimeType

····gif: MimeType

····html: MimeType

····jpeg: MimeType

····name: DynamicClassAttribute

····plaintext: MimeType

····png: MimeType

····svg: MimeType

····value: DynamicClassAttribute

def check_directory(working_dir: Path):

def now_repr():

def u(string: str) -> bytes:

Shortcut to encode string to bytes.


module lightweight.site

class Site:

A static site for generation, which is basically a collection of Content.

Site is one of the few mutable Lightweight components. It is available to content during write, as a property of the provided ctx.

The only required parameter is the URL of the site. Other parameters may be useful for different content types.

The following code output to the out directory the following content:

  • two rendered Jinja 2 HTML templates;
  • CSS rendered from SCSS;
  • copies of img and js directories.
site = Site('https://example.org/')

site.add('index.html', jinja('index.html'))
site.add('about.html', jinja('about.html'))
site.add('css/style.css', sass('styles/main.scss'))
site.add('img')
site.add('js')

site.generate(out='out')

····url: str

····content: Includes

····title: str | None

····def __init__(self, url: str, title: str | None = None, content: Includes | None = None):

····def __repr__(self):

····def __str__(self):

····def __truediv__(self, location: str) -> str:

Create a URL for the location at site.

site = Site('https://example.org/')

url = site / 'resource/images/photo-1.jpeg'
print(url) # https://example.org/resource/images/photo-1.jpeg

····def add(self, location: str, content: Content | str | None = None):

Include the content at the location.

Note the content write is executed only upon calling Site.generate().

The location cannot be absolute. It cannot start with a forward slash.

During the add the cwd (current working directory) is recorded. The content’s write will be executed from this directory.

Check overloads for alternative signatures.

Overloads:
def add(self, location: str):

Include a file, a directory, or multiple files with a glob pattern.

def add(self, location: str, content: Content):

Include the content at the provided location.

def add(self, location: str, content: str):

Copy files from content to location.

····def create_ctx(self, out: Path) -> GenContext:

Override for custom context types.

····def debug(self, text):

····def generate(self, out: str | Path = 'out'):

Generate the site in directory provided as out.

If the out directory does not exist — it will be created along with its whole hierarchy.

If the out directory already exists – it will be deleted with all of it contents.

····def info(self, text):


module lightweight.templates

This module configures a Jinja environment to use with the app (jinja_env). This environment is configured to locate templates and resolve their inner references according to the current working directory (cwd).

Also a strict undefined is enabled. This means that any operations with an undefined Jinja template parameter will result in an error. This is a safer approach in contrast to Jinja defaults.

template is a shortcut for loading templates using this environment.

object jinja_env

An instance of Environment.

def template(location: str | Path) -> Template:

A shorthand for loading a Jinja2 template from the current working directory.