diff --git a/babel/messages/extract.py b/babel/messages/extract.py index f0584d460..73abbb485 100644 --- a/babel/messages/extract.py +++ b/babel/messages/extract.py @@ -126,6 +126,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, + follow_links: bool = False, ) -> Generator[_FileExtractionResult, None, None]: """Extract messages from any source files found in the given directory. @@ -194,6 +195,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 follow_links: Whether symbolic links should be followed in OS's that + support them. By default they are not followed. :see: `pathmatch` """ if dirname is None: @@ -204,7 +207,7 @@ def extract_from_dir( directory_filter = default_directory_filter absname = os.path.abspath(dirname) - for root, dirnames, filenames in os.walk(absname): + for root, dirnames, filenames in os.walk(absname, followlinks=follow_links): dirnames[:] = [ subdir for subdir in dirnames if directory_filter(os.path.join(root, subdir)) diff --git a/babel/messages/frontend.py b/babel/messages/frontend.py index be1a82e05..163668223 100644 --- a/babel/messages/frontend.py +++ b/babel/messages/frontend.py @@ -338,10 +338,12 @@ class ExtractMessages(CommandMixin): 'header comment for the catalog'), ('last-translator=', None, 'set the name and email of the last translator in output'), + ('follow-links', 'l', + 'follow symbolic links when traversing directories'), ] boolean_options = [ 'no-default-keywords', 'no-location', 'omit-header', 'no-wrap', - 'sort-output', 'sort-by-file', 'strip-comments', + 'sort-output', 'sort-by-file', 'strip-comments', 'follow-links', ] as_args = 'input-paths' multiple_value_options = ( @@ -381,6 +383,7 @@ def initialize_options(self): self.version = None self.add_comments = None self.strip_comments = False + self.follow_links = False self.include_lineno = True self.ignore_dirs = None self.header_comment = None @@ -509,6 +512,7 @@ def run(self): callback=callback, strip_comment_tags=self.strip_comments, directory_filter=self.directory_filter, + follow_links=self.follow_links, ) for filename, lineno, message, comments, context in extracted: if os.path.isfile(path): diff --git a/docs/cmdline.rst b/docs/cmdline.rst index e1328fe8f..7c95e4861 100644 --- a/docs/cmdline.rst +++ b/docs/cmdline.rst @@ -129,6 +129,8 @@ a collection of source files:: Patterns for directories to ignore when scanning for messages. Separate multiple patterns with spaces (default ".* ._") + -f, --follow-links + follow symbolic links when traversing directories --header-comment=HEADER_COMMENT header comment for the catalog diff --git a/tests/messages/test_frontend.py b/tests/messages/test_frontend.py index 581ad4a8c..5517f2e60 100644 --- a/tests/messages/test_frontend.py +++ b/tests/messages/test_frontend.py @@ -1682,6 +1682,13 @@ def test_extract_cli_knows_dash_s(): assert cmdinst.strip_comments +def test_extract_cli_knows_follow_links(): + # This tests the follow-links command line argument + cmdinst = configure_cli_command("extract --follow-links -o foo babel") + assert isinstance(cmdinst, ExtractMessages) + assert cmdinst.follow_links + + def test_extract_cli_knows_dash_dash_last_dash_translator(): cmdinst = configure_cli_command('extract --last-translator "FULL NAME EMAIL@ADDRESS" -o foo babel') assert isinstance(cmdinst, ExtractMessages)