remove os.chdir usage, rely on absolute and relative paths more
os.chdir was getting confusing and hurting the log output, and potentially the cause of a couple bugs left to fix, so this removes it, but it means we need to pass around the pages/ absolute path into the markdown parser, because it relies on knowing both the absolute path now (to open files), and also the path relative to the pages dir in order to know where to stop reading parent files/how to generate proper URL-like references to other files. probably this should be refactored at some point to inherit the pages/ path from the SSG somehow, rather than passing it through a bunch of methods, but this seems to work for now fixes #22 Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
This commit is contained in:
parent
e75d5c48d2
commit
8c75947088
@ -24,6 +24,9 @@ logger = logging.getLogger(__name__)
|
|||||||
def generate_feed(feed_type: str, instance_dir: str, dest_dir: str) -> None:
|
def generate_feed(feed_type: str, instance_dir: str, dest_dir: str) -> None:
|
||||||
"""Generate the Atom or RSS feed as requested.
|
"""Generate the Atom or RSS feed as requested.
|
||||||
|
|
||||||
|
Feed entries should be symlinks to .md files in the pages/ directory, so that they
|
||||||
|
are also linkable and can be browsed outside of the feed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
feed_type: 'atom' or 'rss' feed
|
feed_type: 'atom' or 'rss' feed
|
||||||
instance_dir: the directory for the instance, containing both the feed dir and pages
|
instance_dir: the directory for the instance, containing both the feed dir and pages
|
||||||
@ -37,16 +40,18 @@ def generate_feed(feed_type: str, instance_dir: str, dest_dir: str) -> None:
|
|||||||
fg.link(href=f'https://{Config.DOMAIN_NAME}', rel='alternate')
|
fg.link(href=f'https://{Config.DOMAIN_NAME}', rel='alternate')
|
||||||
fg.subtitle(f"Blog posts and other interesting materials from {Config.TITLE_SUFFIX}")
|
fg.subtitle(f"Blog posts and other interesting materials from {Config.TITLE_SUFFIX}")
|
||||||
|
|
||||||
|
# feed symlinks should all be within the core content subdirectory
|
||||||
|
pages_dir = os.path.join(instance_dir, 'pages')
|
||||||
|
|
||||||
# get recent feeds
|
# get recent feeds
|
||||||
feed_path = os.path.join(instance_dir, 'feed')
|
feed_path = os.path.join(instance_dir, 'feed')
|
||||||
feed_entry_paths = [os.path.join(dirpath, filename) for dirpath, _, filenames in os.walk(feed_path)
|
feed_entry_paths = [os.path.join(dirpath, filename) for dirpath, _, filenames in os.walk(feed_path)
|
||||||
for filename in filenames if os.path.islink(os.path.join(dirpath, filename))]
|
for filename in filenames if os.path.islink(os.path.join(dirpath, filename))]
|
||||||
for feed_entry_path in sorted(feed_entry_paths):
|
for feed_entry_path in sorted(feed_entry_paths):
|
||||||
# get the actual file to parse it
|
# get the actual file to parse it
|
||||||
os.chdir(os.path.abspath(os.path.join(instance_dir, 'pages')))
|
resolved_path = os.path.relpath(os.path.realpath(feed_entry_path), pages_dir)
|
||||||
resolved_path = os.path.relpath(os.path.realpath(feed_entry_path), os.path.join(instance_dir, 'pages'))
|
|
||||||
try:
|
try:
|
||||||
content, md, page_name, page_title, mtime = parse_md(resolved_path)
|
content, md, page_name, page_title, mtime = parse_md(os.path.join(pages_dir, resolved_path), pages_dir)
|
||||||
link = f'https://{Config.DOMAIN_NAME}{instance_resource_path_to_request_path(resolved_path)}'
|
link = f'https://{Config.DOMAIN_NAME}{instance_resource_path_to_request_path(resolved_path)}'
|
||||||
except (OSError, ValueError, TypeError):
|
except (OSError, ValueError, TypeError):
|
||||||
logger.exception("error loading/rendering markdown!")
|
logger.exception("error loading/rendering markdown!")
|
||||||
|
@ -44,18 +44,21 @@ def instance_resource_path_to_request_path(path):
|
|||||||
return '/' + re.sub(r'.md$', '', re.sub(r'index.md$', '', path))
|
return '/' + re.sub(r'.md$', '', re.sub(r'index.md$', '', path))
|
||||||
|
|
||||||
|
|
||||||
def parse_md(path: str):
|
def parse_md(path: str, pages_root: str):
|
||||||
"""Given a file to parse, return file content and other derived data along with the md object.
|
"""Given a file to parse, return file content and other derived data along with the md object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: the path to the file to render
|
path: the path to the file to render
|
||||||
|
pages_root: the absolute path to the pages/ dir, which the path should be within. necessary for
|
||||||
|
proper resolution of resolving parent pages (which needs to know when to stop)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
logger.debug("opening path '%s'", path)
|
absolute_path = os.path.join(pages_root, path)
|
||||||
with open(path, 'r') as input_file:
|
logger.debug("opening path '%s'", absolute_path)
|
||||||
|
with open(absolute_path, 'r') as input_file:
|
||||||
mtime = datetime.datetime.fromtimestamp(os.path.getmtime(input_file.name), tz=datetime.timezone.utc)
|
mtime = datetime.datetime.fromtimestamp(os.path.getmtime(input_file.name), tz=datetime.timezone.utc)
|
||||||
entry = input_file.read()
|
entry = input_file.read()
|
||||||
logger.debug("path '%s' read", path)
|
logger.debug("path '%s' read", absolute_path)
|
||||||
md = init_md()
|
md = init_md()
|
||||||
content = Markup(md.convert(entry)) # nosec B704
|
content = Markup(md.convert(entry)) # nosec B704
|
||||||
except (OSError, FileNotFoundError):
|
except (OSError, FileNotFoundError):
|
||||||
@ -67,17 +70,25 @@ def parse_md(path: str):
|
|||||||
|
|
||||||
logger.debug("file metadata: %s", md.Meta)
|
logger.debug("file metadata: %s", md.Meta)
|
||||||
|
|
||||||
page_name = get_meta_str(md, 'title') if md.Meta.get('title') else instance_resource_path_to_request_path(path)
|
rel_path = os.path.relpath(path, pages_root)
|
||||||
|
page_name = get_meta_str(md, 'title') if md.Meta.get('title') else instance_resource_path_to_request_path(rel_path)
|
||||||
page_title = f'{page_name} - {Config.TITLE_SUFFIX}' if page_name else Config.TITLE_SUFFIX
|
page_title = f'{page_name} - {Config.TITLE_SUFFIX}' if page_name else Config.TITLE_SUFFIX
|
||||||
logger.debug("title (potentially derived): %s", page_title)
|
logger.debug("title (potentially derived): %s", page_title)
|
||||||
|
|
||||||
return content, md, page_name, page_title, mtime
|
return content, md, page_name, page_title, mtime
|
||||||
|
|
||||||
|
|
||||||
def handle_markdown_file_path(path: str) -> str:
|
def handle_markdown_file_path(path: str, pages_root: str) -> str:
|
||||||
"""Given a location on disk, attempt to open it and render the markdown within."""
|
"""Given a location on disk, attempt to open it and render the markdown within.
|
||||||
content, md, page_name, page_title, mtime = parse_md(path)
|
|
||||||
parent_navs = generate_parent_navs(path)
|
Args:
|
||||||
|
path: the path to the file to parse and produce metadata for
|
||||||
|
pages_root: the absolute path to the pages/ dir, which the path should be within. necessary for
|
||||||
|
proper resolution of resolving parent pages (which needs to know when to stop)
|
||||||
|
"""
|
||||||
|
content, md, page_name, page_title, mtime = parse_md(path, pages_root)
|
||||||
|
relative_path = os.path.relpath(path, pages_root)
|
||||||
|
parent_navs = generate_parent_navs(relative_path, pages_root)
|
||||||
extra_footer = get_meta_str(md, 'footer') if md.Meta.get('footer') else None
|
extra_footer = get_meta_str(md, 'footer') if md.Meta.get('footer') else None
|
||||||
template_name = get_meta_str(md, 'template') if md.Meta.get('template') else 'base.html'
|
template_name = get_meta_str(md, 'template') if md.Meta.get('template') else 'base.html'
|
||||||
|
|
||||||
@ -92,14 +103,21 @@ def handle_markdown_file_path(path: str) -> str:
|
|||||||
description=get_meta_str(md, 'description'),
|
description=get_meta_str(md, 'description'),
|
||||||
image=Config.BASE_HOST + get_meta_str(md, 'image'),
|
image=Config.BASE_HOST + get_meta_str(md, 'image'),
|
||||||
content=content,
|
content=content,
|
||||||
base_url=Config.BASE_HOST + instance_resource_path_to_request_path(path),
|
base_url=Config.BASE_HOST + instance_resource_path_to_request_path(relative_path),
|
||||||
navs=parent_navs,
|
navs=parent_navs,
|
||||||
mtime=mtime.strftime('%Y-%m-%d %H:%M:%S %Z'),
|
mtime=mtime.strftime('%Y-%m-%d %H:%M:%S %Z'),
|
||||||
extra_footer=extra_footer)
|
extra_footer=extra_footer)
|
||||||
|
|
||||||
|
|
||||||
def generate_parent_navs(path):
|
def generate_parent_navs(path, pages_root: str):
|
||||||
"""Create a series of paths/links to navigate up from the given resource path."""
|
"""Create a series of paths/links to navigate up from the given resource path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: the path to parse and generate parent metadata nav links for
|
||||||
|
pages_root: the absolute path to the pages/ dir, which the path should be within. path is relative,
|
||||||
|
but opening parents requires the full path
|
||||||
|
"""
|
||||||
|
logger.debug("path to generate navs for: %s", path)
|
||||||
if path == 'index.md':
|
if path == 'index.md':
|
||||||
# bail and return the domain name as a terminal case
|
# bail and return the domain name as a terminal case
|
||||||
return [(Config.DOMAIN_NAME, '/')]
|
return [(Config.DOMAIN_NAME, '/')]
|
||||||
@ -124,14 +142,14 @@ def generate_parent_navs(path):
|
|||||||
|
|
||||||
# read the resource
|
# read the resource
|
||||||
try:
|
try:
|
||||||
with open(path, 'r') as entry_file:
|
with open(os.path.join(pages_root, path), 'r') as entry_file:
|
||||||
entry = entry_file.read()
|
entry = entry_file.read()
|
||||||
_ = Markup(md.convert(entry)) # nosec B704
|
_ = Markup(md.convert(entry)) # nosec B704
|
||||||
page_name = (" ".join(md.Meta.get('title')) if md.Meta.get('title')
|
page_name = (" ".join(md.Meta.get('title')) if md.Meta.get('title')
|
||||||
else request_path_to_breadcrumb_display(request_path))
|
else request_path_to_breadcrumb_display(request_path))
|
||||||
return generate_parent_navs(parent_resource_path) + [(page_name, request_path)]
|
return generate_parent_navs(parent_resource_path, pages_root) + [(page_name, request_path)]
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return generate_parent_navs(parent_resource_path) + [(request_path, request_path)]
|
return generate_parent_navs(parent_resource_path, pages_root) + [(request_path, request_path)]
|
||||||
|
|
||||||
|
|
||||||
def request_path_to_breadcrumb_display(path):
|
def request_path_to_breadcrumb_display(path):
|
||||||
|
@ -86,7 +86,6 @@ class StaticSiteGenerator(object):
|
|||||||
convert_markdown: whether or not to convert Markdown files (or simply copy them)
|
convert_markdown: whether or not to convert Markdown files (or simply copy them)
|
||||||
"""
|
"""
|
||||||
cprint(f"copying files from '{source_dir}' to '{dest_dir}'", 'green')
|
cprint(f"copying files from '{source_dir}' to '{dest_dir}'", 'green')
|
||||||
os.chdir(source_dir)
|
|
||||||
for base_dir, subdirs, files in os.walk(source_dir):
|
for base_dir, subdirs, files in os.walk(source_dir):
|
||||||
logger.debug("starting to build against %s || %s || %s", base_dir, subdirs, files)
|
logger.debug("starting to build against %s || %s || %s", base_dir, subdirs, files)
|
||||||
# remove the absolute path of the directory from the base_dir
|
# remove the absolute path of the directory from the base_dir
|
||||||
@ -113,9 +112,12 @@ class StaticSiteGenerator(object):
|
|||||||
dest_dir: the output directory to place the subdir in
|
dest_dir: the output directory to place the subdir in
|
||||||
"""
|
"""
|
||||||
dst = os.path.join(dest_dir, base_dir, subdir)
|
dst = os.path.join(dest_dir, base_dir, subdir)
|
||||||
if os.path.islink(os.path.join(base_dir, subdir)):
|
absolute_dir = os.path.join(source_dir, base_dir, subdir)
|
||||||
|
logger.debug("checking if %s is a symlink or not", absolute_dir)
|
||||||
|
if os.path.islink(absolute_dir):
|
||||||
|
logger.debug("symlink; raw destination is %s", os.path.realpath(absolute_dir))
|
||||||
# keep the link relative to the output directory
|
# keep the link relative to the output directory
|
||||||
src = self.symlink_to_relative_dest(source_dir, os.path.join(base_dir, subdir))
|
src = self.symlink_to_relative_dest(source_dir, absolute_dir)
|
||||||
print(f"creating directory symlink '{dst}' -> '{src}'")
|
print(f"creating directory symlink '{dst}' -> '{src}'")
|
||||||
os.symlink(src, dst, target_is_directory=True)
|
os.symlink(src, dst, target_is_directory=True)
|
||||||
else:
|
else:
|
||||||
@ -137,9 +139,12 @@ class StaticSiteGenerator(object):
|
|||||||
dest_dir: the output directory to place the subdir in
|
dest_dir: the output directory to place the subdir in
|
||||||
"""
|
"""
|
||||||
dst = os.path.join(dest_dir, base_dir, file_)
|
dst = os.path.join(dest_dir, base_dir, file_)
|
||||||
if os.path.islink(os.path.join(base_dir, file_)):
|
absolute_file = os.path.join(source_dir, base_dir, file_)
|
||||||
|
logger.debug("checking if %s is a symlink or not", absolute_file)
|
||||||
|
if os.path.islink(absolute_file):
|
||||||
|
logger.debug("symlink; raw destination is %s", os.path.realpath(absolute_file))
|
||||||
# keep the link relative to the output directory
|
# keep the link relative to the output directory
|
||||||
src = self.symlink_to_relative_dest(source_dir, os.path.join(base_dir, file_))
|
src = self.symlink_to_relative_dest(source_dir, absolute_file)
|
||||||
print(f"creating symlink '{dst}' -> '{src}'")
|
print(f"creating symlink '{dst}' -> '{src}'")
|
||||||
os.symlink(src, dst, target_is_directory=False)
|
os.symlink(src, dst, target_is_directory=False)
|
||||||
if src.endswith('.md') and convert_markdown:
|
if src.endswith('.md') and convert_markdown:
|
||||||
@ -151,7 +156,7 @@ class StaticSiteGenerator(object):
|
|||||||
os.symlink(second_src, second_dst, target_is_directory=False)
|
os.symlink(second_src, second_dst, target_is_directory=False)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
src = os.path.join(base_dir, file_)
|
src = os.path.join(source_dir, base_dir, file_)
|
||||||
print(f"copying file '{src}' -> '{dst}'")
|
print(f"copying file '{src}' -> '{dst}'")
|
||||||
shutil.copy2(src, dst)
|
shutil.copy2(src, dst)
|
||||||
|
|
||||||
@ -160,7 +165,7 @@ class StaticSiteGenerator(object):
|
|||||||
rendered_file = dst.removesuffix('.md') + '.html'
|
rendered_file = dst.removesuffix('.md') + '.html'
|
||||||
print(f"rendering file '{src}' -> '{rendered_file}'")
|
print(f"rendering file '{src}' -> '{rendered_file}'")
|
||||||
try:
|
try:
|
||||||
content = handle_markdown_file_path(src)
|
content = handle_markdown_file_path(src, source_dir)
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
# perhaps this isn't a markdown file at all for some reason; we
|
# perhaps this isn't a markdown file at all for some reason; we
|
||||||
# copied it above so stick with tha
|
# copied it above so stick with tha
|
||||||
|
@ -23,13 +23,11 @@ def test_graphviz_is_rendered():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(src_dir, 'pages'))
|
|
||||||
|
|
||||||
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'test-graphviz.md', tmpdir, True)
|
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'test-graphviz.md', tmpdir, True)
|
||||||
with open(os.path.join(tmpdir, 'test-graphviz.html'), 'r') as graphviz_output:
|
with open(os.path.join(tmpdir, 'test-graphviz.html'), 'r') as graphviz_output:
|
||||||
data = graphviz_output.read()
|
data = graphviz_output.read()
|
||||||
assert 'data:image/png;base64' in data
|
assert 'data:image/png;base64' in data
|
||||||
os.chdir(HERE)
|
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_graphviz_is_not_rendered():
|
def test_invalid_graphviz_is_not_rendered():
|
||||||
@ -37,12 +35,10 @@ def test_invalid_graphviz_is_not_rendered():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(src_dir, 'broken'))
|
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'broken'), '', 'test-invalid-graphviz.md',
|
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'broken'), '', 'test-invalid-graphviz.md',
|
||||||
tmpdir, True)
|
tmpdir, True)
|
||||||
os.chdir(HERE)
|
|
||||||
|
|
||||||
|
|
||||||
def test_figures_are_rendered():
|
def test_figures_are_rendered():
|
||||||
@ -50,7 +46,6 @@ def test_figures_are_rendered():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(src_dir, 'pages'))
|
|
||||||
|
|
||||||
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'figures.md', tmpdir, True)
|
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'figures.md', tmpdir, True)
|
||||||
with open(os.path.join(tmpdir, 'figures.html'), 'r') as graphviz_output:
|
with open(os.path.join(tmpdir, 'figures.html'), 'r') as graphviz_output:
|
||||||
@ -62,7 +57,6 @@ def test_figures_are_rendered():
|
|||||||
assert ('<figure class="left"><img alt="fancy logo" src="bss-square-no-bg.png" />'
|
assert ('<figure class="left"><img alt="fancy logo" src="bss-square-no-bg.png" />'
|
||||||
'<span></span></figure>') in data
|
'<span></span></figure>') in data
|
||||||
assert '<figure><img alt="just a logo" src="bss-square-no-bg.png" /></figure>' in data
|
assert '<figure><img alt="just a logo" src="bss-square-no-bg.png" /></figure>' in data
|
||||||
os.chdir(HERE)
|
|
||||||
|
|
||||||
|
|
||||||
def test_og_image():
|
def test_og_image():
|
||||||
@ -70,7 +64,6 @@ def test_og_image():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(src_dir, 'pages'))
|
|
||||||
|
|
||||||
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'more-metadata.md', tmpdir, True)
|
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'pages'), '', 'more-metadata.md', tmpdir, True)
|
||||||
with open(os.path.join(tmpdir, 'more-metadata.html'), 'r') as markdown_output:
|
with open(os.path.join(tmpdir, 'more-metadata.html'), 'r') as markdown_output:
|
||||||
@ -83,7 +76,6 @@ def test_og_url():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
ssg = StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(src_dir, 'pages'))
|
|
||||||
|
|
||||||
# testing a whole build run because of bugs in how I handle pathing adding a "./" in
|
# testing a whole build run because of bugs in how I handle pathing adding a "./" in
|
||||||
# the generated URLs for content in the pages/ root
|
# the generated URLs for content in the pages/ root
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
"handlers": ["console"]
|
"handlers": ["console"]
|
||||||
},
|
},
|
||||||
|
"incorporealcms.markdown": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"handlers": ["console"]
|
||||||
|
},
|
||||||
"incorporealcms.ssg": {
|
"incorporealcms.ssg": {
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
"handlers": ["console"]
|
"handlers": ["console"]
|
||||||
|
@ -8,33 +8,38 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from incorporealcms import init_instance
|
||||||
from incorporealcms.markdown import (generate_parent_navs, handle_markdown_file_path,
|
from incorporealcms.markdown import (generate_parent_navs, handle_markdown_file_path,
|
||||||
instance_resource_path_to_request_path, parse_md,
|
instance_resource_path_to_request_path, parse_md,
|
||||||
request_path_to_breadcrumb_display)
|
request_path_to_breadcrumb_display)
|
||||||
|
|
||||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
os.chdir(os.path.join(HERE, 'instance/', 'pages/'))
|
INSTANCE_DIR = os.path.join(HERE, 'instance')
|
||||||
|
PAGES_DIR = os.path.join(INSTANCE_DIR, 'pages/')
|
||||||
|
|
||||||
|
# initialize in order to configure debug logging
|
||||||
|
init_instance(INSTANCE_DIR)
|
||||||
|
|
||||||
|
|
||||||
def test_generate_page_navs_index():
|
def test_generate_page_navs_index():
|
||||||
"""Test that the index page has navs to the root (itself)."""
|
"""Test that the index page has navs to the root (itself)."""
|
||||||
assert generate_parent_navs('index.md') == [('example.org', '/')]
|
assert generate_parent_navs('index.md', PAGES_DIR) == [('example.org', '/')]
|
||||||
|
|
||||||
|
|
||||||
def test_generate_page_navs_subdir_index():
|
def test_generate_page_navs_subdir_index():
|
||||||
"""Test that dir pages have navs to the root and themselves."""
|
"""Test that dir pages have navs to the root and themselves."""
|
||||||
assert generate_parent_navs('subdir/index.md') == [('example.org', '/'), ('subdir', '/subdir/')]
|
assert generate_parent_navs('subdir/index.md', PAGES_DIR) == [('example.org', '/'), ('subdir', '/subdir/')]
|
||||||
|
|
||||||
|
|
||||||
def test_generate_page_navs_subdir_real_page():
|
def test_generate_page_navs_subdir_real_page():
|
||||||
"""Test that real pages have navs to the root, their parent, and themselves."""
|
"""Test that real pages have navs to the root, their parent, and themselves."""
|
||||||
assert generate_parent_navs('subdir/page.md') == [('example.org', '/'), ('subdir', '/subdir/'),
|
assert generate_parent_navs('subdir/page.md', PAGES_DIR) == [('example.org', '/'), ('subdir', '/subdir/'),
|
||||||
('Page', '/subdir/page')]
|
('Page', '/subdir/page')]
|
||||||
|
|
||||||
|
|
||||||
def test_generate_page_navs_subdir_with_title_parsing_real_page():
|
def test_generate_page_navs_subdir_with_title_parsing_real_page():
|
||||||
"""Test that title metadata is used in the nav text."""
|
"""Test that title metadata is used in the nav text."""
|
||||||
assert generate_parent_navs('subdir-with-title/page.md') == [
|
assert generate_parent_navs('subdir-with-title/page.md', PAGES_DIR) == [
|
||||||
('example.org', '/'),
|
('example.org', '/'),
|
||||||
('SUB!', '/subdir-with-title/'),
|
('SUB!', '/subdir-with-title/'),
|
||||||
('page', '/subdir-with-title/page')
|
('page', '/subdir-with-title/page')
|
||||||
@ -43,7 +48,7 @@ def test_generate_page_navs_subdir_with_title_parsing_real_page():
|
|||||||
|
|
||||||
def test_generate_page_navs_subdir_with_no_index():
|
def test_generate_page_navs_subdir_with_no_index():
|
||||||
"""Test that breadcrumbs still generate even if a subdir doesn't have an index.md."""
|
"""Test that breadcrumbs still generate even if a subdir doesn't have an index.md."""
|
||||||
assert generate_parent_navs('no-index-dir/page.md') == [
|
assert generate_parent_navs('no-index-dir/page.md', PAGES_DIR) == [
|
||||||
('example.org', '/'),
|
('example.org', '/'),
|
||||||
('/no-index-dir/', '/no-index-dir/'),
|
('/no-index-dir/', '/no-index-dir/'),
|
||||||
('page', '/no-index-dir/page')
|
('page', '/no-index-dir/page')
|
||||||
@ -53,26 +58,26 @@ def test_generate_page_navs_subdir_with_no_index():
|
|||||||
def test_page_includes_themes_with_default():
|
def test_page_includes_themes_with_default():
|
||||||
"""Test that a request contains the configured themes and sets the default as appropriate."""
|
"""Test that a request contains the configured themes and sets the default as appropriate."""
|
||||||
assert '<link rel="stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
|
assert '<link rel="stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<link rel="alternate stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
|
assert '<link rel="alternate stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
|
assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
|
assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
|
|
||||||
|
|
||||||
def test_render_with_style_overrides():
|
def test_render_with_style_overrides():
|
||||||
"""Test that the default can be changed."""
|
"""Test that the default can be changed."""
|
||||||
with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'dark'):
|
with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'dark'):
|
||||||
assert '<link rel="stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
|
assert '<link rel="stylesheet" type="text/css" title="dark" href="/static/css/dark.css">'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
|
assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
|
assert '<a href="" onclick="setStyle(\'light\'); return false;">[light]</a>'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
|
assert '<a href="" onclick="setStyle(\'dark\'); return false;">[dark]</a>'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
|
|
||||||
|
|
||||||
def test_render_with_default_style_override():
|
def test_render_with_default_style_override():
|
||||||
@ -81,23 +86,21 @@ def test_render_with_default_style_override():
|
|||||||
'warm': '/static/css/warm.css'}):
|
'warm': '/static/css/warm.css'}):
|
||||||
with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'warm'):
|
with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'warm'):
|
||||||
assert '<link rel="stylesheet" type="text/css" title="warm" href="/static/css/warm.css">'\
|
assert '<link rel="stylesheet" type="text/css" title="warm" href="/static/css/warm.css">'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<link rel="alternate stylesheet" type="text/css" title="cool" href="/static/css/cool.css">'\
|
assert '<link rel="alternate stylesheet" type="text/css" title="cool" href="/static/css/cool.css">'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
|
assert '<link rel="alternate stylesheet" type="text/css" title="light" href="/static/css/light.css">'\
|
||||||
not in handle_markdown_file_path('index.md')
|
not in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<a href="" onclick="setStyle(\'warm\'); return false;">[warm]</a>'\
|
assert '<a href="" onclick="setStyle(\'warm\'); return false;">[warm]</a>'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
assert '<a href="" onclick="setStyle(\'cool\'); return false;">[cool]</a>'\
|
assert '<a href="" onclick="setStyle(\'cool\'); return false;">[cool]</a>'\
|
||||||
in handle_markdown_file_path('index.md')
|
in handle_markdown_file_path('index.md', PAGES_DIR)
|
||||||
|
|
||||||
|
|
||||||
def test_redirects_error_unsupported():
|
def test_redirects_error_unsupported():
|
||||||
"""Test that we throw a warning about the barely-used Markdown redirect tag, which we can't support via SSG."""
|
"""Test that we throw a warning about the barely-used Markdown redirect tag, which we can't support via SSG."""
|
||||||
os.chdir(os.path.join(HERE, 'instance/', 'broken/'))
|
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
handle_markdown_file_path('redirect.md')
|
handle_markdown_file_path('redirect.md', os.path.join(INSTANCE_DIR, 'broken'))
|
||||||
os.chdir(os.path.join(HERE, 'instance/', 'pages/'))
|
|
||||||
|
|
||||||
|
|
||||||
def test_instance_resource_path_to_request_path_on_index():
|
def test_instance_resource_path_to_request_path_on_index():
|
||||||
@ -131,21 +134,21 @@ def test_request_path_to_breadcrumb_display_patterns():
|
|||||||
|
|
||||||
def test_parse_md_metadata():
|
def test_parse_md_metadata():
|
||||||
"""Test the direct results of parsing a markdown file."""
|
"""Test the direct results of parsing a markdown file."""
|
||||||
content, md, page_name, page_title, mtime = parse_md('more-metadata.md')
|
content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'more-metadata.md'), PAGES_DIR)
|
||||||
assert page_name == 'title for the page'
|
assert page_name == 'title for the page'
|
||||||
assert page_title == 'title for the page - example.org'
|
assert page_title == 'title for the page - example.org'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_md_metadata_forced_no_title():
|
def test_parse_md_metadata_forced_no_title():
|
||||||
"""Test the direct results of parsing a markdown file."""
|
"""Test the direct results of parsing a markdown file."""
|
||||||
content, md, page_name, page_title, mtime = parse_md('forced-no-title.md')
|
content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'forced-no-title.md'), PAGES_DIR)
|
||||||
assert page_name == ''
|
assert page_name == ''
|
||||||
assert page_title == 'example.org'
|
assert page_title == 'example.org'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_md_metadata_no_title_so_path():
|
def test_parse_md_metadata_no_title_so_path():
|
||||||
"""Test the direct results of parsing a markdown file."""
|
"""Test the direct results of parsing a markdown file."""
|
||||||
content, md, page_name, page_title, mtime = parse_md('subdir/index.md')
|
content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'subdir/index.md'), PAGES_DIR)
|
||||||
assert page_name == '/subdir/'
|
assert page_name == '/subdir/'
|
||||||
assert page_title == '/subdir/ - example.org'
|
assert page_title == '/subdir/ - example.org'
|
||||||
|
|
||||||
@ -153,10 +156,10 @@ def test_parse_md_metadata_no_title_so_path():
|
|||||||
def test_parse_md_no_file():
|
def test_parse_md_no_file():
|
||||||
"""Test the direct results of parsing a markdown file."""
|
"""Test the direct results of parsing a markdown file."""
|
||||||
with pytest.raises(FileNotFoundError):
|
with pytest.raises(FileNotFoundError):
|
||||||
content, md, page_name, page_title, mtime = parse_md('nope.md')
|
content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'nope.md'), PAGES_DIR)
|
||||||
|
|
||||||
|
|
||||||
def test_parse_md_bad_file():
|
def test_parse_md_bad_file():
|
||||||
"""Test the direct results of parsing a markdown file."""
|
"""Test the direct results of parsing a markdown file."""
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
content, md, page_name, page_title, mtime = parse_md('actually-a-png.md')
|
content, md, page_name, page_title, mtime = parse_md(os.path.join(PAGES_DIR, 'actually-a-png.md'), PAGES_DIR)
|
||||||
|
@ -3,12 +3,15 @@
|
|||||||
SPDX-FileCopyrightText: © 2025 Brian S. Stephan <bss@incorporeal.org>
|
SPDX-FileCopyrightText: © 2025 Brian S. Stephan <bss@incorporeal.org>
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
"""
|
"""
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import incorporealcms.ssg as ssg
|
import incorporealcms.ssg as ssg
|
||||||
from incorporealcms import init_instance
|
from incorporealcms import init_instance
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
|
||||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
instance_dir = os.path.join(HERE, 'instance')
|
instance_dir = os.path.join(HERE, 'instance')
|
||||||
@ -20,7 +23,6 @@ def test_file_copy():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(instance_dir, 'pages'))
|
|
||||||
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
|
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
|
||||||
True)
|
True)
|
||||||
assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
|
assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
|
||||||
@ -32,7 +34,6 @@ def test_file_copy_no_markdown():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(instance_dir, 'pages'))
|
|
||||||
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
|
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'no-title.md', tmpdir,
|
||||||
False)
|
False)
|
||||||
assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
|
assert os.path.exists(os.path.join(tmpdir, 'no-title.md'))
|
||||||
@ -44,7 +45,6 @@ def test_file_copy_symlink():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(instance_dir, 'pages'))
|
|
||||||
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-foo.txt', tmpdir)
|
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-foo.txt', tmpdir)
|
||||||
# need to copy the destination for os.path.exists to be happy with this
|
# need to copy the destination for os.path.exists to be happy with this
|
||||||
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'foo.txt', tmpdir)
|
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'foo.txt', tmpdir)
|
||||||
@ -57,7 +57,6 @@ def test_file_copy_symlink_of_markdown_also_has_html_symlink():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(instance_dir, 'pages'))
|
|
||||||
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-no-title.md', tmpdir,
|
generator.build_file_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-no-title.md', tmpdir,
|
||||||
True)
|
True)
|
||||||
# need to copy the destination for os.path.exists to be happy with this
|
# need to copy the destination for os.path.exists to be happy with this
|
||||||
@ -74,7 +73,6 @@ def test_dir_copy():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(instance_dir, 'pages'))
|
|
||||||
generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'media', tmpdir)
|
generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'media', tmpdir)
|
||||||
assert os.path.exists(os.path.join(tmpdir, 'media'))
|
assert os.path.exists(os.path.join(tmpdir, 'media'))
|
||||||
assert os.path.isdir(os.path.join(tmpdir, 'media'))
|
assert os.path.isdir(os.path.join(tmpdir, 'media'))
|
||||||
@ -85,7 +83,6 @@ def test_dir_copy_symlink():
|
|||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
src_dir = os.path.join(HERE, 'instance')
|
src_dir = os.path.join(HERE, 'instance')
|
||||||
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
generator = ssg.StaticSiteGenerator(src_dir, tmpdir)
|
||||||
os.chdir(os.path.join(instance_dir, 'pages'))
|
|
||||||
generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-subdir', tmpdir)
|
generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'symlink-to-subdir', tmpdir)
|
||||||
# need to copy the destination for os.path.exists to be happy with this
|
# need to copy the destination for os.path.exists to be happy with this
|
||||||
generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'subdir', tmpdir)
|
generator.build_subdir_in_destination(os.path.join(instance_dir, 'pages'), '', 'subdir', tmpdir)
|
||||||
|
Loading…
Reference in New Issue
Block a user