Skip to content

Commit

Permalink
Add default-ftl-file argument to set default ftl file name (#8)
Browse files Browse the repository at this point in the history
* Add `default-ftl-file`
* Remove default from field `path` of `FluentKey`
  • Loading branch information
andrew000 authored Oct 28, 2024
1 parent 4d807bf commit 4b3cbde
Show file tree
Hide file tree
Showing 17 changed files with 242 additions and 129 deletions.
78 changes: 47 additions & 31 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ build-backend = "hatchling.build"
[tool.pytest.ini_options]
testpaths = "tests"

[tool.mypy]
packages = ["src/ftl_extract"]
exclude = [
"\\.?venv",
"\\.idea",
"\\.tests?",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
Expand All @@ -88,48 +80,72 @@ src = ["src", "tests"]
target-version = "py39"
line-length = 100
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"__pycache__",
"*.pyi",
"venv",
".venv",
"tests/.data_for_testing",
"tests/.files",
"dist",
"build",
]

[tool.ruff.lint]
select = ["ALL"]
ignore = [
"A003",
"ANN002", "ANN003", "ANN101", "ANN102", "ANN401",
"COM812",
"C901",
"D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107", "D203", "D205", "D212",
"DTZ003",
"ERA001",
"F841",
"FA100", "FA102",
"FBT001", "FBT002",
"FIX002",
"INP001", "ISC001",
"I001",
"PLR0911", "PLR0912", "PLR0913", "PLR0915", "PLR5501",
"PLW0120",
"RUF",
"S101", "S311",
"TD002", "TD003"
"RUF001",
"S101",
"TD002", "TD003",
]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"

[tool.mypy]
python_version = "3.9"
packages = ["src/ftl_extract"]
plugins = []
allow_redefinition = true
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_untyped_calls = true
disallow_untyped_defs = true
extra_checks = true
follow_imports = "skip"
follow_imports_for_stubs = false
ignore_missing_imports = false
namespace_packages = true
no_implicit_optional = true
no_implicit_reexport = true
pretty = true
show_absolute_path = true
show_error_codes = true
show_error_context = true
warn_unused_configs = true
warn_unused_ignores = true
disable_error_code = [
"no-redef",
]
exclude = [
"\\.?venv",
"\\.idea",
"\\.tests?",
]
9 changes: 9 additions & 0 deletions src/ftl_extract/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import click

from ftl_extract.const import DEFAULT_FTL_FILE
from ftl_extract.ftl_extractor import extract


Expand Down Expand Up @@ -47,6 +48,12 @@
show_default=True,
help="Comments Junk elements.",
)
@click.option(
"--default-ftl-file",
default=DEFAULT_FTL_FILE,
show_default=True,
type=click.Path(path_type=Path),
)
@click.version_option()
def cli_extract(
code_path: Path,
Expand All @@ -56,6 +63,7 @@ def cli_extract(
ignore_attributes: tuple[str, ...],
expand_ignore_attributes: tuple[str, ...] | None = None,
comment_junks: bool = False,
default_ftl_file: Path = DEFAULT_FTL_FILE,
) -> None:
click.echo(f"Extracting from {code_path}...")
start_time = perf_counter_ns()
Expand All @@ -68,6 +76,7 @@ def cli_extract(
ignore_attributes=ignore_attributes,
expand_ignore_attributes=expand_ignore_attributes,
comment_junks=comment_junks,
default_ftl_file=default_ftl_file,
)

click.echo(f"Done in {(perf_counter_ns() - start_time) * 1e-9:.3f}s.")
28 changes: 23 additions & 5 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,28 +45,37 @@ 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


def post_process_fluent_keys(fluent_keys: dict[str, FluentKey]) -> None:
def post_process_fluent_keys(fluent_keys: dict[str, FluentKey], default_ftl_file: Path) -> None:
"""
Third step: post-process parsed `FluentKey`.
: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: Path
"""
for fluent_key in fluent_keys.values():
if not isinstance(fluent_key.path, Path):
fluent_key.path = Path(fluent_key.path)

if not fluent_key.path.suffix: # if path looks like directory (no suffix)
fluent_key.path /= "_default.ftl"
fluent_key.path /= default_ftl_file


def find_conflicts(
Expand Down Expand Up @@ -105,6 +115,7 @@ def extract_fluent_keys(
path: Path,
i18n_keys: str | Iterable[str],
ignore_attributes: Iterable[str],
default_ftl_file: Path,
) -> dict[str, FluentKey]:
"""
Extract all `FluentKey`s from given path.
Expand All @@ -115,15 +126,22 @@ def extract_fluent_keys(
: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]
"""
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)
post_process_fluent_keys(keys)
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
18 changes: 12 additions & 6 deletions src/ftl_extract/const.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
from __future__ import annotations

from typing import Literal
from pathlib import Path
from typing import TYPE_CHECKING

I18N_LITERAL: Literal["i18n"] = "i18n"
GET_LITERAL: Literal["get"] = "get"
PATH_LITERAL: Literal["_path"] = "_path"
IGNORE_ATTRIBUTES: frozenset[str] = frozenset(
{"set_locale", "use_locale", "use_context", "set_context"}
if TYPE_CHECKING:
from typing import Final, Literal


I18N_LITERAL: Final[Literal["i18n"]] = "i18n"
GET_LITERAL: Final[Literal["get"]] = "get"
PATH_LITERAL: Final[Literal["_path"]] = "_path"
IGNORE_ATTRIBUTES: Final[frozenset[str]] = frozenset(
{"set_locale", "use_locale", "use_context", "set_context"},
)
DEFAULT_FTL_FILE: Final[Path] = Path("_default.ftl")
4 changes: 2 additions & 2 deletions src/ftl_extract/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(self, key: str, current_path: Path, new_path: Path) -> None:
self.new_path = new_path
super().__init__(
f"Key {key!r} already exists with different path: "
f"{self.current_path} != {self.new_path}"
f"{self.current_path} != {self.new_path}",
)


Expand All @@ -33,5 +33,5 @@ def __init__(
self.new_translation = new_translation
super().__init__(
f"Translation {key!r} already exists with different elements: "
f"{self.current_translation} != {self.new_translation}"
f"{self.current_translation} != {self.new_translation}",
)
11 changes: 8 additions & 3 deletions src/ftl_extract/ftl_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from ftl_extract import extract_fluent_keys
from ftl_extract.code_extractor import sort_fluent_keys_by_path
from ftl_extract.const import IGNORE_ATTRIBUTES
from ftl_extract.const import DEFAULT_FTL_FILE, IGNORE_ATTRIBUTES
from ftl_extract.ftl_importer import import_ftl_from_dir
from ftl_extract.process.commentator import comment_ftl_key
from ftl_extract.process.kwargs_extractor import extract_kwargs
Expand All @@ -29,6 +29,7 @@ def extract(
ignore_attributes: Iterable[str] = IGNORE_ATTRIBUTES,
expand_ignore_attributes: Iterable[str] | None = None,
comment_junks: bool = False,
default_ftl_file: Path = DEFAULT_FTL_FILE,
serializer: FluentSerializer | None = None,
) -> None:
if expand_ignore_attributes is not None:
Expand All @@ -42,6 +43,7 @@ def extract(
path=code_path,
i18n_keys=i18n_keys,
ignore_attributes=ignore_attributes,
default_ftl_file=default_ftl_file,
)

for lang in language:
Expand Down Expand Up @@ -104,12 +106,15 @@ def extract(

for fluent_key in leave_as_is:
leave_as_is_with_path.setdefault(
fluent_key.path.relative_to(output_path / lang), []
fluent_key.path.relative_to(output_path / lang),
[],
).append(fluent_key)

for path, keys in sorted_fluent_keys.items():
ftl, _ = generate_ftl(
keys, serializer=serializer, leave_as_is=leave_as_is_with_path.get(path, [])
keys,
serializer=serializer,
leave_as_is=leave_as_is_with_path.get(path, []),
)
(output_path / lang / path).parent.mkdir(parents=True, exist_ok=True)
(output_path / lang / path).write_text(ftl, encoding="utf-8")
Expand Down
5 changes: 3 additions & 2 deletions src/ftl_extract/ftl_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@


def import_from_ftl(
path: Path, locale: str
path: Path,
locale: str,
) -> tuple[dict[str, FluentKey], Resource, list[FluentKey]]:
"""Import `FluentKey`s from FTL."""
ftl_keys = {}
Expand All @@ -39,7 +40,7 @@ def import_from_ftl(
path=path,
locale=locale,
position=position,
)
),
)

return ftl_keys, resource, leave_as_is
Expand Down
Loading

0 comments on commit 4b3cbde

Please sign in to comment.