Get started
Install lamya
lamya requires Python 3.8+
NOTE: Add install instructions after this is deployed
Creating a simple website from the CLI
The command line interface is ideal for simple cases like personal websites, blogs and portfolios.
For those cases the quickest way to get a site up and running is to organize
your content in a directory using the hierarchy you would like to use for
navigation and URLs and run lamya from the command line.
Let’s consider the following example of a simple personal website:
my_site_dir/
└── content/
└── about.md
└── blog/
└── first_blog_post.md
└── second_blog_post.md
└── ...
└── contact.md
└── index.md
Running lamya as a module, while inside the my_site_dir folder, like so:
python -m lamya -url "http://my_url.com" [OPTIONS]
will give us a very simple blank website using the default lamya theme.
A couple things to note:
the
-urlis a required flag, as it’s necessary for canonical linksinstead of running
lamyainside the directory, we could also run it from anywhere, but give it the site_directory argumentwe don’t have an index page inside the
blogfolder, which means that one will be automatically generated for it and by default it will be a collection of all the posts directly underneath it.You can control what posts to aggregate into their respective parent’s index folders using the local Aggregation arguments.
The global aggregation flags are for aggregating content across the whole site into the home index page. That aggregation still happens, even if a home index page is defined and is accessible in the template engine for the home page as
page.aggregated_posts.These aggregated lists of posts are sorted by publish date, which is either taken from the front matter or if that’s not present from the OS last modified time.
the posts -
first_blog_post.mdandsecond_blog_post.mdwill not be part of the navigation, as by default any nested pages are treated as postsif we had another directory inside
content/blog/then that directory would have been added to theblogsubmenu in the navigation
Note
For the long list of command line arguments have a look at CLI Reference, and bear in mind they can all be specified in a config file using the --from_file argument.
Content
Let’s have a look into what the markdown files would look like.
Markdown
Since the default site generator uses markdown and jinja2 there’s no specific requirements other than complying with python’s standard markdown library.
The following markdown extensions are enabled by default:
footnotes
tables
fenced_code
toc
markdown strikethrough if installed
Front matter
Front matter in lamya is actual python code that will get executed at build
time and is delimited by lines containing only + symbols.
Here’s an example of a blog post:
+++
title = "Life the Universe and Everything"
publish_date = "01-02-2022 11:42"
+++
It is a mistake to think you can solve any major problems just with potatoes.
Note
The date format can be specified using the
–read_date_format CLI argument or if importing
the lamya.site_generator the SiteGenerator.read_date_format argument.
These are the only front matter keys lamya understands:
title- a string representing the title of the post.Note
If it is not provided the title will be intuited to be the name of the file with underscores converted to spaces and the first letter of words being capitalized.
publish_date- a string representing the publish date, which will then be interpreted by thedatetime.strptimefunction, with the format provided by the above mentionedread_date_formatargumentscategory- a string providing a category if category pages will be built
Anything else in the front matter is entirely for the use inside of the template engine, since the front matter is passed to it as a dictionary containing all of the information.
Static files
Images, extra stylesheets and javascript files are very common when creating
a website, and as such lamya provides an easy way of supporting them.
Both the CLI and the SiteGenerator
accept a static_directory argument, which specifies a folder, the contents
of which will be directly copied to the build folder. As such, they will
be directly accessible by their relative URLs.
For example, let’s say we’ve added a profile picture to the website structure from above:
my_site_dir/
└── content/
└── about.md
└── blog/
└── first_blog_post.md
└── second_blog_post.md
└── ...
└── contact.md
└── index.md
└── static/
└── img/
└── profile.png
Then, anywhere in the website then we can display the profile picture like so:
<img alt="" src="/img/profile.png">
Templates
Additionally, if you would like to make small changes to a theme without having to fork it and maintain it on your own, the templates directory argument is available to define a folder which will checked for a template’s name before reaching for the template in the theme directory.
What that means is that if the theme you are using uses a template called
default.html and you would like to make a change to that, but leave everything
else the same, then you can write your own default.html in the
templates_directory which will act as an overwrite.
Creating more complex websites using build scripts
The command line way of building websites is certainly sufficient if you are
creating classic personal or blog sites, but if you ever need something more
customizable you are completely free to import the
SiteGenerator and take charge
of how your website is built.
This method implies that you would have a build script of some sort, which when ran will build your website, but of course can do other things as well, such as potentially immediately deploying it, maybe letting subscribers know if there’s a new piece of content, maybe as a part of your website you utilize some scraped content which you can have included in your build script, etc.
The possibilities are literally endless, since you are working with python.
Here’s how to get started with using lamya in that manner.
Say for this example we have an old blog that has multiple authors and the content is separated as so:
my_site_dir/
└── content/
└── about.md
└── blog/
└── arthur_dent
└── first_blog_post.md
└── second_blog_post.md
└── ...
└── ford_prefect
└── first_blog_post.md
└── second_blog_post.md
└── ...
└── contact.md
If we just use the CLI build, then none of the author’s content will be displayed in the blog page, as the posts there are not direct descendants. Instead, an entry for each author would be created in the blog submenu on the navigation.
That’s no good for our example.
So, to fix that we can use lamya in a build script like so:
my_site_dir/build.pyfrom lamya.site_generator import SiteGenerator
site_gen = SiteGenerator(name="Mostly Harmless", url="localhost:8000")
# read the content from the content directory and put it in a tree structure
site_gen.process_content_tree()
##### IMPORTANT ###############################################################
# move all author content to be a direct descendant of the blog folder, so it
# can be displayed in the blog index page, but also store the author information
# in the user_data dict for each blog post, so it can be used in the template
#
# since we call `site_gen.aggregate_posts()` after that, when all the posts
# ARE direct descendants of the blog folder they will be displayed in its
# index page
for author_folder in list(site_gen.content_tree.get("blog").children):
# NOTE: we copy the children lists, as otherwise we'd be changing them in place
for child in list(author_folder.children):
child.parent_to(site_gen.content_tree.get("blog"))
child.user_data["author"] = author_folder.name
site_gen.content_tree.get("blog").children.remove(author_folder)
# aggregate all the content into the home page and all direct descendants into
# their respective parent index pages
site_gen.aggregate_posts()
# build the navigation dict
site_gen.build_navigation()
# render into the build directory
site_gen.render()
What we do in the highlited chunk of code (everything from the line saying IMPORTANT to the site_gen.aggregate_posts() line) is:
iterate over all direct children of the blog folder, which in our case are the two author folders
iterate over all children of the current author folder
parent the child (the actual blog post) to the blog folder
store the author folder name in the
user_dataobject of that post, so it can be accessed in the template engine
remove the author folder from the blog folder, so it’s not displayed in the navigation
Then when we run
site_gen.aggregate_posts(),
all the blog posts are direct descendants of the blog folder, so they are
aggregated into its index page.
Note
If you remove the highlighted code (everything from the line saying IMPORTANT
to the site_gen.aggregate_posts() line) you will be left with the exact
same result as if you had ran lamya through the CLI, since that is exactly
what we do in the __main__.py of lamya.
Note
Also note we’re using localhost:8000 as the url argument, since that
makes it really easy to serve your website via the
http.server python module.
Creating a website, while using the content tree only
The last way of using lamya to create your site, is to only utilize the
lamya.content_tree module, which is only desirable if you have
incredibly specific and non-standard requirements for your website.
What that means is you will be able to do similar things to what we did above where we reparented some content, but when it comes to building a navigation object, categories, archives, etc. and actually rendering the website you are on your own.
Here’s how to get started in those cases:
import lamya.content_tree
from pathlib import Path
ct = lamya.content_tree.ContentTree.from_directory(
Path("content"), accepted_file_types=[".md"])
# visualise your content tree, to help you decide how to manage it
print(ct)
# Root(/) {
# PageOrPost(about)
# Folder(blog) {
# Folder(arthur_dent) {
# PageOrPost(first)
# PageOrPost(second)
# }
# Folder(ford_prefect) {
# PageOrPost(first)
# PageOrPost(second)
# }
# }
# PageOrPost(contact)
# }
# use the ContentTree methods to help you manage your content
#
# things like filtering, grouping, reparenting content or flattening hierarchies
# can be immensely useful for keeping a very clean folder structure, but not
# having that restrict the actual website
#
# additionally the `user_data` dictionaries help pass your data around with
# your content
# then when you're done, use your render method of choice to actually write
# the website