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:
2025-03-21 10:35:56 -05:00
parent e75d5c48d2
commit 8c75947088
7 changed files with 91 additions and 67 deletions

View File

@@ -23,13 +23,11 @@ def test_graphviz_is_rendered():
with tempfile.TemporaryDirectory() as tmpdir:
src_dir = os.path.join(HERE, 'instance')
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)
with open(os.path.join(tmpdir, 'test-graphviz.html'), 'r') as graphviz_output:
data = graphviz_output.read()
assert 'data:image/png;base64' in data
os.chdir(HERE)
def test_invalid_graphviz_is_not_rendered():
@@ -37,12 +35,10 @@ def test_invalid_graphviz_is_not_rendered():
with tempfile.TemporaryDirectory() as tmpdir:
src_dir = os.path.join(HERE, 'instance')
ssg = StaticSiteGenerator(src_dir, tmpdir)
os.chdir(os.path.join(src_dir, 'broken'))
with pytest.raises(ValueError):
ssg.build_file_in_destination(os.path.join(HERE, 'instance', 'broken'), '', 'test-invalid-graphviz.md',
tmpdir, True)
os.chdir(HERE)
def test_figures_are_rendered():
@@ -50,7 +46,6 @@ def test_figures_are_rendered():
with tempfile.TemporaryDirectory() as tmpdir:
src_dir = os.path.join(HERE, 'instance')
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)
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" />'
'<span></span></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():
@@ -70,7 +64,6 @@ def test_og_image():
with tempfile.TemporaryDirectory() as tmpdir:
src_dir = os.path.join(HERE, 'instance')
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)
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:
src_dir = os.path.join(HERE, 'instance')
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
# the generated URLs for content in the pages/ root

View File

@@ -18,6 +18,10 @@
"level": "DEBUG",
"handlers": ["console"]
},
"incorporealcms.markdown": {
"level": "DEBUG",
"handlers": ["console"]
},
"incorporealcms.ssg": {
"level": "DEBUG",
"handlers": ["console"]

View File

@@ -8,33 +8,38 @@ from unittest.mock import patch
import pytest
from incorporealcms import init_instance
from incorporealcms.markdown import (generate_parent_navs, handle_markdown_file_path,
instance_resource_path_to_request_path, parse_md,
request_path_to_breadcrumb_display)
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():
"""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():
"""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():
"""Test that real pages have navs to the root, their parent, and themselves."""
assert generate_parent_navs('subdir/page.md') == [('example.org', '/'), ('subdir', '/subdir/'),
('Page', '/subdir/page')]
assert generate_parent_navs('subdir/page.md', PAGES_DIR) == [('example.org', '/'), ('subdir', '/subdir/'),
('Page', '/subdir/page')]
def test_generate_page_navs_subdir_with_title_parsing_real_page():
"""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', '/'),
('SUB!', '/subdir-with-title/'),
('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():
"""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', '/'),
('/no-index-dir/', '/no-index-dir/'),
('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():
"""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">'\
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">'\
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>'\
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>'\
in handle_markdown_file_path('index.md')
in handle_markdown_file_path('index.md', PAGES_DIR)
def test_render_with_style_overrides():
"""Test that the default can be changed."""
with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'dark'):
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">'\
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>'\
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>'\
in handle_markdown_file_path('index.md')
in handle_markdown_file_path('index.md', PAGES_DIR)
def test_render_with_default_style_override():
@@ -81,23 +86,21 @@ def test_render_with_default_style_override():
'warm': '/static/css/warm.css'}):
with patch('incorporealcms.Config.DEFAULT_PAGE_STYLE', 'warm'):
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">'\
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">'\
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>'\
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>'\
in handle_markdown_file_path('index.md')
in handle_markdown_file_path('index.md', PAGES_DIR)
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."""
os.chdir(os.path.join(HERE, 'instance/', 'broken/'))
with pytest.raises(NotImplementedError):
handle_markdown_file_path('redirect.md')
os.chdir(os.path.join(HERE, 'instance/', 'pages/'))
handle_markdown_file_path('redirect.md', os.path.join(INSTANCE_DIR, 'broken'))
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():
"""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_title == 'title for the page - example.org'
def test_parse_md_metadata_forced_no_title():
"""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_title == 'example.org'
def test_parse_md_metadata_no_title_so_path():
"""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_title == '/subdir/ - example.org'
@@ -153,10 +156,10 @@ def test_parse_md_metadata_no_title_so_path():
def test_parse_md_no_file():
"""Test the direct results of parsing a markdown file."""
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():
"""Test the direct results of parsing a markdown file."""
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)

View File

@@ -3,12 +3,15 @@
SPDX-FileCopyrightText: © 2025 Brian S. Stephan <bss@incorporeal.org>
SPDX-License-Identifier: GPL-3.0-or-later
"""
import logging
import os
import tempfile
import incorporealcms.ssg as ssg
from incorporealcms import init_instance
logger = logging.getLogger(__file__)
HERE = os.path.dirname(os.path.abspath(__file__))
instance_dir = os.path.join(HERE, 'instance')
@@ -20,7 +23,6 @@ def test_file_copy():
with tempfile.TemporaryDirectory() as tmpdir:
src_dir = os.path.join(HERE, 'instance')
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,
True)
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:
src_dir = os.path.join(HERE, 'instance')
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,
False)
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:
src_dir = os.path.join(HERE, 'instance')
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)
# 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)
@@ -57,7 +57,6 @@ def test_file_copy_symlink_of_markdown_also_has_html_symlink():
with tempfile.TemporaryDirectory() as tmpdir:
src_dir = os.path.join(HERE, 'instance')
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,
True)
# 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:
src_dir = os.path.join(HERE, 'instance')
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)
assert os.path.exists(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:
src_dir = os.path.join(HERE, 'instance')
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)
# 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)