From 1d8e4fb665a6d3c066b1d8b0ac635390887d025a Mon Sep 17 00:00:00 2001 From: Aidan Pine Date: Fri, 1 Sep 2023 16:00:05 -0700 Subject: [PATCH] refactor: switch to pydantic 2 BREAKING CHANGE: requires python 3.7 --- .github/workflows/matrix-tests.yml | 2 +- .github/workflows/tests.yml | 19 -- README.md | 4 +- g2p/__init__.py | 10 +- g2p/api.py | 5 +- g2p/app.py | 6 +- g2p/cli.py | 41 ++-- g2p/exceptions.py | 9 + g2p/mappings/__init__.py | 230 ++++++++---------- g2p/mappings/create_fallback_mapping.py | 4 +- g2p/mappings/create_ipa_mapping.py | 54 ++-- g2p/mappings/langs/__init__.py | 11 +- g2p/mappings/langs/alq/config.yaml | 2 +- g2p/mappings/langs/atj/config.yaml | 4 +- g2p/mappings/langs/ckt/config.yaml | 6 +- g2p/mappings/langs/clc/config.yaml | 4 +- g2p/mappings/langs/clm/config.yaml | 4 +- g2p/mappings/langs/crg/config.yaml | 4 +- g2p/mappings/langs/crj/config.yaml | 6 +- g2p/mappings/langs/crk/config.yaml | 4 +- g2p/mappings/langs/crl/config.yaml | 6 +- g2p/mappings/langs/crm/config.yaml | 6 +- g2p/mappings/langs/crx/config.yaml | 6 +- g2p/mappings/langs/csw/config.yaml | 6 +- g2p/mappings/langs/ctp/config.yaml | 4 +- g2p/mappings/langs/dan/config.yaml | 2 +- g2p/mappings/langs/eng/config.yaml | 8 +- g2p/mappings/langs/fin/config.yaml | 2 +- g2p/mappings/langs/font-encodings/config.yaml | 12 +- g2p/mappings/langs/fra/config.yaml | 2 +- g2p/mappings/langs/generated/config.yaml | 76 +++--- g2p/mappings/langs/git/config.yaml | 8 +- g2p/mappings/langs/gla/config.yaml | 2 +- g2p/mappings/langs/gwi/config.yaml | 5 +- g2p/mappings/langs/haa/config.yaml | 6 +- g2p/mappings/langs/ikt/config.yaml | 2 +- g2p/mappings/langs/iku/config.yaml | 6 +- g2p/mappings/langs/kkz/config.yaml | 4 +- g2p/mappings/langs/kwk/config.yaml | 14 +- g2p/mappings/langs/langs.pkl | Bin 5867096 -> 5829448 bytes g2p/mappings/langs/lml/config.yaml | 2 +- g2p/mappings/langs/mic/config.yaml | 2 +- g2p/mappings/langs/moe/config.yaml | 2 +- g2p/mappings/langs/moh/config.yaml | 6 +- g2p/mappings/langs/norm/config.yaml | 2 +- g2p/mappings/langs/oji/config.yaml | 4 +- g2p/mappings/langs/oka/config.yaml | 4 +- g2p/mappings/langs/see/config.yaml | 2 +- g2p/mappings/langs/srs/config.yaml | 4 +- g2p/mappings/langs/str/config.yaml | 4 +- g2p/mappings/langs/tau/config.yml | 4 +- g2p/mappings/langs/tce/config.yaml | 4 +- g2p/mappings/langs/tgx/config.yml | 4 +- g2p/mappings/langs/tli/config.yaml | 4 +- g2p/mappings/langs/ttm/config.yaml | 6 +- g2p/mappings/langs/und/config.yaml | 8 +- g2p/mappings/langs/und/und_to_ipa.json | 62 ++--- g2p/mappings/langs/utils.py | 52 ++-- g2p/mappings/langs/win/config.yaml | 2 +- g2p/mappings/tokenizer.py | 18 +- g2p/mappings/utils.py | 161 +++++------- .../public/mappings/abbreviation_config.yaml | 2 +- .../mappings/bad_langs/lang1/config.yaml | 2 +- .../mappings/bad_langs2/lang1/config.yaml | 2 +- .../public/mappings/case-feed/config.yaml | 6 +- g2p/tests/public/mappings/compose.yaml | 5 +- .../public/mappings/deletion_config_csv.yaml | 2 +- .../public/mappings/deletion_config_json.yaml | 2 +- g2p/tests/public/mappings/gen-map_config.yaml | 8 +- g2p/tests/public/mappings/minimal_config.yaml | 2 +- .../public/mappings/minimal_configs.yaml | 10 +- g2p/tests/public/mappings/null_config.yaml | 2 +- g2p/tests/public/mappings/rule-ordering.yaml | 2 +- g2p/tests/public/mappings/test.yaml | 4 +- .../mappings/tokenize_punct_config.yaml | 2 +- g2p/tests/test_create_mapping.py | 23 +- g2p/tests/test_fallback.py | 10 +- g2p/tests/test_indices.py | 80 +++--- g2p/tests/test_lexicon_transducer.py | 16 +- g2p/tests/test_mappings.py | 146 +++++------ g2p/tests/test_transducer.py | 37 +-- g2p/tests/test_unidecode_transducer.py | 4 +- g2p/tests/test_utils.py | 56 ++--- g2p/tests/test_z_local_config.py | 2 +- g2p/transducer/__init__.py | 28 +-- requirements/requirements.txt | 21 +- setup.py | 2 +- 87 files changed, 698 insertions(+), 741 deletions(-) diff --git a/.github/workflows/matrix-tests.yml b/.github/workflows/matrix-tests.yml index c68d20c0..e714a5f5 100644 --- a/.github/workflows/matrix-tests.yml +++ b/.github/workflows/matrix-tests.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, windows-latest, macos-latest] - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 00e16ddd..f69d5fab 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,25 +45,6 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: false # too many upload errors to keep "true" - test-36: - # This test job exercises only the g2p library code with Python 3.6, which - # is what we still keep compatible with 3.6 - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.6" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -e . - - name: Run tests - run: | - ./run_tests.py dev - test-on-windows: # Make sure stuff stays compatible with Windows by testing there too. runs-on: windows-latest diff --git a/README.md b/README.md index a12d4eab..792e23f5 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ mappings: type: mapping authors: - - mapping: + rules: <<: *shared ``` @@ -241,7 +241,7 @@ mappings: type: mapping authors: - Aidan Pine - mapping: dan_to_ipa.csv + rules: dan_to_ipa.csv abbreviations: dan_abbs.csv rule_ordering: as-written case_sensitive: false diff --git a/g2p/__init__.py b/g2p/__init__.py index b8421fef..8a3912d4 100644 --- a/g2p/__init__.py +++ b/g2p/__init__.py @@ -31,7 +31,6 @@ from g2p.mappings import Mapping from g2p.mappings.langs import LANGS, LANGS_NETWORK from g2p.mappings.tokenizer import Tokenizer, make_tokenizer -from g2p.mappings.utils import _MappingModelDefinition from g2p.transducer import CompositeTransducer, TokenizingTransducer, Transducer _g2p_cache: Dict[ @@ -106,7 +105,7 @@ def make_g2p( # noqa: C901 # Find all mappings needed mappings_needed = [] for lang1, lang2 in zip(path[:-1], path[1:]): - mapping = Mapping(in_lang=lang1, out_lang=lang2) + mapping = Mapping.find_mapping(in_lang=lang1, out_lang=lang2) LOGGER.debug( f"Adding mapping between {lang1} and {lang2} to composite transducer." ) @@ -181,11 +180,10 @@ def get_arpabet_langs(): for _, v in LANGS.items(): for mapping in v["mappings"]: # add mapping to names hash table - config: _MappingModelDefinition = mapping - full_lang_names[config.in_lang] = config.language_name + full_lang_names[mapping["in_lang"]] = mapping["language_name"] # add input id to all available langs list - if config.in_lang not in langs_available: - langs_available.append(config.in_lang) + if mapping["in_lang"] not in langs_available: + langs_available.append(mapping["in_lang"]) # get the key from all networks in g2p module that have a path to 'eng-arpabet', # which is needed for the readalongs diff --git a/g2p/api.py b/g2p/api.py index 53dc8a06..0c7ca35b 100644 --- a/g2p/api.py +++ b/g2p/api.py @@ -12,7 +12,8 @@ from g2p import make_g2p from g2p.exceptions import InvalidLanguageCode, NoPath from g2p.log import LOGGER -from g2p.mappings.langs import LANGS_NETWORK, MAPPINGS_AVAILABLE +from g2p.mappings import MAPPINGS_AVAILABLE +from g2p.mappings.langs import LANGS_NETWORK from g2p.static import __file__ as static_file @@ -36,7 +37,7 @@ class Langs(Resource): def __init__(self): # TODO: exclude parent dir and maybe null values too self.AVAILABLE_MAPPINGS = [ - json.loads(mapping.json()) + json.loads(mapping.model_dump_json()) for mapping in sorted(MAPPINGS_AVAILABLE, key=lambda x: x.in_lang) ] self.parser = reqparse.RequestParser() diff --git a/g2p/app.py b/g2p/app.py index 3bc69cb6..e25fcd6a 100644 --- a/g2p/app.py +++ b/g2p/app.py @@ -143,7 +143,7 @@ def convert(message): mapping_args["abbreviations"] = flatten_abbreviations_format( mapping["abbreviations"] ) - mapping_args["mapping"] = mapping["mapping"] + mapping_args["rules"] = mapping["rules"] mappings_obj = Mapping(**mapping_args) transducer = Transducer(mappings_obj) transducers.append(transducer) @@ -187,7 +187,7 @@ def change_table(message): out_lang="custom", type="mapping", norm_form="NFC", - ).dict() + ).model_dump() kwargs["include"] = False emit( "table response", @@ -211,7 +211,7 @@ def change_table(message): { "mappings": x.plain_mapping(), "abbs": expand_abbreviations_format(x.abbreviations), - "kwargs": json.loads(x.mapping_config.json()), + "kwargs": json.loads(x.model_dump_json()), } for x in mappings ], diff --git a/g2p/cli.py b/g2p/cli.py index 4ed9cec5..5a32b5ae 100644 --- a/g2p/cli.py +++ b/g2p/cli.py @@ -20,7 +20,7 @@ from g2p.app import APP from g2p.exceptions import InvalidLanguageCode, MappingMissing, NoPath from g2p.log import LOGGER -from g2p.mappings import Mapping +from g2p.mappings import MAPPINGS_AVAILABLE, Mapping, Rule from g2p.mappings.create_fallback_mapping import ( DUMMY_INVENTORY, align_to_dummy_fallback, @@ -34,7 +34,6 @@ LANGS_DIR, LANGS_NETWORK, LANGS_PKL_NAME, - MAPPINGS_AVAILABLE, NETWORK_PKL_NAME, reload_db, ) @@ -43,7 +42,7 @@ check_ipa_known_segs, network_to_echart, ) -from g2p.mappings.utils import is_ipa, is_xsampa, load_mapping_from_path, normalize +from g2p.mappings.utils import is_ipa, is_xsampa, normalize from g2p.static import __file__ as static_file from g2p.transducer import Transducer @@ -82,7 +81,7 @@ def parse_from_or_to_lang_spec(lang_spec): if out_lang: try: - mapping = Mapping(in_lang=in_lang, out_lang=out_lang) + mapping = Mapping.find_mapping(in_lang=in_lang, out_lang=out_lang) except MappingMissing as e: raise click.BadParameter( f'Cannot find mapping {in_lang}->{out_lang} for --from or --to spec "{lang_spec}": {e}' @@ -112,14 +111,14 @@ def parse_from_or_to_lang_spec(lang_spec): "supported with the full in-lang_to_out-lang[[in]|[out]] syntax." ) if in_lang == "eng": - mapping = Mapping(in_lang="eng-ipa", out_lang="eng-arpabet") + mapping = Mapping.find_mapping(in_lang="eng-ipa", out_lang="eng-arpabet") in_or_out = "in" return [(mapping, in_or_out)] else: out_lang = in_lang + "-ipa" # check_ipa_known_segs([out_lang]) # this outputs a lot of spurious noise... mappings = [ - (Mapping(in_lang=m.in_lang, out_lang=m.out_lang), "out") + (Mapping.find_mapping(in_lang=m.in_lang, out_lang=m.out_lang), "out") for m in MAPPINGS_AVAILABLE if m.out_lang == out_lang and not is_ipa(m.in_lang) ] @@ -366,7 +365,9 @@ def generate_mapping( # noqa: C901 source_mappings = [] for in_lang in in_langs: try: - source_mapping = Mapping(in_lang=in_lang, out_lang=out_lang) + source_mapping = Mapping.find_mapping( + in_lang=in_lang, out_lang=out_lang + ) except MappingMissing as e: raise click.BadParameter( f'Cannot find IPA mapping from "{in_lang}" to "{out_lang}": {e}', @@ -376,7 +377,7 @@ def generate_mapping( # noqa: C901 if ipa: check_ipa_known_segs([f"{in_lang}-ipa"]) - eng_ipa = Mapping(in_lang="eng-ipa", out_lang="eng-arpabet") + eng_ipa = Mapping.find_mapping(in_lang="eng-ipa", out_lang="eng-arpabet") click.echo(f"Writing English IPA mapping for {out_lang} to file") new_mapping = create_mapping(source_mappings[0], eng_ipa, distance=distance) for m in source_mappings[1:]: @@ -418,11 +419,11 @@ def generate_mapping( # noqa: C901 for from_mapping, in_or_out in from_mappings: LOGGER.info( - f"From mapping: {from_mapping.mapping_config.in_lang}_to_{from_mapping.mapping_config.out_lang}[{in_or_out}]" + f"From mapping: {from_mapping.in_lang}_to_{from_mapping.out_lang}[{in_or_out}]" ) for to_mapping, in_or_out in to_mappings: LOGGER.info( - f"To mapping: {to_mapping.mapping_config.in_lang}_to_{to_mapping.mapping_config.out_lang}[{in_or_out}]" + f"To mapping: {to_mapping.in_lang}_to_{to_mapping.out_lang}[{in_or_out}]" ) new_mapping = create_multi_mapping( @@ -526,9 +527,9 @@ def convert( # noqa: C901 data["mappings"][index]["out_lang"], ) ) - data["mappings"][index] = load_mapping_from_path(config, index) + data["mappings"][index] = Mapping.load_mapping_from_path(config, index) else: - mapping = load_mapping_from_path(config) + mapping = Mapping.load_mapping_from_path(config) data["mappings"] = [mapping] mappings_legal_pairs.append((mapping.in_lang, mapping.out_lang)) for pair in mappings_legal_pairs: @@ -686,6 +687,7 @@ def scan(lang, path): Displays the set of un-mapped characters in a document. Accounts for case sensitivity in the configuration. """ + # breakpoint() # Check input lang exists if lang not in LANGS_NETWORK.nodes: raise click.UsageError(f"'{lang}' is not a valid value for 'LANG'") @@ -703,8 +705,10 @@ def scan(lang, path): # Get input chars in mapping mapped_chars = set() for lang_mapping in mappings: - for x in lang_mapping.mapping: - mapped_chars.add(normalize(x["in"], "NFD")) + assert isinstance(lang_mapping, Mapping) + for x in lang_mapping.rules: + assert isinstance(x, Rule) + mapped_chars.add(normalize(x.in_char, "NFD")) # Find unmapped chars filter_chars = " \n" mapped_string = "".join(mapped_chars) @@ -754,7 +758,7 @@ def show_mappings(lang1, lang2, verbose, csv): elif lang1 is not None: mappings = [ - Mapping(in_lang=m.in_lang, out_lang=m.out_lang) + Mapping.find_mapping(in_lang=m.in_lang, out_lang=m.out_lang) for m in MAPPINGS_AVAILABLE if m.in_lang == lang1 or m.out_lang == lang1 ] @@ -765,14 +769,15 @@ def show_mappings(lang1, lang2, verbose, csv): else: mappings = ( - Mapping(in_lang=m.in_lang, out_lang=m.out_lang) for m in MAPPINGS_AVAILABLE + Mapping.find_mapping(in_lang=m.in_lang, out_lang=m.out_lang) + for m in MAPPINGS_AVAILABLE ) file_type = "csv" if csv else "json" if verbose: for m in mappings: json.dump( - json.loads(m.mapping_config.json()), + json.loads(m.model_dump_json()), sys.stdout, indent=4, ensure_ascii=False, @@ -783,4 +788,4 @@ def show_mappings(lang1, lang2, verbose, csv): print() else: for i, m in enumerate(mappings): - print(f"{i+1}: {m.mapping_config.in_lang} → {m.mapping_config.out_lang}") + print(f"{i+1}: {m.in_lang} → {m.out_lang}") diff --git a/g2p/exceptions.py b/g2p/exceptions.py index 05468a25..b8a81a3d 100644 --- a/g2p/exceptions.py +++ b/g2p/exceptions.py @@ -98,6 +98,15 @@ def __str__(self): ) +class MappingNotInitializedProperlyError(CommandLineError): + def __init__(self, msg="Your Mapping object was not properly initialized"): + super().__init__(self) + self.msg = msg + + def __str__(self): + return self.render(self.msg) + + class IncorrectFileType(CommandLineError): def __init__(self, msg): super().__init__(self) diff --git a/g2p/mappings/__init__.py b/g2p/mappings/__init__.py index e54ddc29..3d892170 100644 --- a/g2p/mappings/__init__.py +++ b/g2p/mappings/__init__.py @@ -16,7 +16,7 @@ from g2p import exceptions from g2p.log import LOGGER -from g2p.mappings.langs import MAPPINGS_AVAILABLE +from g2p.mappings.langs import _MAPPINGS_AVAILABLE from g2p.mappings.langs import __file__ as LANGS_FILE from g2p.mappings.utils import ( MAPPING_TYPE, @@ -29,99 +29,82 @@ create_fixed_width_lookbehind, escape_special_characters, expand_abbreviations, - find_mapping, normalize, ) GEN_DIR = os.path.join(os.path.dirname(LANGS_FILE), "generated") -class Mapping: +class Mapping(_MappingModelDefinition): """Class for lookup tables""" - def __init__( # noqa: C901 - self, - mapping: Union[List[str], List[Rule], Union[Path, str], None] = None, - **kwargs, - ): - self.processed = False - # Sometimes raw data gets passed instead of a path to a config... ugh - # This block determines the mapping configuration - if mapping is not None and ( - isinstance(mapping, list) - or ( - isinstance(mapping, str) - and not mapping.endswith("yaml") - and not mapping.endswith("yml") - ) - ): - kwargs["mapping"] = mapping - self.mapping_config: _MappingModelDefinition = _MappingModelDefinition( - **kwargs - ) - elif kwargs.get("in_lang", False) and kwargs.get("out_lang", False): - loaded_config = find_mapping( - kwargs.get("in_lang", ""), kwargs.get("out_lang", "") - ) - if isinstance(loaded_config, _MappingModelDefinition): - self.mapping_config: _MappingModelDefinition = loaded_config - else: - self.mapping_config: _MappingModelDefinition = _MappingModelDefinition( - **loaded_config - ) - elif kwargs.get("id", False): - loaded_config = self.find_mapping_by_id(kwargs.get("id")) - if isinstance(loaded_config, _MappingModelDefinition): - self.mapping_config: _MappingModelDefinition = loaded_config - else: - self.mapping_config: _MappingModelDefinition = _MappingModelDefinition( - **loaded_config - ) - elif isinstance(mapping, str) and ( - mapping.endswith("yaml") or mapping.endswith("yml") - ): - # This is for if the config.yaml file gets passed - parent_dir = Path(mapping).parent - with open(mapping, encoding="utf8") as f: - loaded_config = yaml.safe_load(f) - loaded_config["parent_dir"] = parent_dir - self.mapping_config: _MappingModelDefinition = _MappingModelDefinition( - **loaded_config - ) - elif not mapping and kwargs.get("type", False) in [ - MAPPING_TYPE.lexicon.value, - MAPPING_TYPE.unidecode.value, - ]: - self.mapping_config: _MappingModelDefinition = _MappingModelDefinition( - **kwargs + @staticmethod + def find_mapping( + in_lang: Union[None, str] = None, out_lang: Union[None, str] = None + ) -> "Mapping": + """Given an input and output, find a mapping to get between them.""" + if in_lang is None or out_lang is None: + raise exceptions.MappingMissing(in_lang, out_lang) + for mapping in MAPPINGS_AVAILABLE: + if mapping.in_lang == in_lang and mapping.out_lang == out_lang: + if mapping.type == "lexicon": + # do *not* deep copy this, because alignments are big! + return mapping.model_copy() + else: + return deepcopy(mapping) + raise exceptions.MappingMissing(in_lang, out_lang) + + @staticmethod + def find_mapping_by_id(map_id: str) -> "Mapping": + """Find the mapping with a given ID""" + for mapping in MAPPINGS_AVAILABLE: + if mapping.id == map_id: + return deepcopy(mapping) + raise exceptions.MappingMissing(map_id, None) + + @staticmethod + def load_mapping_from_path(path_to_mapping_config: Union[str, Path], index=0): + """Loads a mapping from a path, if there is more than one mapping, then it loads based on the int + provided to the 'index' argument. Default is 0. + """ + if isinstance(path_to_mapping_config, str): + path = Path(path_to_mapping_config) + else: + path = path_to_mapping_config + parent_dir = path.parent + with open(path, encoding="utf8") as f: + loaded_config = yaml.safe_load(f) + if not isinstance(loaded_config, dict): + raise exceptions.MalformedMapping( + f"The mapping config at {path} is malformed, please check it is properly formed." ) + if "mappings" in loaded_config: + loaded_config = loaded_config["mappings"][index] + loaded_config["parent_dir"] = parent_dir + return Mapping(**loaded_config) + + def model_post_init(self, *args, **kwargs) -> None: + """After the model is constructed, we process the model specs by applying all the configuration to the rules (ie prevent feeding, unicode normalization etc..)""" + if self.type == MAPPING_TYPE.mapping or self.type is None: + # This is required so that we don't keep escaping special characters for example + self.rules = self.process_model_specs() else: - raise Exception(f"Sorry we can't process {mapping}") - # Process the loaded configuration - self.process_loaded_config() - if self.mapping_config.type == MAPPING_TYPE.unidecode: - self.mapping_config.mapping = [] - elif self.mapping_config.type == MAPPING_TYPE.lexicon: - self.mapping_config.mapping = [] - self.in_lang = self.mapping_config.in_lang - self.out_lang = self.mapping_config.out_lang - if not self.processed: - self.mapping = self.process_model_specs() + self.rules = [] def __len__(self): - return len(self.mapping) + return len(self.rules) def __call__(self): - return self.mapping + return self.rules def __iter__(self): - return iter(self.mapping) + return iter(self.rules) def __getitem__(self, item): if isinstance(item, int): # item is an integer - return self.mapping[item] + return self.rules[item] if isinstance(item, slice): # item is a slice - return self.mapping[item.start or 0 : item.stop or len(self.mapping)] + return self.rules[item.start or 0 : item.stop or len(self.rules)] else: # invalid index type raise TypeError( "{cls} indices must be integers or slices, not {idx}".format( @@ -130,15 +113,6 @@ def __getitem__(self, item): ) ) - @staticmethod - def find_mapping_by_id(map_id: str): - """Find the mapping with a given ID""" - for mapping in MAPPINGS_AVAILABLE: - if (isinstance(mapping, dict) and mapping.get("id", "") == map_id) or ( - isinstance(mapping, _MappingModelDefinition) and mapping.id == map_id - ): - return deepcopy(mapping) - @staticmethod def _string_to_pua(string: str, offset: int) -> str: """Given an string of length n, and an offset m, @@ -157,7 +131,7 @@ def _string_to_pua(string: str, offset: int) -> str: def index(self, item): """Find the location of an item in self""" - return self.mapping.index(item) + return self.rules.index(item) def inventory(self, in_or_out: str = "in"): """Return just inputs or outputs as inventory of mapping""" @@ -165,20 +139,10 @@ def inventory(self, in_or_out: str = "in"): in_or_out = "in_char" if in_or_out == "out": in_or_out = "out_char" - return [getattr(x, in_or_out) for x in self.mapping] - - def process_loaded_config(self): - """For a mapping loaded from a file, take the keyword arguments and supply them to the - Mapping, and get any abbreviations data. - """ - if self.mapping_config.type == MAPPING_TYPE.unidecode: - self.mapping = [] - elif self.mapping_config.type == MAPPING_TYPE.lexicon: - self.mapping = [] - self.alignments = self.mapping_config.alignments - else: - self.mapping = self.mapping_config.mapping - self.abbreviations = self.mapping_config.abbreviations + try: + return [getattr(x, in_or_out) for x in self.rules] + except TypeError as e: + raise exceptions.MappingNotInitializedProperlyError from e def plain_mapping(self, skip_none: bool = False, skip_defaults: bool = False): """Return the plain mapping for displaying or saving to disk. @@ -186,32 +150,32 @@ def plain_mapping(self, skip_none: bool = False, skip_defaults: bool = False): Args: skip_empty_contexts: when set, filter out empty context_before/after """ - assert isinstance(self.mapping, list) - assert isinstance(self.mapping[0], Rule) - return [rule.export_to_dict() for rule in self.mapping] + assert isinstance(self.rules, list) + if self.rules: + assert isinstance(self.rules[0], Rule) + return [rule.export_to_dict() for rule in self.rules] def process_model_specs(self): # noqa: C901 """Process all model specifications""" - - if self.mapping_config.as_is is not None: + if self.as_is is not None: appropriate_setting = ( RULE_ORDERING_ENUM.as_written - if self.mapping_config.as_is + if self.as_is else RULE_ORDERING_ENUM.apply_longest_first ) - self.mapping_config.rule_ordering = appropriate_setting + self.rule_ordering = appropriate_setting LOGGER.warning( f"mapping from {self.in_lang} to {self.out_lang} " 'is using the deprecated parameter "as_is"; ' - f"replace `as_is: {self.mapping_config.as_is}` with `rule_ordering: {appropriate_setting.value}`" + f"replace `as_is: {self.as_is}` with `rule_ordering: {appropriate_setting.value}`" ) # Sorting must happen before the calculation of PUA intermediate forms for proper indexing - if self.mapping_config.rule_ordering == RULE_ORDERING_ENUM.apply_longest_first: - self.mapping_config.mapping = sorted( + if self.rule_ordering == RULE_ORDERING_ENUM.apply_longest_first: + self.rules = sorted( # Temporarily normalize to NFD for heuristic sorting of NFC-defined rules - self.mapping_config.mapping, + self.rules, key=lambda x: len(normalize(x.in_char, "NFD")) if isinstance(x, Rule) else len(normalize(x["in"], "NFD")), @@ -219,14 +183,14 @@ def process_model_specs(self): # noqa: C901 ) non_empty_mappings: List[Rule] = [] - for i, rule in enumerate(self.mapping_config.mapping): + for i, rule in enumerate(self.rules): if isinstance(rule, dict): rule = Rule(**rule) # Expand Abbreviations if ( - self.mapping_config.abbreviations - and self.mapping_config.mapping - and "match_pattern" not in self.mapping_config.mapping[0] + self.abbreviations + and self.rules + and "match_pattern" not in self.rules[0] ): for key in [ "in_char", @@ -239,25 +203,25 @@ def process_model_specs(self): # noqa: C901 expand_abbreviations(getattr(rule, key), self.abbreviations), ) # Reverse Rule - if self.mapping_config.reverse: + if self.reverse: rule.in_char, rule.out_char = rule.out_char, rule.in_char rule.context_before = "" rule.context_after = "" # Escape Special - if self.mapping_config.escape_special: + if self.escape_special: rule = escape_special_characters(rule) # Unicode Normalization - if self.mapping_config.norm_form != NORM_FORM_ENUM.none: + if self.norm_form != NORM_FORM_ENUM.none: for k in ["in_char", "out_char", "context_before", "context_after"]: value = getattr(rule, k) if value: setattr( rule, k, - normalize(value, self.mapping_config.norm_form.value), + normalize(value, self.norm_form.value), ) # Prevent Feeding - if self.mapping_config.prevent_feeding or rule.prevent_feeding: + if self.prevent_feeding or rule.prevent_feeding: rule.intermediate_form = self._string_to_pua(rule.out_char, i) # Create match pattern rule.match_pattern = self.rule_to_regex(rule) @@ -300,7 +264,7 @@ def rule_to_regex(self, rule: Union[Rule, dict]) -> Union[Pattern, None]: inp = create_fixed_width_lookbehind(rule.context_before) + input_match if rule.context_after: inp += f"(?={rule.context_after})" - if not self.mapping_config.case_sensitive: + if not self.case_sensitive: rule_regex = re.compile(inp, re.I) else: rule_regex = re.compile(inp) @@ -324,19 +288,22 @@ def rule_to_regex(self, rule: Union[Rule, dict]) -> Union[Pattern, None]: ) from e return rule_regex - def extend(self, mapping): + def extend(self, mapping: "Mapping"): """Add all the rules from mapping into self, effectively merging two mappings Caveat: if self and mapping have contradictory rules, which one will "win" is unspecified, and may depend on mapping configuration options. """ - self.mapping.extend(mapping.mapping) + try: + self.rules.extend(mapping.rules) + except TypeError as e: + raise exceptions.MappingNotInitializedProperlyError from e def deduplicate(self): """Remove duplicate rules found in self, keeping the first copy found.""" # Since Python 3.6, dict keeps its element in insertion order (while # set does not), so deduplicating the rules is a one-liner: - self.mapping = list({repr(rule): rule for rule in self.mapping}.values()) + self.rules = list({repr(rule): rule for rule in self.rules}.values()) def mapping_to_stream(self, out_stream, file_type: str = "json"): """Write mapping to a stream""" @@ -355,9 +322,11 @@ def mapping_to_stream(self, out_stream, file_type: str = "json"): writer = csv.DictWriter( out_stream, fieldnames=fieldnames, extrasaction="ignore" ) - for io in self.mapping: - assert isinstance(io, Rule) - writer.writerow(io.export_to_dict()) + try: + for io in self.rules: + writer.writerow(io.export_to_dict()) + except TypeError as e: + raise exceptions.MappingNotInitializedProperlyError from e else: raise exceptions.IncorrectFileType(f"File type {file_type} is invalid.") @@ -389,11 +358,9 @@ def config_to_file( LOGGER.warning(f"writing mapping config to file at {output_path}") fn = output_path config_template = json.loads( - self.mapping_config.json(exclude_none=True, exclude={"parent_dir": True}) + self.model_dump_json(exclude_none=True, exclude={"parent_dir": True}) ) - config_template[ - "mapping" - ] = f"{self.mapping_config.in_lang}_to_{self.mapping_config.out_lang}.{mapping_type}" + config_template["rules"] = f"{self.in_lang}_to_{self.out_lang}.{mapping_type}" template = {"mappings": [config_template]} # If config file exists already, just add the mapping. if add_config: @@ -416,3 +383,8 @@ def config_to_file( template = existing_data with open(fn, "w", encoding="utf8", newline="\n") as f: yaml.dump(template, f, Dumper=IndentDumper, default_flow_style=False) + + +MAPPINGS_AVAILABLE: List[Mapping] = [ + Mapping(**mapping) for mapping in _MAPPINGS_AVAILABLE +] diff --git a/g2p/mappings/create_fallback_mapping.py b/g2p/mappings/create_fallback_mapping.py index 091d1da4..a85019e1 100644 --- a/g2p/mappings/create_fallback_mapping.py +++ b/g2p/mappings/create_fallback_mapping.py @@ -16,7 +16,7 @@ def align_to_dummy_fallback( quiet=False, ): """Create a mapping from mapping's output inventory to a minimalist dummy inventory""" - mapping_config = mapping.mapping_config.dict() + mapping_config = mapping.model_dump() config = {"in_lang": mapping_config[f"{io}_lang"], "out_lang": "dummy"} default_char = "t" if is_ipa(mapping_config[f"{io}_lang"]): @@ -53,5 +53,5 @@ def align_to_dummy_fallback( ) x["out"] = default_char - config["mapping"] = list_of_rules + config["rules"] = list_of_rules return Mapping(**config) diff --git a/g2p/mappings/create_ipa_mapping.py b/g2p/mappings/create_ipa_mapping.py index 630d5cb5..8b8a8ba0 100644 --- a/g2p/mappings/create_ipa_mapping.py +++ b/g2p/mappings/create_ipa_mapping.py @@ -49,7 +49,9 @@ def process_character(p, is_xsampa=False): _xsampa_converter = XSampa() p = _xsampa_converter.convert(p) - panphon_preprocessor = Transducer(Mapping(id="panphon_preprocessor")) + panphon_preprocessor = Transducer( + Mapping.find_mapping_by_id("panphon_preprocessor") + ) return panphon_preprocessor(p).output_string @@ -122,10 +124,7 @@ def long_ipa_names(ipa_names: Iterable) -> str: def get_sorted_unique_names(mappings: List[Tuple[Mapping, str]]) -> List[str]: return sorted( - { - getattr(mapping.mapping_config, f"{in_or_out}_lang") - for mapping, in_or_out in mappings - } + {getattr(mapping, f"{in_or_out}_lang") for mapping, in_or_out in mappings} ) def deduplicate(iterable: Iterable) -> List: @@ -137,7 +136,7 @@ def deduplicate(iterable: Iterable) -> List: src_inventory = [] for (mapping, io) in src_mappings: - name = getattr(mapping.mapping_config, f"{io}_lang") + name = getattr(mapping, f"{io}_lang") if not is_ipa(name): LOGGER.warning( "Unsupported orthography of src inventory: %s; must be IPA", name @@ -147,7 +146,7 @@ def deduplicate(iterable: Iterable) -> List: tgt_inventory = [] for (mapping, io) in tgt_mappings: - name = getattr(mapping.mapping_config, f"{io}_lang") + name = getattr(mapping, f"{io}_lang") if not is_ipa(name): LOGGER.warning( "Unsupported orthography of tgt inventory: %s; must be IPA", name @@ -164,7 +163,7 @@ def deduplicate(iterable: Iterable) -> List: "out_lang": compact_ipa_names(map_2_names), "language_name": "IPA", "rule_ordering": RULE_ORDERING_ENUM.apply_longest_first.value, - "mapping": mapping, + "rules": mapping, "prevent_feeding": True, "norm_form": "NFC", "display_name": ( @@ -185,8 +184,8 @@ def create_mapping( ) -> Mapping: """Create a mapping from mapping_1's output inventory to mapping_2's input inventory""" - map_1_name = getattr(mapping_1.mapping_config, f"{mapping_1_io}_lang") - map_2_name = getattr(mapping_2.mapping_config, f"{mapping_2_io}_lang") + map_1_name = getattr(mapping_1, f"{mapping_1_io}_lang") + map_2_name = getattr(mapping_2, f"{mapping_2_io}_lang") if not is_ipa(map_1_name) and not is_xsampa(map_1_name): LOGGER.warning( "Unsupported orthography of inventory 1: %s (must be ipa or x-sampa)", @@ -198,7 +197,7 @@ def create_mapping( map_2_name, ) l1_is_xsampa, l2_is_xsampa = is_xsampa(map_1_name), is_xsampa(map_2_name) - mapping = align_inventories( + rules = align_inventories( mapping_1.inventory(mapping_1_io), mapping_2.inventory(mapping_2_io), l1_is_xsampa, @@ -209,23 +208,22 @@ def create_mapping( # Initialize mapping with input language parameters (as_is, # case_sensitive, prevent_feeding, etc) - config = mapping_1.mapping_config.copy() - # Fix up names, etc. - config.authors = [] - config.display_name = None - config.language_name = None - - config.in_lang = map_1_name - config.out_lang = map_2_name - config.mapping = mapping - - # generated IPA mappings should always prevent feeding and be applied from - # longest first, by virtue of how they are created. - config.prevent_feeding = True - config.rule_ordering = RULE_ORDERING_ENUM.apply_longest_first - - mapping = Mapping(**config.dict()) - return mapping + config = mapping_1.model_copy().model_dump() + config = { + **config, + **{ + "authors": [], + "display_name": None, + "language_name": None, + "in_lang": map_1_name, + "out_lang": map_2_name, + "rules": rules, + "prevent_feeding": True, + "rule_ordering": RULE_ORDERING_ENUM.apply_longest_first, + }, + } + + return Mapping(**config) def align_inventories( diff --git a/g2p/mappings/langs/__init__.py b/g2p/mappings/langs/__init__.py index 99d5b18f..e030ca5a 100644 --- a/g2p/mappings/langs/__init__.py +++ b/g2p/mappings/langs/__init__.py @@ -48,7 +48,7 @@ def get_available_languages(langs: dict) -> list: def get_available_mappings(langs: dict) -> list: mappings_available = [] - for k, v in langs.items(): + for v in langs.values(): if "mappings" in v: mappings_available.extend(v["mappings"]) else: @@ -59,7 +59,8 @@ def get_available_mappings(langs: dict) -> list: LANGS = load_langs() LANGS_NETWORK = load_network() LANGS_AVAILABLE = get_available_languages(LANGS) -MAPPINGS_AVAILABLE = get_available_mappings(LANGS) +# Making private because it should be imported from g2p.mappings instead +_MAPPINGS_AVAILABLE = get_available_mappings(LANGS) def reload_db(): @@ -81,6 +82,6 @@ def reload_db(): LANGS_AVAILABLE.clear() LANGS_AVAILABLE.extend(get_available_languages(LANGS)) - global MAPPINGS_AVAILABLE - MAPPINGS_AVAILABLE.clear() - MAPPINGS_AVAILABLE.extend(get_available_mappings(LANGS)) + global _MAPPINGS_AVAILABLE + _MAPPINGS_AVAILABLE.clear() + _MAPPINGS_AVAILABLE.extend(get_available_mappings(LANGS)) diff --git a/g2p/mappings/langs/alq/config.yaml b/g2p/mappings/langs/alq/config.yaml index 1bca4390..257a2bc4 100644 --- a/g2p/mappings/langs/alq/config.yaml +++ b/g2p/mappings/langs/alq/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - Eric Joanis type: mapping - mapping: alq_to_ipa.csv + rules: alq_to_ipa.csv rule_ordering: as-written case_sensitive: false norm_form: 'NFD' diff --git a/g2p/mappings/langs/atj/config.yaml b/g2p/mappings/langs/atj/config.yaml index c602392e..45d313fd 100644 --- a/g2p/mappings/langs/atj/config.yaml +++ b/g2p/mappings/langs/atj/config.yaml @@ -9,12 +9,12 @@ mappings: authors: - David Huggins-Daines - Patrick Littell - mapping: atj_to_ipa.json + rules: atj_to_ipa.json <<: *shared - display_name: Atikamekw IPA to English IPA in_lang: atj-ipa out_lang: eng-ipa type: mapping - mapping: atj_ipa_to_eng_ipa.json + rules: atj_ipa_to_eng_ipa.json case_sensitive: false <<: *shared diff --git a/g2p/mappings/langs/ckt/config.yaml b/g2p/mappings/langs/ckt/config.yaml index 840a6835..4f9c8f8c 100644 --- a/g2p/mappings/langs/ckt/config.yaml +++ b/g2p/mappings/langs/ckt/config.yaml @@ -8,11 +8,11 @@ mappings: authors: - Vasilisa Andrianets - Patrick Littell - mapping: ckt_to_ipa.json + rules: ckt_to_ipa.json <<: *shared - display_name: Chukchi IPA to English IPA in_lang: ckt-ipa out_lang: eng-ipa type: mapping - mapping: ckt_ipa_to_eng_ipa.json - <<: *shared \ No newline at end of file + rules: ckt_ipa_to_eng_ipa.json + <<: *shared diff --git a/g2p/mappings/langs/clc/config.yaml b/g2p/mappings/langs/clc/config.yaml index 2af531cc..12a9287a 100644 --- a/g2p/mappings/langs/clc/config.yaml +++ b/g2p/mappings/langs/clc/config.yaml @@ -2,7 +2,7 @@ language_name: Tsilhqot'in mappings: - display_name: Doulos - mapping: doulos.csv + rules: doulos.csv in_lang: clc-doulos out_lang: clc - <<: *shared \ No newline at end of file + <<: *shared diff --git a/g2p/mappings/langs/clm/config.yaml b/g2p/mappings/langs/clm/config.yaml index bcc4af59..923cc6de 100644 --- a/g2p/mappings/langs/clm/config.yaml +++ b/g2p/mappings/langs/clm/config.yaml @@ -2,7 +2,7 @@ language_name: Klallam mappings: - display_name: Klallam to IPA - mapping: clm_to_ipa.csv + rules: clm_to_ipa.csv in_lang: clm-equiv out_lang: clm-ipa authors: @@ -18,6 +18,6 @@ mappings: out_lang: clm-equiv authors: - Eric Joanis - mapping: clm_equiv.csv + rules: clm_equiv.csv norm_form: NFD <<: *shared diff --git a/g2p/mappings/langs/crg/config.yaml b/g2p/mappings/langs/crg/config.yaml index 25b98d6d..cc6002ca 100644 --- a/g2p/mappings/langs/crg/config.yaml +++ b/g2p/mappings/langs/crg/config.yaml @@ -2,7 +2,7 @@ language_name: Michif mappings: - display_name: Michif Turtle Mountain Dictionary (TMD) to Michif IPA - mapping: crg-tmd-to-crg-ipa.csv + rules: crg-tmd-to-crg-ipa.csv in_lang: crg-tmd out_lang: crg-ipa case_sensitive: false @@ -16,7 +16,7 @@ mappings: - Christopher Cox <<: *shared - display_name: Michif Double Vowel (DV) to Michif IPA - mapping: crg-dv-to-crg-ipa.csv + rules: crg-dv-to-crg-ipa.csv in_lang: crg-dv out_lang: crg-ipa rule_ordering: as-written diff --git a/g2p/mappings/langs/crj/config.yaml b/g2p/mappings/langs/crj/config.yaml index 2b1bbbf2..6a6576f8 100644 --- a/g2p/mappings/langs/crj/config.yaml +++ b/g2p/mappings/langs/crj/config.yaml @@ -9,7 +9,7 @@ mappings: - Delasie Torkornoo - Aidan Pine - Eric Joanis - mapping: crj_equiv.json + rules: crj_equiv.json rule_ordering: as-written case_sensitive: false <<: *shared @@ -20,7 +20,7 @@ mappings: authors: - David Huggins-Daines - Patrick Littell - mapping: crj_to_ipa.json + rules: crj_to_ipa.json rule_ordering: apply-longest-first case_sensitive: false <<: *shared @@ -28,7 +28,7 @@ mappings: in_lang: crj-ipa out_lang: eng-ipa type: mapping - mapping: crj_ipa_to_eng_ipa.json + rules: crj_ipa_to_eng_ipa.json rule_ordering: apply-longest-first case_sensitive: false <<: *shared diff --git a/g2p/mappings/langs/crk/config.yaml b/g2p/mappings/langs/crk/config.yaml index 49cd8171..c661a1bc 100644 --- a/g2p/mappings/langs/crk/config.yaml +++ b/g2p/mappings/langs/crk/config.yaml @@ -10,7 +10,7 @@ mappings: prevent_feeding: true authors: - Eddie Antonio Santos - mapping: crk-no-symbols_to_ipa.json + rules: crk-no-symbols_to_ipa.json <<: *shared - display_name: Plains Cree Symbols to SRO in_lang: crk @@ -22,5 +22,5 @@ mappings: escape_special: true authors: - Aidan Pine - mapping: crk_to_crk-no-symbols.json + rules: crk_to_crk-no-symbols.json <<: *shared diff --git a/g2p/mappings/langs/crl/config.yaml b/g2p/mappings/langs/crl/config.yaml index 76bca1c7..f43764e1 100644 --- a/g2p/mappings/langs/crl/config.yaml +++ b/g2p/mappings/langs/crl/config.yaml @@ -9,7 +9,7 @@ mappings: - Delasie Torkornoo - Aidan Pine - Eric Joanis - mapping: crl_equiv.json + rules: crl_equiv.json rule_ordering: as-written case_sensitive: false <<: *shared @@ -21,7 +21,7 @@ mappings: - David Huggins-Daines - Patrick Littell - Delasie Torkornoo - mapping: crl_to_ipa.json + rules: crl_to_ipa.json rule_ordering: apply-longest-first case_sensitive: false <<: *shared @@ -29,7 +29,7 @@ mappings: in_lang: crl-ipa out_lang: eng-ipa type: mapping - mapping: crl_ipa_to_eng_ipa.json + rules: crl_ipa_to_eng_ipa.json rule_ordering: apply-longest-first case_sensitive: false <<: *shared diff --git a/g2p/mappings/langs/crm/config.yaml b/g2p/mappings/langs/crm/config.yaml index 2107f2eb..3f962cb5 100644 --- a/g2p/mappings/langs/crm/config.yaml +++ b/g2p/mappings/langs/crm/config.yaml @@ -9,7 +9,7 @@ mappings: - Delasie Torkornoo - Bradley Ellert - Aidan Pine - mapping: crm_equiv.json + rules: crm_equiv.json case_sensitive: false <<: *shared - display_name: Moose Cree to IPA @@ -21,14 +21,14 @@ mappings: - Patrick Littell - Delasie Torkornoo - Bradley Ellert - mapping: crm_to_ipa.json + rules: crm_to_ipa.json case_sensitive: false <<: *shared - display_name: Moose Cree IPA to English IPA in_lang: crm-ipa out_lang: eng-ipa type: mapping - mapping: crm_ipa_to_eng_ipa.json + rules: crm_ipa_to_eng_ipa.json rule_ordering: as-written case_sensitive: false <<: *shared diff --git a/g2p/mappings/langs/crx/config.yaml b/g2p/mappings/langs/crx/config.yaml index 19b59c1a..0ba22a7d 100644 --- a/g2p/mappings/langs/crx/config.yaml +++ b/g2p/mappings/langs/crx/config.yaml @@ -2,7 +2,7 @@ language_name: Stella Nadleh mappings: - display_name: Roman to Syllabics - mapping: stella_orth_to_syllabics.csv + rules: stella_orth_to_syllabics.csv in_lang: crx-sro out_lang: crx-syl case_sensitive: false @@ -10,11 +10,11 @@ mappings: - Aidan Pine <<: *shared - display_name: Syllabics to Roman - mapping: stella_orth_to_syllabics.csv + rules: stella_orth_to_syllabics.csv in_lang: crx-syl out_lang: crx-sro case_sensitive: false reverse: true authors: - Aidan Pine - <<: *shared \ No newline at end of file + <<: *shared diff --git a/g2p/mappings/langs/csw/config.yaml b/g2p/mappings/langs/csw/config.yaml index 0345aea6..b9fc5bd5 100644 --- a/g2p/mappings/langs/csw/config.yaml +++ b/g2p/mappings/langs/csw/config.yaml @@ -9,7 +9,7 @@ mappings: - Delasie Torkornoo - Bradley Ellert - Aidan Pine - mapping: csw_equiv.json + rules: csw_equiv.json <<: *shared - display_name: Swampy Cree to IPA in_lang: csw-equiv @@ -20,12 +20,12 @@ mappings: - Patrick Littell - Delasie Torkornoo - Bradley Ellert - mapping: csw_to_ipa.json + rules: csw_to_ipa.json <<: *shared - display_name: Swampy Cree IPA to English IPA in_lang: csw-ipa out_lang: eng-ipa type: mapping - mapping: csw_ipa_to_eng_ipa.json + rules: csw_ipa_to_eng_ipa.json rule_ordering: as-written <<: *shared diff --git a/g2p/mappings/langs/ctp/config.yaml b/g2p/mappings/langs/ctp/config.yaml index dc2207fa..ab45c889 100644 --- a/g2p/mappings/langs/ctp/config.yaml +++ b/g2p/mappings/langs/ctp/config.yaml @@ -7,7 +7,7 @@ mappings: type: mapping authors: - Patrick Littell - mapping: ctp_to_ipa.json + rules: ctp_to_ipa.json case_sensitive: false rule_ordering: as-written <<: *shared @@ -15,6 +15,6 @@ mappings: in_lang: ctp-ipa out_lang: eng-ipa type: mapping - mapping: ctp_ipa_to_eng_ipa.json + rules: ctp_ipa_to_eng_ipa.json rule_ordering: as-written <<: *shared diff --git a/g2p/mappings/langs/dan/config.yaml b/g2p/mappings/langs/dan/config.yaml index cdadda5c..c7f753f0 100644 --- a/g2p/mappings/langs/dan/config.yaml +++ b/g2p/mappings/langs/dan/config.yaml @@ -7,7 +7,7 @@ mappings: type: mapping authors: - Aidan Pine - mapping: dan_to_ipa.csv + rules: dan_to_ipa.csv abbreviations: dan_abbs.csv rule_ordering: as-written case_sensitive: false diff --git a/g2p/mappings/langs/eng/config.yaml b/g2p/mappings/langs/eng/config.yaml index 7c8cc132..de74c90d 100644 --- a/g2p/mappings/langs/eng/config.yaml +++ b/g2p/mappings/langs/eng/config.yaml @@ -9,7 +9,7 @@ mappings: norm_form: "NFC" authors: - Aidan Pine - mapping: dummy_to_arpabet.json + rules: dummy_to_arpabet.json rule_ordering: apply-longest-first <<: *shared - display_name: Hamming Dummy to Arpabet @@ -20,7 +20,7 @@ mappings: norm_form: "NFC" authors: - Aidan Pine - mapping: dummy_to_arpabet.json + rules: dummy_to_arpabet.json rule_ordering: apply-longest-first <<: *shared - display_name: English IPA to Arpabet @@ -31,7 +31,7 @@ mappings: norm_form: "NFC" authors: - Patrick Littell - mapping: eng_ipa_to_arpabet.json + rules: eng_ipa_to_arpabet.json rule_ordering: apply-longest-first <<: *shared - display_name: English IPA to Arpabet @@ -42,7 +42,7 @@ mappings: norm_form: "NFC" authors: - Patrick Littell - mapping: eng_ipa_to_arpabet.json + rules: eng_ipa_to_arpabet.json rule_ordering: apply-longest-first <<: *shared - display_name: English to IPA diff --git a/g2p/mappings/langs/fin/config.yaml b/g2p/mappings/langs/fin/config.yaml index 2e08ea12..0ae864af 100644 --- a/g2p/mappings/langs/fin/config.yaml +++ b/g2p/mappings/langs/fin/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - David Huggins-Daines type: mapping - mapping: fin_to_ipa.csv + rules: fin_to_ipa.csv rule_ordering: apply-longest-first case_sensitive: false norm_form: 'NFC' diff --git a/g2p/mappings/langs/font-encodings/config.yaml b/g2p/mappings/langs/font-encodings/config.yaml index c28158c4..f27c4b41 100644 --- a/g2p/mappings/langs/font-encodings/config.yaml +++ b/g2p/mappings/langs/font-encodings/config.yaml @@ -4,29 +4,29 @@ mappings: out_lang: hei authors: - Aidan Pine - mapping: hei_doulos.csv + rules: hei_doulos.csv language_name: Heiltsuk - display_name: Times in_lang: hei-times-font out_lang: hei authors: - Aidan Pine - mapping: hei_times.csv + rules: hei_times.csv language_name: Heiltsuk - display_name: Times - mapping: nav_times.csv + rules: nav_times.csv in_lang: nav-times-font out_lang: nav language_name: Navajo authors: - Aidan Pine - display_name: First Nations Unicode - mapping: fn_unicode.csv + rules: fn_unicode.csv in_lang: fn-unicode-font out_lang: fn-unicode norm_form: NFD - escape_special: true + escape_special: false case_sensitive: false language_name: Undetermined authors: - - Aidan Pine \ No newline at end of file + - Aidan Pine diff --git a/g2p/mappings/langs/fra/config.yaml b/g2p/mappings/langs/fra/config.yaml index 199d5edc..0a28b041 100644 --- a/g2p/mappings/langs/fra/config.yaml +++ b/g2p/mappings/langs/fra/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - Eric Joanis type: mapping - mapping: fra_to_ipa.csv + rules: fra_to_ipa.csv abbreviations: fra_abbs.csv rule_ordering: as-written case_sensitive: false diff --git a/g2p/mappings/langs/generated/config.yaml b/g2p/mappings/langs/generated/config.yaml index 98da82b0..f256db2a 100644 --- a/g2p/mappings/langs/generated/config.yaml +++ b/g2p/mappings/langs/generated/config.yaml @@ -3,7 +3,7 @@ mappings: - display_name: Atikamekw IPA to English IPA in_lang: atj-ipa language_name: Atikamekw - mapping: atj-ipa_to_eng-ipa.json + rules: atj-ipa_to_eng-ipa.json out_lang: eng-ipa rule_ordering: apply-longest-first - author: Generated 2019-09-26 13:48:19.852180 @@ -12,7 +12,7 @@ mappings: display_name: Danish IPA to English IPA in_lang: dan-ipa language_name: Danish - mapping: dan-ipa_to_eng-ipa.json + rules: dan-ipa_to_eng-ipa.json out_lang: eng-ipa rule_ordering: apply-longest-first - author: Generated 2020-02-06 11:01:41.458466 @@ -22,7 +22,7 @@ mappings: display_name: French IPA to English IPA in_lang: fra-ipa language_name: French - mapping: fra-ipa_to_eng-ipa.json + rules: fra-ipa_to_eng-ipa.json out_lang: eng-ipa rule_ordering: apply-longest-first - author: Generated 2019-10-24 16:21:41.605592 @@ -31,7 +31,7 @@ mappings: display_name: "SEN\u0106O\u0166EN IPA to English IPA" in_lang: str-ipa language_name: "SEN\u0106O\u0166EN" - mapping: str-ipa_to_eng-ipa.json + rules: str-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa rule_ordering: apply-longest-first @@ -41,7 +41,7 @@ mappings: display_name: "SEN\u0106O\u0166EN IPA to English IPA" in_lang: str-ipa language_name: "SEN\u0106O\u0166EN" - mapping: str-ipa_to_hamming-eng-ipa.json + rules: str-ipa_to_hamming-eng-ipa.json norm_form: NFD out_lang: hamming-eng-ipa rule_ordering: apply-longest-first @@ -49,7 +49,7 @@ mappings: display_name: Algonquin IPA to English IPA in_lang: alq-ipa language_name: Algonquin - mapping: alq-ipa_to_eng-ipa.json + rules: alq-ipa_to_eng-ipa.json out_lang: eng-ipa rule_ordering: apply-longest-first - authors: @@ -59,7 +59,7 @@ mappings: escape_special: false in_lang: see-ipa language_name: see-ipa - mapping: see-ipa_to_eng-ipa.json + rules: see-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa reverse: false @@ -71,7 +71,7 @@ mappings: escape_special: false in_lang: lml-ipa language_name: lml-ipa - mapping: lml-ipa_to_eng-ipa.json + rules: lml-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa reverse: false @@ -83,7 +83,7 @@ mappings: escape_special: false in_lang: oji-ipa language_name: oji-ipa - mapping: oji-ipa_to_eng-ipa.json + rules: oji-ipa_to_eng-ipa.json norm_form: NFC out_lang: eng-ipa prevent_feeding: true @@ -96,7 +96,7 @@ mappings: escape_special: false in_lang: gla-ipa language_name: gla-ipa - mapping: gla-ipa_to_eng-ipa.json + rules: gla-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -109,7 +109,7 @@ mappings: escape_special: false in_lang: crk-ipa language_name: crk-ipa - mapping: crk-ipa_to_eng-ipa.json + rules: crk-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -122,7 +122,7 @@ mappings: escape_special: false in_lang: tce-ipa language_name: tce-ipa - mapping: tce-ipa_to_eng-ipa.json + rules: tce-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -135,7 +135,7 @@ mappings: escape_special: false in_lang: crg-ipa language_name: crg-ipa - mapping: crg-ipa_to_eng-ipa.json + rules: crg-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: true @@ -150,7 +150,7 @@ mappings: escape_special: false in_lang: tli-ipa language_name: Tlingit IPA - mapping: tli-ipa_to_eng-ipa.json + rules: tli-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: true @@ -163,7 +163,7 @@ mappings: escape_special: false in_lang: gwi-ipa language_name: Gwich'in IPA - mapping: gwi-ipa_to_eng-ipa.json + rules: gwi-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -176,7 +176,7 @@ mappings: escape_special: false in_lang: mic-ipa language_name: mic-ipa - mapping: mic-ipa_to_eng-ipa.json + rules: mic-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: true @@ -189,7 +189,7 @@ mappings: escape_special: false in_lang: iku-ipa language_name: iku-ipa - mapping: iku-ipa_to_eng-ipa.json + rules: iku-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -202,7 +202,7 @@ mappings: escape_special: false in_lang: ikt-ipa language_name: ikt-ipa - mapping: ikt-ipa_to_eng-ipa.json + rules: ikt-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -215,7 +215,7 @@ mappings: escape_special: false in_lang: iku-sro-ipa language_name: iku-sro-ipa - mapping: iku-sro-ipa_to_eng-ipa.json + rules: iku-sro-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -228,7 +228,7 @@ mappings: escape_special: false in_lang: haa-ipa language_name: haa-ipa - mapping: haa-ipa_to_eng-ipa.json + rules: haa-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -241,7 +241,7 @@ mappings: escape_special: false in_lang: ttm-ipa language_name: ttm-ipa - mapping: ttm-ipa_to_eng-ipa.json + rules: ttm-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -254,7 +254,7 @@ mappings: escape_special: false in_lang: tau-ipa language_name: tau-ipa - mapping: tau-ipa_to_eng-ipa.json + rules: tau-ipa_to_eng-ipa.json norm_form: NFD out_lang: eng-ipa prevent_feeding: false @@ -267,7 +267,7 @@ mappings: escape_special: false in_lang: moh-ipa language_name: moh-ipa - mapping: moh-ipa_to_eng-ipa.json + rules: moh-ipa_to_eng-ipa.json norm_form: NFC out_lang: eng-ipa prevent_feeding: false @@ -280,7 +280,7 @@ mappings: escape_special: false in_lang: moh-ipa language_name: moh-ipa - mapping: moh-ipa_to_hamming-eng-ipa.json + rules: moh-ipa_to_hamming-eng-ipa.json norm_form: NFC out_lang: hamming-eng-ipa prevent_feeding: false @@ -293,7 +293,7 @@ mappings: escape_special: false in_lang: moh-equiv language_name: moh-equiv - mapping: moh-equiv_to_dummy.json + rules: moh-equiv_to_dummy.json norm_form: NFD out_lang: dummy prevent_feeding: false @@ -306,7 +306,7 @@ mappings: escape_special: false in_lang: moh-equiv language_name: moh-equiv - mapping: moh-equiv_to_hamming_dummy.json + rules: moh-equiv_to_hamming_dummy.json norm_form: NFD out_lang: hamming-dummy prevent_feeding: false @@ -319,7 +319,7 @@ mappings: escape_special: false in_lang: str-equiv language_name: str-equiv - mapping: str-equiv_to_dummy.json + rules: str-equiv_to_dummy.json norm_form: NFD out_lang: dummy prevent_feeding: false @@ -332,7 +332,7 @@ mappings: escape_special: false in_lang: str-equiv language_name: str-equiv - mapping: str-equiv_to_dummy.json + rules: str-equiv_to_dummy.json norm_form: NFD out_lang: hamming-dummy prevent_feeding: false @@ -345,7 +345,7 @@ mappings: escape_special: false in_lang: und-ascii language_name: und-ascii - mapping: und-ascii_to_dummy.json + rules: und-ascii_to_dummy.json norm_form: NFD out_lang: dummy prevent_feeding: false @@ -358,7 +358,7 @@ mappings: escape_special: false in_lang: und-ascii language_name: und-ascii - mapping: und-ascii_to_hamming_dummy.json + rules: und-ascii_to_hamming_dummy.json norm_form: NFD out_lang: hamming-dummy prevent_feeding: false @@ -371,7 +371,7 @@ mappings: escape_special: false in_lang: iku-ipa language_name: IPA - mapping: iku-ipa_to_hamming-eng-ipa.json + rules: iku-ipa_to_hamming-eng-ipa.json norm_form: NFC out_lang: hamming-eng-ipa prevent_feeding: true @@ -384,7 +384,7 @@ mappings: escape_special: false in_lang: und-ipa language_name: IPA - mapping: und-ipa_to_hamming-eng-ipa.json + rules: und-ipa_to_hamming-eng-ipa.json norm_form: NFC out_lang: hamming-eng-ipa prevent_feeding: true @@ -397,7 +397,7 @@ mappings: escape_special: false in_lang: ikt-ipa language_name: IPA - mapping: ikt-ipa_to_hamming-eng-ipa.json + rules: ikt-ipa_to_hamming-eng-ipa.json norm_form: NFC out_lang: hamming-eng-ipa prevent_feeding: true @@ -410,7 +410,7 @@ mappings: escape_special: false in_lang: moe-ipa language_name: IPA - mapping: moe-ipa_to_eng-ipa.json + rules: moe-ipa_to_eng-ipa.json norm_form: NFC out_lang: eng-ipa prevent_feeding: true @@ -423,7 +423,7 @@ mappings: escape_special: false in_lang: win-ipa language_name: win-ipa - mapping: win-ipa_to_eng-ipa.json + rules: win-ipa_to_eng-ipa.json norm_form: NFC out_lang: eng-ipa prevent_feeding: true @@ -437,7 +437,7 @@ mappings: escape_special: false in_lang: fin-ipa language_name: fin-ipa - mapping: fin-ipa_to_eng-ipa.json + rules: fin-ipa_to_eng-ipa.json norm_form: NFC out_lang: eng-ipa prevent_feeding: true @@ -450,7 +450,7 @@ mappings: escape_special: false in_lang: oka-ipa language_name: IPA - mapping: oka-ipa_to_eng-ipa.json + rules: oka-ipa_to_eng-ipa.json norm_form: NFC out_lang: eng-ipa prevent_feeding: true @@ -463,7 +463,7 @@ mappings: escape_special: false in_lang: clm-ipa language_name: IPA - mapping: clm-ipa_to_eng-ipa.json + rules: clm-ipa_to_eng-ipa.json norm_form: NFC out_lang: eng-ipa prevent_feeding: true diff --git a/g2p/mappings/langs/git/config.yaml b/g2p/mappings/langs/git/config.yaml index 193685f0..d5191d71 100644 --- a/g2p/mappings/langs/git/config.yaml +++ b/g2p/mappings/langs/git/config.yaml @@ -8,21 +8,21 @@ mappings: case_sensitive: false authors: - Fineen Davis - mapping: git_to_ipa.json + rules: git_to_ipa.json <<: *shared - display_name: Rigsby APA in_lang: git out_lang: git-apa authors: - Fineen Davis - mapping: RAPA_Deterministic.csv + rules: RAPA_Deterministic.csv <<: *shared - display_name: Unicode Equivalencies in_lang: git out_lang: git-equiv authors: - Aidan Pine - mapping: equiv.csv + rules: equiv.csv <<: *shared - display_name: Gitksan IPA to English IPA in_lang: git-ipa @@ -30,5 +30,5 @@ mappings: rule_ordering: apply-longest-first authors: - Aidan Pine - mapping: git_ipa_to_eng_ipa.json + rules: git_ipa_to_eng_ipa.json <<: *shared diff --git a/g2p/mappings/langs/gla/config.yaml b/g2p/mappings/langs/gla/config.yaml index eea83cbf..f1c3ee50 100644 --- a/g2p/mappings/langs/gla/config.yaml +++ b/g2p/mappings/langs/gla/config.yaml @@ -9,5 +9,5 @@ mappings: rule_ordering: apply-longest-first authors: - Aidan Pine - mapping: gla_to_ipa.json + rules: gla_to_ipa.json <<: *shared diff --git a/g2p/mappings/langs/gwi/config.yaml b/g2p/mappings/langs/gwi/config.yaml index a9f8e0b4..8db72414 100644 --- a/g2p/mappings/langs/gwi/config.yaml +++ b/g2p/mappings/langs/gwi/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - Sabrina Yu type: mapping - mapping: gwi_equiv.json + rules: gwi_equiv.json prevent_feeding: false rule_ordering: as-written case_sensitive: false @@ -19,10 +19,9 @@ mappings: authors: - Sabrina Yu type: mapping - mapping: gwi_to_ipa.json + rules: gwi_to_ipa.json prevent_feeding: true rule_ordering: as-written case_sensitive: false norm_form: NFD <<: *shared - diff --git a/g2p/mappings/langs/haa/config.yaml b/g2p/mappings/langs/haa/config.yaml index a8b7e1d8..4942e0ee 100644 --- a/g2p/mappings/langs/haa/config.yaml +++ b/g2p/mappings/langs/haa/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: haa_equiv.csv + rules: haa_equiv.csv prevent_feeding: false rule_ordering: as-written case_sensitive: false @@ -19,10 +19,10 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: haa_to_ipa.csv + rules: haa_to_ipa.csv abbreviations: haa_abbs.csv prevent_feeding: true rule_ordering: as-written case_sensitive: false norm_form: NFD - <<: *shared \ No newline at end of file + <<: *shared diff --git a/g2p/mappings/langs/ikt/config.yaml b/g2p/mappings/langs/ikt/config.yaml index be955d52..b90c5088 100644 --- a/g2p/mappings/langs/ikt/config.yaml +++ b/g2p/mappings/langs/ikt/config.yaml @@ -10,5 +10,5 @@ mappings: rule_ordering: apply-longest-first authors: - Patrick Littell - mapping: ikt_to_ipa.json + rules: ikt_to_ipa.json <<: *shared diff --git a/g2p/mappings/langs/iku/config.yaml b/g2p/mappings/langs/iku/config.yaml index d38f020f..a239b027 100644 --- a/g2p/mappings/langs/iku/config.yaml +++ b/g2p/mappings/langs/iku/config.yaml @@ -8,7 +8,7 @@ mappings: rule_ordering: apply-longest-first authors: - Patrick Littell - mapping: iku_to_iku_equiv.json + rules: iku_to_iku_equiv.json language_name: Inuktitut Syllabics - display_name: Inuktitut to IPA in_lang: iku-equiv @@ -17,7 +17,7 @@ mappings: rule_ordering: apply-longest-first authors: - Patrick Littell - mapping: iku_equiv_to_ipa.json + rules: iku_equiv_to_ipa.json <<: *shared - display_name: Inuktitut (SRO) to IPA in_lang: iku-sro @@ -28,5 +28,5 @@ mappings: rule_ordering: apply-longest-first authors: - Patrick Littell - mapping: iku_sro_to_ipa.json + rules: iku_sro_to_ipa.json language_name: Inuktitut Romanized diff --git a/g2p/mappings/langs/kkz/config.yaml b/g2p/mappings/langs/kkz/config.yaml index 47e47851..ea92dcf8 100644 --- a/g2p/mappings/langs/kkz/config.yaml +++ b/g2p/mappings/langs/kkz/config.yaml @@ -2,7 +2,7 @@ language_name: Kaska mappings: - display_name: Kaska to IPA - mapping: kkz_to_ipa.json + rules: kkz_to_ipa.json in_lang: kkz out_lang: kkz-ipa rule_ordering: apply-longest-first @@ -12,7 +12,7 @@ mappings: - Christopher Cox <<: *shared - display_name: Kaska IPA to English IPA - mapping: kkz_ipa_to_eng_ipa.json + rules: kkz_ipa_to_eng_ipa.json in_lang: kkz-ipa out_lang: eng-ipa norm_form: NFD diff --git a/g2p/mappings/langs/kwk/config.yaml b/g2p/mappings/langs/kwk/config.yaml index 6d59635d..1e18ec25 100644 --- a/g2p/mappings/langs/kwk/config.yaml +++ b/g2p/mappings/langs/kwk/config.yaml @@ -2,7 +2,7 @@ language_name: Kwak'wala mappings: - display_name: Kwak'wala IPA to English IPA - mapping: kwk_ipa_to_eng_ipa.json + rules: kwk_ipa_to_eng_ipa.json in_lang: kwk-ipa out_lang: eng-ipa case_sensitive: false @@ -10,7 +10,7 @@ mappings: - Patrick Littell <<: *shared - display_name: Kwak'wala (NAPA) to IPA - mapping: kwk_napa_to_ipa.csv + rules: kwk_napa_to_ipa.csv in_lang: kwk-napa out_lang: kwk-ipa rule_ordering: apply-longest-first @@ -19,7 +19,7 @@ mappings: - Fineen Davis language_name: Kwak'wala (NAPA orthography) - display_name: Kwak'wala (U'mista) to IPA - mapping: kwk_umista_to_ipa.json + rules: kwk_umista_to_ipa.json in_lang: kwk-umista out_lang: kwk-ipa rule_ordering: apply-longest-first @@ -27,7 +27,7 @@ mappings: - Patrick Littell <<: *shared - display_name: Kwak'wala (U'mista) Equivalencies - mapping: umista_equiv.csv + rules: umista_equiv.csv in_lang: kwk-umista out_lang: kwk-umista-con rule_ordering: apply-longest-first @@ -35,7 +35,7 @@ mappings: - Fineen Davis language_name: Kwak'wala (U'mista orthography) - display_name: Kwak'wala (NAPA) Equivalencies - mapping: napa_equiv_ubc.csv + rules: napa_equiv_ubc.csv in_lang: kwk-napa-ubc out_lang: kwk-napa-ubc-con rule_ordering: apply-longest-first @@ -43,7 +43,7 @@ mappings: - Fineen Davis <<: *shared - display_name: Kwak'wala (NAPA) Equivalencies - mapping: napa_equiv_uvic.csv + rules: napa_equiv_uvic.csv in_lang: kwk-napa-uvic out_lang: kwk-napa-uvic-con rule_ordering: apply-longest-first @@ -51,7 +51,7 @@ mappings: - Fineen Davis <<: *shared - display_name: Kwak'wala (Boas) to Umista - mapping: kwk_boas_to_umista.csv + rules: kwk_boas_to_umista.csv in_lang: kwk-boas out_lang: kwk-umista rule_ordering: apply-longest-first diff --git a/g2p/mappings/langs/langs.pkl b/g2p/mappings/langs/langs.pkl index ca82decbc77957548ae04a98ee5772a6f8313029..34637fe18eaf30b0eff014c7f34d3595a96e8807 100644 GIT binary patch delta 272213 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

D(&!^?Ao&T@->E} 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 H&WisFYdK!@ delta 321972 zcmce<3!J4@btg)lbE^7*H19WJgCMA&AZo-|EseC$3f+K!n&>dq)kS}G>e*GNx@>~^ zRDuybhN`Yp96A^xq9_s+9q^#>5or*sdFf;_$s{wsNoRhOOlER3Np3PXcal4~>$#un zD{v-|{G_|j{(oz)z4m+Uz5naSfBK(lf6)HZTH|Al<=SH79cBBNU2mFx!^F_^^yuWS z**DC!M#pD6?GtajihsFkYUjxKWg|C@PL8%lrzSf~o!5T6^U=<^?X@@FxN~&4b>ofn zul3VIGb58N_-UroUNb$^D#k~5blU5#o|+x~$kp&q`0(6z-y1va{_AH)W*~=t$Zuq* zb9TE9`FD!r+iO(^NE;slT{`Xh(D?37t8;B<@m=@5dF_t5(ec*kB>cW+*GOxq)tUjJ z#hUhpZQHNC>c)5Df3|G9{wnyfUElWZ%Q~OzT)X&R-}w6Cb$~hM+Uu{}a^v=EFWYkM z)@_$N-1?!}8|P<6Tdk2vgm-t}{{BrZxym5MFbk9(0 z8lM}Pr4BK-V`vUqZ>!l|eDnjyFOAh-vGl6?5oNKCJ5TWnYziBSqt7jlDUL0UD~^Z% zKcP60KA{F*oVu+zjsIDk-d+Qe&L5qI!fb4>+uRx*8=4pyo9`57wvTfE+Sr=fxb^DI zo#L!*#o4zPuelwReq(U~G_W__4qCshxcK&UP?C2RmuxFGFJ09*zbr1>Rt$c)b1wGg z+UR7b-8T(AXKJnmf7=lo41Q^KiaU!jXD}=P{5l4|=5+`Ovk1Q@g%1;)n^5VSey3e) zQBwcTXYZw!67&lYHi5$KkA)Sx;kVuKZ|y$lXLlA4q`yMby|Z{Q`L*l=3*K2g6#rbz z!*BD+Z&Ogn`pDA@iBXCh0DD7%y#{`ZyD*gQz!xAZsLoJC$tJ8FfX-`*htIx7Nu%Ls z`qd+pG#4g~%oV>BwpwkoXD$>U{b*-yN8J3jpKp9Wx#qa?ZRcNf;l>N!`u4Y6{Ps7$^$i!i`GU7z{I3~WEgv{=oZhz|H8rbIcU1i2hEp^|6E(!+w<-E&a~Y@4pilkmR0?8(3+l4 zTGVsU6rN9-1IVDt%YHARIX|B?>F1z1Kc6(|=b$lT5x*Zdz2~57J)d;1=b+g=pENzl zpIS)v`w+DI=aaVo9Cu{QE*X;{1^XF@6llQDRZ#H=GM=H-4E8Cmk)NegWuKBu6D2!nYTI6ci3U= z+D{(tG}pr4;^9COzbT2)_@RmaiW9+-_p#(sW_blXDql~9tyB{qlf-Cf(Zp{{qMy59 zM9{?VNaEe8*b>Y0T}h2bQvLLMk{AuCn)rQ5jOJHO{DCAcr

YKm8admRTE?uYdY+ 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 J&&|F6{{T$o`6U1V diff --git a/g2p/mappings/langs/lml/config.yaml b/g2p/mappings/langs/lml/config.yaml index d45f1e0c..3a4331fa 100644 --- a/g2p/mappings/langs/lml/config.yaml +++ b/g2p/mappings/langs/lml/config.yaml @@ -2,7 +2,7 @@ language_name: Raga mappings: - display_name: Raga to IPA - mapping: lml_to_ipa.csv + rules: lml_to_ipa.csv in_lang: lml out_lang: lml-ipa case_sensitive: false diff --git a/g2p/mappings/langs/mic/config.yaml b/g2p/mappings/langs/mic/config.yaml index f9ce7e22..4abeb010 100644 --- a/g2p/mappings/langs/mic/config.yaml +++ b/g2p/mappings/langs/mic/config.yaml @@ -2,7 +2,7 @@ language_name: Mi'kmaq mappings: - display_name: Mi'kmaq to IPA - mapping: mic_to_ipa.json + rules: mic_to_ipa.json in_lang: mic out_lang: mic-ipa case_sensitive: false diff --git a/g2p/mappings/langs/moe/config.yaml b/g2p/mappings/langs/moe/config.yaml index 9e4c493e..f952067c 100644 --- a/g2p/mappings/langs/moe/config.yaml +++ b/g2p/mappings/langs/moe/config.yaml @@ -8,7 +8,7 @@ mappings: authors: - Delasie Torkornoo - Bradley Ellert - mapping: moe_to_ipa.json + rules: moe_to_ipa.json abbreviations: moe_abbs.csv case_sensitive: false <<: *shared diff --git a/g2p/mappings/langs/moh/config.yaml b/g2p/mappings/langs/moh/config.yaml index 8233e1c1..a2f72fa2 100644 --- a/g2p/mappings/langs/moh/config.yaml +++ b/g2p/mappings/langs/moh/config.yaml @@ -2,7 +2,7 @@ language_name: Kanien'kéha mappings: - display_name: Kanien'kéha to IPA - mapping: moh_to_ipa.json + rules: moh_to_ipa.json in_lang: moh-equiv out_lang: moh-ipa case_sensitive: false @@ -14,7 +14,7 @@ mappings: - Akwiratékha' Martin <<: *shared - display_name: IPA to Kanien'kéha - mapping: moh_to_ipa.json + rules: moh_to_ipa.json in_lang: moh-ipa reverse: true out_lang: moh @@ -27,7 +27,7 @@ mappings: - Akwiratékha' Martin <<: *shared - display_name: Kanien'kéha Equivalencies - mapping: moh_equiv.json + rules: moh_equiv.json in_lang: moh out_lang: moh-equiv case_sensitive: false diff --git a/g2p/mappings/langs/norm/config.yaml b/g2p/mappings/langs/norm/config.yaml index d188a954..e0f8aaf1 100644 --- a/g2p/mappings/langs/norm/config.yaml +++ b/g2p/mappings/langs/norm/config.yaml @@ -2,7 +2,7 @@ language_name: Normalization mappings: - display_name: Panphon Normalization - mapping: panphon_preprocessor.csv + rules: panphon_preprocessor.csv id: panphon_preprocessor in_lang: ipa out_lang: ipa diff --git a/g2p/mappings/langs/oji/config.yaml b/g2p/mappings/langs/oji/config.yaml index a44cc95f..0cbf2c3e 100644 --- a/g2p/mappings/langs/oji/config.yaml +++ b/g2p/mappings/langs/oji/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - David Huggins-Daines type: mapping - mapping: oji_to_ipa.csv + rules: oji_to_ipa.csv prevent_feeding: true rule_ordering: as-written case_sensitive: false @@ -19,7 +19,7 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: oji_syllabics_to_orth.csv + rules: oji_syllabics_to_orth.csv prevent_feeding: true rule_ordering: as-written case_sensitive: false diff --git a/g2p/mappings/langs/oka/config.yaml b/g2p/mappings/langs/oka/config.yaml index c9905ca0..f8066858 100644 --- a/g2p/mappings/langs/oka/config.yaml +++ b/g2p/mappings/langs/oka/config.yaml @@ -2,7 +2,7 @@ language_name: nsyilxcən mappings: - display_name: nsyilxcən to IPA - mapping: oka_to_ipa.csv + rules: oka_to_ipa.csv in_lang: oka-equiv out_lang: oka-ipa authors: @@ -17,6 +17,6 @@ mappings: out_lang: oka-equiv authors: - Eric Joanis - mapping: oka_equiv.csv + rules: oka_equiv.csv norm_form: NFD <<: *shared diff --git a/g2p/mappings/langs/see/config.yaml b/g2p/mappings/langs/see/config.yaml index 0471d98e..32a15b18 100644 --- a/g2p/mappings/langs/see/config.yaml +++ b/g2p/mappings/langs/see/config.yaml @@ -2,7 +2,7 @@ language_name: Seneca mappings: - display_name: Seneca to IPA - mapping: see_to_ipa.csv + rules: see_to_ipa.csv in_lang: see out_lang: see-ipa case_sensitive: false diff --git a/g2p/mappings/langs/srs/config.yaml b/g2p/mappings/langs/srs/config.yaml index 994a59fe..4cdaa31b 100755 --- a/g2p/mappings/langs/srs/config.yaml +++ b/g2p/mappings/langs/srs/config.yaml @@ -2,7 +2,7 @@ language_name: Tsuut'ina mappings: - display_name: Tsuut'ina to IPA - mapping: srs_to_ipa.json + rules: srs_to_ipa.json in_lang: srs out_lang: srs-ipa rule_ordering: as-written @@ -12,7 +12,7 @@ mappings: - Christopher Cox <<: *shared - display_name: Tsuut'ina IPA to English IPA - mapping: srs_ipa_to_eng_ipa.json + rules: srs_ipa_to_eng_ipa.json in_lang: srs-ipa out_lang: eng-ipa norm_form: NFD diff --git a/g2p/mappings/langs/str/config.yaml b/g2p/mappings/langs/str/config.yaml index a333b7bc..0ea2510e 100644 --- a/g2p/mappings/langs/str/config.yaml +++ b/g2p/mappings/langs/str/config.yaml @@ -2,7 +2,7 @@ language_name: SENĆOŦEN mappings: - display_name: SENĆOŦEN equivalency - mapping: str_equiv.json + rules: str_equiv.json in_lang: str out_lang: str-equiv rule_ordering: as-written @@ -12,7 +12,7 @@ mappings: - Shankhalika Srikanth <<: *shared - display_name: SENĆOŦEN to IPA - mapping: str_to_ipa.json + rules: str_to_ipa.json in_lang: str-equiv out_lang: str-ipa rule_ordering: as-written diff --git a/g2p/mappings/langs/tau/config.yml b/g2p/mappings/langs/tau/config.yml index 1e8dea83..1627f8d0 100644 --- a/g2p/mappings/langs/tau/config.yml +++ b/g2p/mappings/langs/tau/config.yml @@ -7,7 +7,7 @@ mappings: authors: - Sabrina Yu type: mapping - mapping: tau_equiv.json + rules: tau_equiv.json prevent_feeding: false rule_ordering: as-written case_sensitive: false @@ -19,7 +19,7 @@ mappings: authors: - Sabrina Yu type: mapping - mapping: tau_to_ipa.json + rules: tau_to_ipa.json prevent_feeding: true rule_ordering: as-written case_sensitive: false diff --git a/g2p/mappings/langs/tce/config.yaml b/g2p/mappings/langs/tce/config.yaml index 2ba68ba4..89c4062e 100644 --- a/g2p/mappings/langs/tce/config.yaml +++ b/g2p/mappings/langs/tce/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: tce_equiv.csv + rules: tce_equiv.csv prevent_feeding: false rule_ordering: as-written case_sensitive: false @@ -19,7 +19,7 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: tce_to_ipa.csv + rules: tce_to_ipa.csv prevent_feeding: true rule_ordering: as-written case_sensitive: false diff --git a/g2p/mappings/langs/tgx/config.yml b/g2p/mappings/langs/tgx/config.yml index eba81e57..53c3b50d 100644 --- a/g2p/mappings/langs/tgx/config.yml +++ b/g2p/mappings/langs/tgx/config.yml @@ -2,7 +2,7 @@ language_name: Tagish mappings: - display_name: Tagish to IPA - mapping: tgx_to_ipa.json + rules: tgx_to_ipa.json in_lang: tgx out_lang: tgx-ipa rule_ordering: apply-longest-first @@ -12,7 +12,7 @@ mappings: - Christopher Cox <<: *shared - display_name: Tagish IPA to English IPA - mapping: tgx_ipa_to_eng_ipa.json + rules: tgx_ipa_to_eng_ipa.json in_lang: tgx-ipa out_lang: eng-ipa norm_form: NFD diff --git a/g2p/mappings/langs/tli/config.yaml b/g2p/mappings/langs/tli/config.yaml index 0a7a0f3a..beff710c 100644 --- a/g2p/mappings/langs/tli/config.yaml +++ b/g2p/mappings/langs/tli/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: tli_equiv.csv + rules: tli_equiv.csv prevent_feeding: false rule_ordering: as-written case_sensitive: false @@ -19,7 +19,7 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: tli_to_ipa.csv + rules: tli_to_ipa.csv prevent_feeding: true rule_ordering: as-written case_sensitive: false diff --git a/g2p/mappings/langs/ttm/config.yaml b/g2p/mappings/langs/ttm/config.yaml index d3a3c1f7..29dfbe75 100644 --- a/g2p/mappings/langs/ttm/config.yaml +++ b/g2p/mappings/langs/ttm/config.yaml @@ -7,7 +7,7 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: ttm_equiv.csv + rules: ttm_equiv.csv prevent_feeding: false rule_ordering: as-written case_sensitive: false @@ -19,9 +19,9 @@ mappings: authors: - Shankhalika Srikanth type: mapping - mapping: ttm_to_ipa.csv + rules: ttm_to_ipa.csv prevent_feeding: true rule_ordering: as-written case_sensitive: false norm_form: NFD - <<: *shared \ No newline at end of file + <<: *shared diff --git a/g2p/mappings/langs/und/config.yaml b/g2p/mappings/langs/und/config.yaml index 5dae9405..2f95e2a0 100644 --- a/g2p/mappings/langs/und/config.yaml +++ b/g2p/mappings/langs/und/config.yaml @@ -2,17 +2,17 @@ language_name: Undetermined mappings: - display_name: Undetermined ASCII to IPA - mapping: und_to_ipa.json + rules: und_to_ipa.json in_lang: und-ascii out_lang: und-ipa norm: NFD case_sensitive: false - escape_special: true + escape_special: false authors: - Patrick Littell <<: *shared - display_name: Undetermined IPA to English IPA - mapping: und_ipa_to_eng_ipa.json + rules: und_ipa_to_eng_ipa.json in_lang: und-ipa out_lang: eng-ipa rule_ordering: apply-longest-first @@ -20,7 +20,7 @@ mappings: - Patrick Littell <<: *shared - display_name: Undetermined IPA to English IPA - mapping: und_ipa_to_eng_ipa.json + rules: und_ipa_to_eng_ipa.json in_lang: und-ipa out_lang: hamming-eng-ipa rule_ordering: apply-longest-first diff --git a/g2p/mappings/langs/und/und_to_ipa.json b/g2p/mappings/langs/und/und_to_ipa.json index 713e9648..64e1c1a9 100644 --- a/g2p/mappings/langs/und/und_to_ipa.json +++ b/g2p/mappings/langs/und/und_to_ipa.json @@ -1,33 +1,33 @@ [ - {"in": "a", "out": "a"}, - {"in": "b", "out": "b"}, - {"in": "c", "out": "t͡ʃ"}, - {"in": "d", "out": "d"}, - {"in": "e", "out": "e"}, - {"in": "f", "out": "f"}, - {"in": "g", "out": "ɡ"}, - {"in": "h", "out": "h"}, - {"in": "i", "out": "i"}, - {"in": "j", "out": "ʒ"}, - {"in": "k", "out": "k"}, - {"in": "l", "out": "l"}, - {"in": "m", "out": "m"}, - {"in": "n", "out": "n"}, - {"in": "o", "out": "o"}, - {"in": "p", "out": "p"}, - {"in": "q", "out": "q"}, - {"in": "r", "out": "r"}, - {"in": "s", "out": "s"}, - {"in": "t", "out": "t"}, - {"in": "u", "out": "u"}, - {"in": "v", "out": "v"}, - {"in": "w", "out": "w"}, - {"in": "x", "out": "x"}, - {"in": "y", "out": "j"}, - {"in": "z", "out": "z"}, - {"in": "@", "out": "ə"}, - {"in": "?", "out": "ʔ"}, - {"in": "'", "out": "ʔ"}, - {"in": ",", "out": "ʔ"}, - {"in": ":", "out": ""} + { "in": "a", "out": "a" }, + { "in": "b", "out": "b" }, + { "in": "c", "out": "t͡ʃ" }, + { "in": "d", "out": "d" }, + { "in": "e", "out": "e" }, + { "in": "f", "out": "f" }, + { "in": "g", "out": "ɡ" }, + { "in": "h", "out": "h" }, + { "in": "i", "out": "i" }, + { "in": "j", "out": "ʒ" }, + { "in": "k", "out": "k" }, + { "in": "l", "out": "l" }, + { "in": "m", "out": "m" }, + { "in": "n", "out": "n" }, + { "in": "o", "out": "o" }, + { "in": "p", "out": "p" }, + { "in": "q", "out": "q" }, + { "in": "r", "out": "r" }, + { "in": "s", "out": "s" }, + { "in": "t", "out": "t" }, + { "in": "u", "out": "u" }, + { "in": "v", "out": "v" }, + { "in": "w", "out": "w" }, + { "in": "x", "out": "x" }, + { "in": "y", "out": "j" }, + { "in": "z", "out": "z" }, + { "in": "@", "out": "ə" }, + { "in": "\\?", "out": "ʔ" }, + { "in": "'", "out": "ʔ" }, + { "in": ",", "out": "ʔ" }, + { "in": ":", "out": "" } ] diff --git a/g2p/mappings/langs/utils.py b/g2p/mappings/langs/utils.py index f29bf79e..7beece94 100644 --- a/g2p/mappings/langs/utils.py +++ b/g2p/mappings/langs/utils.py @@ -12,17 +12,11 @@ from networkx import DiGraph, write_gpickle from networkx.algorithms.dag import ancestors, descendants -from g2p.exceptions import MalformedMapping +from g2p.exceptions import MalformedMapping, MappingNotInitializedProperlyError from g2p.log import LOGGER -from g2p.mappings import Mapping -from g2p.mappings.langs import ( - LANGS_DIR, - LANGS_NETWORK, - LANGS_NWORK_PATH, - LANGS_PKL, - MAPPINGS_AVAILABLE, -) -from g2p.mappings.utils import MAPPING_TYPE, is_ipa, load_mapping_from_path +from g2p.mappings import MAPPINGS_AVAILABLE, Mapping +from g2p.mappings.langs import LANGS_DIR, LANGS_NETWORK, LANGS_NWORK_PATH, LANGS_PKL +from g2p.mappings.utils import MAPPING_TYPE, Rule, is_ipa # panphon.distance.Distance() takes a long time to initialize, so... # a) we don't want to load it if we don't need it, i.e., don't use a constant @@ -57,14 +51,18 @@ def check_ipa_known_segs(mappings_to_check=False) -> bool: for mapping in [x for x in MAPPINGS_AVAILABLE if x.out_lang in mappings_to_check]: if is_ipa(mapping.out_lang) and mapping.type == MAPPING_TYPE.mapping: reverse = mapping.reverse - for rule in mapping.mapping: - output = rule["in"] if reverse else rule["out"] - if not is_panphon(output): - LOGGER.warning( - f"Output '{rule['out']}' in rule {rule} in mapping between {mapping.in_lang} " - f"and {mapping.out_lang} is not recognized as valid IPA by panphon." - ) - found_error = True + try: + for rule in mapping.rules: + assert isinstance(rule, Rule) + output = rule.in_char if reverse else rule.out_char + if not is_panphon(output): + LOGGER.warning( + f"Output '{rule.out_char}' in rule {rule} in mapping between {mapping.in_lang} " + f"and {mapping.out_lang} is not recognized as valid IPA by panphon." + ) + found_error = True + except TypeError as e: + raise MappingNotInitializedProperlyError from e if found_error: LOGGER.warning( "Please refer to https://github.com/dmort27/panphon for information about panphon." @@ -83,7 +81,9 @@ def is_panphon(string, display_warnings=False): import g2p.transducer dst = getPanphonDistanceSingleton() - panphon_preprocessor = g2p.transducer.Transducer(Mapping(id="panphon_preprocessor")) + panphon_preprocessor = g2p.transducer.Transducer( + Mapping.find_mapping_by_id("panphon_preprocessor") + ) preprocessed_string = panphon_preprocessor(string).output_string # Use a loop that prints the warnings on all strings that are not panphon, even though # logically this should not be necessary to calculate the answer. @@ -126,7 +126,9 @@ def is_arpabet(string): global _ARPABET_SET if _ARPABET_SET is None: _ARPABET_SET = set( - Mapping(in_lang="eng-ipa", out_lang="eng-arpabet").inventory("out") + Mapping.find_mapping(in_lang="eng-ipa", out_lang="eng-arpabet").inventory( + "out" + ) ) # print(f"arpabet_set={_ARPABET_SET}") for sound in string.split(): @@ -172,9 +174,15 @@ def cache_langs( f"language_name missing in {path} from mapping " f"from {in_lang} to {out_lang}" ) - data["mappings"][index] = load_mapping_from_path(path, index) + data["mappings"][index] = json.loads( + Mapping.load_mapping_from_path(path, index).model_dump_json( + exclude={"parent_dir": True} + ) + ) + # if "abbreviations" in data['mappings'][index] and data['mappings'][index]['abbreviations'] is not None: + # breakpoint() else: - data = load_mapping_from_path(path) + data = Mapping.load_mapping_from_path(path) if "language_name" not in data: raise MalformedMapping(f"language_name missing in {path}") langs[code] = data diff --git a/g2p/mappings/langs/win/config.yaml b/g2p/mappings/langs/win/config.yaml index 30c45fcb..1d7b3ed1 100644 --- a/g2p/mappings/langs/win/config.yaml +++ b/g2p/mappings/langs/win/config.yaml @@ -2,7 +2,7 @@ language_name: Hoocąk mappings: - display_name: Hoocąk to IPA - mapping: win_to_ipa.json + rules: win_to_ipa.json in_lang: win out_lang: win-ipa case_sensitive: false diff --git a/g2p/mappings/tokenizer.py b/g2p/mappings/tokenizer.py index a383b193..a480e665 100644 --- a/g2p/mappings/tokenizer.py +++ b/g2p/mappings/tokenizer.py @@ -65,8 +65,8 @@ class SpecializedTokenizer(Tokenizer): def __init__(self, mapping: Mapping): self.delim = "" self.inventory = mapping.inventory("in") - self.lang = mapping.mapping_config.language_name - self.case_sensitive = mapping.mapping_config.case_sensitive + self.lang = mapping.language_name + self.case_sensitive = mapping.case_sensitive self.dot_is_letter = False # create regex self._build_regex() @@ -103,8 +103,8 @@ def __init__(self, mappings: List[Mapping]): self.delim = "" assert mappings self.inventory = sum([m.inventory("in") for m in mappings], []) - self.lang = mappings[0].mapping_config.language_name - self.case_sensitive = mappings[0].mapping_config.case_sensitive + self.lang = mappings[0].language_name + self.case_sensitive = mappings[0].case_sensitive self.dot_is_letter = False self._build_regex() # LOGGER.warning(pprint.pformat([self.lang, self.delim, self.case_sensitive, self.inventory])) @@ -183,10 +183,14 @@ def make_tokenizer( # noqa C901 # Build a multi-hop tokenizer assert len(out_lang) > 1 try: - mappings = [Mapping(in_lang=in_lang, out_lang=out_lang[0])] + mappings = [ + Mapping.find_mapping(in_lang=in_lang, out_lang=out_lang[0]) + ] for i in range(1, len(out_lang)): mappings.append( - Mapping(in_lang=out_lang[i - 1], out_lang=out_lang[i]) + Mapping.find_mapping( + in_lang=out_lang[i - 1], out_lang=out_lang[i] + ) ) self.tokenizers[tokenizer_key] = MultiHopTokenizer(mappings) except MappingMissing: @@ -197,7 +201,7 @@ def make_tokenizer( # noqa C901 else: # Build a one-hop tokenizer try: - mapping = Mapping(in_lang=in_lang, out_lang=out_lang) + mapping = Mapping.find_mapping(in_lang=in_lang, out_lang=out_lang) self.tokenizers[tokenizer_key] = SpecializedTokenizer(mapping) except MappingMissing: self.tokenizers[tokenizer_key] = self.tokenizers[None] diff --git a/g2p/mappings/utils.py b/g2p/mappings/utils.py index 18f9b162..f7a5fac8 100644 --- a/g2p/mappings/utils.py +++ b/g2p/mappings/utils.py @@ -17,7 +17,15 @@ import regex as re import yaml -from pydantic import BaseModel, DirectoryPath, Extra, Field, validator +from pydantic import ( + BaseModel, + ConfigDict, + DirectoryPath, + Field, + field_serializer, + field_validator, + validator, +) from g2p import exceptions from g2p.log import LOGGER @@ -41,24 +49,19 @@ class Rule(BaseModel): context_after: str = "" """The context after 'in' required for the rule to apply""" - prevent_feeding = False + prevent_feeding: bool = False """Whether to prevent the rule from feeding other rules""" - match_pattern: Optional[Pattern] + match_pattern: Optional[Pattern] = None """An automatically generated match_pattern basec on the in_char, context_before and context_after""" - intermediate_form: Optional[str] + intermediate_form: Optional[str] = None """An optional intermediate form. Should be automatically generated only when prevent_feeding is True""" - comment: Optional[str] + comment: Optional[str] = None """An optional comment about the rule.""" - class Config: - # I'm opting to just ignore extra fields - # We can't ban them, since some mappings (ex. crj_equiv.json) - # use fields that we shouldn't include, but shouldn't remove either ('roman') - extra = Extra.ignore - allow_population_by_field_name = True + model_config = ConfigDict(extra="ignore", populate_by_name=True) def export_to_dict( self, exclude=None, exclude_none=True, exclude_defaults=True, by_alias=True @@ -66,7 +69,7 @@ def export_to_dict( """All the options for exporting are tedious to keep track of so this is a helper function""" if exclude is None: exclude = {"match_pattern": True, "intermediate_form": True} - return self.dict( + return self.model_dump( exclude=exclude, exclude_none=exclude_none, exclude_defaults=exclude_defaults, @@ -364,7 +367,7 @@ def load_from_file(path: Union[Path, str]) -> list: raise exceptions.IncorrectFileType( f"File {path} is not a valid mapping filetype." ) - return validate(mapping, path) + return mapping def find_mapping_type(name): @@ -379,65 +382,6 @@ def find_mapping_type(name): return "custom" -def load_mapping_from_path(path_to_mapping_config, index=0): - """Loads a mapping from a path, if there is more than one mapping, then it loads based on the int - provided to the 'index' argument. Default is 0. - """ - path = Path(path_to_mapping_config) - parent_dir = path.parent - with open(path, encoding="utf8") as f: - loaded_config = yaml.safe_load(f) - if not isinstance(loaded_config, dict): - raise exceptions.MalformedMapping( - f"The mapping config at {path} is malformed, please check it is properly formed." - ) - if "mappings" in loaded_config: - loaded_config = loaded_config["mappings"][index] - loaded_config["parent_dir"] = parent_dir - return _MappingModelDefinition(**loaded_config) - - -def find_mapping(in_lang: str, out_lang: str) -> list: - """Given an input and output, find a mapping to get between them.""" - for mapping in langs.MAPPINGS_AVAILABLE: - if isinstance(mapping, _MappingModelDefinition): - map_in_lang = mapping.in_lang - map_out_lang = mapping.out_lang - mapping_type = mapping.type - else: - map_in_lang = mapping.get("in_lang", "") - map_out_lang = mapping.get("out_lang", "") - mapping_type = mapping.get("type") - if map_in_lang == in_lang and map_out_lang == out_lang: - if mapping_type == "lexicon": - # do *not* deep copy this, because alignments are big! - return mapping.copy() - else: - return deepcopy(mapping) - raise exceptions.MappingMissing(in_lang, out_lang) - - -def validate(mapping, path): - try: - for io in mapping: - if "context_before" not in io: - io["context_before"] = "" - if "context_after" not in io: - io["context_after"] = "" - valid = all("in" in d for d in mapping) and all("out" in d for d in mapping) - if not valid: - raise exceptions.MalformedMapping( - 'Missing "in" or "out" in an entry in {}.'.format(path) - ) - return mapping - except TypeError as e: - # The JSON probably is not just a list (ie could be legacy readalongs format) - # TODO: proper exception handling - raise exceptions.MalformedMapping( - "Formatting error in mapping in {}.".format(path) - ) from e - - def escape_special_characters(to_escape: Union[Rule, Dict[str, str]]) -> Rule: if isinstance(to_escape, dict): to_escape = Rule(**to_escape) @@ -695,10 +639,10 @@ class RULE_ORDERING_ENUM(str, Enum): class _MappingModelDefinition(BaseModel): - parent_dir: Optional[DirectoryPath] + parent_dir: Optional[DirectoryPath] = None """Optionally resolve all paths to a parent directory""" - id: Optional[str] + id: Optional[str] = None """A unique ID for the mapping""" in_lang: str = "und" @@ -707,13 +651,13 @@ class _MappingModelDefinition(BaseModel): out_lang: str = "und" """The output language ID""" - language_name: Optional[str] + language_name: Optional[str] = None """The name of the language""" - display_name: Optional[str] + display_name: Optional[str] = None """The display name of the mapping""" - as_is: Optional[bool] + as_is: Optional[bool] = None """Deprecated: Please use rule_ordering='as_written' """ case_sensitive: bool = True @@ -747,31 +691,48 @@ class _MappingModelDefinition(BaseModel): prevent_feeding: bool = False """Converts each rule into an intermediary form in the Unicode PUA""" - type: Optional[MAPPING_TYPE] + type: Optional[MAPPING_TYPE] = None """Type of mapping, either "mapping" (rules), "unidecode" (magical Unicode guessing) or "lexicon" (lookup in an aligned lexicon).""" - alignments: Optional[Union[str, List[str]]] + alignments: Optional[Union[str, List[str]]] = None """A string specifying a file from which to load alignments when type = "lexicon", or the actual alignments.""" authors: Optional[List[str]] = [f"Generated {dt.datetime.now()}"] """A list of authors responsible for the mapping.""" - abbreviations: Optional[Union[Path, Dict[str, List[str]]]] + abbreviations: Optional[Union[Path, Dict[str, List[str]]]] = None """Either a path to an 'abbreviations' file or a list of 'abbreviations' for your mappings. Please see https://blog.mothertongues.org/g2p-advanced-mappings/ for more information.""" - mapping: Optional[Union[Path, List[Rule]]] - """Either a path to a 'mapping' file or a list of _Rules""" + rules: Optional[Union[Path, List[Rule]]] = None + """Either a path to a file of a list of rules or a list of Rules""" + + model_config = ConfigDict(str_strip_whitespace=False, extra="allow") + + @field_serializer("rules") + def serialize_mapping(self, rules: Union[List[Rule], None]): + if not rules: + return rules + serialized = [] + for rule in rules: + if isinstance(rule, dict): + rule = Rule(**rule) + serialized.append(rule.export_to_dict()) + return serialized - class Config: - anystr_strip_whitespace = False + @field_serializer("abbreviations") + def serialize_abbreviations(self, abbreviations: Union[dict, None]): + return dict(abbreviations) if abbreviations else abbreviations - @validator("norm_form", pre=True) + @field_validator("norm_form", mode="before") + @classmethod def validate_norm_form(cls, v): if not v or v is None: v = "none" return v + # TODO[pydantic]: We couldn't refactor the `validator`, please replace it by `field_validator` manually. + # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators for more information. @validator("alignments") def load_alignments(cls, alignments, values): if isinstance(alignments, list) or alignments is None: @@ -786,6 +747,8 @@ def load_alignments(cls, alignments, values): alignments = Path(values["parent_dir"]) / alignments return load_alignments_from_file(str(alignments.absolute())) + # TODO[pydantic]: We couldn't refactor the `validator`, please replace it by `field_validator` manually. + # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators for more information. @validator("abbreviations") def load_abbs(cls, abb, values): if isinstance(abb, dict) or abb is None: @@ -796,22 +759,22 @@ def load_abbs(cls, abb, values): abb = Path(values["parent_dir"]) / abb return load_abbreviations_from_file(str(abb.absolute())) - @validator("mapping") - def load_mapping(cls, mapping, values): - if isinstance(mapping, str): - mapping = Path(mapping) - if ( - "parent_dir" in values - and isinstance(mapping, Path) - and values["parent_dir"] - ): - mapping = Path(values["parent_dir"]) / mapping + # TODO[pydantic]: We couldn't refactor the `validator`, please replace it by `field_validator` manually. + # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators for more information. + @validator("rules", pre=True) + def load_mapping(cls, rules, values): + if rules is None: + return [] + if isinstance(rules, str): + rules = Path(rules) + if "parent_dir" in values and isinstance(rules, Path) and values["parent_dir"]: + rules = Path(values["parent_dir"]) / rules return ( - mapping - if isinstance(mapping, list) - else load_from_file(str(mapping.absolute())) + rules if isinstance(rules, list) else load_from_file(str(rules.absolute())) ) + # TODO[pydantic]: We couldn't refactor the `validator`, please replace it by `field_validator` manually. + # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators for more information. @validator("language_name", always=True) def create_language_name(cls, language_name, values): # Default language name to in_lang @@ -819,6 +782,8 @@ def create_language_name(cls, language_name, values): language_name = values["in_lang"] return language_name + # TODO[pydantic]: We couldn't refactor the `validator`, please replace it by `field_validator` manually. + # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators for more information. @validator("display_name", always=True) def create_display_name(cls, display_name, values): if display_name is None: diff --git a/g2p/tests/public/mappings/abbreviation_config.yaml b/g2p/tests/public/mappings/abbreviation_config.yaml index a3aaadc2..ab18a07a 100644 --- a/g2p/tests/public/mappings/abbreviation_config.yaml +++ b/g2p/tests/public/mappings/abbreviation_config.yaml @@ -1,6 +1,6 @@ language_name: Abbreviation display_name: Minimal to Minimal -mapping: abbreviation_mapping.csv +rules: abbreviation_mapping.csv in_lang: min out_lang: min rule_ordering: as-written diff --git a/g2p/tests/public/mappings/bad_langs/lang1/config.yaml b/g2p/tests/public/mappings/bad_langs/lang1/config.yaml index 55514bb0..f899176f 100644 --- a/g2p/tests/public/mappings/bad_langs/lang1/config.yaml +++ b/g2p/tests/public/mappings/bad_langs/lang1/config.yaml @@ -1,5 +1,5 @@ display_name: Minimal to Minimal -mapping: minimal.csv +rules: minimal.csv in_lang: min out_lang: min rule_ordering: as-written diff --git a/g2p/tests/public/mappings/bad_langs2/lang1/config.yaml b/g2p/tests/public/mappings/bad_langs2/lang1/config.yaml index d3bbf368..1c47b4b0 100644 --- a/g2p/tests/public/mappings/bad_langs2/lang1/config.yaml +++ b/g2p/tests/public/mappings/bad_langs2/lang1/config.yaml @@ -1,6 +1,6 @@ mappings: - display_name: Minimal to Minimal - mapping: minimal.csv + rules: minimal.csv in_lang: min out_lang: min rule_ordering: as-written diff --git a/g2p/tests/public/mappings/case-feed/config.yaml b/g2p/tests/public/mappings/case-feed/config.yaml index c2072b02..ec1ff8f6 100644 --- a/g2p/tests/public/mappings/case-feed/config.yaml +++ b/g2p/tests/public/mappings/case-feed/config.yaml @@ -2,7 +2,7 @@ language_name: Example using case to prevent feeding rules but feed context mappings: - display name: case-feed input lowercaser - mapping: empty.csv + rules: empty.csv in_lang: cf-in out_lang: cf-in-lc case_sensitive: false @@ -10,7 +10,7 @@ mappings: - Eric Joanis <<: *shared - display_name: case-feed main mapping in is lc, out is uc, thus no feeding - mapping: cf-in-lc-to-cf-out-uc.csv + rules: cf-in-lc-to-cf-out-uc.csv in_lang: cf-in-lc out_lang: cf-out-uc case_sensitive: true @@ -19,7 +19,7 @@ mappings: - Eric Joanis <<: *shared - display name: case-feed output lowercaser - mapping: empty.csv + rules: empty.csv in_lang: cf-out-uc out_lang: cf-out case_sensitive: false diff --git a/g2p/tests/public/mappings/compose.yaml b/g2p/tests/public/mappings/compose.yaml index 816c81ef..36e715ab 100644 --- a/g2p/tests/public/mappings/compose.yaml +++ b/g2p/tests/public/mappings/compose.yaml @@ -2,17 +2,16 @@ language_name: Composition tests mappings: - display_name: Step 1 - mapping: compose1-2.csv + rules: compose1-2.csv in_lang: c1 out_lang: c2 norm_form: NFC authors: - Eric Joanis - display_name: Step 2 - mapping: compose2-3.csv + rules: compose2-3.csv in_lang: c2 out_lang: c3 norm_form: NFD authors: - Eric Joanis - diff --git a/g2p/tests/public/mappings/deletion_config_csv.yaml b/g2p/tests/public/mappings/deletion_config_csv.yaml index 09ddbe0d..31edce31 100644 --- a/g2p/tests/public/mappings/deletion_config_csv.yaml +++ b/g2p/tests/public/mappings/deletion_config_csv.yaml @@ -1,6 +1,6 @@ language_name: Minimal display_name: Minimal to Minimal -mapping: deletion.csv +rules: deletion.csv in_lang: min out_lang: min rule_ordering: as-written diff --git a/g2p/tests/public/mappings/deletion_config_json.yaml b/g2p/tests/public/mappings/deletion_config_json.yaml index 64281d49..be839a3d 100644 --- a/g2p/tests/public/mappings/deletion_config_json.yaml +++ b/g2p/tests/public/mappings/deletion_config_json.yaml @@ -1,6 +1,6 @@ language_name: Minimal display_name: Minimal to Minimal -mapping: deletion.json +rules: deletion.json in_lang: min out_lang: min rule_ordering: as-written diff --git a/g2p/tests/public/mappings/gen-map_config.yaml b/g2p/tests/public/mappings/gen-map_config.yaml index f5345da3..165bfc33 100644 --- a/g2p/tests/public/mappings/gen-map_config.yaml +++ b/g2p/tests/public/mappings/gen-map_config.yaml @@ -4,20 +4,20 @@ - Eric Joanis mappings: - display_name: GenMap 1 to IPA - mapping: gen-map-1.csv + rules: gen-map-1.csv in_lang: gm1 out_lang: gm1-ipa <<: *shared - display_name: GenMap 2 to IPA - mapping: gen-map-2.csv + rules: gen-map-2.csv in_lang: gm2 out_lang: gm2-ipa <<: *shared - display_name: GenMap 3a to IPA - mapping: gen-map-3a.csv + rules: gen-map-3a.csv in_lang: gm3a out_lang: gm3-ipa - display_name: GenMap 3b to IPA - mapping: gen-map-3b.csv + rules: gen-map-3b.csv in_lang: gm3b out_lang: gm3-ipa diff --git a/g2p/tests/public/mappings/minimal_config.yaml b/g2p/tests/public/mappings/minimal_config.yaml index 6f101d60..1c34d0bd 100644 --- a/g2p/tests/public/mappings/minimal_config.yaml +++ b/g2p/tests/public/mappings/minimal_config.yaml @@ -1,6 +1,6 @@ language_name: Minimal display_name: Minimal to Minimal -mapping: minimal.csv +rules: minimal.csv in_lang: min out_lang: min rule_ordering: as-written diff --git a/g2p/tests/public/mappings/minimal_configs.yaml b/g2p/tests/public/mappings/minimal_configs.yaml index 294b19b4..f9d01b58 100644 --- a/g2p/tests/public/mappings/minimal_configs.yaml +++ b/g2p/tests/public/mappings/minimal_configs.yaml @@ -3,7 +3,7 @@ mappings: - language_name: Minimal display_name: Minimal CSV to Minimal - mapping: minimal.csv + rules: minimal.csv in_lang: min out_lang: min rule_ordering: as-written @@ -16,7 +16,7 @@ mappings: <<: *shared - language_name: Minimal display_name: Minimal TSV to Minimal - mapping: minimal.tsv + rules: minimal.tsv in_lang: min out_lang: min rule_ordering: as-written @@ -29,7 +29,7 @@ mappings: <<: *shared - language_name: Minimal display_name: Minimal PSV to Minimal - mapping: minimal.psv + rules: minimal.psv in_lang: min out_lang: min rule_ordering: as-written @@ -42,7 +42,7 @@ mappings: <<: *shared - language_name: Minimal display_name: Minimal JSON to Minimal - mapping: minimal.json + rules: minimal.json in_lang: min out_lang: min rule_ordering: as-written @@ -55,7 +55,7 @@ mappings: <<: *shared - language_name: Minimal display_name: Minimal XLSX to Minimal - mapping: minimal.xlsx + rules: minimal.xlsx in_lang: min out_lang: min rule_ordering: as-written diff --git a/g2p/tests/public/mappings/null_config.yaml b/g2p/tests/public/mappings/null_config.yaml index adaece46..bf59c927 100644 --- a/g2p/tests/public/mappings/null_config.yaml +++ b/g2p/tests/public/mappings/null_config.yaml @@ -1,6 +1,6 @@ language_name: Null display_name: Null to Null -mapping: null.csv +rules: null.csv in_lang: null-in out_lang: null-out rule_ordering: as-written diff --git a/g2p/tests/public/mappings/rule-ordering.yaml b/g2p/tests/public/mappings/rule-ordering.yaml index 5d53f1e0..1d7d3f29 100644 --- a/g2p/tests/public/mappings/rule-ordering.yaml +++ b/g2p/tests/public/mappings/rule-ordering.yaml @@ -1,6 +1,6 @@ language_name: Minimal display_name: Minimal to Minimal -mapping: minimal.csv +rules: minimal.csv in_lang: min out_lang: min rule_ordering: apply-longest-first diff --git a/g2p/tests/public/mappings/test.yaml b/g2p/tests/public/mappings/test.yaml index 9e32d997..5239dffe 100644 --- a/g2p/tests/public/mappings/test.yaml +++ b/g2p/tests/public/mappings/test.yaml @@ -6,7 +6,7 @@ mappings: type: mapping authors: - Aidan Pine - mapping: test_to_ipa.csv + rules: test_to_ipa.csv - language_name: Local Config display_name: Local Config to IPA in_lang: local-config-in @@ -14,4 +14,4 @@ mappings: type: mapping authors: - Aidan Pine - mapping: test_to_ipa.csv + rules: test_to_ipa.csv diff --git a/g2p/tests/public/mappings/tokenize_punct_config.yaml b/g2p/tests/public/mappings/tokenize_punct_config.yaml index 54dbff57..1ef12224 100644 --- a/g2p/tests/public/mappings/tokenize_punct_config.yaml +++ b/g2p/tests/public/mappings/tokenize_punct_config.yaml @@ -1,6 +1,6 @@ language_name: tok punct display_name: Tokenize Punctuation Case Insensitive -mapping: tokenize_punct.csv +rules: tokenize_punct.csv comment: "test mapping for Readalongs-Studio issue #40" issue_url: "https://github.com/ReadAlongs/Studio/issues/40" in_lang: tok-in diff --git a/g2p/tests/test_create_mapping.py b/g2p/tests/test_create_mapping.py index 60ea920d..2161c2b7 100755 --- a/g2p/tests/test_create_mapping.py +++ b/g2p/tests/test_create_mapping.py @@ -32,7 +32,10 @@ def setUp(self): {"in": "ʒ", "out": "ZH"}, ] self.target_mapping = Mapping( - self.mappings, in_lang="eng-ipa", out_lang="eng-arpabet", out_delimiter=" " + rules=self.mappings, + in_lang="eng-ipa", + out_lang="eng-arpabet", + out_delimiter=" ", ) def test_unigram_mappings(self): @@ -41,7 +44,7 @@ def test_unigram_mappings(self): {"in": "ᐅ", "out": "u"}, {"in": "ᐊ", "out": "a"}, ] - src_mapping = Mapping(src_mappings, in_lang="crj", out_lang="crj-ipa") + src_mapping = Mapping(rules=src_mappings, in_lang="crj", out_lang="crj-ipa") mapping = create_mapping(src_mapping, self.target_mapping, quiet=True) transducer = Transducer(mapping) self.assertEqual(transducer("a").output_string, "ɑ") @@ -54,7 +57,7 @@ def test_bigram_mappings(self): {"in": "ᑎ", "out": "ti"}, {"in": "ᑭ", "out": "ki"}, ] - src_mapping = Mapping(src_mappings, in_lang="crj", out_lang="crj-ipa") + src_mapping = Mapping(rules=src_mappings, in_lang="crj", out_lang="crj-ipa") mapping = create_mapping(src_mapping, self.target_mapping, quiet=True) transducer = Transducer(mapping) self.assertEqual(transducer("pi").output_string, "pi") @@ -67,7 +70,7 @@ def test_trigram_mappings(self): {"in": "ᒍ", "out": "t͡ʃu"}, {"in": "ᒐ", "out": "t͡ʃa"}, ] - src_mapping = Mapping(src_mappings, in_lang="crj", out_lang="crj-ipa") + src_mapping = Mapping(rules=src_mappings, in_lang="crj", out_lang="crj-ipa") mapping = create_mapping(src_mapping, self.target_mapping, quiet=True) transducer = Transducer(mapping) self.assertEqual(transducer("t͡ʃi").output_string, "tʃi") @@ -80,7 +83,7 @@ def test_long_mappings(self): {"in": "ᐧᑌ", "out": "tʷeː"}, {"in": "ᐧᑫ", "out": "kʷeː"}, ] - src_mapping = Mapping(src_mappings, in_lang="crj", out_lang="crj-ipa") + src_mapping = Mapping(rules=src_mappings, in_lang="crj", out_lang="crj-ipa") mapping = create_mapping(src_mapping, self.target_mapping, quiet=True) transducer = Transducer(mapping) self.assertEqual(transducer("pʷeː").output_string, "pweː") @@ -89,7 +92,7 @@ def test_long_mappings(self): def test_distance_errors(self): src_mappings = [{"in": "ᐃ", "out": "i"}] - src_mapping = Mapping(src_mappings, in_lang="crj", out_lang="crj-ipa") + src_mapping = Mapping(rules=src_mappings, in_lang="crj", out_lang="crj-ipa") # Exercise looking up distances in the known list with self.assertRaises(ValueError): _ = create_mapping( @@ -129,18 +132,18 @@ def test_distances(self): {"in": "ᒋ", "out": "t͡ʃi"}, {"in": "ᕃ", "out": "ʁaj"}, ] - src_mapping = Mapping(src_mappings, in_lang="crj", out_lang="crj-ipa") + src_mapping = Mapping(rules=src_mappings, in_lang="crj", out_lang="crj-ipa") mapping = create_mapping(src_mapping, self.target_mapping, quiet=True) # print("mapping", mapping, list(mapping), "distance", "default") self.assertTrue(isinstance(mapping, Mapping)) - set_of_mappings = {tuple(rule.out_char for rule in mapping.mapping)} + set_of_mappings = {tuple(rule.out_char for rule in mapping.rules)} for distance in DISTANCE_METRICS: mapping = create_mapping( src_mapping, self.target_mapping, distance=distance, quiet=True ) # print("mapping", mapping, list(mapping), "distance", distance) self.assertTrue(isinstance(mapping, Mapping)) - set_of_mappings.add(tuple(rule.out_char for rule in mapping.mapping)) + set_of_mappings.add(tuple(rule.out_char for rule in mapping.rules)) mapping = create_multi_mapping( [(src_mapping, "out")], @@ -149,7 +152,7 @@ def test_distances(self): quiet=True, ) self.assertTrue(isinstance(mapping, Mapping)) - set_of_mappings.add(tuple(rule.out_char for rule in mapping.mapping)) + set_of_mappings.add(tuple(rule.out_char for rule in mapping.rules)) self.assertGreater(len(set_of_mappings), 3) diff --git a/g2p/tests/test_fallback.py b/g2p/tests/test_fallback.py index 959942a9..ea39dbc3 100755 --- a/g2p/tests/test_fallback.py +++ b/g2p/tests/test_fallback.py @@ -21,7 +21,7 @@ def setUp(self): def test_mapping(self): self.maxDiff = None mapping = Mapping( - [ + rules=[ {"in": "a", "out": "æ"}, {"in": "e", "out": "ɐ"}, {"in": "i", "out": "ɑ̃"}, @@ -34,7 +34,7 @@ def test_mapping(self): out_lang="test-out", ) ipa_mapping = Mapping( - [ + rules=[ {"in": "a", "out": "æ"}, {"in": "e", "out": "ɐ"}, {"in": "i", "out": "ɑ̃"}, @@ -46,7 +46,7 @@ def test_mapping(self): ) test_in = align_to_dummy_fallback(mapping, quiet=True) self.assertEqual( - test_in.mapping, + test_in.rules, [ Rule(in_char="a", out_char="ɑ", match_pattern="a"), Rule(in_char="e", out_char="i", match_pattern="e"), @@ -60,7 +60,7 @@ def test_mapping(self): test_out = align_to_dummy_fallback(mapping, "out", quiet=True) self.assertEqual( - test_out.mapping, + test_out.rules, [ Rule(in_char="æ", out_char="ɑi", match_pattern="æ"), Rule(in_char="ɐ", out_char="ɑ", match_pattern="ɐ"), @@ -73,7 +73,7 @@ def test_mapping(self): ) test_ipa = align_to_dummy_fallback(ipa_mapping, "out", quiet=True) self.assertEqual( - test_ipa.mapping, + test_ipa.rules, [ Rule(in_char="æ", out_char="ɑ", match_pattern="æ"), Rule(in_char="ɐ", out_char="ɑ", match_pattern="ɐ"), diff --git a/g2p/tests/test_indices.py b/g2p/tests/test_indices.py index 96f7d5e0..6e5b68b3 100755 --- a/g2p/tests/test_indices.py +++ b/g2p/tests/test_indices.py @@ -176,36 +176,40 @@ def __init__(self, *args): # Let's use __init__() to set all these up just once at class creation # time, instead of setUp() which repeatedly does it for each test case super().__init__(*args) - self.test_mapping_one = Mapping([{"in": "t", "out": "p", "context_after": "e"}]) - self.test_mapping_two = Mapping([{"in": "e", "out": ""}]) + self.test_mapping_one = Mapping( + rules=[{"in": "t", "out": "p", "context_after": "e"}] + ) + self.test_mapping_two = Mapping(rules=[{"in": "e", "out": ""}]) self.test_mapping_three = Mapping( - [{"in": "t", "out": "ch", "context_after": "e"}] + rules=[{"in": "t", "out": "ch", "context_after": "e"}] ) - self.test_mapping_four = Mapping([{"in": "te", "out": "p"}]) + self.test_mapping_four = Mapping(rules=[{"in": "te", "out": "p"}]) # We know this issues a warning, so let's silence it by asserting it. with self.assertLogs(LOGGER, "WARNING"): self.test_mapping_five = Mapping( - [{"context_before": "t", "context_after": "$", "in": "", "out": "y"}] + rules=[ + {"context_before": "t", "context_after": "$", "in": "", "out": "y"} + ] ) - self.test_mapping_six = Mapping([{"in": "e{1}s{2}", "out": "s{2}e{1}"}]) + self.test_mapping_six = Mapping(rules=[{"in": "e{1}s{2}", "out": "s{2}e{1}"}]) self.test_mapping_seven = Mapping( - [{"in": "s", "out": "sh"}, {"in": "sh", "out": "s"}], + rules=[{"in": "s", "out": "sh"}, {"in": "sh", "out": "s"}], rule_ordering="apply-longest-first", ) self.test_mapping_seven_as_written = Mapping( - [{"in": "s", "out": "sh"}, {"in": "sh", "out": "s"}] + rules=[{"in": "s", "out": "sh"}, {"in": "sh", "out": "s"}] ) self.test_mapping_eight = Mapping( - [{"in": "te", "out": "che"}, {"in": "t", "out": "s"}] + rules=[{"in": "te", "out": "che"}, {"in": "t", "out": "s"}] ) - self.test_mapping_nine = Mapping([{"in": "aa", "out": ""}]) - self.test_mapping_ten = Mapping([{"in": "abc", "out": "a"}]) - self.test_mapping_eleven = Mapping([{"in": "a", "out": "aaaa"}]) + self.test_mapping_nine = Mapping(rules=[{"in": "aa", "out": ""}]) + self.test_mapping_ten = Mapping(rules=[{"in": "abc", "out": "a"}]) + self.test_mapping_eleven = Mapping(rules=[{"in": "a", "out": "aaaa"}]) self.test_mapping_combining = Mapping( - [{"in": "k{1}\u0313{2}", "out": "'{2}k{1}"}] + rules=[{"in": "k{1}\u0313{2}", "out": "'{2}k{1}"}] ) self.test_mapping_wacky = Mapping( - [ + rules=[ { "in": "\U0001f600{1}\U0001f603\U0001f604{2}\U0001f604{3}", "out": "\U0001f604\U0001f604\U0001f604{2}\U0001f604{3}\U0001f604{1}", @@ -213,31 +217,35 @@ def __init__(self, *args): ] ) self.test_mapping_wacky_lite = Mapping( - [{"in": "a{1}bc{2}c{3}", "out": "ccc{2}c{3}c{1}"}] + rules=[{"in": "a{1}bc{2}c{3}", "out": "ccc{2}c{3}c{1}"}] + ) + self.test_mapping_circum = Mapping( + rules=[{"in": "a{1}c{2}", "out": "c{2}a{1}c{2}"}] ) - self.test_mapping_circum = Mapping([{"in": "a{1}c{2}", "out": "c{2}a{1}c{2}"}]) self.test_mapping_explicit_equal_1 = Mapping( - [{"in": "a{1}b{1}", "out": "c{1}d{1}"}] + rules=[{"in": "a{1}b{1}", "out": "c{1}d{1}"}] ) - self.test_mapping_explicit_equal_2 = Mapping([{"in": "ab{1}", "out": "cd{1}"}]) - self.test_mapping_explicit_equal_3 = Mapping([{"in": "ab", "out": "cd"}]) + self.test_mapping_explicit_equal_2 = Mapping( + rules=[{"in": "ab{1}", "out": "cd{1}"}] + ) + self.test_mapping_explicit_equal_3 = Mapping(rules=[{"in": "ab", "out": "cd"}]) self.test_mapping_explicit_equal_4 = Mapping( - [{"in": "a{1}b{2}", "out": "c{1}d{2}"}] + rules=[{"in": "a{1}b{2}", "out": "c{1}d{2}"}] ) self.test_issue_173_1 = Mapping( - [ + rules=[ {"in": "x{1}y{2}z{3}", "out": "a{2}b{1}"}, {"in": "d{1}e{2}f{3}", "out": "d{1}e{2}f{3}"}, ] ) self.test_issue_173_2 = Mapping( - [ + rules=[ {"in": "x{1}y{2}z{3}", "out": "a{1}b{2}"}, {"in": "d{1}e{2}f{3}", "out": "d{1}e{2}f{3}"}, ] ) self.test_issue_157_mapping = Mapping( - [ + rules=[ {"in": "a", "out": "d"}, {"in": "bc", "out": "e"}, {"in": "g{1}h{2}i{3}", "out": "G{2}H{1}I{3}J{1}"}, @@ -245,13 +253,13 @@ def __init__(self, *args): ] ) self.test_feeding_mapping_1 = Mapping( - [{"in": "ab", "out": "a"}, {"in": "a", "out": "cd"}] + rules=[{"in": "ab", "out": "a"}, {"in": "a", "out": "cd"}] ) self.test_feeding_mapping_2 = Mapping( - [{"in": "a", "out": "cd"}, {"in": "cd", "out": "b"}] + rules=[{"in": "a", "out": "cd"}, {"in": "cd", "out": "b"}] ) - self.test_issue_173_3 = Mapping([{"in": "ab{1}c{2}", "out": "X{1}Y{2}"}]) - self.test_issue_173_4 = Mapping([{"in": "a{1}bc{2}", "out": "xy{1}z{2}"}]) + self.test_issue_173_3 = Mapping(rules=[{"in": "ab{1}c{2}", "out": "X{1}Y{2}"}]) + self.test_issue_173_4 = Mapping(rules=[{"in": "a{1}bc{2}", "out": "xy{1}z{2}"}]) self.trans_one = Transducer(self.test_mapping_one) self.trans_two = Transducer(self.test_mapping_two) self.trans_three = Transducer(self.test_mapping_three) @@ -531,7 +539,7 @@ def test_case_twelve(self): # Empty inputs are not allowed (should it actually throw an exception?) with self.assertLogs() as cm: self.test_mapping_twelve = Mapping( - [{"in": "", "out": "aa", "context_before": "b"}] + rules=[{"in": "", "out": "aa", "context_before": "b"}] ) self.trans_twelve = Transducer(self.test_mapping_twelve) transducer = self.trans_twelve("b") @@ -543,7 +551,9 @@ def test_case_twelve(self): self.assertEqual(transducer.output_string, "b") def test_case_acdc(self): - transducer = Transducer(Mapping([{"in": "a{1}c{2}", "out": "c{2}a{1}c{2}"}])) + transducer = Transducer( + Mapping(rules=[{"in": "a{1}c{2}", "out": "c{2}a{1}c{2}"}]) + ) tg = transducer("acdc") self.assertEqual(tg.output_string, "cacdc") self.assertEqual(tg.edges, [(0, 1), (1, 0), (1, 2), (2, 3), (3, 4)]) @@ -552,9 +562,9 @@ def test_case_acdc(self): ) def test_case_acac(self): - transducer = Transducer(Mapping([{"in": "ab{1}c{2}", "out": "ab{2}"}])) + transducer = Transducer(Mapping(rules=[{"in": "ab{1}c{2}", "out": "ab{2}"}])) transducer_default = Transducer( - Mapping([{"in": "ab", "out": ""}, {"in": "c", "out": "ab"}]) + Mapping(rules=[{"in": "ab", "out": ""}, {"in": "c", "out": "ab"}]) ) tg = transducer("abcabc") self.assertEqual(tg.output_string, "abab") @@ -593,10 +603,14 @@ def test_case_acac(self): def test_arpabet(self): transducer = Transducer( - Mapping([{"in": "ĩ", "out": "IY N"}], norm_form="NFC", out_delimiter=" ") + Mapping( + rules=[{"in": "ĩ", "out": "IY N"}], norm_form="NFC", out_delimiter=" " + ) ) transducer_nfd = Transducer( - Mapping([{"in": "ĩ", "out": "IY N"}], norm_form="NFD", out_delimiter=" ") + Mapping( + rules=[{"in": "ĩ", "out": "IY N"}], norm_form="NFD", out_delimiter=" " + ) ) tg = transducer(normalize("NFC", "ĩĩ")) tg_nfd = transducer_nfd(normalize("NFD", "ĩĩ")) diff --git a/g2p/tests/test_lexicon_transducer.py b/g2p/tests/test_lexicon_transducer.py index 9494bbde..67fd842a 100644 --- a/g2p/tests/test_lexicon_transducer.py +++ b/g2p/tests/test_lexicon_transducer.py @@ -22,8 +22,8 @@ def test_lexicon_mapping(self): os.path.dirname(public_data), "mappings", "hello.aligned.txt" ), ) - self.assertEqual(m.mapping, []) - self.assertEqual(m.mapping_config.type, "lexicon") + self.assertEqual(m.rules, []) + self.assertEqual(m.type, "lexicon") t = Transducer(m) tg = t("hello") self.assertEqual(tg.output_string, "HH EH L OW ") @@ -127,13 +127,13 @@ def test_lexicon_mapping(self): def test_load_lexicon_mapping(self): """Test loading a lexicon mapping through a config file.""" with self.assertLogs(LOGGER, level="INFO"): - m = Mapping( + m = Mapping.load_mapping_from_path( os.path.join( os.path.dirname(public_data), "mappings", "lexicon_config.yaml" ) ) - self.assertEqual(m.mapping, []) - self.assertEqual(m.mapping_config.type, "lexicon") + self.assertEqual(m.rules, []) + self.assertEqual(m.type, "lexicon") t = Transducer(m) tg = t("hello") self.assertEqual(tg.output_string, "HH EH L OW ") @@ -146,7 +146,7 @@ def test_bad_lexicon_mapping(self): with self.assertRaises(FileNotFoundError), self.assertLogs( LOGGER, level="INFO" ): - _ = Mapping( + _ = Mapping.load_mapping_from_path( os.path.join( os.path.dirname(public_data), "mappings", "bad_lexicon_config.yaml" ) @@ -154,8 +154,8 @@ def test_bad_lexicon_mapping(self): def test_eng_lexicon(self): """Test the cached eng to eng-ipa lexicon as a Mapping.""" - m = Mapping(in_lang="eng", out_lang="eng-ipa") - self.assertEqual(m.mapping_config.type, "lexicon") + m = Mapping.find_mapping(in_lang="eng", out_lang="eng-ipa") + self.assertEqual(m.type, "lexicon") t = Transducer(m) tg = t("hello") self.assertEqual(tg.output_string, "hʌloʊ") diff --git a/g2p/tests/test_mappings.py b/g2p/tests/test_mappings.py index c63b36bf..3c548552 100755 --- a/g2p/tests/test_mappings.py +++ b/g2p/tests/test_mappings.py @@ -9,7 +9,7 @@ from typing import List from unittest import TestCase, main -from pydantic.error_wrappers import ValidationError +from pydantic import ValidationError from g2p import exceptions from g2p.log import LOGGER @@ -33,13 +33,13 @@ class MappingTest(TestCase): def setUp(self): self.test_mapping_no_norm = Mapping( - [ + rules=[ {"in": "\u00e1", "out": "\u00e1"}, {"in": "\u0061\u0301", "out": "\u0061\u0301"}, ], norm_form="none", ) - self.test_mapping_norm = Mapping([{"in": "\u00e1", "out": "\u00e1"}]) + self.test_mapping_norm = Mapping(rules=[{"in": "\u00e1", "out": "\u00e1"}]) with open( os.path.join(os.path.dirname(public_data), "git_to_ipa.json"), encoding="utf8", @@ -48,18 +48,18 @@ def setUp(self): def test_normalization(self): self.assertEqual( - ud.normalize("NFD", "\u00e1"), self.test_mapping_norm.mapping[0].in_char + ud.normalize("NFD", "\u00e1"), self.test_mapping_norm.rules[0].in_char ) - self.assertNotEqual(self.test_mapping_norm.mapping[0].in_char, "\u00e1") - self.assertEqual(self.test_mapping_norm.mapping[0].in_char, "\u0061\u0301") - self.assertEqual(self.test_mapping_no_norm.mapping[0].in_char, "\u00e1") - self.assertEqual(self.test_mapping_no_norm.mapping[0].out_char, "\u00e1") - self.assertEqual(self.test_mapping_no_norm.mapping[1].in_char, "\u0061\u0301") - self.assertEqual(self.test_mapping_no_norm.mapping[1].out_char, "\u0061\u0301") + self.assertNotEqual(self.test_mapping_norm.rules[0].in_char, "\u00e1") + self.assertEqual(self.test_mapping_norm.rules[0].in_char, "\u0061\u0301") + self.assertEqual(self.test_mapping_no_norm.rules[0].in_char, "\u00e1") + self.assertEqual(self.test_mapping_no_norm.rules[0].out_char, "\u00e1") + self.assertEqual(self.test_mapping_no_norm.rules[1].in_char, "\u0061\u0301") + self.assertEqual(self.test_mapping_no_norm.rules[1].out_char, "\u0061\u0301") def test_json_map(self): json_map = Mapping( - self.json_map["map"], + rules=self.json_map["map"], **{k: v for k, v in self.json_map.items() if k != "map"} ) self.assertEqual(len(json_map), 34) @@ -75,11 +75,10 @@ def test_as_is(self): log_output = io.StringIO() with redirect_stderr(log_output): mapping_sorted = Mapping( - [{"in": "a", "out": "b"}, {"in": "aa", "out": "c"}], as_is=False + rules=[{"in": "a", "out": "b"}, {"in": "aa", "out": "c"}], as_is=False ) self.assertTrue( - mapping_sorted.mapping_config.rule_ordering - == RULE_ORDERING_ENUM.apply_longest_first + mapping_sorted.rule_ordering == RULE_ORDERING_ENUM.apply_longest_first ) self.assertIn( "deprecated", @@ -96,11 +95,10 @@ def test_as_is(self): log_output = io.StringIO() with redirect_stderr(log_output): mapping = Mapping( - [{"in": "a", "out": "b"}, {"in": "aa", "out": "c"}], as_is=True + rules=[{"in": "a", "out": "b"}, {"in": "aa", "out": "c"}], as_is=True ) self.assertFalse( - mapping.mapping_config.rule_ordering - == RULE_ORDERING_ENUM.apply_longest_first + mapping.rule_ordering == RULE_ORDERING_ENUM.apply_longest_first ) self.assertIn( "deprecated", @@ -114,10 +112,11 @@ def test_as_is(self): ) # test the default (rule_ordering="as-written") - mapping_as_is = Mapping([{"in": "a", "out": "b"}, {"in": "aa", "out": "c"}]) + mapping_as_is = Mapping( + rules=[{"in": "a", "out": "b"}, {"in": "aa", "out": "c"}] + ) self.assertFalse( - mapping.mapping_config.rule_ordering - == RULE_ORDERING_ENUM.apply_longest_first + mapping.rule_ordering == RULE_ORDERING_ENUM.apply_longest_first ) # test the alternative (rule_ordering="apply-longest-first") @@ -139,14 +138,16 @@ def test_rule_ordering(self): rules = [{"in": "a", "out": "b"}, {"in": "aa", "out": "c"}] transducer_longest_first = Transducer( - Mapping(rules, rule_ordering="apply-longest-first") + Mapping(rules=rules, rule_ordering="apply-longest-first") ) self.assertEqual(transducer_longest_first("aa").output_string, "c") - transducer_as_written = Transducer(Mapping(rules, rule_ordering="as-written")) + transducer_as_written = Transducer( + Mapping(rules=rules, rule_ordering="as-written") + ) self.assertEqual(transducer_as_written("aa").output_string, "bb") - transducer_default = Transducer(Mapping(rules)) + transducer_default = Transducer(Mapping(rules=rules)) self.assertEqual(transducer_default("aa").output_string, "bb") def test_rule_ordering_given_invalid_value(self): @@ -161,11 +162,11 @@ def test_rule_ordering_given_invalid_value(self): log_output = io.StringIO() with redirect_stderr(log_output) and self.assertRaises(ValidationError): - Mapping(rules, rule_ordering=incorrect_value) + Mapping(rules=rules, rule_ordering=incorrect_value) def test_case_sensitive(self): - mapping = Mapping([{"in": "A", "out": "b"}], case_sensitive=False) - mapping_case_sensitive = Mapping([{"in": "A", "out": "b"}]) + mapping = Mapping(rules=[{"in": "A", "out": "b"}], case_sensitive=False) + mapping_case_sensitive = Mapping(rules=[{"in": "A", "out": "b"}]) transducer = Transducer(mapping) transducer_case_sensitive = Transducer(mapping_case_sensitive) self.assertEqual(transducer("a").output_string, "b") @@ -173,13 +174,15 @@ def test_case_sensitive(self): self.assertEqual(transducer("A").output_string, "b") def test_escape_special(self): - mapping = Mapping([{"in": r"\d", "out": "digit"}]) - mapping_escaped = Mapping([{"in": r"\d", "out": "b"}], escape_special=True) + mapping = Mapping(rules=[{"in": r"\d", "out": "digit"}]) + mapping_escaped = Mapping( + rules=[{"in": r"\d", "out": "b"}], escape_special=True + ) mapping_input_and_output_special_escaped = Mapping( - [{"in": "&", "out": "&"}], escape_special=True + rules=[{"in": "&", "out": "&"}], escape_special=True ) mapping_specific_from_fpcc = Mapping( - [{"in": r"^", "out": "A"}, {"in": "o", "out": r"."}], + rules=[{"in": r"^", "out": "A"}, {"in": "o", "out": r"."}], rule_ordering="apply-longest-first", escape_special=True, ) @@ -197,9 +200,9 @@ def test_escape_special(self): self.assertEqual(transducer_fpcc("^o").output_string, "A.") def test_norm_form(self): - mapping_nfc = Mapping([{"in": "a\u0301", "out": "a"}]) # Defaults to NFC - mapping_nfd = Mapping([{"in": "a\u0301", "out": "a"}], norm_form="NFD") - mapping_none = Mapping([{"in": "a\u0301", "out": "a"}], norm_form=False) + mapping_nfc = Mapping(rules=[{"in": "a\u0301", "out": "a"}]) # Defaults to NFC + mapping_nfd = Mapping(rules=[{"in": "a\u0301", "out": "a"}], norm_form="NFD") + mapping_none = Mapping(rules=[{"in": "a\u0301", "out": "a"}], norm_form=False) transducer_nfc = Transducer(mapping_nfc) transducer_nfd = Transducer(mapping_nfd) @@ -213,8 +216,8 @@ def test_norm_form(self): self.assertEqual(transducer_none("\u00E1").output_string, "\u00E1") def test_reverse(self): - mapping = Mapping([{"in": "a", "out": "b"}]) - mapping_reversed = Mapping([{"in": "a", "out": "b"}], reverse=True) + mapping = Mapping(rules=[{"in": "a", "out": "b"}]) + mapping_reversed = Mapping(rules=[{"in": "a", "out": "b"}], reverse=True) transducer = Transducer(mapping) transducer_reversed = Transducer(mapping_reversed) self.assertEqual(transducer("a").output_string, "b") @@ -223,7 +226,7 @@ def test_reverse(self): self.assertEqual(transducer_reversed("b").output_string, "a") def test_minimal(self): - mapping = Mapping( + mapping = Mapping.load_mapping_from_path( os.path.join( os.path.dirname(public_data), "mappings", "minimal_config.yaml" ) @@ -232,22 +235,21 @@ def test_minimal(self): self.assertEqual(transducer("abb").output_string, "aaa") self.assertEqual(transducer("a").output_string, "a") self.assertFalse( - mapping.mapping_config.rule_ordering - == RULE_ORDERING_ENUM.apply_longest_first + mapping.rule_ordering == RULE_ORDERING_ENUM.apply_longest_first ) - self.assertFalse(mapping.mapping_config.case_sensitive) - self.assertTrue(mapping.mapping_config.escape_special) - self.assertEqual(mapping.mapping_config.norm_form, NORM_FORM_ENUM.NFD) - self.assertTrue(mapping.mapping_config.reverse) + self.assertFalse(mapping.case_sensitive) + self.assertTrue(mapping.escape_special) + self.assertEqual(mapping.norm_form, NORM_FORM_ENUM.NFD) + self.assertTrue(mapping.reverse) def test_abbreviations(self): - mapping = Mapping( + mapping = Mapping.load_mapping_from_path( os.path.join( os.path.dirname(public_data), "mappings", "abbreviation_config.yaml" ) ) - self.assertEqual(mapping.mapping[0].in_char, "i|u") - self.assertEqual(mapping.mapping[1].in_char, "a|e|i|o|u") + self.assertEqual(mapping.rules[0].in_char, "i|u") + self.assertEqual(mapping.rules[1].in_char, "a|e|i|o|u") transducer = Transducer(mapping) self.assertEqual(transducer("i").output_string, "1") self.assertEqual(transducer("e").output_string, "2") @@ -256,32 +258,29 @@ def test_rule_ordering_from_config(self): """ Same as test_minimal, but uses "rule-ordering" instead of "as-is" in the config. """ - mapping = Mapping( + mapping = Mapping.load_mapping_from_path( os.path.join(os.path.dirname(public_data), "mappings", "rule-ordering.yaml") ) transducer = Transducer(mapping) self.assertEqual(transducer("abb").output_string, "aaa") self.assertEqual(transducer("a").output_string, "a") - self.assertTrue( - mapping.mapping_config.rule_ordering - == RULE_ORDERING_ENUM.apply_longest_first - ) - self.assertEqual( - mapping.mapping_config.rule_ordering, RULE_ORDERING_ENUM.apply_longest_first - ) - self.assertFalse(mapping.mapping_config.case_sensitive) - self.assertTrue(mapping.mapping_config.escape_special) - self.assertEqual(mapping.mapping_config.norm_form, NORM_FORM_ENUM.NFD) - self.assertTrue(mapping.mapping_config.reverse) + self.assertTrue(mapping.rule_ordering == RULE_ORDERING_ENUM.apply_longest_first) + self.assertEqual(mapping.rule_ordering, RULE_ORDERING_ENUM.apply_longest_first) + self.assertFalse(mapping.case_sensitive) + self.assertTrue(mapping.escape_special) + self.assertEqual(mapping.norm_form, NORM_FORM_ENUM.NFD) + self.assertTrue(mapping.reverse) def test_null_input(self): with self.assertLogs(LOGGER, level="WARNING"): - mapping = Mapping([{"in": "", "out": "a"}]) + mapping = Mapping(rules=[{"in": "", "out": "a"}]) self.assertFalse(mapping()) def test_no_escape(self): mapping = Mapping( - os.path.join(os.path.dirname(public_data), "mappings", "no_escape.csv") + rules=os.path.join( + os.path.dirname(public_data), "mappings", "no_escape.csv" + ) ) transducer = Transducer(mapping) self.assertEqual(transducer("?").output_string, "ʔ") @@ -290,12 +289,13 @@ def test_invalid_regex(self): rules = [{"in": "fo(o", "out": "bar"}] with self.assertLogs(LOGGER, level="ERROR"): with self.assertRaises(exceptions.MalformedMapping) as cm: - _ = Mapping(rules) + _ = Mapping(rules=rules) self.assertIn("regex", cm.exception.message) def test_invalid_rules_json(self): rules = [{"in": "a"}, {"out": "c"}] - self.assertRaises(ValidationError, Mapping, rules) + with self.assertRaises(ValidationError): + Mapping(rules=rules) def test_invalid_rules_csv(self): tf = NamedTemporaryFile( @@ -303,7 +303,8 @@ def test_invalid_rules_csv(self): ) tf.write("good-in,good-out\n\ngood-in-no-out\n") tf.close() - self.assertRaises(exceptions.MalformedMapping, Mapping, tf.name) + with self.assertRaises(exceptions.MalformedMapping): + Mapping(rules=tf.name) os.unlink(tf.name) def test_invalid_rules_filetype(self): @@ -312,26 +313,29 @@ def test_invalid_rules_filetype(self): ) tf.write("good-in,good-out\n\ngood-in-no-out\n") tf.close() - self.assertRaises(exceptions.IncorrectFileType, Mapping, tf.name) + with self.assertRaises(exceptions.IncorrectFileType): + Mapping(rules=tf.name) os.unlink(tf.name) def test_extend_and_deduplicate(self): - mapping1 = Mapping(rules_from_strings("a:b", "c:d", "g:h")) - mapping2 = Mapping(rules_from_strings("a:x", "c:d", "e:f")) + mapping1 = Mapping(rules=rules_from_strings("a:b", "c:d", "g:h")) + mapping2 = Mapping(rules=rules_from_strings("a:x", "c:d", "e:f")) extend_ref = Mapping( - rules_from_strings("a:b", "c:d", "g:h", "a:x", "c:d", "e:f") + rules=rules_from_strings("a:b", "c:d", "g:h", "a:x", "c:d", "e:f") ) mapping1.extend(mapping2) - self.assertEqual(mapping1.mapping, extend_ref.mapping) - dedup_ref = Mapping(rules_from_strings("a:b", "c:d", "g:h", "a:x", "e:f")) + self.assertEqual(mapping1.rules, extend_ref.rules) + dedup_ref = Mapping(rules=rules_from_strings("a:b", "c:d", "g:h", "a:x", "e:f")) mapping1.deduplicate() - self.assertEqual(mapping1.mapping, dedup_ref.mapping) + self.assertEqual(mapping1.rules, dedup_ref.rules) def test_g2p_studio_csv(self): # Ensure that a single CSV file from Studio works properly with self.assertLogs(LOGGER, level="WARNING"): # silence "" input warnings mapping = Mapping( - os.path.join(os.path.dirname(public_data), "mappings", "g2p_studio.csv") + rules=os.path.join( + os.path.dirname(public_data), "mappings", "g2p_studio.csv" + ) ) transducer = Transducer(mapping) self.assertEqual( @@ -360,7 +364,7 @@ def test_g2p_studio_csv(self): tf.write(fh.read()) tf.close() with self.assertLogs(LOGGER, level="WARNING"): # silence "" input warnings - mapping = Mapping(tf.name) + mapping = Mapping(rules=tf.name) transducer = Transducer(mapping) self.assertEqual( transducer("tee on herkullista").output_string, "teː on herkullistɑ" diff --git a/g2p/tests/test_transducer.py b/g2p/tests/test_transducer.py index ed781950..73b61904 100755 --- a/g2p/tests/test_transducer.py +++ b/g2p/tests/test_transducer.py @@ -13,31 +13,36 @@ class TransducerTest(TestCase): @classmethod def setUpClass(cls): - cls.test_mapping_moh = Mapping(in_lang="moh-equiv", out_lang="moh-ipa") + cls.test_mapping_moh = Mapping.find_mapping( + in_lang="moh-equiv", out_lang="moh-ipa" + ) cls.test_mapping = Mapping( - [{"in": "a", "out": "b"}], in_lang="spam", out_lang="eggs" + rules=[{"in": "a", "out": "b"}], in_lang="spam", out_lang="eggs" ) cls.test_mapping_rev = Mapping( - [{"in": "a", "out": "b"}], reverse=True, in_lang="eggs", out_lang="parrot" + rules=[{"in": "a", "out": "b"}], + reverse=True, + in_lang="eggs", + out_lang="parrot", ) cls.test_mapping_ordered_feed = Mapping( - [{"in": "a", "out": "b"}, {"in": "b", "out": "c"}] + rules=[{"in": "a", "out": "b"}, {"in": "b", "out": "c"}] ) cls.test_mapping_ordered_counter_feed = Mapping( - [{"in": "b", "out": "c"}, {"in": "a", "out": "b"}] + rules=[{"in": "b", "out": "c"}, {"in": "a", "out": "b"}] ) cls.test_longest_first = Mapping( - [{"in": "j", "out": "ʣ"}, {"in": "'y", "out": "jˀ"}] + rules=[{"in": "j", "out": "ʣ"}, {"in": "'y", "out": "jˀ"}] ) cls.test_rules_as_written_mapping = Mapping( - [{"in": "j", "out": "ʣ"}, {"in": "'y", "out": "jˀ"}], + rules=[{"in": "j", "out": "ʣ"}, {"in": "'y", "out": "jˀ"}], rule_ordering="apply-longest-first", ) cls.test_case_sensitive_mapping = Mapping( - [{"in": "'n", "out": "n̓"}], case_sensitive=True + rules=[{"in": "'n", "out": "n̓"}], case_sensitive=True ) cls.test_case_insensitive_mapping = Mapping( - [{"in": "'n", "out": "n̓"}], case_sensitive=False + rules=[{"in": "'n", "out": "n̓"}], case_sensitive=False ) cls.test_case_sensitive_transducer = Transducer(cls.test_case_sensitive_mapping) cls.test_case_insensitive_transducer = Transducer( @@ -59,18 +64,22 @@ def setUpClass(cls): [cls.test_trans_rev, cls.test_trans] ) cls.test_regex_set_transducer_sanity = Transducer( - Mapping([{"in": "a", "out": "b", "context_before": "c"}]) + Mapping(rules=[{"in": "a", "out": "b", "context_before": "c"}]) ) cls.test_regex_set_transducer = Transducer( - Mapping([{"in": "a", "out": "b", "context_before": "[cd]|[fgh]"}]) + Mapping(rules=[{"in": "a", "out": "b", "context_before": "[cd]|[fgh]"}]) + ) + cls.test_deletion_transducer = Transducer( + Mapping(rules=[{"in": "a", "out": ""}]) ) - cls.test_deletion_transducer = Transducer(Mapping([{"in": "a", "out": ""}])) - csv_deletion_mapping = Mapping( + csv_deletion_mapping = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "deletion_config_csv.yaml") ) cls.test_deletion_transducer_csv = Transducer(csv_deletion_mapping) cls.test_deletion_transducer_json = Transducer( - Mapping(os.path.join(PUBLIC_DIR, "mappings", "deletion_config_json.yaml")) + Mapping.load_mapping_from_path( + os.path.join(PUBLIC_DIR, "mappings", "deletion_config_json.yaml") + ) ) def test_properties(self): diff --git a/g2p/tests/test_unidecode_transducer.py b/g2p/tests/test_unidecode_transducer.py index 7dc9accb..4574f176 100755 --- a/g2p/tests/test_unidecode_transducer.py +++ b/g2p/tests/test_unidecode_transducer.py @@ -11,8 +11,8 @@ class UnidecodeTransducerTest(TestCase): def test_unidecode_mapping(self): m = Mapping(type="unidecode") - self.assertEqual(m.mapping, []) - self.assertEqual(m.mapping_config.type, "unidecode") + self.assertEqual(m.rules, []) + self.assertEqual(m.type, "unidecode") t = Transducer(m) tg = t("été Nunavut ᓄᓇᕗᑦ") self.assertEqual(tg.output_string, "ete Nunavut nonafot") diff --git a/g2p/tests/test_utils.py b/g2p/tests/test_utils.py index 40e64452..989bfbe0 100755 --- a/g2p/tests/test_utils.py +++ b/g2p/tests/test_utils.py @@ -14,7 +14,7 @@ from g2p.exceptions import IncorrectFileType, MalformedMapping, RecursionError from g2p.log import LOGGER from g2p.mappings import Mapping, utils -from g2p.mappings.utils import Rule +from g2p.mappings.utils import RULE_ORDERING_ENUM, Rule from g2p.tests.public import PUBLIC_DIR @@ -107,35 +107,32 @@ def test_fixed_width(self): def test_load_mapping(self): with self.assertRaises(MalformedMapping): - utils.load_mapping_from_path( + Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "malformed_config.yaml") ) - minimal = utils.load_mapping_from_path( + minimal = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "minimal_config.yaml") ) - csv = utils.load_mapping_from_path( + csv = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "minimal_configs.yaml"), 0 ) - tsv = utils.load_mapping_from_path( + tsv = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "minimal_configs.yaml"), 1 ) - psv = utils.load_mapping_from_path( + psv = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "minimal_configs.yaml"), 2 ) - json = utils.load_mapping_from_path( + json = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "minimal_configs.yaml"), 3 ) - xlsx = utils.load_mapping_from_path( + xlsx = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "minimal_configs.yaml"), 4 ) - self.assertEqual(minimal.mapping, csv.mapping) - self.assertEqual(minimal.mapping, tsv.mapping) - self.assertEqual(minimal.mapping, psv.mapping) - self.assertEqual(minimal.mapping, json.mapping) - self.assertEqual(minimal.mapping, xlsx.mapping) - - def test_validate(self): - pass + self.assertEqual(minimal.rules, csv.rules) + self.assertEqual(minimal.rules, tsv.rules) + self.assertEqual(minimal.rules, psv.rules) + self.assertEqual(minimal.rules, json.rules) + self.assertEqual(minimal.rules, xlsx.rules) def test_escape_special(self): self.assertEqual( @@ -156,14 +153,13 @@ def test_load_abbs(self): self.assertEqual(abbs["VOWEL"], ["a", "e", "i", "o", "u"]) def test_generated_mapping(self): - config = { - "in_lang": "test", - "out_lang": "test-out", - "rule_ordering": "apply-longest-first", - } # config = utils.generate_config('test', 'test-out', 'Test', 'TestOut') - config["mapping"] = [{"in": "a", "out": "b"}] - mapping = Mapping(**config) + mapping = Mapping( + in_lang="test", + out_lang="test-out", + rule_ordering=RULE_ORDERING_ENUM.apply_longest_first, + rules=[Rule(in_char="a", out_char="b")], + ) with self.assertLogs(LOGGER, level="WARNING"): mapping.config_to_file( os.path.join(PUBLIC_DIR, "mappings", "test_config.yaml") @@ -173,23 +169,25 @@ def test_generated_mapping(self): os.path.join(PUBLIC_DIR, "mappings", "generated_add.yaml") ) mapping.mapping_to_file(os.path.join(PUBLIC_DIR, "mappings")) - test_config = utils.load_mapping_from_path( + test_config = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "test_config.yaml") ) - test_config_added = utils.load_mapping_from_path( + test_config_added = Mapping.load_mapping_from_path( os.path.join(PUBLIC_DIR, "mappings", "generated_add.yaml") ) self.assertEqual( - test_config.mapping, - [{"in": "a", "out": "b", "context_before": "", "context_after": ""}], + test_config.rules[0].export_to_dict(), + Rule( + **{"in": "a", "out": "b", "context_before": "", "context_after": ""} + ).export_to_dict(), ) self.assertEqual(test_config.in_lang, "test") self.assertEqual(test_config.out_lang, "test-out") self.assertEqual(test_config.language_name, "test") self.assertEqual(test_config.display_name, "test custom to test-out custom") self.assertEqual( - test_config_added.mapping, - [{"in": "a", "out": "b", "context_before": "", "context_after": ""}], + test_config_added.rules[0].export_to_dict(), + {"in": "a", "out": "b"}, ) self.assertEqual(test_config_added.in_lang, "test") self.assertEqual(test_config_added.out_lang, "test-out") diff --git a/g2p/tests/test_z_local_config.py b/g2p/tests/test_z_local_config.py index e318f7c0..66f859fb 100755 --- a/g2p/tests/test_z_local_config.py +++ b/g2p/tests/test_z_local_config.py @@ -117,7 +117,7 @@ def test_missing_files(self): with tempfile.TemporaryDirectory() as tmpdir: config_file = os.path.join(tmpdir, "mapping-file-not-found.yaml") with open(config_file, "wt", encoding="utf8") as f: - print("mapping: no-such-file.csv", file=f) + print("rules: no-such-file.csv", file=f) results = self.runner.invoke( convert, ["--config", config_file, "a", "b", "c"] ) diff --git a/g2p/transducer/__init__.py b/g2p/transducer/__init__.py index d8636e83..d652192e 100644 --- a/g2p/transducer/__init__.py +++ b/g2p/transducer/__init__.py @@ -419,14 +419,14 @@ class Transducer: def __init__(self, mapping: Mapping): self.mapping = mapping - self.case_sensitive = mapping.mapping_config.case_sensitive - self.norm_form = mapping.mapping_config.norm_form - self.out_delimiter = mapping.mapping_config.out_delimiter + self.case_sensitive = mapping.case_sensitive + self.norm_form = mapping.norm_form + self.out_delimiter = mapping.out_delimiter self._index_match_pattern = re.compile(r"(?<={)\d+(?=})") self._char_match_pattern = re.compile(r"[^0-9\{\}]+(?={\d+})", re.U) def __repr__(self): - return f"{self.__class__} between {self.mapping.mapping_config.in_lang} and {self.mapping.mapping_config.out_lang}" + return f"{self.__class__} between {self.mapping.in_lang} and {self.mapping.out_lang}" def __call__(self, to_convert: str, index: bool = False, debugger: bool = False): """The basic method to transduce an input. A proxy for self.apply_rules. @@ -461,12 +461,12 @@ def _pua_to_index(string: str) -> int: @property def in_lang(self) -> str: """Input language node name""" - return self.mapping.mapping_config.in_lang + return self.mapping.in_lang @property def out_lang(self) -> str: """Output language node name""" - return self.mapping.mapping_config.out_lang + return self.mapping.out_lang @property def transducers(self) -> List["Transducer"]: # noqa: F821 @@ -849,11 +849,10 @@ def apply_lexicon(self, to_convert: str): return tg def apply_rules(self, to_convert: str): # noqa: C901 - if self.mapping.mapping_config.type == MAPPING_TYPE.unidecode: + if self.mapping.type == MAPPING_TYPE.unidecode: return self.apply_unidecode(to_convert) - elif self.mapping.mapping_config.type == MAPPING_TYPE.lexicon: + elif self.mapping.type == MAPPING_TYPE.lexicon: return self.apply_lexicon(to_convert) - # perform any normalization to_convert = unicode_escape(to_convert) saved_to_convert = to_convert @@ -874,7 +873,7 @@ def apply_rules(self, to_convert: str): # noqa: C901 # these variables tracks changes in the output string across processing # matches of the same pattern diff_from_input = defaultdict(int, {n: 0 for n in range(len(tg.output_string))}) - for io in self.mapping.mapping: + for io in self.mapping.rules: assert isinstance(io, Rule) # Do not allow empty rules if not io.in_char and not io.out_char: @@ -885,9 +884,8 @@ def apply_rules(self, to_convert: str): # noqa: C901 diff_from_output = defaultdict( int, {n: 0 for n in range(len(tg.output_string))} ) - for match_i, match in enumerate( - reversed(list(io.match_pattern.finditer(tg.output_string))) - ): + matches = reversed(list(io.match_pattern.finditer(tg.output_string))) + for match_i, match in enumerate(matches): debug_string = tg.output_string start = match.start() end = match.end() @@ -965,7 +963,7 @@ def check( display_warnings=False, original_input=None, ) -> bool: - out_lang = self.mapping.mapping_config.out_lang + out_lang = self.mapping.out_lang if "eng-arpabet" in out_lang: if is_arpabet(tg.output_string): return True @@ -1119,7 +1117,7 @@ def __init__(self, transducers: List[Transducer]): self.norm_form = transducers[0].norm_form if transducers else "none" def __repr__(self): - return f"{self.__class__} between {self._transducers[0].mapping.mapping_config.in_lang} and {self._transducers[-1].mapping.mapping_config.out_lang}" + return f"{self.__class__} between {self._transducers[0].mapping.in_lang} and {self._transducers[-1].mapping.out_lang}" def __call__(self, to_convert: str): return self.apply_rules(to_convert) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index a0502e82..ca09958d 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -14,18 +14,9 @@ requests tqdm text_unidecode -# Python 3.7+ requirements with current packages -Flask==2.2.5; python_version >= "3.7" -werkzeug==2.2.3; python_version >= "3.7" -flask_socketio>=5.0.0; python_version >= "3.7" -python-engineio>=4.0.0; python_version >= "3.7" -python-socketio>=5.0.0; python_version >= "3.7" -pydantic>=1.10.2,<2.0; python_version >= "3.7" - -# Python 3.6 requirements due to newer packages not available 3.6 -Flask>=2.0.0,<=2.1.3; python_version < "3.7" -werkzeug==2.0.3; python_version < "3.7" -flask_socketio>=4.3.2; python_version < "3.7" -python-engineio>=3.14.2; python_version < "3.7" -python-socketio>=4.6.1; python_version < "3.7" -pydantic==1.9.2; python_version < "3.7" +Flask==2.2.5 +werkzeug==2.2.3 +flask_socketio>=5.0.0 +python-engineio>=4.0.0 +python-socketio>=5.0.0 +pydantic>=2.3 diff --git a/setup.py b/setup.py index 2b01cbbe..110c8c43 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name="g2p", - python_requires=">=3.6", + python_requires=">=3.7", version=VERSION, author="Aidan Pine", author_email="hello@aidanpine.ca",