Package: alteza
AltezaAlteza is a static site generatordriven byPyPage.
Examples of other static site generators can befound here.The differentiator with Alteza is that the site author (if familiar with Python) will have a lot more
fine-grained control over the output, than what (as far as I'm aware) any of the existing options offer.The learning curve is also shorter with Alteza. I've tried to follow part ofxmonad's philosophy
of keeping things small and simple. Alteza doesn't try to do a lot of things; instead it simply offers the core crucial
functionality that is common to most static site generators.Alteza also imposes very littlerequiredstructure or a particular "way of doing things" on your website (other than
requiring unique names). You retain the freedom to organize your website as you wish. (The nameAltezacomes from
a word that may be translated toillustriousnessin Español.)A key design aspect of Alteza is writing little scripts and executing such code to generate your website. Your static
site can contain arbitrary Python that is executed at the time of site generation.PyPage,
in particular, makes it seamless to include actual Python code inside page templates. (This of
course means that you must run Alteza with trusted code, or in an isolated container.)InstallationYou caninstallAlteza easily withpip:pip install altezaTry runningalteza -hto see the command-line options available.User GuideThe directory structure is generally mirrored in the generated site.By default, nothing is copied/published to the generated site.A file must explicitly indicate using apublic: truevariable/field that it is to be published.So directories with no public files, are non-existent in the generated site.Filesreachablefrom marked-as-public files will also be publicly accessible.Here, reachability is discovered when a providedlinkfunction is used to link to other files.There are two kinds of files that are subject to processing withPyPage: Markdown files (ending with.md) and any file with a.pybefore its actual extension.Markdown Files:Markdown files are first processed to have their "front matter" extracted usingMeta-Data.The first blank line or---ends the front matter section.The front matter is processed as YAML, and the fields are injected into thepypageenvironment.The Markdown file is processed usingpypage, with its Python environment enhanced by the YAML fields from the front matter.The environment dictionary after the Markdown is processed by pypage is treated as the "return value" of this.mdfile.This "return value" dictionary has acontentkey added to it which maps to thepypageoutput for this.mdfile.This Markdown file is passed to atemplatespecified in configuration, for a second round of processing by PyPage.Templates are HTML files processed by PyPage. The PyPage-processed Markdown HTML output is passed to the template as the variablebodyvariable. The template itself is executed by PyPage.The template should use thisbodyvalue via PyPage (with{{ boydy }}in order to render thebody's contents.(See more on configuration files in the next section.)The template is defined using atemplatevariable declared in a__config__.pyfile.Thetemplate's value must be the entire contents of a template HTML file. A convenience functionreadfileis provided for this. So you can writetemplate = readfile('some_template.html')in a config file.Templates may be overriden in descendant__config__.pyfiles, or in the Markdownitselfusing a PyPage multiline code tag (not inline code tag).Markdown files result ina directory, with anindex.htmlfile containing the Markdown's output.Other Dynamic Files (i.e. any file with a.pybefore the last.in its file name):These files are processed with PyPageoncewith no template application step afterward.Other content files are not read. They areselectivelyeithersymlinked orcopied.Python Environment and Configuration:Note:Python code in both.mdand other.py.*files are run using Python's built-inexec(andeval) functions, and when they're run, we passed in a dictionary for theirglobalsargument. We call that dict theenvironment, orenv.Configuration is done through file(s) called__config__.py.First, we recursively go through all directories top-down.At each directory (descending downward), we execute an__config__.pyfile, if one is present. After
execution, we absorb any variables in it that do not start with a_into theenvdict.This behavior cna be used to override values. For example a top-level directory can define adefault_template, which can then be overriden by inner directories.The deepest.md/.py.*files get executed first. After it executes, we check if aenvcontains a fieldpublicthat is set asTrue. If it does, we mark that file for publication. Other than recording the value ofpublicafter each dynamic file is executed, any modification toenvmade by a dynamic file are discarded (and not absorbed, unlike with__config__.py).I would recommend not using__config__.pyto setpublicasTrue, as that would make the entire directory and all its descendants public (unless that behavior is exactly what is desired). Reachability withlink(described below) is, in my opinion, a better way to makeonly reachablecontent public.Name Registry andlink.The name of every file in the input content is stored in a "name registry" of sorts that's used bylink.Currently, names, without their file extension, have to be unique across input content. This might change in the future.The Name Registry will error out if it encounters any non-unique names. (I understand this is a significant limitation, so I might support marking this simply opt-in behavior with a--uniqueflag in the future.)Any non-dynamic content file that has beenlink-ed to is marked for publication (i.e. copying or symlinking).A Python function namedlinkis injected into the top levelenv.This function can be used to get relative links to any other file.linkwill automatically determine & return the relative path to a file.For example, one can do<a href="{{link('some-other-blog-post')}}">, and the generated site will have a relative link to it (i.e. to its directory if a Markdown file, and to the file itself otherwise).Reachability of files is determined using this function, and unreachable files will be treated as non-public (and thus not exist in the generated site).Extensions may be omitted for dynamic files (i.e..mdfor Markdown, and.py*for any file with.pybefore its extension).I.e. one can write bothlink('magic-turtle')orlink('magic-turtle.md')for the filemagic-turtle.md, andlink('pygments-styles')orlink('pygments-styles.py.css')for the filepygments-styles.py.css.Usage, Testing & DevelopmentRunningIf you've installed Alteza with pip, you can just runalteza, e.g.:alteza-hIf you're working on Alteza itself, then run thealtezamodule itself, from the project directory directly, e.g.python3 -m alteza -h.Command-line ArgumentsThe-hargument above will print the list of available arguments:usage: __main__.py [--copy_assets] [--trailing_slash] [--content CONTENT] [--output OUTPUT] [-h]

options:
  --copy_assets         (bool, default=False) Copy assets instead of symlinking to them
  --trailing_slash      (bool, default=False) Include a trailing slash in links to markdown pages
  --content CONTENT
                        (str, default=test_content) Directory to read the input content from.
  --output OUTPUT
                        (str, default=test_output) Directory to send the output. WARNING: This will be deleted first.
  -h, --help            show this help message and exitAs might be obvious above, you set thecontentto your content directory. The output directory will be deleted entirely, before being written to.To test againsttest_content(and generate output totest_output), run it like this:python-malteza--contenttest_content--outputtest_outputCode StyleI'm usingblack. To re-format the code, just run:black alteza.
Fwiw, I've configured my IDE (PyCharm) to always auto-format withblack.Type CheckingTo ensure better code quality, Alteza is type-checked with five different type checking systems:Mypy, Meta'sPyre, Microsoft'sPyright, Google'sPytype, andPyflakes; as well as linted withPylint.To run some type checks:mypyalteza# should have zero errorspyflakesalteza# should have zero errorspyrecheck# should have zero errors as wellpyrightalteza# should have zero errors alsopytypealteza# should have zero errors tooOr, all at once with:mypy alteza ; pyre check ; pyright alteza ; pytype alteza ; pyflakes alteza.LintingLinting policy is very strict.Pylintmust issue a perfect 10/10 score, otherwise thePylint CI checkwill fail.To test whether lints are passing, simply run:pylint -j 0 altezaOf course, when it makes sense, lints are suppressed next to the relevant line, in code. Also, unlike typical Python code, the naming convention generally-followed in this codebase iscamelCase. Pylint checks have been mostly disabled for names.DependenciesTo install dependencies for development, run:python3-mpipinstall-rrequirements.txt
python3-mpipinstall-rrequirements-dev.txtTo use a virtual environment (after creating one withpython3 -m venv venv):sourcevenv/bin/activate# ... install requirements ...# ... do some development ...deactive# end the venvLicenseThis project is licensed under the AGPL v3, but I'm reserving the right to re-license it under a license with fewer restrictions, e.g. the Apache License 2.0, and any PRs constitute consent to re-license as such.