Skip to content

Commit

Permalink
fix: look for files in a few places, regardless of path separators. #82
Browse files Browse the repository at this point in the history
  • Loading branch information
Ned Batchelder committed Feb 18, 2023
1 parent c030bf2 commit 530c9de
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 25 deletions.
9 changes: 9 additions & 0 deletions changelog.d/20230218_155917_nedbat_search_for_files.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Fixed
.....

- Settings specified as ``file:`` will now search in the changelog directory
and then the current directory for the file. The only exception is if the
first component is ``.`` or ``..``, then only the current directory is
considered. Fixes `issue 82`_.

.. _issue 82: https://github.com/nedbat/scriv/issues/82
10 changes: 5 additions & 5 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ Settings use the usual syntax, but with some extra features:
File Prefix
-----------

A ``file:`` prefix means the setting value is a file name, and the actual
setting value will be read from that file. If the file name has path
separators, it is relative to the current directory. If it doesn't have path
separators, then it is either in the fragment directory (changelog.d by
default), or one of the built-in provided templates.
A ``file:`` prefix means the setting is a file name or path, and the actual
setting value will be read from that file. The file name will be searched for
in three places: the fragment directory (changelog.d by default), the current
directory, or one of a few built-in templates. If the first path component is
``.`` or ``..``, then only the current directory is considered.

Scriv provides two built-in templates:

Expand Down
30 changes: 13 additions & 17 deletions src/scriv/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,32 +399,28 @@ def read_file_value(self, file_name: str) -> str:
"""
Find the value of a setting that has been specified as a file name.
"""
no_file = False
has_path = bool(re.search(r"[/\\]", file_name))
if has_path:
# It has path components: relative to current directory.
file_path = Path(".") / file_name
else:
# Plain file name: in fragdir, or built-in.
file_path = Path(self.fragment_directory) / file_name

if file_path.exists():
value = file_path.read_text()
elif has_path:
# With a path, it has to exist.
no_file = True
value = None
possibilities = []
if not re.match(r"\.\.?[/\\]", file_name):
possibilities.append(Path(self.fragment_directory) / file_name)
possibilities.append(Path(".") / file_name)

for file_path in possibilities:
if file_path.exists():
value = file_path.read_text()
break
else:
# No path, and doesn't exist: try it as a built-in.
try:
file_bytes = pkgutil.get_data("scriv", "templates/" + file_name)
except OSError:
no_file = True
pass
else:
assert file_bytes
value = file_bytes.decode("utf-8")

if no_file:
raise ScrivException(f"No such file: {file_path}")
if value is None:
raise ScrivException(f"No such file: {file_name}")

return value

Expand Down
30 changes: 27 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ def test_custom_template(changelog_d):
assert fmt == "Custom template."


def test_file_with_path(temp_dir, changelog_d):
# A file: spec with path components is relative to the current directory.
def test_file_with_dots(temp_dir, changelog_d):
# A file: spec with dot components is relative to the current directory.
(changelog_d / "start_here.j2").write_text("The wrong one")
(temp_dir / "start_here.j2").write_text("The right one")
fmt = Config(
Expand All @@ -159,6 +159,30 @@ def test_file_with_path(temp_dir, changelog_d):
assert fmt == "The right one"


def test_file_with_path_search_order(temp_dir, changelog_d):
# A file: spec with path components is relative to the changelog directory
# and then the current directory.
(changelog_d / "files").mkdir()
(changelog_d / "files" / "start_here.j2").write_text("The right one")
(temp_dir / "files").mkdir()
(temp_dir / "files" / "start_here.j2").write_text("The wrong one")
fmt = Config(
new_fragment_template="file: files/start_here.j2"
).new_fragment_template
assert fmt == "The right one"


def test_file_with_path_only_current_dir(temp_dir, changelog_d):
# A file: spec with path components is relative to the changelog directory
# and then the current directory.
(temp_dir / "files").mkdir()
(temp_dir / "files" / "start_here.j2").write_text("The right one")
fmt = Config(
new_fragment_template="file: files/start_here.j2"
).new_fragment_template
assert fmt == "The right one"


def test_missing_file_with_path(temp_dir, changelog_d):
# A file: spec with path components is relative to the current directory.
(changelog_d / "start_here.j2").write_text("The wrong one")
Expand All @@ -184,7 +208,7 @@ def test_no_such_template():
# be raised.
msg = (
r"Couldn't read 'new_fragment_template' setting: "
+ r"No such file: changelog\.d[/\\]foo\.j2"
+ r"No such file: foo\.j2"
)
with pytest.raises(ScrivException, match=msg):
config = Config(new_fragment_template="file: foo.j2")
Expand Down

0 comments on commit 530c9de

Please sign in to comment.