summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Luevano Alvarado <david@luevano.xyz>2022-12-04 15:06:48 -0600
committerDavid Luevano Alvarado <david@luevano.xyz>2022-12-04 15:06:48 -0600
commit0bc00ce9352ba843d62c189b68e0e07724cc4b58 (patch)
tree7cfb33a04649f6860f2d29cf0ec124a0f4aa7e3a /src
parent5794ce299e0283ed98e102ee1faaeaf86206f588 (diff)
migrate from INI to YAML, breaks compatibility
config file and template files need to be converted to the new format to use with YAML config
Diffstat (limited to 'src')
-rw-r--r--src/pyssg/builder.py10
-rw-r--r--src/pyssg/configuration.py79
-rw-r--r--src/pyssg/database.py5
-rw-r--r--src/pyssg/md_parser.py5
-rw-r--r--src/pyssg/page.py5
-rw-r--r--src/pyssg/plt/default.ini16
-rw-r--r--src/pyssg/plt/default.yaml18
-rw-r--r--src/pyssg/plt/index.html4
-rw-r--r--src/pyssg/plt/mandatory_config.yaml14
-rw-r--r--src/pyssg/plt/page.html2
-rw-r--r--src/pyssg/plt/rss.xml4
-rw-r--r--src/pyssg/plt/static_config.yaml8
-rw-r--r--src/pyssg/plt/tag.html2
-rw-r--r--src/pyssg/pyssg.py22
-rw-r--r--src/pyssg/yaml_parser.py45
15 files changed, 157 insertions, 82 deletions
diff --git a/src/pyssg/builder.py b/src/pyssg/builder.py
index 9834e1d..391c7e0 100644
--- a/src/pyssg/builder.py
+++ b/src/pyssg/builder.py
@@ -1,7 +1,6 @@
import os
from copy import deepcopy
from operator import itemgetter
-from configparser import ConfigParser
from logging import Logger, getLogger
from jinja2 import Environment, Template, FileSystemLoader as FSLoader
@@ -15,10 +14,10 @@ log: Logger = getLogger(__name__)
class Builder:
- def __init__(self, config: ConfigParser,
+ def __init__(self, config: dict,
db: Database):
log.debug('initializing site builder')
- self.config: ConfigParser = config
+ self.config: dict = config
self.db: Database = db
# the autoescape option could be a security risk if used in a dynamic
@@ -104,7 +103,8 @@ class Builder:
log.debug('file "%s" has been modified or is new, copying', f)
copy_file(src_file, dst_file)
else:
- if self.config.getboolean('other', 'force'):
+ # TODO: need to check if this holds after yaml update
+ if self.config['info']['force']:
log.debug('file "%s" hasn\'t been modified, but option force is set to true, copying anyways', f)
copy_file(src_file, dst_file)
else:
@@ -117,7 +117,7 @@ class Builder:
temp_files: list[Page]
# check if only updated should be created
- if self.config.getboolean('other', 'force'):
+ if self.config['info']['force']:
log.debug('all html will be rendered, force is set to true')
temp_files = self.all_files
else:
diff --git a/src/pyssg/configuration.py b/src/pyssg/configuration.py
index 895df5c..1d05289 100644
--- a/src/pyssg/configuration.py
+++ b/src/pyssg/configuration.py
@@ -1,63 +1,64 @@
import sys
-import yaml
-import pprint
from importlib.metadata import version
from importlib.resources import path as rpath
from datetime import datetime, timezone
-from configparser import ConfigParser
from logging import Logger, getLogger
from .utils import get_expanded_path
+from .yaml_parser import get_parsed_yaml
log: Logger = getLogger(__name__)
-
-
DEFAULT_CONFIG_PATH: str = '$XDG_CONFIG_HOME/pyssg/config.yaml'
-VERSION = version('pyssg')
-
+VERSION: str = version('pyssg')
-def __expand_all_paths(config: ConfigParser) -> None:
- log.debug('expanding all path options')
- for option in config.options('path'):
- path: str = config['path'][option]
- config.set('path', option, get_expanded_path(path))
+def __check_well_formed_config(config: dict) -> None:
+ log.debug('checking that config file is well formed (at least contains mandatory fields')
+ mandatory_config: dict = get_parsed_yaml('mandatory_config.yaml', 'pyssg.plt')[0]
-def __check_well_formed_config(config: ConfigParser) -> None:
- log.debug('checking that config file is well formed')
- default_config: ConfigParser = ConfigParser()
- with rpath('pyssg.plt', 'default.ini') as p:
- log.debug('reading config file "%s"', p)
- default_config.read(p)
-
- for section in default_config.sections():
+ for section in mandatory_config.keys():
log.debug('checking section "%s"', section)
- if not config.has_section(section):
+ if not config[section]:
log.error('config does not have section "%s"', section)
sys.exit(1)
- for option in default_config.options(section):
+ # the case for elements that don't have nested elements
+ if not mandatory_config[section]:
+ log.debug('section "%s" doesn\'t need nested elements', section)
+ continue
+ for option in mandatory_config[section].keys():
log.debug('checking option "%s"', option)
- if not config.has_option(section, option):
+ if option not in config[section] or not config[section][option]:
log.error('config does not have option "%s" in section "%s"', option, section)
sys.exit(1)
-def get_parsed_config(path: str) -> ConfigParser:
- config: ConfigParser = ConfigParser()
+def __expand_all_paths(config: dict) -> None:
+ log.debug('expanding all path options: %s', config['path'].keys())
+ for option in config['path'].keys():
+ config['path'][option] = get_expanded_path(config['path'][option])
+
+
+# not necessary to type deeper than the first dict
+def get_parsed_config(path: str) -> list[dict]:
log.debug('reading config file "%s"', path)
- config.read(path)
-
- __check_well_formed_config(config)
- __expand_all_paths(config)
-
- # set other required options
- log.debug('setting extra config options')
- config.set('fmt', 'rss_date', '%%a, %%d %%b %%Y %%H:%%M:%%S GMT')
- config.set('fmt', 'sitemap_date', '%%Y-%%m-%%d')
- config.set('info', 'version', VERSION)
- config.set('info', 'rss_run_date', datetime.now(
- tz=timezone.utc).strftime(config['fmt']['rss_date']))
- config.set('info', 'sitemap_run_date', datetime.now(
- tz=timezone.utc).strftime(config['fmt']['sitemap_date']))
+ config: list[dict] = get_parsed_yaml(path) # type: ignore
+
+ __check_well_formed_config(config[0])
+ __expand_all_paths(config[0])
+
+ return config
+
+
+# not necessary to type deeper than the first dict,
+# static config means config that shouldn't be changed by the user
+def get_static_config() -> dict[str, dict]:
+ log.debug('reading and setting static config')
+ config: dict = get_parsed_yaml('static_config.yaml', 'pyssg.plt')[0] # type: ignore
+
+ config['info']['version'] = VERSION
+ config['info']['rss_run_date'] = datetime.now(tz=timezone.utc)\
+ .strftime(config['fmt']['rss_date'])
+ config['info']['sitemap_run_date'] = datetime.now(tz=timezone.utc)\
+ .strftime(config['fmt']['sitemap_date'])
return config
diff --git a/src/pyssg/database.py b/src/pyssg/database.py
index 5a174c9..34bf534 100644
--- a/src/pyssg/database.py
+++ b/src/pyssg/database.py
@@ -2,7 +2,6 @@ import os
import sys
import csv
from logging import Logger, getLogger
-from configparser import ConfigParser
from .utils import get_checksum
from .database_entry import DatabaseEntry
@@ -15,11 +14,9 @@ class Database:
__COLUMN_NUM: int = 5
__COLUMN_DELIMITER: str = '|'
- def __init__(self, db_path: str,
- config: ConfigParser):
+ def __init__(self, db_path: str) -> None:
log.debug('initializing the page db on path "%s"', db_path)
self.db_path: str = db_path
- self.config: ConfigParser = config
self.e: dict[str, DatabaseEntry] = dict()
diff --git a/src/pyssg/md_parser.py b/src/pyssg/md_parser.py
index 061fcd5..5f4fb46 100644
--- a/src/pyssg/md_parser.py
+++ b/src/pyssg/md_parser.py
@@ -1,7 +1,6 @@
import os
from operator import itemgetter
from markdown import Markdown
-from configparser import ConfigParser
from logging import Logger, getLogger
from markdown import Markdown
@@ -44,12 +43,12 @@ def _get_md_obj() -> Markdown:
# page and file is basically a synonym here...
class MDParser:
def __init__(self, files: list[str],
- config: ConfigParser,
+ config: dict,
db: Database):
log.debug('initializing the md parser with %d files', len(files))
self.files: list[str] = files
- self.config: ConfigParser = config
+ self.config: dict = config
self.db: Database = db
self.md: Markdown = _get_md_obj()
diff --git a/src/pyssg/page.py b/src/pyssg/page.py
index 4f2ee43..4a12f62 100644
--- a/src/pyssg/page.py
+++ b/src/pyssg/page.py
@@ -2,7 +2,6 @@ import os
import sys
from datetime import datetime, timezone
from logging import Logger, getLogger
-from configparser import ConfigParser
log: Logger = getLogger(__name__)
@@ -14,7 +13,7 @@ class Page:
mtime: float,
html: str,
meta: dict,
- config: ConfigParser):
+ config: dict):
log.debug('initializing the page object with name "%s"', name)
# initial data
self.name: str = name
@@ -22,7 +21,7 @@ class Page:
self.mtimestamp: float = mtime
self.content: str = html
self.meta: dict = meta
- self.config: ConfigParser = config
+ self.config: dict = config
# data from self.meta
self.title: str
diff --git a/src/pyssg/plt/default.ini b/src/pyssg/plt/default.ini
deleted file mode 100644
index ab4eac1..0000000
--- a/src/pyssg/plt/default.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[path]
-src=src
-dst=dst
-plt=plt
-[url]
-main=https://example.com
-static=https://static.example.com
-default_image=/images/default.png
-[fmt]
-date=%%a, %%b %%d, %%Y @ %%H:%%M %%Z
-list_date=%%b %%d
-list_sep_date=%%B %%Y
-[info]
-title=Example site
-[other]
-force=False \ No newline at end of file
diff --git a/src/pyssg/plt/default.yaml b/src/pyssg/plt/default.yaml
new file mode 100644
index 0000000..c90d44d
--- /dev/null
+++ b/src/pyssg/plt/default.yaml
@@ -0,0 +1,18 @@
+%YAML 1.2
+---
+define: &root "$HOME/pyssg/site_example/"
+
+title: "Example site"
+path:
+ src: !join [*root, "src"]
+ dst: !join [*root, "dst"]
+ plt: !join [*root, "plt"]
+url:
+ main: "https://example.com"
+ static: "https://static.example.com"
+ default_image: "/images/default.png"
+fmt:
+ date: "%a, %b %d, %Y @ %H:%M %Z"
+ list_date: "%b %d"
+ list_sep_date: "%B %Y"
+... \ No newline at end of file
diff --git a/src/pyssg/plt/index.html b/src/pyssg/plt/index.html
index d061625..96d66ef 100644
--- a/src/pyssg/plt/index.html
+++ b/src/pyssg/plt/index.html
@@ -3,10 +3,10 @@
<head>
<meta charset="utf-8">
<base href="{{config['url']['static']}}">
- <title>Index -- {{config['info']['title']}}</title>
+ <title>Index -- {{config['title']}}</title>
</head>
<body>
- <h1>Index -- {{config['info']['title']}}</h1>
+ <h1>Index -- {{config['title']}}</h1>
<p>Some text here.</p>
<p>Tags:
diff --git a/src/pyssg/plt/mandatory_config.yaml b/src/pyssg/plt/mandatory_config.yaml
new file mode 100644
index 0000000..52bfa04
--- /dev/null
+++ b/src/pyssg/plt/mandatory_config.yaml
@@ -0,0 +1,14 @@
+%YAML 1.2
+---
+title:
+path:
+ src:
+ dst:
+ plt:
+url:
+ main:
+fmt:
+ date:
+ list_date:
+ list_sep_date:
+... \ No newline at end of file
diff --git a/src/pyssg/plt/page.html b/src/pyssg/plt/page.html
index 39101c4..d7f5e43 100644
--- a/src/pyssg/plt/page.html
+++ b/src/pyssg/plt/page.html
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<base href="{{config['url']['static']}}">
- <title>{{page.title}} -- {{config['info']['title']}}</title>
+ <title>{{page.title}} -- {{config['title']}}</title>
</head>
<body>
<h1>{{page.title}}</h1>
diff --git a/src/pyssg/plt/rss.xml b/src/pyssg/plt/rss.xml
index 31abd48..6a3eb00 100644
--- a/src/pyssg/plt/rss.xml
+++ b/src/pyssg/plt/rss.xml
@@ -3,7 +3,7 @@
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
- <title>{{config['info']['title']}}</title>
+ <title>{{config['title']}}</title>
<link>{{config['url']['main']}}</link>
<atom:link href="{{config['url']['main']}}/rss.xml" rel="self" type="application/rss+xml"/>
<description>Short site description.</description>
@@ -19,7 +19,7 @@
<ttl>30</ttl>
<image>
<url>{{config['url']['static']}}/images/blog.png</url>
- <title>{{config['info']['title']}}</title>
+ <title>{{config['title']}}</title>
<link>{{config['url']['main']}}</link>
</image>
{%for p in all_pages%}
diff --git a/src/pyssg/plt/static_config.yaml b/src/pyssg/plt/static_config.yaml
new file mode 100644
index 0000000..745c767
--- /dev/null
+++ b/src/pyssg/plt/static_config.yaml
@@ -0,0 +1,8 @@
+%YAML 1.2
+---
+fmt:
+ rss_date: "%a, %d %b %Y %H:%M:%S GMT"
+ sitemap_date: "%Y-%m-%d"
+info:
+ version: "0.0.0"
+... \ No newline at end of file
diff --git a/src/pyssg/plt/tag.html b/src/pyssg/plt/tag.html
index eadfb95..59cbdf1 100644
--- a/src/pyssg/plt/tag.html
+++ b/src/pyssg/plt/tag.html
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<base href="{{config['url']['static']}}">
- <title>Posts filtered by {{tag[0]}} -- {{config['info']['title']}}</title>
+ <title>Posts filtered by {{tag[0]}} -- {{config['title']}}</title>
</head>
<body>
<h1>Posts filtered by {{tag[0]}}</h1>
diff --git a/src/pyssg/pyssg.py b/src/pyssg/pyssg.py
index 2734a99..acf4542 100644
--- a/src/pyssg/pyssg.py
+++ b/src/pyssg/pyssg.py
@@ -2,13 +2,12 @@ import os
import sys
from importlib.resources import path as rpath
from typing import Union
-from configparser import ConfigParser
from logging import Logger, getLogger, DEBUG
from argparse import ArgumentParser
from .arg_parser import get_parser
from .utils import create_dir, copy_file, get_expanded_path
-from .configuration import get_parsed_config, DEFAULT_CONFIG_PATH, VERSION
+from .configuration import get_parsed_config, get_static_config, DEFAULT_CONFIG_PATH, VERSION
from .database import Database
from .builder import Builder
@@ -60,7 +59,7 @@ def main() -> None:
if args['copy_default_config']:
log.info('copying default config file')
create_dir(config_dir)
- with rpath('pyssg.plt', 'default.ini') as p:
+ with rpath('pyssg.plt', 'default.yaml') as p:
copy_file(str(p), config_path)
sys.exit(0)
@@ -70,8 +69,18 @@ def main() -> None:
' first time if you haven\'t already', config_path)
sys.exit(1)
- config: ConfigParser = get_parsed_config(config_path)
- config.set('info', 'debug', str(args['debug']))
+ log.debug('reading config files')
+ config_all: list[dict] = get_parsed_config(config_path)
+ static_config: dict = get_static_config()
+
+ # easier to add static into config than changing existing code
+ config: dict = config_all[0]
+ config['fmt']['rss_date'] = static_config['fmt']['rss_date']
+ config['fmt']['sitemap_date'] = static_config['fmt']['sitemap_date']
+ config['info'] = dict()
+ config['info']['version'] = static_config['info']['version']
+ config['info']['debug'] = str(args['debug'])
+ config['info']['force'] = str(args['force'])
if args['init']:
log.info('initializing the directory structure and copying over templates')
@@ -94,8 +103,9 @@ def main() -> None:
if args['build']:
log.info('building the html files')
+ # TODO: need to add this to the config and not assume it
db_path: str = os.path.join(config['path']['src'], '.files')
- db: Database = Database(db_path, config)
+ db: Database = Database(db_path)
db.read()
builder: Builder = Builder(config, db)
diff --git a/src/pyssg/yaml_parser.py b/src/pyssg/yaml_parser.py
new file mode 100644
index 0000000..48c2eec
--- /dev/null
+++ b/src/pyssg/yaml_parser.py
@@ -0,0 +1,45 @@
+import yaml
+from yaml import SafeLoader
+from yaml.nodes import SequenceNode
+from io import TextIOWrapper
+from importlib.resources import path as rpath
+from logging import Logger, getLogger
+
+log: Logger = getLogger(__name__)
+
+
+# required to concat values in yaml using !join [value, value, ...]
+def __join_constructor(loader: SafeLoader, node: SequenceNode) -> str:
+ seq = loader.construct_sequence(node)
+ return ''.join([str(i) for i in seq])
+log.warning('adding the custom join constructor to yaml.SafeLoader')
+SafeLoader.add_constructor('!join', __join_constructor)
+
+
+# "file" is either a path or the yaml content itself
+def __read_raw_yaml(file: TextIOWrapper) -> list[dict]:
+ all_docs: list[dict] = []
+ all_docs_gen = yaml.safe_load_all(file)
+ for doc in all_docs_gen:
+ all_docs.append(doc)
+
+ return all_docs
+
+
+def get_parsed_yaml(resource: str, package: str='') -> list[dict]:
+ all_yaml_docs: list[dict] = []
+ if package == '':
+ log.debug('no package specified, reading file "%s"', resource)
+ with open(resource, 'r') as f:
+ all_yaml_docs = __read_raw_yaml(f)
+ else:
+ log.debug('package "%s" specified, reading resource "%s"',
+ package, resource)
+ with rpath(package, resource) as p:
+ with open(p, 'r') as f:
+ all_yaml_docs = __read_raw_yaml(f)
+
+ log.info('found %s document(s) for configuration "%s"',
+ len(all_yaml_docs), f'{package}.{resource}' if package != '' else resource)
+
+ return all_yaml_docs