127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
import errno
|
|
import logging
|
|
from compare_locales import mozpath
|
|
from .project import ProjectConfig
|
|
from .matcher import expand
|
|
import toml
|
|
|
|
|
|
class ConfigNotFound(EnvironmentError):
|
|
def __init__(self, path):
|
|
super().__init__(errno.ENOENT, "Configuration file not found", path)
|
|
|
|
|
|
class ParseContext:
|
|
def __init__(self, path, env, ignore_missing_includes):
|
|
self.path = path
|
|
self.env = env
|
|
self.ignore_missing_includes = ignore_missing_includes
|
|
self.data = None
|
|
self.pc = ProjectConfig(path)
|
|
|
|
|
|
class TOMLParser:
|
|
def parse(self, path, env=None, ignore_missing_includes=False):
|
|
ctx = self.context(
|
|
path, env=env, ignore_missing_includes=ignore_missing_includes
|
|
)
|
|
self.load(ctx)
|
|
self.processBasePath(ctx)
|
|
self.processEnv(ctx)
|
|
self.processPaths(ctx)
|
|
self.processFilters(ctx)
|
|
self.processIncludes(ctx)
|
|
self.processExcludes(ctx)
|
|
self.processLocales(ctx)
|
|
return self.asConfig(ctx)
|
|
|
|
def context(self, path, env=None, ignore_missing_includes=False):
|
|
return ParseContext(
|
|
path,
|
|
env if env is not None else {},
|
|
ignore_missing_includes,
|
|
)
|
|
|
|
def load(self, ctx):
|
|
try:
|
|
with open(ctx.path) as fin:
|
|
ctx.data = toml.load(fin)
|
|
except (toml.TomlDecodeError, OSError):
|
|
raise ConfigNotFound(ctx.path)
|
|
|
|
def processBasePath(self, ctx):
|
|
assert ctx.data is not None
|
|
ctx.pc.set_root(ctx.data.get("basepath", "."))
|
|
|
|
def processEnv(self, ctx):
|
|
assert ctx.data is not None
|
|
ctx.pc.add_environment(**ctx.data.get("env", {}))
|
|
# add parser environment, possibly overwriting file variables
|
|
ctx.pc.add_environment(**ctx.env)
|
|
|
|
def processLocales(self, ctx):
|
|
assert ctx.data is not None
|
|
if "locales" in ctx.data:
|
|
ctx.pc.set_locales(ctx.data["locales"])
|
|
|
|
def processPaths(self, ctx):
|
|
assert ctx.data is not None
|
|
for data in ctx.data.get("paths", []):
|
|
paths = {"l10n": data["l10n"]}
|
|
if "locales" in data:
|
|
paths["locales"] = data["locales"]
|
|
if "reference" in data:
|
|
paths["reference"] = data["reference"]
|
|
if "test" in data:
|
|
paths["test"] = data["test"]
|
|
ctx.pc.add_paths(paths)
|
|
|
|
def processFilters(self, ctx):
|
|
assert ctx.data is not None
|
|
for data in ctx.data.get("filters", []):
|
|
paths = data["path"]
|
|
if isinstance(paths, str):
|
|
paths = [paths]
|
|
rule = {"path": paths, "action": data["action"]}
|
|
if "key" in data:
|
|
rule["key"] = data["key"]
|
|
ctx.pc.add_rules(rule)
|
|
|
|
def processIncludes(self, ctx):
|
|
for child in self._processChild(ctx, "includes"):
|
|
ctx.pc.add_child(child)
|
|
|
|
def processExcludes(self, ctx):
|
|
for child in self._processChild(ctx, "excludes"):
|
|
ctx.pc.exclude(child)
|
|
|
|
def _processChild(self, ctx, field):
|
|
assert ctx.data is not None
|
|
if field not in ctx.data:
|
|
return
|
|
for child_config in ctx.data[field]:
|
|
# resolve child_config['path'] against our root and env
|
|
p = mozpath.normpath(
|
|
expand(ctx.pc.root, child_config["path"], ctx.pc.environ)
|
|
)
|
|
try:
|
|
child = self.parse(
|
|
p, env=ctx.env, ignore_missing_includes=ctx.ignore_missing_includes
|
|
)
|
|
except ConfigNotFound as e:
|
|
if not ctx.ignore_missing_includes:
|
|
raise
|
|
(
|
|
logging.getLogger("compare-locales.io").error(
|
|
"%s: %s", e.strerror, e.filename
|
|
)
|
|
)
|
|
continue
|
|
yield child
|
|
|
|
def asConfig(self, ctx):
|
|
return ctx.pc
|