Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Babel Changelog
===============

Version 2.19.0 (Unreleased)
----------------------------

Features
~~~~~~~~

* Messages: Add ``ignore_hidden`` parameter to ``extract_from_dir()`` to skip
files and directories whose names start with a dot by @2025sl93041-gkb in :gh:`471`

Version 2.18.0
--------------

Expand Down
9 changes: 8 additions & 1 deletion babel/messages/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def extract_from_dir(
callback: Callable[[str, str, dict[str, Any]], object] | None = None,
strip_comment_tags: bool = False,
directory_filter: Callable[[str], bool] | None = None,
ignore_hidden: bool = False,
) -> Generator[_FileExtractionResult, None, None]:
"""Extract messages from any source files found in the given directory.

Expand Down Expand Up @@ -228,6 +229,8 @@ def extract_from_dir(
:param directory_filter: a callback to determine whether a directory should
be recursed into. Receives the full directory path;
should return True if the directory is valid.
:param ignore_hidden: if True, skip files and directories whose names start
with a dot (e.g. ``.git``, ``.hidden.py``).
:see: `pathmatch`
"""
if dirname is None:
Expand All @@ -246,11 +249,15 @@ def extract_from_dir(

for root, dirnames, filenames in os.walk(dirname):
dirnames[:] = [
subdir for subdir in dirnames if directory_filter(os.path.join(root, subdir))
subdir for subdir in dirnames
if directory_filter(os.path.join(root, subdir))
and (not ignore_hidden or not subdir.startswith('.'))
]
dirnames.sort()
filenames.sort()
for filename in filenames:
if ignore_hidden and filename.startswith('.'):
continue
filepath = os.path.join(root, filename).replace(os.sep, '/')

yield from check_and_call_extract_file(
Expand Down
32 changes: 32 additions & 0 deletions tests/messages/test_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,35 @@ def test_issue_1195_2():
'NOTE: This should still be considered, even if',
'the text is far away',
]


def test_extract_from_dir_ignore_hidden(tmp_path):
(tmp_path / "visible.py").write_text("_('visible')\n")
(tmp_path / ".hidden.py").write_text("_('hidden_file')\n")
hidden_dir = tmp_path / ".hiddendir"
hidden_dir.mkdir()
(hidden_dir / "inside.py").write_text("_('inside_hidden_dir')\n")

# ignore_hidden=False (default): hidden file extracted; hidden dir skipped
# by default directory_filter which already ignores dot dirs
results_default = list(extract.extract_from_dir(tmp_path))
msgids_default = [r[2] for r in results_default]
assert 'visible' in msgids_default
assert 'hidden_file' in msgids_default

# ignore_hidden=True: hidden files AND hidden dirs skipped
results_no_hidden = list(extract.extract_from_dir(tmp_path, ignore_hidden=True))
msgids_no_hidden = [r[2] for r in results_no_hidden]
assert 'visible' in msgids_no_hidden
assert 'hidden_file' not in msgids_no_hidden

# ignore_hidden=True also overrides a permissive directory_filter
results_override = list(extract.extract_from_dir(
tmp_path,
directory_filter=lambda p: True,
ignore_hidden=True,
))
msgids_override = [r[2] for r in results_override]
assert 'visible' in msgids_override
assert 'hidden_file' not in msgids_override
assert 'inside_hidden_dir' not in msgids_override