From 11a2ca070caa1d6273a9371f7eb8e09fd9eac9fc Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Sat, 26 Sep 2026 16:20:31 +0100 Subject: [PATCH] fix: accept Path filenames in OrgEnv Convert environment filenames to strings so matching Path and string inputs work together while genuinely different filenames remain rejected. Fixes #58 --- src/orgparse/node.py | 11 +++++----- src/orgparse/tests/test_load.py | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 src/orgparse/tests/test_load.py diff --git a/src/orgparse/node.py b/src/orgparse/node.py index d46d915..e4e85b0 100644 --- a/src/orgparse/node.py +++ b/src/orgparse/node.py @@ -3,6 +3,7 @@ import itertools import re from collections.abc import Iterable, Iterator, Sequence +from pathlib import Path from typing import Any, cast from .date import ( @@ -348,7 +349,7 @@ def __init__( self, todos: Sequence[str] | None = None, dones: Sequence[str] | None = None, - filename: str = '', + filename: str | Path = '', ) -> None: if dones is None: dones = ['DONE'] @@ -357,7 +358,7 @@ def __init__( self._todos = list(todos) self._dones = list(dones) self._todo_not_specified_in_comment = True - self._filename = filename + self._filename = str(filename) self._nodes: list[OrgBaseNode] = [] @property @@ -429,10 +430,10 @@ def all_todo_keys(self): @property def filename(self) -> str: """ - Return a path to the source file or similar information. + Return the source filename as a string. - If the org objects are not loaded from a file, this value - will be a string of the form ````. + A :class:`pathlib.Path` passed to ``OrgEnv`` is converted to a string. + Documents loaded without a filename use a placeholder such as ````. """ return self._filename diff --git a/src/orgparse/tests/test_load.py b/src/orgparse/tests/test_load.py new file mode 100644 index 0000000..bc18b04 --- /dev/null +++ b/src/orgparse/tests/test_load.py @@ -0,0 +1,38 @@ +"""Regression tests for loading documents with an explicit environment.""" + +from pathlib import Path + +import pytest + +from .. import OrgEnv, load + + +@pytest.mark.parametrize('path_type', [str, Path]) +@pytest.mark.parametrize('filename_type', [str, Path]) +def test_load_with_env( + *, tmp_path: Path, path_type: type[str] | type[Path], filename_type: type[str] | type[Path] +) -> None: + """Accept matching Path/string filenames and preserve the environment's custom TODO states.""" + path = tmp_path / 'notes.org' + path.write_text('* NEXT Task\n', encoding='utf-8') + env = OrgEnv(filename=filename_type(path), todos=['NEXT']) + + root = load(path_type(path), env=env) + + assert root.env is env + assert root.env.filename == str(path) + [node] = root.children + assert node.env is env + assert node.todo == 'NEXT' + assert node.heading == 'Task' + + +@pytest.mark.parametrize('filename_type', [str, Path]) +def test_load_with_mismatched_env(*, tmp_path: Path, filename_type: type[str] | type[Path]) -> None: + """Reject different source filenames, even when the basename matches.""" + path = tmp_path / 'notes.org' + path.write_text('* Task\n', encoding='utf-8') + env = OrgEnv(filename=filename_type(tmp_path / 'other' / 'notes.org')) + + with pytest.raises(ValueError, match='If env is specified, filename must match'): + load(path, env=env)