summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Luevano Alvarado <david@luevano.xyz>2022-04-17 19:39:38 -0600
committerDavid Luevano Alvarado <david@luevano.xyz>2022-04-17 19:39:38 -0600
commit6bec182703885761699f6d53bc9034933b03197e (patch)
tree489ceb8a2223a9e1067e0eabfd91a62d1c7ec051
parentd2dfa337171fdad8ed48ce95e3b8f91e291423b7 (diff)
add initial logging capabilities
still need to add logging to rest of the program: builder, database, discovery, page, parser and rest of the pyssg (build part)
-rw-r--r--README.md1
-rw-r--r--src/pyssg/__init__.py17
-rw-r--r--src/pyssg/__main__.py2
-rw-r--r--src/pyssg/configuration.py14
-rw-r--r--src/pyssg/per_level_formatter.py28
-rw-r--r--src/pyssg/pyssg.py26
-rw-r--r--src/pyssg/utils.py15
7 files changed, 89 insertions, 14 deletions
diff --git a/README.md b/README.md
index 7845805..2163a95 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ Inspired (initially) by Roman Zolotarev's [`ssg5`](https://rgz.ee/bin/ssg5) and
- [x] Avoid the program to freak out when there are directories created in advance.
- [ ] Provide more meaningful error messages when you are missing mandatory tags in your `.md` files.
- [ ] More complex directory structure to support multiple subdomains and different types of pages.
+- [ ] Add option/change to using an SQL database instead of the custom solution.
### Markdown features
diff --git a/src/pyssg/__init__.py b/src/pyssg/__init__.py
index 074ae72..cdd4cd1 100644
--- a/src/pyssg/__init__.py
+++ b/src/pyssg/__init__.py
@@ -1,4 +1,19 @@
from .pyssg import main
+import logging
+from logging import Logger, StreamHandler
+from .per_level_formatter import PerLevelFormatter
-__all__ = ['main']
+# since this is the root package, setup the logger here,
+# set DEBUG here for testing purposes, can't make it
+# dynamic yet (with a flag, for example)
+__LOG_LEVEL: int = logging.INFO
+log: Logger = logging.getLogger(__name__)
+log.setLevel(__LOG_LEVEL)
+ch: StreamHandler = StreamHandler()
+ch.setLevel(__LOG_LEVEL)
+ch.setFormatter(PerLevelFormatter())
+log.addHandler(ch)
+
+# not meant to be used as a package, so just give main
+__all__ = ['main'] \ No newline at end of file
diff --git a/src/pyssg/__main__.py b/src/pyssg/__main__.py
index 9ed4d74..5213889 100644
--- a/src/pyssg/__main__.py
+++ b/src/pyssg/__main__.py
@@ -1,5 +1,7 @@
from .pyssg import main
+# since this is not used as a package, rather it's used as a command line tool,
+# this is never called because pyssg:main is called directly when running pyssg
if __name__ == '__main__':
main()
diff --git a/src/pyssg/configuration.py b/src/pyssg/configuration.py
index dd6bfaa..a721dba 100644
--- a/src/pyssg/configuration.py
+++ b/src/pyssg/configuration.py
@@ -3,6 +3,10 @@ from importlib.metadata import version
from importlib.resources import path as rpath
from datetime import datetime, timezone
from configparser import ConfigParser
+import logging
+from logging import Logger
+
+log: Logger = logging.getLogger(__name__)
DEFAULT_CONFIG_PATH = '$XDG_CONFIG_HOME/pyssg/config.ini'
@@ -12,25 +16,31 @@ VERSION = version('pyssg')
def __check_well_formed_config(config: ConfigParser) -> None:
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():
+ log.debug('checking section "%s"', section)
if not config.has_section(section):
- print(f'config does not have section "{section}"')
+ log.error('config does not have section "%s"', section)
sys.exit(1)
for option in default_config.options(section):
+ log.debug('checking option "%s"', option)
if not config.has_option(section, option):
- print(f'config does not have option "{option}" in section "{section}"')
+ 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()
+ log.debug('reading config file "%s"', path)
config.read(path)
+ log.debug('checking that config file is well formed')
__check_well_formed_config(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)
diff --git a/src/pyssg/per_level_formatter.py b/src/pyssg/per_level_formatter.py
new file mode 100644
index 0000000..2010483
--- /dev/null
+++ b/src/pyssg/per_level_formatter.py
@@ -0,0 +1,28 @@
+import logging
+from logging import Formatter
+
+
+class PerLevelFormatter(logging.Formatter):
+ # colors for the terminal in ansi
+ yellow = "\x1b[33m"
+ red = "\x1b[31m"
+ bold_red = "\x1b[31;1m"
+ reset = "\x1b[0m"
+
+ DATE_FMT = '%Y-%m-%d %H:%M:%S'
+ COMMON_FMT = '[%(levelname)s] [%(module)s:%(funcName)s:%(lineno)d]: %(message)s'
+ FORMATS = {
+ logging.DEBUG: COMMON_FMT,
+ logging.INFO: '%(message)s',
+ logging.WARNING: f'{yellow}{COMMON_FMT}{reset}',
+ logging.ERROR: f'{red}{COMMON_FMT}{reset}',
+ logging.CRITICAL: f'{bold_red}{COMMON_FMT}{reset}'
+ }
+
+
+ def format(self, record: str) -> str:
+ fmt: str = self.FORMATS.get(record.levelno)
+ formatter: Formatter = logging.Formatter(
+ fmt=fmt, datefmt=self.DATE_FMT, style='%')
+
+ return formatter.format(record)
diff --git a/src/pyssg/pyssg.py b/src/pyssg/pyssg.py
index 9b82231..958b397 100644
--- a/src/pyssg/pyssg.py
+++ b/src/pyssg/pyssg.py
@@ -1,5 +1,7 @@
import os
import sys
+import logging
+from logging import Logger, StreamHandler
from importlib.resources import path as rpath
from typing import Union
from configparser import ConfigParser
@@ -10,43 +12,53 @@ from yafg import YafgExtension
from MarkdownHighlight.highlight import HighlightExtension
from markdown_checklist.extension import ChecklistExtension
+from .per_level_formatter import PerLevelFormatter
from .utils import create_dir, copy_file, sanity_check_path
from .arg_parser import get_parsed_arguments
from .configuration import get_parsed_config, DEFAULT_CONFIG_PATH, VERSION
from .database import Database
from .builder import Builder
+log: Logger = logging.getLogger(__name__)
+
def main() -> None:
args: dict[str, Union[str, bool]] = vars(get_parsed_arguments())
+
if not len(sys.argv) > 1:
- print(f'pyssg v{VERSION} - no arguments passed, --help for more')
+ log.info('pyssg v%s - no arguments passed, --help for more', VERSION)
sys.exit(0)
if args['version']:
- print(f'pyssg v{VERSION}')
+ log.info('pyssg v%s', VERSION)
sys.exit(0)
+ log.debug('checking config file path')
config_path: str = args['config'] if args['config'] else DEFAULT_CONFIG_PATH
config_path = os.path.normpath(os.path.expandvars(config_path))
sanity_check_path(config_path)
config_dir, _ = os.path.split(config_path)
+ log.debug('checked config file path, final config path "%s"', config_path)
if args['copy_default_config']:
+ log.info('copying default config file')
create_dir(config_dir)
with rpath('pyssg.plt', 'default.ini') as p:
copy_file(p, config_path)
sys.exit(0)
if not os.path.exists(config_path):
- print(f'''config file does't exist in path "{config_path}"; make sure
- the path is correct; use --copy-default-config to if you
- haven't already''')
+ log.error('config file does\'t exist in path "%s"; make sure'
+ ' the path is correct; use --copy-default-config if it\'s the'
+ ' first time if you haven\'t already', config_path)
sys.exit(1)
+ log.debug('parsing config file')
config: ConfigParser = get_parsed_config(config_path)
+ log.debug('parsed config file')
if args['init']:
+ log.info('initializing the directory structure and copying over templates')
create_dir(config.get('path', 'src'))
create_dir(os.path.join(config.get('path', 'dst'), 'tag'), True)
create_dir(config.get('path', 'plt'))
@@ -55,12 +67,15 @@ def main() -> None:
'tag.html',
'rss.xml',
'sitemap.xml')
+ log.debug('list of files to copy over: (%s)', ', '.join(files))
for f in files:
plt_file: str = os.path.join(config.get('path', 'plt'), f)
with rpath('pyssg.plt', f) as p:
copy_file(p, plt_file)
sys.exit(0)
+ # TODO: add logging to all of the build part, that includes the builder,
+ # database, discovery, page and parser
if args['build']:
# start the db
db: Database = Database(os.path.join(config.get('path', 'src'), '.files'))
@@ -73,7 +88,6 @@ def main() -> None:
trim_blocks=True,
lstrip_blocks=True)
-
# md extensions
exts: list = ['extra',
'meta',
diff --git a/src/pyssg/utils.py b/src/pyssg/utils.py
index 8e5d90e..2194fe1 100644
--- a/src/pyssg/utils.py
+++ b/src/pyssg/utils.py
@@ -1,6 +1,10 @@
import os
import sys
import shutil
+import logging
+from logging import Logger
+
+log: Logger = logging.getLogger(__name__)
def create_dir(path: str, p: bool=False) -> None:
@@ -9,20 +13,21 @@ def create_dir(path: str, p: bool=False) -> None:
os.makedirs(path)
else:
os.mkdir(path)
- print(f'created directory "{path}"')
+ log.info('created directory "%s"', path)
except FileExistsError:
- print(f'directory "{path}" already exists')
+ log.info('directory "%s" already exists, ignoring', path)
def copy_file(src: str, dst: str) -> None:
if not os.path.exists(dst):
shutil.copy(src, dst)
- print(f'copied file "{src}" to "{dst}"')
+ log.info('copied file "%s" to "%s"', src, dst)
else:
- print(f'"{dst}" already exists')
+ log.info('file "%s" already exists, ignoring', dst)
def sanity_check_path(path: str) -> None:
if '$' in path:
- print(f'"$" character found in path: "{path}"; could be due to non-existant env var.')
+ log.error('"$" character found in path "%s";'
+ ' could be due to non-existant env var.', path)
sys.exit(1)