Stapy is a Static Site Generator. It works with Python on any operating system without additional packages. https://www.stapy.net
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
magentix 4e2a3fc300 Bump version 1 day ago
.woodpecker Pylint pipeline added 4 weeks ago
plugins Fix minifier when css is not a physical file 5 days ago
source Specific plugin method 4 weeks ago
tests Do not force line break after block 3 weeks ago
web/prod deleted file restored 3 months ago
.pylintrc Code style updated according pylint 4 weeks ago
CHANGELOG.md Fix minifier when css is not a physical file 5 days ago
LICENCE Theme improvements 11 months ago
README.md Bump version 1 day ago
build.py Display full error in terminal 1 month ago
stapy.py Reset pages before load 5 days ago

README.md

alt Stapy

Stapy License made-with-python linting: pylint status-badge

Stapy is a Static Site Generator. It works with Python on any operating system without additional packages.

Requirements

Requires Python 3.6 or newer on any operating system.

Installation

Download the last Stapy release and unzip the archive.

tar.gz

stapy-1.15.4.tar.gz

wget https://www.stapy.net/download/stapy-1.15.4.tar.gz
tar zxvf stapy-1.15.4.tar.gz

zip

stapy-1.15.4.zip

wget https://www.stapy.net/download/stapy-1.15.4.zip
unzip stapy-1.15.4.zip

Serve

Start working on the website by running the HTTP server.

Unix

python3 stapy.py

Then navigate to the URL http://localhost:1985 in your browser.

Windows

Double-click on the stapy.py file (Python is required, easily install from Microsoft Store if needed).

If the py files open with an editor, right-click and select: Open with... Python 3.X

Then navigate to the URL http://localhost:1985 in your browser.

=^..^= Welcome to Stapy 1.15.4
Serving under http://localhost:1985

Build

When the site is ready, build it for publishing.

Unix

python3 stapy.py build

Windows

Double-click on the build.py file.

=^..^= Welcome to Stapy 1.15.4
Build in progress...
[prod] 56 pages generated in 0.1456 seconds
[devel] 56 pages generated in 0.1348 seconds

Environments

Static files are generated in the web directory. This directory contains all the necessary environment directories (devel, prod...).

For the production, add a prod directory in the web directory. It will contain all pages and files you need to deploy (html, css, js, images...).

Resources

All necessary resources like js, css or images are copied from the source/assets directory in all environment directories (e.g. web/prod).

Route

For a page, the server search a json file in source/pages directory. The name of the json file is the same as the URL path. Examples:

URL Path Json file
/ index.html.json
/hello.html hello.html.json
/hello/world.html hello/world.html.json
/hello/world/ hello/world/index.html.json

Reserved routes

Route Result (json)
/_pages List all the pages
/_pages/data List all the pages with the data
/_environments List the environments
/_page/hello.html Get the data of the given path
/_content/content/hello.html Get the content of the given file

Configuration

The page json file contains all the data required to generate the page:

{
  "template": "template/default.html",
  "enabled": true,
  
  "content": "content/index.html",
  
  "meta_title": "Page meta title",
  "meta_description": "Page meta description",
  "title": "Page title"
}
  • The template defines the HTML skeleton file. If missing it will be a blank template with only the block "content".
  • The enabled key determines whether the page should appear and be generated during the build. If the key is missing, the value is assumed to be true.

Other keys are free and used in the template.

Set the environment variables with the environment suffix:

{
  "url.local": "http://localhost:1985/",
  "url.prod": "https://www.example.com/"
}

The environment suffix must have the same name as your environment directory. For local server rendering, the suffix is always "local".

A variable can have a default value:

{
  "my_text": "All environments display this text",
  "my_text.prod": "Except the prod with this"
}

Layout

A file named html.json in the source/layout directory is used for the default html page configuration. It will be merged with the page's json file. This is useful for a global configuration.

layout/html.json

{
  "title": "Default title",
  "template": "template/default.html"
}

pages/index.html.json

{
  "title": "Home title",
  "content": "content/index.html"
}

layout/html.json + pages/index.html.json

{
  "title": "Home title",
  "template": "template/default.html",
  "content": "content/index.html"
}

You can add layout file for any file extensions you need:

  • layout/xml.json
  • layout/txt.json
  • ...

A common.json config file is available for all pages (all extensions).

  • layout/common.json

Finally, it is possible to add a layout file for the page subdirectories:

  • layout/blog/html.json
  • layout/blog/2022/html.json
JSON Weight Required
layout/common.json 1 N
layout/html.json 2 N
layout/blog/common.json 3 N
layout/blog/html.json 4 N
layout/blog/2022/html.json 5 N
pages/blog/2022/my-first-post.html.json 6 Y

Json data with higher weight will override and extend lower weights.

Tip: Add _page/ before the path to fetch json merged data.

http://localhost:1985/_page/index.html
http://localhost:1985/_page/hello.html

Template

The template file is the skeleton of the page:

<!DOCTYPE html>
<html lang="fr">
    <head>
        <meta charset="utf-8">
        <title>{{ meta_title }}</title>
        <meta name="description" content="{{ meta_description }}" />
        <link rel="stylesheet" href="{{ url }}css/style.css" />
    </head>
    <body>
        <header>
            {% block.header %}
        </header>
        <main>
            {% content %}
        </main>
        <footer>
            {% block.footer %}
        </footer>
    </body>
</html>

Expressions

  • All variables enclosed in double curly braces {{ }} will be replaced with the value declared in the json file for the var.
  • All variables enclosed in curly brace percent {% %} will be replaced with the content of the file declared in the json file for the var.
  • All method names enclosed in curly brace colon {: :} will be replaced with the return of the method in a plugin.

Note: Escape the brace character to not parse an expression:

\{% block.keep.exp %\}

Child block

Call a child block template declared in page data with {% %} syntax.

{
  "block.post": "template/block/post.html"
}
{% block.post %}

Add arguments to the block:

{% block.post firstname:"John" lastname:"Doe" %}

The arguments will be accessible in the "block.post" template with a $ before the var:

Hello {{ $firstname }} {{ $lastname }}!

Use specific json data for the child content with a + separator (spaces are required):

{% block.post + my-first-post.html %}

The json/my-first-post.html.json data will be accessible in the "block.post" template with a $ before the var:

<a href="{{ url }}{{ $_path }}">{{ $post_title }}</a>

To loop json data with a query, use ~ as separator (spaces are required):

{% block.post ~ {key:value} {key:dir} {start:end} {separator} %}
  • key:value - Search value in the data of the specified key
  • key:dir - Sort by key, asc or desc
  • start:end - Retrieve data from start to end
  • separator - An optional separator between each block

Example:

{% block.post ~ tags:post date:desc 1:10 %}

This query retrieves the 10 first pages with post value in tags, sorted by date. The tags and date vars must be present in the json data of the pages:

{
  "date": "2022-01-01",
  "tags": ["post"]
}

The json data will be accessible in the "block.post" template with a $ before the var.

Add an optional block separator at the end of the query:

{% json.post ~ tags:post date:desc 1:1000 "," %}

Notes:

Use double quotes when the values contain spaces:

{% block.post author:"John Doe" ~ tags:"my tag" date:desc 1:10 "<hr />" %}

Multiline expressions are allowed:

{% block.post
    author:"John Doe"
    ~ tags:"my tag" date:desc 1:10 "<hr />"
%}

A block called in the same block never throws an infinite loop error. The child block is ignored.

Reserved variables

List

Variable Description Example
_path Cleaned page path blog/
_full_path Full page path blog/index.html
_env Environment name prod

Examples

{{ url }}{{ _path }}
<!-- https://www.example.com/blog/ -->
{{ url }}{{ _full_path }}
<!-- https://www.example.com/blog/index.html -->
<script type="text/javascript" src="{{ url }}js/init.{{ _env }}.js"></script>
<!-- https://www.example.com/js/init.prod.js -->

Plugins

A plugin allows you to add custom code when rendering the page.

Method Description Method argument 1 Method argument 2 (dict) Return
file_content_opened Update any file content (html, json, md, css, js...) File content (str or bytes) {path, mode, _stapy} str or bytes
page_data_merged Update the current page json data Page data (dict) {path, _stapy} dict
before_content_parsed Update the page template content before parsing Page content (str) {data, env, path, _stapy} str
after_content_parsed Update the page template content after parsing Page content (str) {data, env, path, _stapy} str
child_content_data Update child data before content generation Child data (dict) {key, env, path, _stapy} dict
child_content_query_result Update data result before content generation All child data (list) {key, env, path, _stapy} list
custom_http_response Send custom response on a request Response result (tuple or None) {path, request, _stapy} tuple or None
? A free named method called with {: :} syntax Page data (dict) {env, path, _stapy, ?} str

A plugin is a free named python script in the plugins directory. Plugin files are loaded in alphabetical order.

The _stapy key in argument 2 contains StapyFileSystem, StapyJsonQuery and StapyGenerator objects.

Example

To convert Markdown to HTML, add a file markdown.py in the plugins directory with the following content:

import markdown


def file_content_opened(content: str, args: dict) -> str:
    if args['path'].endswith('.md'):
        return markdown.markdown(content)
    return content

pip3 install markdown

You can now use Markdown syntax in your page content:

Hello World!

[Back to home]({{ url }})

Custom method

In any template file, call a free named method with curly brace colon {: :} syntax.

Search for method in all plugins:

{: my_plugin_method :}

Execute method only in the specified plugin:

{: custom.my_plugin_method :}

The plugin name is the plugin file name without the .py extension.

custom.my_plugin_method means to display my_plugin_method result from the plugins/custom.py script.

# plugins/custom.py

def my_plugin_method(data: dict, args: dict) -> str:
    return '<strong>Hello World!</strong>'

Add arguments to the method:

{: custom.my_plugin_method firstname:"John" lastname:"Doe" :}
# plugins/custom.py

def my_plugin_method(data: dict, args: dict) -> str:
    return '<strong>Hello ' + args['firstname'] + ' ' + args['lastname'] + '</strong>'

Method arguments are optional when you don't need them:

# plugins/custom.py

def my_plugin_method() -> str:
    return '<strong>Hello World!</strong>'

Protected methods

All methods other than the custom plugin method should be prefixed by underscore. They can never be called in a template.

# plugins/custom.py

def custom_plugin_method(data: dict, args: dict) -> str:
    return _my_method(data)
    
def _my_method(data: dict) -> str:
    return '<strong>Hello World!</strong>!'

{: custom_plugin_method :} <!-- OK -->
{: _my_method :} <!-- Forbidden -->

Deployment

All the files in your production environment (e.g. web/prod) must be exposed.

  • On a classic hosting, you can send the web/prod directory content by SFTP.
  • On an automated deployment tool like Netlify, Cloudflare Pages or Vercel, you need to create a repository on GitHub and push the files (Only push the web/prod content or all the Stapy files and configure the tool to deploy the production environment directory).
  • With GitHub Pages you can use GitHub Actions to build and deploy the website (Push the Stapy files without the web directory and add steps in the workflow to create web/prod and build the site).

Themes

Themes for Stapy: