Skip to content

Commit

Permalink
Added a more advanced method to locate maindir
Browse files Browse the repository at this point in the history
Added a more advanced method to locate maindir and filename for usage
with the python scripts.
  • Loading branch information
hakonhagland committed Mar 15, 2024
1 parent f339ed9 commit 586726c
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 2 deletions.
5 changes: 3 additions & 2 deletions scripts/python/src/fodt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ClickOptions():
'--filename',
envvar='FODT_FILENAME',
required=True,
help='Name of the FODT file to extract from.'
help='Name of the FODT file to extract from. Used in combination with the --maindir option. This can be an absolute path or a relative path. If the filename is an absolute path, the --maindir option is ignored and the filename is used as is. If the filename is a relative path, and not found by concatenating maindir and filename it is searched for relative to the current working directory. If found, maindir is derived from the filename by searching its parent directories for a file main.fodt.'
)(func)
keyword_dir = lambda func: click.option(
'--keyword-dir',
Expand All @@ -27,7 +27,7 @@ def decorator(func):
required=required,
default=default,
type=str,
help='Directory to save generated files.'
help='Directory where the main.fodt file is located. Often used in combination with the --filename option. Defaults to ../../parts (this default is based on that it is likely the user will run the script from the scripts/python directory) The environment variable FODT_MAIN_DIR can also be used to provide this value. If the filename is an absolute path, this option is ignored and maindir is derived from the filename by searching its parent directories for a file main.fodt. If the filename is a relative path, and not found by concatenating maindir and filename it is searched for relative to the current working directory. If found, maindir is derived from the filename by searching its parent directories for a file main.fodt.'
)(func)
return decorator

Expand All @@ -38,6 +38,7 @@ class Directories():
keywords = "keywords"
meta = "meta"
meta_sections = "sections"
parts = "parts"
styles = "styles"
chapters = "chapters"
sections = "sections"
Expand Down
45 changes: 45 additions & 0 deletions scripts/python/src/fodt/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,27 @@ def create_backup_document(filename) -> None:
backup_file = backupdir / filename.name
return backup_file

@staticmethod
def derive_maindir_from_filename(filename: str) -> Path:
filename = Path(filename)
assert filename.is_absolute()
# Search parent directories for main.fodt in a directory called "parts"
while True:
# Check if we have reached the root directory
# filename.parent == filename is True if filename is the root directory
if filename.parent == filename:
raise FileNotFoundError(f"Could not derive maindir from filename: "
f"Could not find '{FileNames.main_document}' in a directory "
f"called '{Directories.parts}' by searching the parent "
f"directories of filename."
)
return filename.parent
if filename.parent.name == Directories.parts:
if (filename.parent / FileNames.main_document).exists():
return filename.parent
filename = filename.parent
# This should never be reached

@staticmethod
def get_keyword_dir(keyword_dir: str) -> str:
if keyword_dir is None:
Expand Down Expand Up @@ -75,6 +96,30 @@ def keyword_fodt_file_path(
def keywords_inverse_map(keyw_list: list[str]) -> dict[str, int]:
return {keyw_list[i]: i + 1 for i in range(len(keyw_list))}


@staticmethod
def locate_maindir_and_filename(
maindir: str,
filename: str
) -> tuple[Path, Path]:
filename = Path(filename)
maindir = Path(maindir) # maindir can be absolute or relative
# If filename is an absolute path, ignore maindir
if filename.is_absolute():
assert filename.exists()
maindir = Helpers.derive_maindir_from_filename(filename)
return maindir, Path(filename)
else:
# Try to find filename by concatenating maindir and filename
filename = maindir / filename
if filename.exists():
return maindir, filename
# If not found, search for filename relative to the current working directory
filename = Path.cwd() / filename
if filename.exists():
maindir = Helpers.derive_maindir_from_filename(filename)
return maindir, filename

@staticmethod
def read_keyword_order(outputdir: Path, chapter: int, section: int) -> list[str]:
file = Helpers.keyword_file(outputdir, chapter, section)
Expand Down
2 changes: 2 additions & 0 deletions scripts/python/src/fodt/remove_span_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import click

from fodt.constants import ClickOptions
from fodt.helpers import Helpers
from fodt.xml_helpers import XMLHelper

class RemoveEmptyLinesHandler(xml.sax.handler.ContentHandler):
Expand Down Expand Up @@ -346,4 +347,5 @@ def remove_version_span_tags(
) -> None:
"""Remove version span tags from all .fodt subdocuments."""
logging.basicConfig(level=logging.INFO)
maindir, filename = Helpers.locate_maindir_and_filename(maindir, filename)
RemoveSpanTags(maindir, filename, max_files).remove_span_tags()
88 changes: 88 additions & 0 deletions scripts/python/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import os
from pathlib import Path

import pytest
from fodt.constants import Directories, FileNames
from fodt.helpers import Helpers

class TestLocateMainDir:
def test_locate_with_absolute_path_exists(self, tmp_path: Path) -> None:
maindir = tmp_path / Directories.parts
maindir.mkdir()
mainfile = maindir / FileNames.main_document
mainfile.touch()
filename_dir = maindir / Directories.chapters
filename_dir.mkdir()
filename = filename_dir / "1.fodt"
filename.touch()
result_maindir, result_filename = Helpers.locate_maindir_and_filename(
str(maindir), str(filename)
)
assert result_maindir == maindir
assert result_filename == filename

def test_locate_with_absolute_path_exists_no_main(self, tmp_path: Path) -> None:
maindir = tmp_path / Directories.parts
maindir.mkdir()
mainfile = maindir / FileNames.main_document
# mainfile.touch() # Do not create the main file
filename_dir = maindir / Directories.chapters
filename_dir.mkdir()
filename = filename_dir / "1.fodt"
filename.touch()
with pytest.raises(FileNotFoundError) as excinfo:
Helpers.locate_maindir_and_filename(
str(maindir), str(filename)
)
assert (f"Could not find '{FileNames.main_document}' in a directory "
f"called '{Directories.parts}'" in str(excinfo.value))

def test_locate_with_relative_path_in_maindir_exists(self, tmp_path: Path) -> None:
maindir = tmp_path / Directories.parts
maindir.mkdir()
mainfile = maindir / FileNames.main_document
mainfile.touch() # Ensure the main document exists
# Change directory to maindir
os.chdir(str(maindir))
filename_dir = Path(Directories.appendices)
filename_dir.mkdir()
filename = "A.fodt"
filename_path = filename_dir / filename
filename_path.touch() # Create the file within maindir
filename_abs_path = maindir / filename_path
result_maindir, result_filename = Helpers.locate_maindir_and_filename(
str(maindir), str(filename_path)
)
assert result_maindir == maindir
assert result_filename == filename_abs_path

def test_locate_with_relative_path_not_in_maindir_but_in_cwd(
self, tmp_path: Path
):
cwd = tmp_path / "cwd"
cwd.mkdir()
os.chdir(str(cwd))
filename = "1.fodt"
filename_path = cwd / filename
filename_path.touch() # Create the file in CWD
maindir = tmp_path # Some dummy path that is not the maindir
with pytest.raises(FileNotFoundError) as excinfo:
Helpers.locate_maindir_and_filename(
str(maindir), str(filename_path)
)
assert excinfo.match(
f"Could not find '{FileNames.main_document}' in a directory "
f"called '{Directories.parts}' by searching the parent "
f"directories of filename."
)

def test_locate_with_absolute_path_not_exists(self, tmp_path: Path):
maindir = tmp_path / Directories.parts
maindir.mkdir()
filename = tmp_path / "nonexistent.fodt"
# Do not create the file, simulating a non-existent file scenario

with pytest.raises(AssertionError):
Helpers.locate_maindir_and_filename(
str(maindir), str(filename)
)

0 comments on commit 586726c

Please sign in to comment.