diff options
Diffstat (limited to 'util/locale_database/cldr.py')
-rw-r--r-- | util/locale_database/cldr.py | 303 |
1 files changed, 139 insertions, 164 deletions
diff --git a/util/locale_database/cldr.py b/util/locale_database/cldr.py index f2b6616fce..9e0bae9667 100644 --- a/util/locale_database/cldr.py +++ b/util/locale_database/cldr.py @@ -1,31 +1,5 @@ -# -*- coding: utf-8; -*- -############################################################################# -## -## Copyright (C) 2020 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the test suite of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:GPL-EXCEPT$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 3 as published by the Free Software -## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2021 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 """Digesting the CLDR's data. Provides two classes: @@ -36,15 +10,17 @@ The former should normally be all you need to access. See individual classes for further detail. """ +from typing import Iterable, TextIO from xml.dom import minidom from weakref import WeakValueDictionary as CacheDict -import os +from pathlib import Path from ldml import Error, Node, XmlScanner, Supplement, LocaleScanner +from localetools import names_clash from qlocalexml import Locale class CldrReader (object): - def __init__(self, root, grumble = lambda msg: None, whitter = lambda msg: None): + def __init__(self, root: Path, grumble = lambda msg: None, whitter = lambda msg: None): """Set up a reader object for reading CLDR data. Single parameter, root, is the file-system path to the root of @@ -67,7 +43,7 @@ class CldrReader (object): Yields pairs (have, give) of 4-tuples; if what you have matches the left member, giving the right member is probably sensible. Each 4-tuple's entries are the full names of a - language, a script, a country (strictly territory) and a + language, a script, a territory (usually a country) and a variant (currently ignored).""" skips = [] for got, use in self.root.likelySubTags(): @@ -79,7 +55,7 @@ class CldrReader (object): and e.message.startswith('Unknown ') and ' code ' in e.message): skips.append(use) else: - self.grumble('Skipping likelySubtag "{}" -> "{}" ({})\n'.format(got, use, e.message)) + self.grumble(f'Skipping likelySubtag "{got}" -> "{use}" ({e})\n') continue if all(code.startswith('Any') and code[3].isupper() for code in have[:-1]): continue @@ -87,7 +63,7 @@ class CldrReader (object): give = (give[0], # Substitute according to http://www.unicode.org/reports/tr35/#Likely_Subtags have[1] if give[1] == 'AnyScript' else give[1], - have[2] if give[2] == 'AnyCountry' else give[2], + have[2] if give[2] == 'AnyTerritory' else give[2], give[3]) # AnyVariant similarly ? yield have, give @@ -99,50 +75,49 @@ class CldrReader (object): pass # self.__wrapped(self.whitter, 'Skipping likelySubtags (for unknown codes): ', skips) def readLocales(self, calendars = ('gregorian',)): - locales = tuple(self.__allLocales(calendars)) - return dict(((k.language_id, k.script_id, k.country_id, k.variant_code), - k) for k in locales) + return {(k.language_id, k.script_id, k.territory_id, k.variant_code): k + for k in self.__allLocales(calendars)} def __allLocales(self, calendars): def skip(locale, reason): - return 'Skipping defaultContent locale "{}" ({})\n'.format(locale, reason) + return f'Skipping defaultContent locale "{locale}" ({reason})\n' for locale in self.root.defaultContentLocales: try: - language, script, country, variant = self.__splitLocale(locale) + language, script, territory, variant = self.__splitLocale(locale) except ValueError: self.whitter(skip(locale, 'only language tag')) continue - if not (script or country): + if not (script or territory): self.grumble(skip(locale, 'second tag is neither script nor territory')) continue - if not (language and country): + if not (language and territory): continue try: yield self.__getLocaleData(self.root.locale(locale), calendars, - language, script, country, variant) + language, script, territory, variant) except Error as e: self.grumble(skip(locale, e.message)) for locale in self.root.fileLocales: try: chain = self.root.locale(locale) - language, script, country, variant = chain.tagCodes() + language, script, territory, variant = chain.tagCodes() assert language # TODO: this skip should probably be based on likely - # sub-tags, instead of empty country: if locale has a + # sub-tags, instead of empty territory: if locale has a # likely-subtag expansion, that's what QLocale uses, # and we'll be saving its data for the expanded locale # anyway, so don't need to record it for itself. # See also QLocaleXmlReader.loadLocaleMap's grumble. - if not country: + if not territory: continue - yield self.__getLocaleData(chain, calendars, language, script, country, variant) + yield self.__getLocaleData(chain, calendars, language, script, territory, variant) except Error as e: - self.grumble('Skipping file locale "{}" ({})\n'.format(locale, e.message)) + self.grumble(f'Skipping file locale "{locale}" ({e})\n') import textwrap @staticmethod @@ -153,13 +128,13 @@ class CldrReader (object): def __parseTags(self, locale): tags = self.__splitLocale(locale) - language = tags.next() - script = country = variant = '' + language = next(tags) + script = territory = variant = '' try: - script, country, variant = tags + script, territory, variant = tags except ValueError: pass - return tuple(p[1] for p in self.root.codesToIdName(language, script, country, variant)) + return tuple(p[1] for p in self.root.codesToIdName(language, script, territory, variant)) def __splitLocale(self, name): """Generate (language, script, territory, variant) from a locale name @@ -171,14 +146,18 @@ class CldrReader (object): single tag (i.e. contains no underscores). Always yields 1 or 4 values, never 2 or 3.""" tags = iter(name.split('_')) - yield tags.next() # Language - tag = tags.next() # may raise StopIteration + yield next(tags) # Language + + try: + tag = next(tags) + except StopIteration: + return # Script is always four letters, always capitalised: if len(tag) == 4 and tag[0].isupper() and tag[1:].islower(): yield tag try: - tag = tags.next() + tag = next(tags) except StopIteration: tag = '' else: @@ -188,7 +167,7 @@ class CldrReader (object): if tag and tag.isupper() or tag.isdigit(): yield tag try: - tag = tags.next() + tag = next(tags) except StopIteration: tag = '' else: @@ -201,21 +180,22 @@ class CldrReader (object): else: yield '' - # If nothing is left, StopIteration will avoid the warning: - if not tag: - tag = tags.next() - self.grumble('Ignoring unparsed cruft {} in {}\n'.format('_'.join(tag + tuple(tags)), name)) + rest = [tag] if tag else [] + rest.extend(tags) - def __getLocaleData(self, scan, calendars, language, script, country, variant): - ids, names = zip(*self.root.codesToIdName(language, script, country, variant)) - assert ids[0] > 0 and ids[2] > 0, (language, script, country, variant) + if rest: + self.grumble(f'Ignoring unparsed cruft {"_".join(rest)} in {name}\n') + + def __getLocaleData(self, scan, calendars, language, script, territory, variant): + ids, names = zip(*self.root.codesToIdName(language, script, territory, variant)) + assert ids[0] > 0 and ids[2] > 0, (language, script, territory, variant) locale = Locale( language = names[0], language_code = language, language_id = ids[0], script = names[1], script_code = script, script_id = ids[1], - country = names[2], country_code = country, country_id = ids[2], + territory = names[2], territory_code = territory, territory_id = ids[2], variant_code = variant) - firstDay, weStart, weEnd = self.root.weekData(country) + firstDay, weStart, weEnd = self.root.weekData(territory) assert all(day in ('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun') for day in (firstDay, weStart, weEnd)) @@ -223,7 +203,7 @@ class CldrReader (object): weekendStart = weStart, weekendEnd = weEnd) - iso, digits, rounding = self.root.currencyData(country) + iso, digits, rounding = self.root.currencyData(territory) locale.update(currencyIsoCode = iso, currencyDigits = int(digits), currencyRounding = int(rounding)) @@ -231,7 +211,7 @@ class CldrReader (object): locale.update(scan.currencyData(iso)) locale.update(scan.numericData(self.root.numberSystem, self.whitter)) locale.update(scan.textPatternData()) - locale.update(scan.endonyms(language, script, country, variant)) + locale.update(scan.endonyms(language, script, territory, variant)) locale.update(scan.unitData()) # byte, kB, MB, GB, ..., KiB, MiB, GiB, ... locale.update(scan.calendarNames(calendars)) # Names of days and months @@ -242,7 +222,7 @@ class CldrReader (object): # the cache. If a process were to instantiate this class with distinct # roots, each cache would be filled by the first to need it ! class CldrAccess (object): - def __init__(self, root): + def __init__(self, root: Path): """Set up a master object for accessing CLDR data. Single parameter, root, is the file-system path to the root of @@ -250,18 +230,18 @@ class CldrAccess (object): contain dtd/, main/ and supplemental/ sub-directories.""" self.root = root - def xml(self, *path): + def xml(self, relative_path: str): """Load a single XML file and return its root element as an XmlScanner. The path is interpreted relative to self.root""" - return XmlScanner(Node(self.__xml(path))) + return XmlScanner(Node(self.__xml(relative_path))) def supplement(self, name): """Loads supplemental data as a Supplement object. The name should be that of a file in common/supplemental/, without path. """ - return Supplement(Node(self.__xml(('common', 'supplemental', name)))) + return Supplement(Node(self.__xml(f'common/supplemental/{name}'))) def locale(self, name): """Loads all data for a locale as a LocaleScanner object. @@ -273,17 +253,18 @@ class CldrAccess (object): inheritance, where relevant.""" return LocaleScanner(name, self.__localeRoots(name), self.__rootLocale) + def englishNaming(self, tag): # see QLocaleXmlWriter.enumData() + return self.__codeMap(tag).get + @property - def fileLocales(self, joinPath = os.path.join, listDirectory = os.listdir, - splitExtension = os.path.splitext): + def fileLocales(self) -> Iterable[str]: """Generator for locale IDs seen in file-names. All *.xml other than root.xml in common/main/ are assumed to identify locales.""" - for name in listDirectory(joinPath(self.root, 'common', 'main')): - stem, ext = splitExtension(name) - if ext == '.xml' and stem != 'root': - yield stem + for path in self.root.joinpath('common/main').glob('*.xml'): + if path.stem != 'root': + yield path.stem @property def defaultContentLocales(self): @@ -304,44 +285,44 @@ class CldrAccess (object): def numberSystem(self, system): """Get a description of a numbering system. - Returns a mapping, with keys u'digits', u'type' and u'id'; the + Returns a mapping, with keys 'digits', 'type' and 'id'; the value for this last is system. Raises KeyError for unknown number system, ldml.Error on failure to load data.""" try: return self.__numberSystems[system] except KeyError: - raise Error('Unsupported number system: {}'.format(system)) + raise Error(f'Unsupported number system: {system}') - def weekData(self, country): + def weekData(self, territory): """Data on the weekly cycle. Returns a triple (W, S, E) of en's short names for week-days; W is the first day of the week, S the start of the week-end - and E the end of the week-end. Where data for a country is + and E the end of the week-end. Where data for a territory is unavailable, the data for CLDR's territory 001 (The World) is used.""" try: - return self.__weekData[country] + return self.__weekData[territory] except KeyError: return self.__weekData['001'] - def currencyData(self, country): - """Returns currency data for the given country code. + def currencyData(self, territory): + """Returns currency data for the given territory code. Return value is a tuple (ISO4217 code, digit count, rounding - mode). If CLDR provides no data for this country, ('', 2, 1) + mode). If CLDR provides no data for this territory, ('', 2, 1) is the default result. """ try: - return self.__currencyData[country] + return self.__currencyData[territory] except KeyError: return '', 2, 1 - def codesToIdName(self, language, script, country, variant = ''): + def codesToIdName(self, language, script, territory, variant = ''): """Maps each code to the appropriate ID and name. Returns a 4-tuple of (ID, name) pairs corresponding to the - language, script, country and variant given. Raises a + language, script, territory and variant given. Raises a suitable error if any of them is unknown, indicating all that are unknown plus suitable names for any that could sensibly be added to enumdata.py to make them known. @@ -353,33 +334,33 @@ class CldrAccess (object): try: return (enum('language')[language], enum('script')[script], - enum('country')[country], + enum('territory')[territory], enum('variant')[variant]) except KeyError: pass - parts, values = [], [language, script, country, variant] - for index, key in enumerate(('language', 'script', 'country', 'variant')): + parts, values = [], [language, script, territory, variant] + for index, key in enumerate(('language', 'script', 'territory', 'variant')): naming, enums = self.__codeMap(key), enum(key) value = values[index] if value not in enums: - text = '{} code {}'.format(key, value) + text = f'{key} code {value}' name = naming.get(value) if name and value != 'POSIX': - text += u' (could add {})'.format(name) + text += f' (could add {name})' parts.append(text) if len(parts) > 1: parts[-1] = 'and ' + parts[-1] - assert parts + else: + assert parts + if parts[0].startswith('variant'): + raise Error(f'No support for {parts[0]}', + language, script, territory, variant) raise Error('Unknown ' + ', '.join(parts), - language, script, country, variant) + language, script, territory, variant) @staticmethod - def __checkEnum(given, proper, scraps, - remap = { u'å': 'a', u'ã': 'a', u'ç': 'c', u'é': 'e', u'í': 'i', u'ü': 'u'}, - prefix = { 'St.': 'Saint', 'U.S.': 'United States' }, - suffixes = ( 'Han', ), - skip = u'\u02bc'): + def __checkEnum(given, proper, scraps): # Each is a { code: full name } mapping for code, name in given.items(): try: right = proper[code] @@ -387,35 +368,23 @@ class CldrAccess (object): # No en.xml name for this code, but supplementalData's # parentLocale may still believe in it: if code not in scraps: - yield name, '[Found no CLDR name for code {}]'.format(code) - continue - if name == right: continue - ok = right.replace('&', 'And') - for k, v in prefix.items(): - if ok.startswith(k + ' '): - ok = v + ok[len(k):] - while '(' in ok: - try: f, t = ok.index('('), ok.index(')') - except ValueError: break - ok = ok[:f].rstrip() + ' ' + ok[t:].lstrip() - if any(name == ok + ' ' + s for s in suffixes): - continue - if ''.join(ch for ch in name.lower() if not ch.isspace()) in ''.join( - remap.get(ch, ch) for ch in ok.lower() if ch.isalpha() and ch not in skip): + yield name, f'[Found no CLDR name for code {code}]' continue - yield name, ok + cleaned = names_clash(right, name) + if cleaned: + yield name, cleaned def checkEnumData(self, grumble): scraps = set() for k in self.__parentLocale.keys(): for f in k.split('_'): scraps.add(f) - from enumdata import language_list, country_list, script_list - language = dict((v, k) for k, v in language_list.values() if not v.isspace()) - country = dict((v, k) for k, v in country_list.values() if v != 'ZZ') - script = dict((v, k) for k, v in script_list.values() if v != 'Zzzz') + from enumdata import language_map, territory_map, script_map + language = {v: k for k, v in language_map.values() if not v.isspace()} + territory = {v: k for k, v in territory_map.values() if v != 'ZZ'} + script = {v: k for k, v in script_map.values() if v != 'Zzzz'} lang = dict(self.__checkEnum(language, self.__codeMap('language'), scraps)) - land = dict(self.__checkEnum(country, self.__codeMap('country'), scraps)) + land = dict(self.__checkEnum(territory, self.__codeMap('territory'), scraps)) text = dict(self.__checkEnum(script, self.__codeMap('script'), scraps)) if lang or land or text: grumble("""\ @@ -424,15 +393,15 @@ enumdata.py (keeping the old name as an alias): """) if lang: grumble('Language:\n\t' - + '\n\t'.join('{} -> {}'.format(k, v) for k, v in lang.items()) + + '\n\t'.join(f'{k} -> {v}' for k, v in lang.items()) + '\n') if land: - grumble('Country:\n\t' - + '\n\t'.join('{} -> {}'.format(k, v) for k, v in land.items()) + grumble('Territory:\n\t' + + '\n\t'.join(f'{k} -> {v}' for k, v in land.items()) + '\n') if text: grumble('Script:\n\t' - + '\n\t'.join('{} -> {}'.format(k, v) for k, v in text.items()) + + '\n\t'.join(f'{k} -> {v}' for k, v in text.items()) + '\n') grumble('\n') @@ -442,7 +411,7 @@ enumdata.py (keeping the old name as an alias): MS-Win have their own eccentric names for time-zones. CLDR helpfully provides a translation to more orthodox names. - Singe argument, lookup, is a mapping from known MS-Win names + Single argument, lookup, is a mapping from known MS-Win names for locales to a unique integer index (starting at 1). The XML structure we read has the form: @@ -460,7 +429,7 @@ enumdata.py (keeping the old name as an alias): </supplementalData> """ zones = self.supplement('windowsZones.xml') - enum = self.__enumMap('country') + enum = self.__enumMap('territory') badZones, unLands, defaults, windows = set(), set(), {}, {} for name, attrs in zones.find('windowsZones/mapTimezones'): @@ -469,8 +438,8 @@ enumdata.py (keeping the old name as an alias): wid, code = attrs['other'], attrs['territory'] data = dict(windowsId = wid, - countryCode = code, - ianaList = attrs['type']) + territoryCode = code, + ianaList = ' '.join(attrs['type'].split())) try: key = lookup[wid] @@ -479,7 +448,7 @@ enumdata.py (keeping the old name as an alias): key = 0 data['windowsKey'] = key - if code == u'001': + if code == '001': defaults[key] = data['ianaList'] else: try: @@ -487,11 +456,11 @@ enumdata.py (keeping the old name as an alias): except KeyError: unLands.append(code) continue - data.update(countryId = cid, country = name) + data.update(territoryId = cid, territory = name) windows[key, cid] = data if unLands: - raise Error('Unknown country codes, please add to enumdata.py: ' + raise Error('Unknown territory codes, please add to enumdata.py: ' + ', '.join(sorted(unLands))) if badZones: @@ -507,20 +476,20 @@ enumdata.py (keeping the old name as an alias): return self.__cldrVersion # Implementation details - def __xml(self, path, cache = CacheDict(), read = minidom.parse, joinPath = os.path.join): + def __xml(self, relative_path: str, cache = CacheDict(), read = minidom.parse): try: - doc = cache[path] + doc = cache[relative_path] except KeyError: - cache[path] = doc = read(joinPath(self.root, *path)).documentElement + cache[relative_path] = doc = read(str(self.root.joinpath(relative_path))).documentElement return doc - def __open(self, path, joinPath=os.path.join): - return open(joinPath(self.root, *path)) + def __open(self, relative_path: str) -> TextIO: + return self.root.joinpath(relative_path).open() @property def __rootLocale(self, cache = []): if not cache: - cache.append(self.xml('common', 'main', 'root.xml')) + cache.append(self.xml('common/main/root.xml')) return cache[0] @property @@ -530,7 +499,7 @@ enumdata.py (keeping the old name as an alias): return cache[0] @property - def __numberSystems(self, cache = {}, joinPath=os.path.join): + def __numberSystems(self, cache = {}): if not cache: for ignore, attrs in self.supplement('numberingSystems.xml').find('numberingSystems'): cache[attrs['id']] = attrs @@ -563,7 +532,7 @@ enumdata.py (keeping the old name as an alias): source = self.__supplementalData for key in ('firstDay', 'weekendStart', 'weekendEnd'): result = {} - for ignore, attrs in source.find('weekData/' + key): + for ignore, attrs in source.find(f'weekData/{key}'): assert ignore == key day = attrs['day'] assert day in ('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'), day @@ -580,7 +549,7 @@ enumdata.py (keeping the old name as an alias): for elt in source.findNodes('currencyData/region'): iso, digits, rounding = '', 2, 1 try: - country = elt.dom.attributes['iso3166'].nodeValue + territory = elt.dom.attributes['iso3166'].nodeValue except KeyError: continue for child in elt.findAllChildren('currency'): @@ -596,16 +565,16 @@ enumdata.py (keeping the old name as an alias): break if iso: for tag, data in source.find( - 'currencyData/fractions/info[iso4217={}]'.format(iso)): + f'currencyData/fractions/info[iso4217={iso}]'): digits = data['digits'] rounding = data['rounding'] - cache[country] = iso, digits, rounding + cache[territory] = iso, digits, rounding assert cache return cache @property - def __unDistinguishedAttributes(self, cache = {}, joinPath = os.path.join): + def __unDistinguishedAttributes(self, cache = {}): """Mapping from tag names to lists of attributes. LDML defines some attributes as 'distinguishing': if a node @@ -625,7 +594,7 @@ enumdata.py (keeping the old name as an alias): return cache - def __scanLdmlDtd(self, joinPath = os.path.join): + def __scanLdmlDtd(self): """Scan the LDML DTD, record CLDR version Yields (tag, attrs) pairs: on elements with a given tag, @@ -635,7 +604,7 @@ enumdata.py (keeping the old name as an alias): Sets self.__cldrVersion as a side-effect, since this information is found in the same file.""" - with self.__open(('common', 'dtd', 'ldml.dtd')) as dtd: + with self.__open('common/dtd/ldml.dtd') as dtd: tag, ignored, last = None, None, None for line in dtd: @@ -670,15 +639,15 @@ enumdata.py (keeping the old name as an alias): def __enumMap(self, key, cache = {}): if not cache: cache['variant'] = {'': (0, 'This should never be seen outside ldml.py')} - # They're not actually lists: mappings from numeric value - # to pairs of full name and short code. What we want, in - # each case, is a mapping from code to the other two. - from enumdata import language_list, script_list, country_list - for form, book, empty in (('language', language_list, 'AnyLanguage'), - ('script', script_list, 'AnyScript'), - ('country', country_list, 'AnyCountry')): - cache[form] = dict((pair[1], (num, pair[0])) - for num, pair in book.items() if pair[0] != 'C') + # They're mappings from numeric value to pairs of full + # name and short code. What we want, in each case, is a + # mapping from code to the other two. + from enumdata import language_map, script_map, territory_map + for form, book, empty in (('language', language_map, 'AnyLanguage'), + ('script', script_map, 'AnyScript'), + ('territory', territory_map, 'AnyTerritory')): + cache[form] = {pair[1]: (num, pair[0]) + for num, pair in book.items() if pair[0] != 'C'} # (Have to filter out the C locale, as we give it the # same (all space) code as AnyLanguage, whose code # should probably be 'und' instead.) @@ -693,9 +662,9 @@ enumdata.py (keeping the old name as an alias): def __codeMap(self, key, cache = {}, # Maps our name for it to CLDR's name: naming = {'language': 'languages', 'script': 'scripts', - 'country': 'territories', 'variant': 'variants'}): + 'territory': 'territories', 'variant': 'variants'}): if not cache: - root = self.xml('common', 'main', 'en.xml').root.findUniqueChild('localeDisplayNames') + root = self.xml('common/main/en.xml').root.findUniqueChild('localeDisplayNames') for dst, src in naming.items(): cache[dst] = dict(self.__codeMapScan(root.findUniqueChild(src))) assert cache @@ -721,7 +690,13 @@ enumdata.py (keeping the old name as an alias): except (KeyError, ValueError, TypeError): pass else: - if key not in seen or not elt.attributes.has_key('alt'): + # Prefer stand-alone forms of names when present, ignore other + # alt="..." entries. For example, Traditional and Simplified + # Han omit "Han" in the plain form, but include it for + # stand-alone. As the stand-alone version appears later, it + # over-writes the plain one. + if (key not in seen or 'alt' not in elt.attributes + or elt.attributes['alt'].nodeValue == 'stand-alone'): yield key, value seen.add(key) @@ -730,7 +705,8 @@ enumdata.py (keeping the old name as an alias): def __parentLocale(self, cache = {}): # see http://www.unicode.org/reports/tr35/#Parent_Locales if not cache: - for tag, attrs in self.__supplementalData.find('parentLocales'): + for tag, attrs in self.__supplementalData.find('parentLocales', + ('component',)): parent = attrs.get('parent', '') for child in attrs['locales'].split(): cache[child] = parent @@ -738,10 +714,9 @@ enumdata.py (keeping the old name as an alias): return cache - def __localeAsDoc(self, name, aliasFor = None, - joinPath = os.path.join, exists = os.path.isfile): - path = ('common', 'main', name + '.xml') - if exists(joinPath(self.root, *path)): + def __localeAsDoc(self, name: str, aliasFor = None): + path = f'common/main/{name}.xml' + if self.root.joinpath(path).exists(): elt = self.__xml(path) for child in Node(elt).findAllChildren('alias'): try: @@ -754,8 +729,8 @@ enumdata.py (keeping the old name as an alias): return elt if aliasFor: - raise Error('Fatal error: found an alias "{}" -> "{}", but found no file for the alias' - .format(aliasFor, name)) + raise Error(f'Fatal error: found an alias "{aliasFor}" -> "{name}", ' + 'but found no file for the alias') def __scanLocaleRoots(self, name): while name and name != 'root': @@ -780,4 +755,4 @@ enumdata.py (keeping the old name as an alias): return chain # Unpolute the namespace: we don't need to export these. -del minidom, CacheDict, os +del minidom, CacheDict |