essayist 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
essayist-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ayx-dg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: essayist
3
+ Version: 0.1.0
4
+ Summary: A Pandoc + Jinja2 static site generator for Markdown blogs.
5
+ Author: ayx-dg
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ayx-dg/essayist
8
+ Project-URL: Repository, https://github.com/ayx-dg/essayist
9
+ Keywords: static-site-generator,blog,markdown,pandoc,jinja2
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: jinja2
17
+ Requires-Dist: pyyaml
18
+ Requires-Dist: tomli; python_version < "3.11"
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest; extra == "test"
21
+ Dynamic: license-file
22
+
23
+ # essayist
24
+
25
+ A lightweight static site generator for Markdown blogs, powered by
26
+ [Pandoc](https://pandoc.org/) and [Jinja2](https://jinja.palletsprojects.com/).
27
+
28
+ Convert a directory of Markdown posts (with YAML front matter) into a styled
29
+ HTML blog with an index page and an RSS feed.
30
+
31
+ ## Requirements
32
+
33
+ - Python ≥ 3.10
34
+ - [Pandoc](https://pandoc.org/installing.html) available on `PATH`
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ pip install essayist
40
+ ```
41
+
42
+ Or install from source:
43
+
44
+ ```bash
45
+ git clone https://github.com/ayx-dg/essayist
46
+ cd essayist
47
+ pip install -e .
48
+ ```
49
+
50
+ ## Quick start
51
+
52
+ ### 1. Set up your Markdown directory
53
+
54
+ Create a `markdown/posts/` folder and add `.md` files with YAML front matter:
55
+
56
+ ```markdown
57
+ ---
58
+ title: My First Post
59
+ date: 2025-01-15
60
+ tags: [intro, welcome]
61
+ publish: public
62
+ ---
63
+
64
+ # My First Post
65
+
66
+ Here is the body of my post, written in Markdown.
67
+ ```
68
+
69
+ ### 2. Create a config file
70
+
71
+ Save this as `essayist.toml` in your project root:
72
+
73
+ ```toml
74
+ markdown_dir = "markdown/posts"
75
+ post_dir = "public/posts"
76
+ blogname = "My Blog"
77
+ site_url = "https://example.com"
78
+ panargs = ["--mathml", "--toc", "--shift-heading-level-by=1"]
79
+
80
+ build_index = true
81
+ index_title = "All Posts"
82
+ build_rss = true
83
+ home_md = "markdown/index.md"
84
+ home_output = "public/index.html"
85
+ style_css = "style-note.css"
86
+ ```
87
+
88
+ ### 3. Build
89
+
90
+ ```bash
91
+ essayist build
92
+ ```
93
+
94
+ Output will be written to `public/`. Open `public/index.html` in a browser.
95
+
96
+ ## CLI usage
97
+
98
+ ```
99
+ essayist build [--config essayist.toml] [OPTIONS]
100
+ essayist version
101
+ ```
102
+
103
+ CLI flags override the corresponding config-file values:
104
+
105
+ | Flag | Description |
106
+ |------------------|------------------------------------|
107
+ | `-c, --config` | Path to TOML config (default: `essayist.toml`) |
108
+ | `--markdown-dir` | Directory with Markdown posts |
109
+ | `--post-dir` | Output directory for rendered HTML |
110
+ | `--template-dir` | Custom Jinja2 template directory |
111
+ | `--blogname` | Blog name for page titles |
112
+ | `--site-url` | Base URL for RSS feeds |
113
+ | `--gallery` | Enable the gallery Lua filter |
114
+
115
+ ## Python API
116
+
117
+ ```python
118
+ from essayist import Blog, Config, build_site
119
+
120
+ # High-level: load config and build everything
121
+ cfg = Config(
122
+ markdown_dir="markdown/posts",
123
+ post_dir="public/posts",
124
+ blogname="My Blog",
125
+ site_url="https://example.com",
126
+ panargs=["--mathml", "--toc"],
127
+ )
128
+ build_site(cfg)
129
+
130
+ # Or use the Blog class directly
131
+ blog = Blog(
132
+ markdown_dir="markdown/posts",
133
+ post_dir="public/posts",
134
+ template_dir="path/to/custom/templates", # None → use bundled defaults
135
+ )
136
+ blog.update_data()
137
+ blog.build_posts()
138
+ blog.build_index()
139
+ blog.build_rss("public/posts/rss.xml")
140
+ ```
141
+
142
+ ## Config reference
143
+
144
+ All keys in `essayist.toml`:
145
+
146
+ | Key | Type | Default | Description |
147
+ |-------------------|-------------|-------------------|----------------------------------------------------|
148
+ | `markdown_dir` | string | `markdown/posts` | Directory with `.md` post files |
149
+ | `post_dir` | string | `public/posts` | Output directory for rendered HTML |
150
+ | `template_dir` | string/null | `null` (bundled) | Jinja2 template directory (`null` = use defaults) |
151
+ | `blogname` | string | `""` | Blog name appended to page titles |
152
+ | `google_group_id` | string | `""` | Enables the email-comment block in posts |
153
+ | `site_url` | string | `https://example.com` | Base URL used in RSS `<link>` tags |
154
+ | `panargs` | array | `[]` | Extra flags passed to Pandoc |
155
+ | `gallery` | bool | `false` | Enable the bundled image-gallery Lua filter |
156
+ | `build_index` | bool | `true` | Generate the post-list index page |
157
+ | `index_title` | string | `Index` | Title for the index page |
158
+ | `build_rss` | bool | `true` | Generate an RSS 2.0 feed |
159
+ | `rss_path` | string/null | `<post_dir>/rss.xml` | Where to write the RSS feed |
160
+ | `home_md` | string/null | `null` | Markdown file for the home page (if any) |
161
+ | `home_template` | string | `home.html` | Template used to render the home page |
162
+ | `home_output` | string | `public/index.html` | Output path for the home page |
163
+ | `style_css` | string/null | `null` | CSS file copied next to rendered posts |
164
+ | `data_path` | string | `data.json` | Path for the auto-generated post index |
165
+
166
+ ## Post front matter
167
+
168
+ Every Markdown post should start with a YAML front matter block:
169
+
170
+ ```yaml
171
+ ---
172
+ title: Post Title # required
173
+ date: 2025-01-15 # used for ordering in index and RSS
174
+ tags: [tag1, tag2] # optional, passed to templates
175
+ publish: public # public | draft | unlisted
176
+ maillist_title: ... # optional, email comment link
177
+ google_group_link: ... # optional, link to mail thread
178
+ ---
179
+ ```
180
+
181
+ **Visibility rules:**
182
+
183
+ - `public` — appears in index and RSS
184
+ - `draft` — skipped entirely (existing output is cleaned up)
185
+ - `unlisted` — appears in RSS but not in the index
186
+
187
+ ## Templates
188
+
189
+ The package ships with five default templates:
190
+
191
+ | Template | Purpose |
192
+ |-------------|---------------------------------|
193
+ | `header.html` | `<head>` block with CSS and RSS link |
194
+ | `footer.html` | Footer with RSS link |
195
+ | `post.html` | Single blog post page |
196
+ | `index.html` | Post listing (all public posts)|
197
+ | `home.html` | Home / landing page |
198
+
199
+ Supply a `template_dir` in your config to override any of these. The templates
200
+ use [Jinja2](https://jinja.palletsprojects.com/) syntax and receive variables
201
+ like `heading`, `paragraphs`, `title`, `prev_post`, `next_post`, etc.
202
+
203
+ ## Bundled Lua filters
204
+
205
+ | Filter | Description |
206
+ |-------------|------------------------------------------|
207
+ | `gallery.lua` | Groups images in a paragraph into a responsive flex gallery |
208
+
209
+ Enable with `gallery = true` in config, or pass `--gallery` on the CLI.
210
+
211
+ ## Development
212
+
213
+ ```bash
214
+ pip install -e ".[test]"
215
+ pytest
216
+ ```
217
+
218
+ ## License
219
+
220
+ MIT
@@ -0,0 +1,198 @@
1
+ # essayist
2
+
3
+ A lightweight static site generator for Markdown blogs, powered by
4
+ [Pandoc](https://pandoc.org/) and [Jinja2](https://jinja.palletsprojects.com/).
5
+
6
+ Convert a directory of Markdown posts (with YAML front matter) into a styled
7
+ HTML blog with an index page and an RSS feed.
8
+
9
+ ## Requirements
10
+
11
+ - Python ≥ 3.10
12
+ - [Pandoc](https://pandoc.org/installing.html) available on `PATH`
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install essayist
18
+ ```
19
+
20
+ Or install from source:
21
+
22
+ ```bash
23
+ git clone https://github.com/ayx-dg/essayist
24
+ cd essayist
25
+ pip install -e .
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ ### 1. Set up your Markdown directory
31
+
32
+ Create a `markdown/posts/` folder and add `.md` files with YAML front matter:
33
+
34
+ ```markdown
35
+ ---
36
+ title: My First Post
37
+ date: 2025-01-15
38
+ tags: [intro, welcome]
39
+ publish: public
40
+ ---
41
+
42
+ # My First Post
43
+
44
+ Here is the body of my post, written in Markdown.
45
+ ```
46
+
47
+ ### 2. Create a config file
48
+
49
+ Save this as `essayist.toml` in your project root:
50
+
51
+ ```toml
52
+ markdown_dir = "markdown/posts"
53
+ post_dir = "public/posts"
54
+ blogname = "My Blog"
55
+ site_url = "https://example.com"
56
+ panargs = ["--mathml", "--toc", "--shift-heading-level-by=1"]
57
+
58
+ build_index = true
59
+ index_title = "All Posts"
60
+ build_rss = true
61
+ home_md = "markdown/index.md"
62
+ home_output = "public/index.html"
63
+ style_css = "style-note.css"
64
+ ```
65
+
66
+ ### 3. Build
67
+
68
+ ```bash
69
+ essayist build
70
+ ```
71
+
72
+ Output will be written to `public/`. Open `public/index.html` in a browser.
73
+
74
+ ## CLI usage
75
+
76
+ ```
77
+ essayist build [--config essayist.toml] [OPTIONS]
78
+ essayist version
79
+ ```
80
+
81
+ CLI flags override the corresponding config-file values:
82
+
83
+ | Flag | Description |
84
+ |------------------|------------------------------------|
85
+ | `-c, --config` | Path to TOML config (default: `essayist.toml`) |
86
+ | `--markdown-dir` | Directory with Markdown posts |
87
+ | `--post-dir` | Output directory for rendered HTML |
88
+ | `--template-dir` | Custom Jinja2 template directory |
89
+ | `--blogname` | Blog name for page titles |
90
+ | `--site-url` | Base URL for RSS feeds |
91
+ | `--gallery` | Enable the gallery Lua filter |
92
+
93
+ ## Python API
94
+
95
+ ```python
96
+ from essayist import Blog, Config, build_site
97
+
98
+ # High-level: load config and build everything
99
+ cfg = Config(
100
+ markdown_dir="markdown/posts",
101
+ post_dir="public/posts",
102
+ blogname="My Blog",
103
+ site_url="https://example.com",
104
+ panargs=["--mathml", "--toc"],
105
+ )
106
+ build_site(cfg)
107
+
108
+ # Or use the Blog class directly
109
+ blog = Blog(
110
+ markdown_dir="markdown/posts",
111
+ post_dir="public/posts",
112
+ template_dir="path/to/custom/templates", # None → use bundled defaults
113
+ )
114
+ blog.update_data()
115
+ blog.build_posts()
116
+ blog.build_index()
117
+ blog.build_rss("public/posts/rss.xml")
118
+ ```
119
+
120
+ ## Config reference
121
+
122
+ All keys in `essayist.toml`:
123
+
124
+ | Key | Type | Default | Description |
125
+ |-------------------|-------------|-------------------|----------------------------------------------------|
126
+ | `markdown_dir` | string | `markdown/posts` | Directory with `.md` post files |
127
+ | `post_dir` | string | `public/posts` | Output directory for rendered HTML |
128
+ | `template_dir` | string/null | `null` (bundled) | Jinja2 template directory (`null` = use defaults) |
129
+ | `blogname` | string | `""` | Blog name appended to page titles |
130
+ | `google_group_id` | string | `""` | Enables the email-comment block in posts |
131
+ | `site_url` | string | `https://example.com` | Base URL used in RSS `<link>` tags |
132
+ | `panargs` | array | `[]` | Extra flags passed to Pandoc |
133
+ | `gallery` | bool | `false` | Enable the bundled image-gallery Lua filter |
134
+ | `build_index` | bool | `true` | Generate the post-list index page |
135
+ | `index_title` | string | `Index` | Title for the index page |
136
+ | `build_rss` | bool | `true` | Generate an RSS 2.0 feed |
137
+ | `rss_path` | string/null | `<post_dir>/rss.xml` | Where to write the RSS feed |
138
+ | `home_md` | string/null | `null` | Markdown file for the home page (if any) |
139
+ | `home_template` | string | `home.html` | Template used to render the home page |
140
+ | `home_output` | string | `public/index.html` | Output path for the home page |
141
+ | `style_css` | string/null | `null` | CSS file copied next to rendered posts |
142
+ | `data_path` | string | `data.json` | Path for the auto-generated post index |
143
+
144
+ ## Post front matter
145
+
146
+ Every Markdown post should start with a YAML front matter block:
147
+
148
+ ```yaml
149
+ ---
150
+ title: Post Title # required
151
+ date: 2025-01-15 # used for ordering in index and RSS
152
+ tags: [tag1, tag2] # optional, passed to templates
153
+ publish: public # public | draft | unlisted
154
+ maillist_title: ... # optional, email comment link
155
+ google_group_link: ... # optional, link to mail thread
156
+ ---
157
+ ```
158
+
159
+ **Visibility rules:**
160
+
161
+ - `public` — appears in index and RSS
162
+ - `draft` — skipped entirely (existing output is cleaned up)
163
+ - `unlisted` — appears in RSS but not in the index
164
+
165
+ ## Templates
166
+
167
+ The package ships with five default templates:
168
+
169
+ | Template | Purpose |
170
+ |-------------|---------------------------------|
171
+ | `header.html` | `<head>` block with CSS and RSS link |
172
+ | `footer.html` | Footer with RSS link |
173
+ | `post.html` | Single blog post page |
174
+ | `index.html` | Post listing (all public posts)|
175
+ | `home.html` | Home / landing page |
176
+
177
+ Supply a `template_dir` in your config to override any of these. The templates
178
+ use [Jinja2](https://jinja.palletsprojects.com/) syntax and receive variables
179
+ like `heading`, `paragraphs`, `title`, `prev_post`, `next_post`, etc.
180
+
181
+ ## Bundled Lua filters
182
+
183
+ | Filter | Description |
184
+ |-------------|------------------------------------------|
185
+ | `gallery.lua` | Groups images in a paragraph into a responsive flex gallery |
186
+
187
+ Enable with `gallery = true` in config, or pass `--gallery` on the CLI.
188
+
189
+ ## Development
190
+
191
+ ```bash
192
+ pip install -e ".[test]"
193
+ pytest
194
+ ```
195
+
196
+ ## License
197
+
198
+ MIT
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "essayist"
7
+ version = "0.1.0"
8
+ description = "A Pandoc + Jinja2 static site generator for Markdown blogs."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "ayx-dg" }]
13
+ keywords = ["static-site-generator", "blog", "markdown", "pandoc", "jinja2"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Topic :: Internet :: WWW/HTTP :: Site Management",
18
+ ]
19
+ dependencies = [
20
+ "jinja2",
21
+ "pyyaml",
22
+ 'tomli; python_version < "3.11"',
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ test = ["pytest"]
27
+
28
+ [project.scripts]
29
+ essayist = "essayist.cli:main"
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/ayx-dg/essayist"
33
+ Repository = "https://github.com/ayx-dg/essayist"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+
38
+ [tool.setuptools.package-data]
39
+ essayist = ["templates/*.html", "filters/*.lua", "style-note.css", "py.typed"]
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ """essayist: a Pandoc + Jinja2 static site generator.
2
+
3
+ This package turns a directory of Markdown posts (with YAML front matter) into
4
+ a static HTML site, an index page and an RSS feed.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from .builder import build_site
10
+ from .config import Config
11
+ from .core import Blog, pandoc, text_file_to_string
12
+
13
+ __version__ = "0.1.0"
14
+
15
+ __all__ = [
16
+ "Blog",
17
+ "Config",
18
+ "build_site",
19
+ "pandoc",
20
+ "text_file_to_string",
21
+ "__version__",
22
+ ]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,58 @@
1
+ """High level build orchestration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import shutil
7
+
8
+ from .config import Config
9
+ from .core import Blog, text_file_to_string
10
+
11
+
12
+ def build_site(config: Config) -> Blog:
13
+ """Run a full site build described by ``config`` and return the Blog."""
14
+ meta = {}
15
+ if config.blogname:
16
+ meta["blogname"] = config.blogname
17
+ if config.google_group_id:
18
+ meta["google_group_id"] = config.google_group_id
19
+
20
+ blog = Blog(
21
+ markdown_dir=config.markdown_dir,
22
+ post_dir=config.post_dir,
23
+ template_dir=config.template_dir,
24
+ meta=meta,
25
+ data_path=config.data_path,
26
+ site_url=config.site_url,
27
+ )
28
+ # set effective panargs (with bundled gallery filter if requested)
29
+ blog.default_panargs = config.effective_panargs(blog)
30
+
31
+ blog.update_data()
32
+ blog.build_posts()
33
+
34
+ if config.style_css:
35
+ dst_dir = os.path.dirname(os.path.join(config.post_dir, "style-note.css"))
36
+ os.makedirs(dst_dir, exist_ok=True)
37
+ shutil.copy2(config.style_css, os.path.join(config.post_dir, "style-note.css"))
38
+
39
+ if config.build_index:
40
+ blog.build_index(index_path=config.post_dir, index_title=config.index_title)
41
+
42
+ if config.build_rss:
43
+ rss_path = config.rss_path or os.path.join(config.post_dir, "rss.xml")
44
+ blog.build_rss(rss_path=rss_path)
45
+
46
+ if config.home_md:
47
+ home_html = blog.render_page(
48
+ content=text_file_to_string(config.home_md),
49
+ template_name=config.home_template,
50
+ meta={},
51
+ panargs=["--mathml"],
52
+ )
53
+ home_dir = os.path.dirname(config.home_output)
54
+ os.makedirs(home_dir, exist_ok=True)
55
+ with open(config.home_output, "w", encoding="utf-8") as f:
56
+ f.write(home_html)
57
+
58
+ return blog
@@ -0,0 +1,90 @@
1
+ """Command line interface for the static site generator."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import os
7
+ import sys
8
+ from dataclasses import fields
9
+
10
+ try: # Python >= 3.11
11
+ import tomllib
12
+ except ModuleNotFoundError: # Python 3.10
13
+ import tomli as tomllib
14
+
15
+ from .builder import build_site
16
+ from .config import Config
17
+
18
+
19
+ def _load_config(path: str | None) -> Config:
20
+ """Load a :class:`Config` from a TOML file, if given."""
21
+ if not path:
22
+ return Config()
23
+ with open(path, "rb") as f:
24
+ data = tomllib.load(f)
25
+ # Accept either a top-level table or a [tool.essayist] table.
26
+ cfg = data.get("essayist", data)
27
+ known = {f.name for f in fields(Config)}
28
+ kwargs = {k: v for k, v in cfg.items() if k in known}
29
+ base = os.path.dirname(os.path.abspath(path))
30
+ config = Config(**kwargs)
31
+ config.resolve(base)
32
+ return config
33
+
34
+
35
+ def build_parser() -> argparse.ArgumentParser:
36
+ parser = argparse.ArgumentParser(
37
+ prog="essayist",
38
+ description="A Pandoc + Jinja2 static site generator.",
39
+ )
40
+ sub = parser.add_subparsers(dest="command", required=True)
41
+
42
+ p_build = sub.add_parser("build", help="Build the static site.")
43
+ p_build.add_argument(
44
+ "-c", "--config", default="essayist.toml", help="Path to TOML config file."
45
+ )
46
+ p_build.add_argument("--markdown-dir", help="Directory with Markdown posts.")
47
+ p_build.add_argument("--post-dir", help="Output directory for posts.")
48
+ p_build.add_argument("--template-dir", help="Directory with Jinja2 templates.")
49
+ p_build.add_argument("--blogname", help="Blog name shown in page titles.")
50
+ p_build.add_argument("--site-url", help="Base URL used in RSS feeds.")
51
+ p_build.add_argument(
52
+ "--gallery", action="store_true", help="Enable the gallery lua filter."
53
+ )
54
+ p_build.set_defaults(func=_cmd_build)
55
+
56
+ sub.add_parser("version", help="Print the package version.").set_defaults(
57
+ func=_cmd_version
58
+ )
59
+ return parser
60
+
61
+
62
+ def _cmd_build(args: argparse.Namespace) -> int:
63
+ config = _load_config(args.config)
64
+ # CLI flags override the config file.
65
+ for key in ("markdown_dir", "post_dir", "template_dir", "blogname", "site_url"):
66
+ value = getattr(args, key, None)
67
+ if value:
68
+ setattr(config, key, value)
69
+ if args.gallery:
70
+ config.gallery = True
71
+ build_site(config)
72
+ print("Build complete")
73
+ return 0
74
+
75
+
76
+ def _cmd_version(args: argparse.Namespace) -> int:
77
+ from . import __version__
78
+
79
+ print(__version__)
80
+ return 0
81
+
82
+
83
+ def main(argv: list[str] | None = None) -> int:
84
+ parser = build_parser()
85
+ args = parser.parse_args(argv)
86
+ return args.func(args)
87
+
88
+
89
+ if __name__ == "__main__":
90
+ sys.exit(main())