ampoule.md
Python script, ASCII text executable
title: Ampoule source-url: https://roundabout-host.com/roundabout/ampoule topics: ["software", "web", "python", "ampoule", "jinja2", "docs", "gpl"] ---
Ampoule is a lightweight, simple yet flexible, static site generator written in Python. It uses Jinja2 for templating. This site was generated using Ampoule.
Features
Extremely simple and small, only a few hundred lines of code.
Only depends on Jinja2, Ruamel YAML, bs4, and colorama.
Jinja2 templating will be familiar to Flask users. Now you can use the same templates for both dynamic and static sites.
More of a framework. Sites are generated by a short Python script that you write to customise what pages it loads, which templates it uses, and what data it passes to them, or create custom filters, tests and more.
Supports YAML front matter for pages. It can be accessed using indexing syntax.
Indexes can be sorted using a function, iterated and can index any directory, recursively or not. They can also transform URLs to make them end in ".html".
Object-oriented design. The same objects used in that script can also be passed to the templates.
Any markup language can be used, as long as it can be converted to HTML. You just need to configure a filter for it. You can even mix multiple markup languages in the same site.
Ships with a light markdown implementation.
Easy to use for both programmers and non-programmers. While you do need a script, you can also use an off-the-shelf one.
Themes can be exactly how you want.
Keeping static files is easy, because indexes can be static.
Static files are always binary and not templated. The same happens for files that can't be decoded.
URL-based definitions. Pages are added using the URL that will be used to access them.
Reinforces the web as a publishing medium. Static sites are not for everyone, but if you want to publish something, it's the best way.
And GitHub will give you free hosting, because it's static and very cheap to serve. Roundabout-host now also offers free hosting for static sites and will soon offer a way to generate them using CI and the generator you prefer.
It's free software and available under the GPLv3.
No JavaScript is required, but it can of course be used if you want.
Decently fast: even if you've got a huge site, it should not take more than 30 seconds. Local rebuilding will also be added. And it's still much faster than any dynamic site.
Beautiful logging thanks to colorama.
Great for educational use; you can learn Python, HTML, CSS, JavaScript, and Jinja2 all at once.
You can make your site in an hour, and then it's time to focus on writing what you want to publish.
If you see fit, it's easy to convert to a dynamic site. A Flask implementation is planned.
Clear and magic-free. You can see exactly what's happening and why. No magic, no configuration files, no hidden behaviour. The code is so short you can read it.
Minimal example
import string from datetime import datetime import string import ampoule_ssg as ampoule from ampoule_ssg import markdown # Create a site object. This is where we are adding pages to. The argument is the directory # where the site will be built. site = ampoule.Site("my_site") # Use this as "| markdown" in Jinja2 templates to convert any Markdown source to HTML. @site.filter("markdown") def markdown_filter(text): return markdown.markdown2html(text) # Make the URLs web-friendly and make it end in ".html" so it will be correctly formatted # by dumb servers. def article_url(url): url = url.lower().rpartition(".")[0] new_url = "" for i in url: if i in string.ascii_lowercase: new_url += i elif i in string.whitespace: new_url += "-" return new_url + ".html" # Set context that will be passed to all templates. You can still override this. site.context["timestamp"] = datetime.now() site.context["ampoule"] = ampoule # Add the index of articles. In the template, we're looping over it to list them all. articles = ampoule.Index("articles", url_transform=article_url, sort_by=lambda x: x.date) # This makes it take all indexed files and put them under the /articles URL, keeping the # index's URL transformation and placing all of them in the article.html template. This # will be passed as "document" to the template. site.add_from_index(articles, "/articles", "article.html") # Create the main page which has access to the index so it can list all articles. main_page = ampoule.Page(site, "home.html", articles=articles) # Add the page. Note how we're binding it to a path; it will automatically be set as # index.html in that directory, and the URL is site-relative, not the OS root. site.add_page("/", main_page) # Add static files using a recursive static index. It will add all files in the static # directory and all its subdirectories, without putting them into templates. You could # still use them in templates, so you can make a photo gallery or something. site.add_from_index( # We're excluding Markdown files because we're using them as licence information # for when the site is distributed together with the fonts. You can exclude any # file you want using regex. ampoule.Index("static", recursive=True, exclude=r"\.md$", static=True), "/static", # There is no template, because the index is static. ) # Makes Ampoule take all pages and put them in a directory. site.build()
More information
Name origin
An ampoule is smaller than a flask. Because it is related to Flask (it uses Jinja2) but is a much smaller static version of it, the name makes sense.
What about the other static site generators?
There are many static site generators out there, but they all have their own problems. In particular, I haven't seen one that uses code to describe the site, rather than a configuration file. This makes it much more flexible and powerful.
Also, Ampoule is familiar to Python programmers, because it's written in Python and uses Jinja2, a templating engine that is also used in Flask. It's even the smallest static site generator:
Hugo: written in Go, uses go html/template, and it has 133k lines of Go, not counting
Jekyll: written in Ruby, uses Liquid, and it has 17300 lines of Ruby, not counting Interestingly, it's got more Markdown than Ruby.
Gatsby: they call it a framework, and rightfully so, because it's overkill for actually e. for publishing content) sites, even though JS people use it for precisely that t's written in JavaScript, uses React, and it's git 380k lines of JavaScript and combined. (For comparison, it's over 1/100 of Linux itself, which is HUGE considering high-level language and only has to do so much.)
Pelican: written in Python, uses Jinja2, and it has 12400 lines of Python, not counting
Docusaurus: written in TypeScript, uses React (of course, because it's made by Facebook),
VuePress: written in JavaScript, uses Vue, and it has 11k lines of JavaScript, Vue and
Zola: written in Rust, uses Tera, and it has 17k lines of Rust, not counting comments or Also, it's designed to be monolithic and not extensible at all.
Whereas I have only got 750 lines of Python, not counting comments or blanks. Add the script to generate the site, and it's still under 1000 lines.
I don't want to criticise other static site generators, they all do some things well, but they're not what I want. I want a simple, small, flexible and versatile static site generator that is low-maintenance and easy to use. I don't know about you, but maybe you want the same thing.
The JS-based ones are particularly unsuitable for most people, because they're slow, bloated, hard to install, and most often actually generate an SPA, which is not what you want for a blog or documentation or web book or anything like that.
Why generated static sites?
If you don't want generated static sites, you've got two other options.
Dynamic sites
bloated;
slow;
requires smart server;
requires maintenance;
requires security;
requires a database;
hard to post content;
databases can't be managed with git;
hard to import content;
no free hosting;
Static sites
hard to manage layouts;
hard to list the content;
hard to update indexes;
no support for metadata;
markup languages must be manually converted;
With a generated static site, you get the best of both worlds. It's the best publishing platform, because it's just files, but it still provides the convenience of just writing content and having it magically appear on the site and formatted correctly.
How to install
Please note that this is not yet available on PyPI. For now you'll need to download the code
(ideally using git) and install it with pip
as a local package by giving it the path to the
directory containing setup.py
.
Full documentation
To demonstrate just how easy it is, the docs can all fit on one page.
class ampoule_ssg.Site
Site
is the main class of Ampoule; it represents a single website. It is responsible for
handling added pages, the template engine and features, as well as building it.
def __init__(self, build_dir: typing.Union[str, bytes, os.PathLike], template_dir: typing.Union[str, bytes, os.PathLike] = "templates")
Create a new site object. build_dir
is the directory where the site will be built.
template_dir
is the directory where the templates are stored. Both are relative to the
script current working directory.
def add_page(self, location: typing.Union[str, bytes, os.PathLike], page: typing.union[Static, Page])
Add a page object to the site at the server-relative URL location
. The page object can be
either a Static
or a Page
.
def add_from_index(self, index: Index, location: typing.Union[str, bytes, os.PathLike], template: str = None, **kwargs)
Add all pages from an index to the site with the root at the server-relative URL location
.
The pages will be rendered with the template template
and the context kwargs
. will be
passed to all of them. If the index is static, the pages will not be rendered with a template,
but rather copied as-is.
For each page, the document
object found in the index will be passed to the template under
that name.
def filter(self, name: str)
A decorator that registers a filter function with the site. The function should take at least one argument, the value to be filtered, and return the filtered value.
def test(self, name: str)
A decorator that registers a test function with the site. The function should take at least one argument, the value to be tested, and return a boolean.
def build(self, dont_delete: typing.Optional[list[str]] = None)
Build (save) the site to the build directory it was constructed with. This will create the
directory if it does not exist, clear it (but not delete it) and then write all the pages.
You can set dont_delete
to a list of files that should not be deleted when the directory
is cleared, for example, the .git
.
context: dict[str, typing.Any]
A dictionary containing names that are available to all pages. It can be overriden by the page's context or modified at any time.
class ampoule_ssg.Page(str)
Page
is a class that represents a single page on the site. A page is composed of a
template, a document and a context.
def __new__(cls, site: Site, template: str, document: Document = None, **kwargs)
Create a new page object. site
is the site object that the page belongs to. template
is
the template the document will be put in. document
is the document object that will be
passed to the template. kwargs
are names that will be available to the template for
additional context.
If there's no document, it will not be available to the template. This is useful for single pages with fully static content, like a contact page.
class ampoule_ssg.Static(bytes)
Static
is a class that represents a single static file on the site. A static file is
just the content, in binary format, and it doesn't use templating.
def __new__(cls, site: Site, document: Document)
Create a new static object. site
is the site object that the static file belongs to.
document
is the document object that will be written to the file; it can contain any
encoding, even text, and will be written as-is.
class ampoule_ssg.Index
An index is a collection of documents that can be iterated over or added to a site using
a common template (see ampoule_ssg.Site.add_from_index
).
def __init__(self, directory: typing.Union[str, bytes, os.PathLike], recursive: bool = False, url_transform: typing.Callable = lambda x: x, sort_by: typing.Callable = lambda x: x.file_name, exclude: typing.Union[str, NoneType] = None, static: bool = False)
Create a new index. directory
is the directory to get content from. If recursive
is
true, the whole tree of that directory will be indexed. url_transform
is a function that
will be applied to the file name to get the new file name. Generally you want to set it so
it makes them end in .html
so dumb servers can serve them correctly. However, for static
files you most likely will not set it. sort_by
is the key after which to sort the
documents after they are indexed; by default it is the file name. exclude
is a regular
expression that will be used to exclude files from the index. If the index is static
,
all documents will be parsed as-is, without removing front matter.
def __iter__(self)
Return an iterator for the index.
def __next__(self)
Get the next document in the index.
def __repr__(self)
Return a string representation of the index. It contains the directory and the names of the documents in it.
def __len__(self)
Return the number of documents in the index, that is, its length.
class ampoule_ssg.Document
A document is a file, not rendered, but available for use. It is what is passed to the
template as document
for processing. Generally, you won't create these yourself, but
rather use them as they are returned by an index. However, if you do need one, you can
create it manually and pass it to a page.
Documents will parse YAML front matter for textual files, unless disabled. The front matter is available as an attribute of the document, and can be accessed using indexing syntax.
def __init__(self, file_name: typing.Union[str, bytes, os.PathLike], url_transform: typing.Callable = lambda x: x, front_matter_enabled: bool = True)
Create a new document. file_name
is the name of the file. url_transform
is a function
that will be applied to the file name to get the new file name; it has the same meaning as
in the Index
. front_matter_enabled
is a boolean that determines whether the document
will parse YAML front matter.
def __repr__(self)
Return a string containing Document
and the file name.
def __getitem__(self, item: str)
Access the document's front matter. If front matter is disabled or not available, this will never work.
def __setitem__(self, item: str, value: typing.Any)
Change the document's front matter. It works even if it wasn't parsed, because YAML behaves like a dictionary.
def __delitem__(self, item: str)
Delete an item from the document's front matter.
def __contains__(self, item: str)
Check if an item is in the document's front matter.
Licence
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
1--- 2title: Ampoule 3source-url: https://roundabout-host.com/roundabout/ampoule 4topics: ["software", "web", "python", "ampoule", "jinja2", "docs", "gpl"] 5--- 6 7Ampoule is a lightweight, simple yet flexible, static site generator written in Python. 8It uses Jinja2 for templating. This site was generated using Ampoule. 9 10Features 11-------- 12 13* **_Extremely_ simple and small**, only a few hundred lines of code. 14* _Only_ depends on Jinja2, Ruamel YAML, bs4, and colorama. 15* **Jinja2 templating** will be familiar to Flask users. Now you can use the same templates for 16both dynamic and static sites. 17* More of **a framework**. Sites are generated by a short **Python script** that you write to customise 18what **pages** it loads, which **templates** it uses, and what **data** it passes to them, or create 19custom **filters**, **tests** and more. 20* Supports **YAML front matter** for pages. It can be accessed using indexing syntax. 21* **Indexes** can be sorted using a function, iterated and can index any directory, recursively 22or not. They can also **transform URLs** to make them end in ".html". 23* **Object-oriented** design. The same objects used in that script can also be passed to the 24templates. 25* Any **markup language** can be used, as long as it can be converted to HTML. You just need to 26configure a filter for it. You can even mix multiple markup languages in the same site. 27* Ships with a light **markdown** implementation. 28* Easy to use for **_both_ programmers and non-programmers**. While you do need a script, you can 29also use an off-the-shelf one. 30* **Themes** can be *exactly how you want*. 31* Keeping **static files** is easy, because indexes can be static. 32* Static files are always **binary** and not templated. The same happens for files that can't be 33decoded. 34* **URL**-based definitions. Pages are added using the URL that will be used to access them. 35* Reinforces the **web** as a **publishing medium**. Static sites are not for everyone, but if you 36want to **publish** something, it's the best way. 37* And GitHub will give you **free hosting**, because it's static and *very cheap to serve*. 38Roundabout-host now also offers free hosting for static sites and will soon offer a way to 39generate them using CI and the generator you prefer. 40* It's **free software** and available under the **GPLv3**. 41* **No JavaScript** is required, but it can of course be used if you want. 42* Decently **fast**: even if you've got a huge site, it should not take more than *30 seconds*. 43Local rebuilding will also be added. And it's still much faster than any dynamic site. 44* Beautiful logging thanks to colorama. 45* Great for educational use; you can learn **Python**, **HTML**, **CSS**, **JavaScript**, 46and **Jinja2** all at once. 47* You can **make your site** in *an hour*, and then it's time to focus on writing what you want 48to publish. 49* If you see fit, it's easy to **convert** to a dynamic site. A **Flask implementation** is 50planned. 51* Clear and **magic-free**. You can see exactly what's happening and why. No magic, no 52configuration files, no hidden behaviour. The code is so short you can read it. 53 54Minimal example 55--------------- 56 57```python 58import string 59from datetime import datetime 60import string 61 62import ampoule_ssg as ampoule 63from ampoule_ssg import markdown 64 65# Create a site object. This is where we are adding pages to. The argument is the directory 66# where the site will be built. 67site = ampoule.Site("my_site") 68 69 70# Use this as "| markdown" in Jinja2 templates to convert any Markdown source to HTML. 71@site.filter("markdown") 72def markdown_filter(text): 73return markdown.markdown2html(text) 74 75 76# Make the URLs web-friendly and make it end in ".html" so it will be correctly formatted 77# by dumb servers. 78def article_url(url): 79url = url.lower().rpartition(".")[0] 80 81new_url = "" 82for i in url: 83if i in string.ascii_lowercase: 84new_url += i 85elif i in string.whitespace: 86new_url += "-" 87 88return new_url + ".html" 89 90 91# Set context that will be passed to all templates. You can still override this. 92site.context["timestamp"] = datetime.now() 93site.context["ampoule"] = ampoule 94 95# Add the index of articles. In the template, we're looping over it to list them all. 96articles = ampoule.Index("articles", url_transform=article_url, sort_by=lambda x: x.date) 97# This makes it take all indexed files and put them under the /articles URL, keeping the 98# index's URL transformation and placing all of them in the article.html template. This 99# will be passed as "document" to the template. 100site.add_from_index(articles, "/articles", "article.html") 101 102# Create the main page which has access to the index so it can list all articles. 103main_page = ampoule.Page(site, "home.html", articles=articles) 104 105# Add the page. Note how we're binding it to a path; it will automatically be set as 106# index.html in that directory, and the URL is site-relative, not the OS root. 107site.add_page("/", main_page) 108 109# Add static files using a recursive static index. It will add all files in the static 110# directory and all its subdirectories, without putting them into templates. You could 111# still use them in templates, so you can make a photo gallery or something. 112site.add_from_index( 113# We're excluding Markdown files because we're using them as licence information 114# for when the site is distributed together with the fonts. You can exclude any 115# file you want using regex. 116ampoule.Index("static", recursive=True, exclude=r"\.md$", static=True), 117"/static", 118# There is no template, because the index is static. 119) 120 121# Makes Ampoule take all pages and put them in a directory. 122site.build() 123``` 124 125More information 126---------------- 127 128### Name origin 129An ampoule is smaller than a flask. Because it is related to Flask (it uses Jinja2) but is 130a much smaller static version of it, the name makes sense. 131 132### What about the other static site generators? 133There are many static site generators out there, but they all have their own problems. 134In particular, I haven't seen one that uses code to describe the site, rather than a 135configuration file. This makes it much more flexible and powerful. 136 137Also, Ampoule is familiar to Python programmers, because it's written in Python and uses 138Jinja2, a templating engine that is also used in Flask. It's even the smallest static site 139generator: 140 1411. Hugo: written in Go, uses go html/template, and it has 133k lines of Go, not counting 142comments or blanks. 1432. Jekyll: written in Ruby, uses Liquid, and it has 17300 lines of Ruby, not counting 144comments or blanks. Interestingly, it's got more Markdown than Ruby. 1453. Gatsby: they call it a framework, and rightfully so, because it's overkill for actually 146static (i.e. for publishing content) sites, even though JS people use it for precisely that 147purpose. It's written in JavaScript, uses React, and it's git 380k lines of JavaScript and 148TypeScript combined. (For comparison, it's over 1/100 of Linux itself, which is HUGE considering 149it uses a high-level language and only has to do so much.) 1504. Pelican: written in Python, uses Jinja2, and it has 12400 lines of Python, not counting 151comments or blanks. 1525. Docusaurus: written in TypeScript, uses React (of course, because it's made by Facebook), 153and it has 140k lines of TypeScript and JavaScript combined. 1546. VuePress: written in JavaScript, uses Vue, and it has 11k lines of JavaScript, Vue and 155TypeScript combined. 1567. Zola: written in Rust, uses Tera, and it has 17k lines of Rust, not counting comments or 157blanks. Also, it's designed to be monolithic and not extensible at all. 158 159Whereas I have only got 750 lines of Python, not counting comments or blanks. Add the script 160to generate the site, and it's still under 1000 lines. 161 162I don't want to criticise other static site generators, they all do some things well, but 163they're not what I want. I want a simple, small, flexible and versatile static site generator 164that is low-maintenance and easy to use. I don't know about you, but maybe you want the same 165thing. 166 167The JS-based ones are particularly unsuitable for most people, because they're slow, bloated, 168hard to install, and most often actually generate an SPA, which is not what you want for a 169blog or documentation or web book or anything like that. 170 171### Why generated static sites? 172If you don't want generated static sites, you've got two other options. 173 174#### Dynamic sites 175* bloated; 176* slow; 177* requires smart server; 178* requires maintenance; 179* requires security; 180* requires a database; 181* hard to post content; 182* databases can't be managed with git; 183* hard to import content; 184* no free hosting; 185 186#### Static sites 187* hard to manage layouts; 188* hard to list the content; 189* hard to update indexes; 190* no support for metadata; 191* markup languages must be manually converted; 192 193With a *generated* static site, you get the best of both worlds. It's the best publishing 194platform, because it's just files, but it still provides the convenience of just writing 195content and having it magically appear on the site and formatted correctly. 196 197How to install 198-------------- 199 200Please note that this is not yet available on PyPI. For now you'll need to download the code 201(ideally using git) and install it with `pip` as a local package by giving it the path to the 202directory containing `setup.py`. 203 204Full documentation 205------------------ 206 207To demonstrate just how easy it is, the docs can all fit on one page. 208 209### class `ampoule_ssg.Site` 210`Site` is the main class of Ampoule; it represents a single website. It is responsible for 211handling added pages, the template engine and features, as well as building it. 212 213#### def `__init__(self, build_dir: typing.Union[str, bytes, os.PathLike], template_dir: typing.Union[str, bytes, os.PathLike] = "templates")` 214Create a new site object. `build_dir` is the directory where the site will be built. 215`template_dir` is the directory where the templates are stored. Both are relative to the 216script current working directory. 217 218#### def `add_page(self, location: typing.Union[str, bytes, os.PathLike], page: typing.union[Static, Page])` 219Add a page object to the site at the server-relative URL `location`. The page object can be 220either a `Static` or a `Page`. 221 222#### def `add_from_index(self, index: Index, location: typing.Union[str, bytes, os.PathLike], template: str = None, **kwargs)` 223Add all pages from an index to the site with the root at the server-relative URL `location`. 224The pages will be rendered with the template `template` and the context `kwargs`. will be 225passed to all of them. If the index is static, the pages will not be rendered with a template, 226but rather copied as-is. 227 228For each page, the `document` object found in the index will be passed to the template under 229that name. 230 231#### def `filter(self, name: str)` 232A decorator that registers a filter function with the site. The function should take at least 233one argument, the value to be filtered, and return the filtered value. 234 235#### def `test(self, name: str)` 236A decorator that registers a test function with the site. The function should take at least 237one argument, the value to be tested, and return a boolean. 238 239#### def `build(self, dont_delete: typing.Optional[list[str]] = None)` 240Build (save) the site to the build directory it was constructed with. This will create the 241directory if it does not exist, clear it (but not delete it) and then write all the pages. 242You can set `dont_delete` to a list of files that should not be deleted when the directory 243is cleared, for example, the `.git`. 244 245#### `context: dict[str, typing.Any]` 246A dictionary containing names that are available to all pages. It can be overriden by the 247page's context or modified at any time. 248 249### class `ampoule_ssg.Page(str)` 250`Page` is a class that represents a single page on the site. A page is composed of a 251template, a document and a context. 252 253#### def `__new__(cls, site: Site, template: str, document: Document = None, **kwargs)` 254Create a new page object. `site` is the site object that the page belongs to. `template` is 255the template the document will be put in. `document` is the document object that will be 256passed to the template. `kwargs` are names that will be available to the template for 257additional context. 258 259If there's no document, it will not be available to the template. This is useful for single 260pages with fully static content, like a contact page. 261 262### class `ampoule_ssg.Static(bytes)` 263`Static` is a class that represents a single static file on the site. A static file is 264just the content, in binary format, and it doesn't use templating. 265 266#### def `__new__(cls, site: Site, document: Document)` 267Create a new static object. `site` is the site object that the static file belongs to. 268`document` is the document object that will be written to the file; it can contain any 269encoding, even text, and will be written as-is. 270 271### class `ampoule_ssg.Index` 272An index is a collection of documents that can be iterated over or added to a site using 273a common template (see `ampoule_ssg.Site.add_from_index`). 274 275#### def `__init__(self, directory: typing.Union[str, bytes, os.PathLike], recursive: bool = False, url_transform: typing.Callable = lambda x: x, sort_by: typing.Callable = lambda x: x.file_name, exclude: typing.Union[str, NoneType] = None, static: bool = False)` 276Create a new index. `directory` is the directory to get content from. If `recursive` is 277true, the whole tree of that directory will be indexed. `url_transform` is a function that 278will be applied to the file name to get the new file name. Generally you want to set it so 279it makes them end in `.html` so dumb servers can serve them correctly. However, for static 280files you most likely will not set it. `sort_by` is the key after which to sort the 281documents after they are indexed; by default it is the file name. `exclude` is a regular 282expression that will be used to exclude files from the index. If the index is `static`, 283all documents will be parsed as-is, without removing front matter. 284 285#### def `__iter__(self)` 286Return an iterator for the index. 287 288#### def `__next__(self)` 289Get the next document in the index. 290 291#### def `__repr__(self)` 292Return a string representation of the index. It contains the directory and the names 293of the documents in it. 294 295#### def `__len__(self)` 296Return the number of documents in the index, that is, its length. 297 298### class `ampoule_ssg.Document` 299A document is a file, not rendered, but available for use. It is what is passed to the 300template as `document` for processing. Generally, you won't create these yourself, but 301rather use them as they are returned by an index. However, if you do need one, you can 302create it manually and pass it to a page. 303 304Documents will parse YAML front matter for textual files, unless disabled. The front matter 305is available as an attribute of the document, and can be accessed using indexing syntax. 306 307#### def `__init__(self, file_name: typing.Union[str, bytes, os.PathLike], url_transform: typing.Callable = lambda x: x, front_matter_enabled: bool = True)` 308Create a new document. `file_name` is the name of the file. `url_transform` is a function 309that will be applied to the file name to get the new file name; it has the same meaning as 310in the `Index`. `front_matter_enabled` is a boolean that determines whether the document 311will parse YAML front matter. 312 313#### def `__repr__(self)` 314Return a string containing `Document` and the file name. 315 316#### def `__getitem__(self, item: str)` 317Access the document's front matter. If front matter is disabled or not available, this will 318never work. 319 320#### def `__setitem__(self, item: str, value: typing.Any)` 321Change the document's front matter. It works even if it wasn't parsed, because YAML 322behaves like a dictionary. 323 324#### def `__delitem__(self, item: str)` 325Delete an item from the document's front matter. 326 327#### def `__contains__(self, item: str)` 328Check if an item is in the document's front matter. 329 330Licence 331------- 332 333This program is free software: you can redistribute it and/or modify it under the terms of 334the GNU General Public License as published by the Free Software Foundation, either version 3 335of the License, or (at your option) any later version. 336 337This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 338without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 339the GNU General Public License for more details. 340 341You should have received a copy of the GNU General Public License along with this program. 342If not, see <https://www.gnu.org/licenses/>. 343