Source code
Revision control
Copy as Markdown
Other Tools
#!/usr/bin/env python
# Copyright Mozilla Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import logging
import sys
from argparse import ArgumentParser, RawDescriptionHelpFormatter
from enum import Enum
from importlib.util import module_from_spec, spec_from_file_location
from os.path import abspath
from textwrap import dedent
from traceback import format_exc
from moz.l10n.migrate import all_migrations
from moz.l10n.paths.android_locale import get_android_locale
from moz.l10n.paths.config import L10nConfigPaths
from moz.l10n.paths.discover import L10nDiscoverPaths, MissingSourceDirectoryError
log = logging.getLogger(__name__)
Result = Enum("Result", ("OK", "SKIP", "UNSUPPORTED", "FAIL"))
def cli(args: list[str] | None = None) -> None:
parser = ArgumentParser(
description=dedent(
"""
Apply migrations to localization resources.
Returns 0 on success, 1 on internal error, or 2 on argument error.
"""
),
formatter_class=RawDescriptionHelpFormatter,
)
parser.add_argument(
"-q", "--quiet", action="store_true", help="only log input argument errors"
)
parser.add_argument(
"-v", "--verbose", action="count", default=0, help="increase logging verbosity"
)
parser.add_argument(
"-n",
"--dry-run",
action="store_true",
help="do not apply changes to file system",
)
parser.add_argument(
"--config", metavar="PATH", type=str, help="path to l10n.toml config file"
)
parser.add_argument(
"--root",
metavar="PATH",
type=str,
help="path to localization root, if --config is not set",
)
parser.add_argument(
"--ref",
metavar="PATH",
type=str,
help="path to localization reference root, if separate from --root",
)
parser.add_argument(
"migration", nargs="+", type=str, help="path to migration module(s)"
)
ns = parser.parse_args(args)
log_level = (
logging.ERROR
if ns.quiet
else (
logging.WARNING
if ns.verbose == 0
else logging.INFO
if ns.verbose == 1
else logging.DEBUG
)
)
logging.basicConfig(format="%(message)s", level=log_level)
try:
apply_migrations(
ns.migration,
config_path=ns.config,
discover_root=ns.root,
discover_ref_root=ns.ref,
dry_run=ns.dry_run,
)
except MissingSourceDirectoryError:
log.error(f"Reference root not found in {ns.ref or ns.root}")
log.debug(format_exc())
sys.exit(2)
except ValueError as err:
log.error(str(*err.args))
log.debug(format_exc())
sys.exit(2)
except Exception:
log.warning(format_exc())
sys.exit(1)
def apply_migrations(
migration_paths: list[str],
*,
config_path: str | None = None,
discover_root: str | None = None,
discover_ref_root: str | None = None,
dry_run: bool = False,
) -> None:
paths: L10nConfigPaths | L10nDiscoverPaths | None = None
if config_path:
if discover_root:
raise ValueError("--config and --root must not be both set.")
paths = L10nConfigPaths(
config_path, locale_map={"android_locale": get_android_locale}
)
elif discover_root:
if discover_ref_root is not None:
discover_ref_root = abspath(discover_ref_root)
paths = L10nDiscoverPaths(abspath(discover_root), ref_root=discover_ref_root)
all_migrations.clear()
for idx, migration_path in enumerate(migration_paths):
name = f"migration_{idx}"
spec = spec_from_file_location(name, migration_path)
if spec is None or spec.loader is None:
raise ValueError(f"Loading {migration_path} failed")
module = module_from_spec(spec)
sys.modules[name] = module
try:
spec.loader.exec_module(module)
except Exception as err:
raise ValueError(f"Loading {migration_path} failed") from err
if not all_migrations:
raise ValueError("No migrations found")
for migration in all_migrations:
if paths is not None:
migration.set_paths(paths)
migration.apply(dry_run=dry_run)
if __name__ == "__main__":
cli()