Skip to content

Commit

Permalink
Remove default field path of FluentKey
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew000 committed Oct 27, 2024
1 parent b50edc9 commit 1b34030
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 10 deletions.
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ pretty = true
show_absolute_path = true
show_error_codes = true
show_error_context = true
warn_redundant_casts = true
warn_unused_configs = true
warn_unused_ignores = true
disable_error_code = [
Expand Down
21 changes: 17 additions & 4 deletions src/ftl_extract/code_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def parse_file(
path: Path,
i18n_keys: str | Iterable[str],
ignore_attributes: Iterable[str],
default_ftl_file: Path,
) -> dict[str, FluentKey]:
"""
Second step: parse given .py file and find all i18n calls.
Expand All @@ -44,11 +45,18 @@ def parse_file(
:type i18n_keys: str | Sequence[str]
:param ignore_attributes: Ignore attributes, like `i18n.set_locale`.
:type ignore_attributes: Sequence[str]
:param default_ftl_file: Default name of FTL file.
:type default_ftl_file: Path
:return: Dict with `key` and `FluentKey`.
:rtype: dict[str, FluentKey]
"""
node = ast.parse(path.read_bytes())
matcher = I18nMatcher(code_path=path, func_names=i18n_keys, ignore_attributes=ignore_attributes)
matcher = I18nMatcher(
code_path=path,
default_ftl_file=default_ftl_file,
func_names=i18n_keys,
ignore_attributes=ignore_attributes,
)
matcher.visit(node)
return matcher.fluent_keys

Expand All @@ -60,7 +68,7 @@ def post_process_fluent_keys(fluent_keys: dict[str, FluentKey], default_ftl_file
:param fluent_keys: Dict with `key` and `FluentKey` that will be post-processed.
:type fluent_keys: dict[str, FluentKey]
:param default_ftl_file: Default name of FTL file.
:type default_ftl_file: str
:type default_ftl_file: Path
"""
for fluent_key in fluent_keys.values():
if not isinstance(fluent_key.path, Path):
Expand Down Expand Up @@ -119,15 +127,20 @@ def extract_fluent_keys(
:param ignore_attributes: Ignore attributes, like `i18n.set_locale`.
:type ignore_attributes: Sequence[str]
:param default_ftl_file: Default name of FTL file.
:type default_ftl_file: str
:type default_ftl_file: Path
:return: Dict with `key` and `FluentKey`.
:rtype: dict[str, FluentKey]
"""
fluent_keys: dict[str, FluentKey] = {}

for file in find_py_files(path):
keys = parse_file(path=file, i18n_keys=i18n_keys, ignore_attributes=ignore_attributes)
keys = parse_file(
path=file,
i18n_keys=i18n_keys,
ignore_attributes=ignore_attributes,
default_ftl_file=default_ftl_file,
)
post_process_fluent_keys(keys, default_ftl_file)
find_conflicts(fluent_keys, keys)
fluent_keys.update(keys)
Expand Down
9 changes: 8 additions & 1 deletion src/ftl_extract/matcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class FluentKey:
code_path: Path
key: str
translation: fluent_ast.EntryType
path: Path = field(default=Path("_default.ftl"))
path: Path
locale: str | None = field(default=None)
position: int | float = field(default=inf)

Expand All @@ -47,6 +47,7 @@ class I18nMatcher(ast.NodeVisitor):
def __init__(
self,
code_path: Path,
default_ftl_file: Path,
func_names: str | Iterable[str] = I18N_LITERAL,
ignore_attributes: str | Iterable[str] = IGNORE_ATTRIBUTES,
) -> None:
Expand All @@ -66,6 +67,7 @@ def __init__(
if isinstance(ignore_attributes, str)
else frozenset(ignore_attributes)
)
self.default_ftl_file = default_ftl_file
self.fluent_keys: dict[str, FluentKey] = {}

def visit_Call(self, node: ast.Call) -> None: # noqa: N802
Expand Down Expand Up @@ -96,6 +98,7 @@ def visit_Call(self, node: ast.Call) -> None: # noqa: N802
code_path=self.code_path,
key=key,
keywords=node.keywords,
default_ftl_file=self.default_ftl_file,
)

process_fluent_key(self.fluent_keys, fluent_key)
Expand All @@ -109,6 +112,7 @@ def visit_Call(self, node: ast.Call) -> None: # noqa: N802
code_path=self.code_path,
key="-".join(reversed(attrs)),
keywords=node.keywords,
default_ftl_file=self.default_ftl_file,
)
process_fluent_key(self.fluent_keys, fluent_key)
else:
Expand All @@ -122,6 +126,7 @@ def visit_Call(self, node: ast.Call) -> None: # noqa: N802
code_path=self.code_path,
key=cast(ast.Constant, node.args[0]).value,
keywords=node.keywords,
default_ftl_file=self.default_ftl_file,
)
process_fluent_key(self.fluent_keys, fluent_key)

Expand All @@ -132,6 +137,7 @@ def create_fluent_key(
code_path: Path,
key: str,
keywords: list[ast.keyword],
default_ftl_file: Path,
) -> FluentKey:
fluent_key = FluentKey(
code_path=code_path,
Expand All @@ -140,6 +146,7 @@ def create_fluent_key(
id=fluent_ast.Identifier(name=key),
value=fluent_ast.Pattern(elements=[fluent_ast.TextElement(value=key)]),
),
path=default_ftl_file,
)

for kw in keywords:
Expand Down
14 changes: 10 additions & 4 deletions tests/test_extract/test_cli_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from fluent.syntax import ast as fl_ast

from ftl_extract.cli import cli_extract
from ftl_extract.const import DEFAULT_FTL_FILE
from ftl_extract.ftl_extractor import extract
from ftl_extract.matcher import FluentKey, I18nMatcher

Expand Down Expand Up @@ -187,6 +188,7 @@ def test_comment_junk_elements_if_needed(setup_environment: tuple[Path, Path]) -

mock_junk_key = MagicMock(spec=FluentKey)
mock_junk_key.translation = MagicMock(spec=fl_ast.Junk)
mock_junk_key.path = MagicMock(spec=Path)
mock_serializer = MagicMock(spec=FluentSerializer)

with (
Expand Down Expand Up @@ -305,7 +307,7 @@ def test_keys_to_comment_and_add_on_different_kwargs(setup_environment: tuple[Pa

def test_i18n_matcher_skips_call_with_no_args(setup_environment: tuple[Path, Path]) -> None:
code_path, output_path = setup_environment
matcher = I18nMatcher(code_path)
matcher = I18nMatcher(code_path, default_ftl_file=DEFAULT_FTL_FILE)

node = ast.Call(func=ast.Attribute(value=ast.Name(id="i18n"), attr="get"), args=[], keywords=[])
matcher.visit_Call(node)
Expand All @@ -315,7 +317,7 @@ def test_i18n_matcher_skips_call_with_no_args(setup_environment: tuple[Path, Pat

def test_generic_visit_called_on_else_block(setup_environment: tuple[Path, Path]) -> None:
code_path, output_path = setup_environment
matcher = I18nMatcher(code_path)
matcher = I18nMatcher(code_path, default_ftl_file=DEFAULT_FTL_FILE)

node = ast.Call(
func=ast.Attribute(value=ast.Name(id="i18n"), attr="get"),
Expand All @@ -332,7 +334,11 @@ def test_generic_visit_called_when_attr_in_ignore_attributes(
setup_environment: tuple[Path, Path],
) -> None:
code_path, output_path = setup_environment
matcher = I18nMatcher(code_path, ignore_attributes={"ignore_this"})
matcher = I18nMatcher(
code_path,
default_ftl_file=DEFAULT_FTL_FILE,
ignore_attributes={"ignore_this"},
)

# Create a mock AST node for a function call with an attribute in ignore_attributes
node = ast.Call(
Expand All @@ -354,7 +360,7 @@ def test_generic_visit_called_when_attr_in_ignore_attributes(

def test_i18n_matcher_skips_call_with_no_args_in_elif(setup_environment: tuple[Path, Path]) -> None:
code_path, output_path = setup_environment
matcher = I18nMatcher(code_path)
matcher = I18nMatcher(code_path, default_ftl_file=DEFAULT_FTL_FILE)

node = ast.Call(func=ast.Name(id="i18n", ctx=ast.Load()), args=[], keywords=[])
matcher.visit_Call(node)
Expand Down
9 changes: 9 additions & 0 deletions tests/test_kwargs_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from fluent.syntax import ast

from ftl_extract.const import DEFAULT_FTL_FILE
from ftl_extract.matcher import FluentKey
from ftl_extract.process.kwargs_extractor import extract_kwargs

Expand All @@ -22,6 +23,7 @@ def extracts_variable_names_from_simple_variable() -> None:
],
),
),
path=DEFAULT_FTL_FILE,
),
)
assert kwargs == {"username"}
Expand Down Expand Up @@ -56,6 +58,7 @@ def extracts_variable_names_from_select_expression() -> None:
],
),
),
path=DEFAULT_FTL_FILE,
),
)
assert kwargs == {"gender"}
Expand All @@ -70,6 +73,7 @@ def returns_empty_set_for_messages_without_variables() -> None:
id=ast.Identifier("no_variables"),
value=ast.Pattern(elements=[ast.TextElement("Just a text message.")]),
),
path=DEFAULT_FTL_FILE,
),
)
assert kwargs == set()
Expand All @@ -81,6 +85,7 @@ def test_returns_empty_set_for_comment_translation() -> None:
code_path=Path("test.py"),
key="key-1",
translation=ast.Comment(content="This is a comment"),
path=DEFAULT_FTL_FILE,
),
)
assert kwargs == set()
Expand All @@ -95,6 +100,7 @@ def test_returns_empty_set_for_translation_without_value() -> None:
id=ast.Identifier("message_no_value"),
value=None, # Explicitly setting value to None
),
path=DEFAULT_FTL_FILE,
),
)
assert kwargs == set()
Expand All @@ -120,6 +126,7 @@ def test_extracts_variable_names_from_mixed_elements() -> None:
],
),
),
path=DEFAULT_FTL_FILE,
),
)
assert kwargs == {"username", "balance"}
Expand Down Expand Up @@ -158,6 +165,7 @@ def test_extracts_selector_variable_name_from_select_expression() -> None:
],
),
),
path=DEFAULT_FTL_FILE,
),
)
assert kwargs == {"user_status"}
Expand Down Expand Up @@ -292,6 +300,7 @@ def test_nested_extraction() -> None:
],
),
),
path=DEFAULT_FTL_FILE,
locale="en",
position=0,
),
Expand Down
10 changes: 10 additions & 0 deletions tests/test_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest
from fluent.syntax import FluentSerializer, ast

from ftl_extract.const import DEFAULT_FTL_FILE
from ftl_extract.matcher import FluentKey
from ftl_extract.process.serializer import generate_ftl

Expand All @@ -17,6 +18,7 @@ def single_fluent_key() -> list[FluentKey]:
id=ast.Identifier("greeting"),
value=ast.Pattern(elements=[ast.TextElement("Hello, world!")]),
),
path=DEFAULT_FTL_FILE,
),
]

Expand All @@ -31,6 +33,7 @@ def multiple_fluent_keys() -> list[FluentKey]:
id=ast.Identifier("greeting"),
value=ast.Pattern(elements=[ast.TextElement("Hello, world!")]),
),
path=DEFAULT_FTL_FILE,
),
FluentKey(
code_path=Path("test.py"),
Expand All @@ -39,6 +42,7 @@ def multiple_fluent_keys() -> list[FluentKey]:
id=ast.Identifier("farewell"),
value=ast.Pattern(elements=[ast.TextElement("Goodbye, world!")]),
),
path=DEFAULT_FTL_FILE,
),
]

Expand Down Expand Up @@ -95,6 +99,7 @@ def test_generate_ftl_includes_leave_as_is_elements() -> None:
id=ast.Identifier("test_message"),
value=ast.Pattern(elements=[ast.TextElement("Test message content")]),
),
path=DEFAULT_FTL_FILE,
),
],
serializer=FluentSerializer(with_junk=True),
Expand All @@ -103,16 +108,19 @@ def test_generate_ftl_includes_leave_as_is_elements() -> None:
code_path=Path(),
key="",
translation=ast.Comment(content="This is a comment"),
path=DEFAULT_FTL_FILE,
),
FluentKey(
code_path=Path(),
key="",
translation=ast.GroupComment(content="This is a group comment"),
path=DEFAULT_FTL_FILE,
),
FluentKey(
code_path=Path(),
key="",
translation=ast.ResourceComment(content="This is a resource comment"),
path=DEFAULT_FTL_FILE,
),
FluentKey(
code_path=Path(),
Expand All @@ -121,13 +129,15 @@ def test_generate_ftl_includes_leave_as_is_elements() -> None:
id=ast.Identifier("test_term"),
value=ast.Pattern(elements=[ast.TextElement("Test term content")]),
),
path=DEFAULT_FTL_FILE,
),
FluentKey(
code_path=Path(),
key="",
translation=ast.Junk(
content="This is junk content",
),
path=DEFAULT_FTL_FILE,
),
],
)
Expand Down

0 comments on commit 1b34030

Please sign in to comment.