-
-
Notifications
You must be signed in to change notification settings - Fork 33.6k
gh-130453: pygettext: Allow specifying multiple keywords with the same function name #131380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
67c5cf7
63516d1
4426daf
9f8e9b1
ec02b07
4aa5151
2dcab79
a354d19
ab02d21
bac8b8d
a3d5d56
51b48f0
4daad1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # SOME DESCRIPTIVE TITLE. | ||
| # Copyright (C) YEAR ORGANIZATION | ||
| # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | ||
| # | ||
| msgid "" | ||
| msgstr "" | ||
| "Project-Id-Version: PACKAGE VERSION\n" | ||
| "POT-Creation-Date: 2000-01-01 00:00+0000\n" | ||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||
| "Language-Team: LANGUAGE <LL@li.org>\n" | ||
| "MIME-Version: 1.0\n" | ||
| "Content-Type: text/plain; charset=UTF-8\n" | ||
| "Content-Transfer-Encoding: 8bit\n" | ||
| "Generated-By: pygettext.py 1.5\n" | ||
|
|
||
|
|
||
| #: multiple_keywords.py:3 | ||
| msgid "bar" | ||
| msgstr "" | ||
|
|
||
| #: multiple_keywords.py:5 | ||
| msgctxt "baz" | ||
| msgid "qux" | ||
| msgstr "" | ||
|
|
||
| #: multiple_keywords.py:9 | ||
| msgctxt "corge" | ||
| msgid "grault" | ||
| msgstr "" | ||
|
|
||
| #: multiple_keywords.py:11 | ||
| msgctxt "xyzzy" | ||
| msgid "foo" | ||
| msgid_plural "foos" | ||
| msgstr[0] "" | ||
| msgstr[1] "" | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| from gettext import gettext as foo | ||
|
|
||
| foo('bar') | ||
|
|
||
| foo('baz', 'qux') | ||
|
|
||
| # The 't' specifier is not supported, so the following | ||
| # call is extracted as pgettext instead of ngettext. | ||
| foo('corge', 'grault', 1) | ||
|
|
||
| foo('xyzzy', 'foo', 'foos', 1) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Allow passing multiple keyword arguments with the same function name in | ||
| :program:`pygettext`. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -282,15 +282,15 @@ def getFilesForName(name): | |||||
| # Key is the function name, value is a dictionary mapping argument positions to the | ||||||
| # type of the argument. The type is one of 'msgid', 'msgid_plural', or 'msgctxt'. | ||||||
| DEFAULTKEYWORDS = { | ||||||
| '_': {0: 'msgid'}, | ||||||
| 'gettext': {0: 'msgid'}, | ||||||
| 'ngettext': {0: 'msgid', 1: 'msgid_plural'}, | ||||||
| 'pgettext': {0: 'msgctxt', 1: 'msgid'}, | ||||||
| 'npgettext': {0: 'msgctxt', 1: 'msgid', 2: 'msgid_plural'}, | ||||||
| 'dgettext': {1: 'msgid'}, | ||||||
| 'dngettext': {1: 'msgid', 2: 'msgid_plural'}, | ||||||
| 'dpgettext': {1: 'msgctxt', 2: 'msgid'}, | ||||||
| 'dnpgettext': {1: 'msgctxt', 2: 'msgid', 3: 'msgid_plural'}, | ||||||
| '_': {'msgid': 0}, | ||||||
| 'gettext': {'msgid': 0}, | ||||||
| 'ngettext': {'msgid': 0, 'msgid_plural': 1}, | ||||||
| 'pgettext': {'msgctxt': 0, 'msgid': 1}, | ||||||
| 'npgettext': {'msgctxt': 0, 'msgid': 1, 'msgid_plural': 2}, | ||||||
| 'dgettext': {'msgid': 1}, | ||||||
| 'dngettext': {'msgid': 1, 'msgid_plural': 2}, | ||||||
| 'dpgettext': {'msgctxt': 1, 'msgid': 2}, | ||||||
| 'dnpgettext': {'msgctxt': 1, 'msgid': 2, 'msgid_plural': 3}, | ||||||
serhiy-storchaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| } | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -327,7 +327,7 @@ def parse_spec(spec): | |||||
| parts = spec.strip().split(':', 1) | ||||||
| if len(parts) == 1: | ||||||
| name = parts[0] | ||||||
| return name, {0: 'msgid'} | ||||||
| return name, {'msgid': 0} | ||||||
|
|
||||||
| name, args = parts | ||||||
| if not args: | ||||||
|
|
@@ -373,7 +373,23 @@ def parse_spec(spec): | |||||
| raise ValueError(f'Invalid keyword spec {spec!r}: ' | ||||||
| 'msgctxt cannot appear without msgid') | ||||||
|
|
||||||
| return name, {v: k for k, v in result.items()} | ||||||
| return name, result | ||||||
|
|
||||||
|
|
||||||
| def process_keywords(keywords, *, no_default_keywords): | ||||||
| custom_keywords_list = [parse_spec(spec) for spec in keywords] | ||||||
| custom_keywords = {} | ||||||
serhiy-storchaka marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| for name, spec in custom_keywords_list: | ||||||
| if name not in custom_keywords: | ||||||
| custom_keywords[name] = [] | ||||||
| custom_keywords[name].append(spec) | ||||||
|
|
||||||
| if no_default_keywords: | ||||||
| return custom_keywords | ||||||
|
|
||||||
| default_keywords = {name: [spec] for name, spec in DEFAULTKEYWORDS.items()} | ||||||
| # custom keywords override default keywords | ||||||
| return default_keywords | custom_keywords | ||||||
|
|
||||||
|
|
||||||
| @dataclass(frozen=True) | ||||||
|
|
@@ -459,32 +475,41 @@ def _extract_docstring(self, node): | |||||
|
|
||||||
| def _extract_message(self, node): | ||||||
| func_name = self._get_func_name(node) | ||||||
| spec = self.options.keywords.get(func_name) | ||||||
| if spec is None: | ||||||
| return | ||||||
| errors = [] | ||||||
| for spec in self.options.keywords.get(func_name, []): | ||||||
| err = self._extract_message_with_spec(node, spec) | ||||||
| if err is None: | ||||||
| break | ||||||
| errors.append(err) | ||||||
| else: | ||||||
| for err in errors: | ||||||
| print(err, file=sys.stderr) | ||||||
|
|
||||||
| def _extract_message_with_spec(self, node, spec): | ||||||
| """Extract a gettext call with the given spec. | ||||||
|
|
||||||
| max_index = max(spec) | ||||||
| Return `None` if the gettext call was successfully extracted, | ||||||
tomasr8 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
| otherwise return an error message. | ||||||
| """ | ||||||
| max_index = max(spec.values()) | ||||||
| has_var_positional = any(isinstance(arg, ast.Starred) for | ||||||
| arg in node.args[:max_index+1]) | ||||||
| if has_var_positional: | ||||||
| print(f'*** {self.filename}:{node.lineno}: Variable positional ' | ||||||
| f'arguments are not allowed in gettext calls', file=sys.stderr) | ||||||
| return | ||||||
| return (f'*** {self.filename}:{node.lineno}: Variable positional ' | ||||||
| f'arguments are not allowed in gettext calls') | ||||||
|
||||||
| f'arguments are not allowed in gettext calls') | |
| f'arguments are not allowed in gettext calls') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error will still be printed multiple times.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With --keyword=_:1 --keyword=_:2, multiple errors will be printed for _(): "Expected at least 1 positional argument(s)...", "Expected at least 2 positional argument(s)...".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is misleading, since only one of arguments needs to be a string.