1  """
     2      babel.core
     3      ~~~~~~~~~~
     4
     5      Core locale representation and locale data access.
     6
     7      :copyright: (c) 2013-2023 by the Babel Team.
     8      :license: BSD, see LICENSE for more details.
     9  """
    10  from __future__ import annotations
    11  import os
    12  import pickle
    13  from collections.abc import Iterable, Mapping
    14  from typing import TYPE_CHECKING, Any
    15  from babel import localedata
    16  from babel.plural import PluralRule
    17  __all__ = ['UnknownLocaleError', 'Locale', 'default_locale', 'negotiate_locale', 'parse_locale']
    18  if TYPE_CHECKING:
    19      from typing_extensions import Literal, TypeAlias
    20      _GLOBAL_KEY: TypeAlias = Literal['all_currencies', 'currency_fractions', 'language_aliases', 'likely_subtags', 'meta_zones', 'parent_exceptions', 'script_aliases', 'territory_aliases', 'territory_currencies', 'territory_languages', 'territory_zones', 'variant_aliases', 'windows_zone_mapping', 'zone_aliases', 'zone_territories']
    21      _global_data: Mapping[_GLOBAL_KEY, Mapping[str, Any]] | None
    22  _global_data = None
    23  _default_plural_rule = PluralRule({})
    24
    25  def get_global(key: _GLOBAL_KEY) -> Mapping[str, Any]:
    26      """Return the dictionary for the given key in the global data.
    27
    28      The global data is stored in the ``babel/global.dat`` file and contains
    29      information independent of individual locales.
    30
    31      >>> get_global('zone_aliases')['UTC']
    32      u'Etc/UTC'
    33      >>> get_global('zone_territories')['Europe/Berlin']
    34      u'DE'
    35
    36      The keys available are:
    37
    38      - ``all_currencies``
    39      - ``currency_fractions``
    40      - ``language_aliases``
    41      - ``likely_subtags``
    42      - ``parent_exceptions``
    43      - ``script_aliases``
    44      - ``territory_aliases``
    45      - ``territory_currencies``
    46      - ``territory_languages``
    47      - ``territory_zones``
    48      - ``variant_aliases``
    49      - ``windows_zone_mapping``
    50      - ``zone_aliases``
    51      - ``zone_territories``
    52
    53      .. note:: The internal structure of the data may change between versions.
    54
    55      .. versionadded:: 0.9
    56
    57      :param key: the data key
    58      """
    59      pass
    60  LOCALE_ALIASES = {'ar': 'ar_SY', 'bg': 'bg_BG', 'bs': 'bs_BA', 'ca': 'ca_ES', 'cs': 'cs_CZ', 'da': 'da_DK', 'de': 'de_DE', 'el': 'el_GR', 'en': 'en_US', 'es': 'es_ES', 'et': 'et_EE', 'fa': 'fa_IR', 'fi': 'fi_FI', 'fr': 'fr_FR', 'gl': 'gl_ES', 'he': 'he_IL', 'hu': 'hu_HU', 'id': 'id_ID', 'is': 'is_IS', 'it': 'it_IT', 'ja': 'ja_JP', 'km': 'km_KH', 'ko': 'ko_KR', 'lt': 'lt_LT', 'lv': 'lv_LV', 'mk': 'mk_MK', 'nl': 'nl_NL', 'nn': 'nn_NO', 'no': 'nb_NO', 'pl': 'pl_PL', 'pt': 'pt_PT', 'ro': 'ro_RO', 'ru': 'ru_RU', 'sk': 'sk_SK', 'sl': 'sl_SI', 'sv': 'sv_SE', 'th': 'th_TH', 'tr': 'tr_TR', 'uk': 'uk_UA'}
    61
    62  class UnknownLocaleError(Exception):
    63      """Exception thrown when a locale is requested for which no locale data
    64      is available.
    65      """
    66
    67      def __init__(self, identifier: str) -> None:
    68          """Create the exception.
    69
    70          :param identifier: the identifier string of the unsupported locale
    71          """
    72          Exception.__init__(self, f'unknown locale {identifier!r}')
    73          self.identifier = identifier
    74
    75  class Locale:
    76      """Representation of a specific locale.
    77
    78      >>> locale = Locale('en', 'US')
    79      >>> repr(locale)
    80      "Locale('en', territory='US')"
    81      >>> locale.display_name
    82      u'English (United States)'
    83
    84      A `Locale` object can also be instantiated from a raw locale string:
    85
    86      >>> locale = Locale.parse('en-US', sep='-')
    87      >>> repr(locale)
    88      "Locale('en', territory='US')"
    89
    90      `Locale` objects provide access to a collection of locale data, such as
    91      territory and language names, number and date format patterns, and more:
    92
    93      >>> locale.number_symbols['latn']['decimal']
    94      u'.'
    95
    96      If a locale is requested for which no locale data is available, an
    97      `UnknownLocaleError` is raised:
    98
    99      >>> Locale.parse('en_XX')
   100      Traceback (most recent call last):
   101          ...
   102      UnknownLocaleError: unknown locale 'en_XX'
   103
   104      For more information see :rfc:`3066`.
   105      """
   106
   107      def __init__(self, language: str, territory: str | None=None, script: str | None=None, variant: str | None=None, modifier: str | None=None) -> None:
   108          """Initialize the locale object from the given identifier components.
   109
   110          >>> locale = Locale('en', 'US')
   111          >>> locale.language
   112          'en'
   113          >>> locale.territory
   114          'US'
   115
   116          :param language: the language code
   117          :param territory: the territory (country or region) code
   118          :param script: the script code
   119          :param variant: the variant code
   120          :param modifier: a modifier (following the '@' symbol, sometimes called '@variant')
   121          :raise `UnknownLocaleError`: if no locale data is available for the
   122                                       requested locale
   123          """
   124          self.language = language
   125          self.territory = territory
   126          self.script = script
   127          self.variant = variant
   128          self.modifier = modifier
   129          self.__data: localedata.LocaleDataDict | None = None
   130          identifier = str(self)
   131          identifier_without_modifier = identifier.partition('@')[0]
   132          if not localedata.exists(identifier_without_modifier):
   133              raise UnknownLocaleError(identifier)
   134
   135      @classmethod
   136      def default(cls, category: str | None=None, aliases: Mapping[str, str]=LOCALE_ALIASES) -> Locale:
   137          """Return the system default locale for the specified category.
   138
   139          >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES']:
   140          ...     os.environ[name] = ''
   141          >>> os.environ['LANG'] = 'fr_FR.UTF-8'
   142          >>> Locale.default('LC_MESSAGES')
   143          Locale('fr', territory='FR')
   144
   145          The following fallbacks to the variable are always considered:
   146
   147          - ``LANGUAGE``
   148          - ``LC_ALL``
   149          - ``LC_CTYPE``
   150          - ``LANG``
   151
   152          :param category: one of the ``LC_XXX`` environment variable names
   153          :param aliases: a dictionary of aliases for locale identifiers
   154          """
   155          pass
   156
   157      @classmethod
   158      def negotiate(cls, preferred: Iterable[str], available: Iterable[str], sep: str='_', aliases: Mapping[str, str]=LOCALE_ALIASES) -> Locale | None:
   159          """Find the best match between available and requested locale strings.
   160
   161          >>> Locale.negotiate(['de_DE', 'en_US'], ['de_DE', 'de_AT'])
   162          Locale('de', territory='DE')
   163          >>> Locale.negotiate(['de_DE', 'en_US'], ['en', 'de'])
   164          Locale('de')
   165          >>> Locale.negotiate(['de_DE', 'de'], ['en_US'])
   166
   167          You can specify the character used in the locale identifiers to separate
   168          the different components. This separator is applied to both lists. Also,
   169          case is ignored in the comparison:
   170
   171          >>> Locale.negotiate(['de-DE', 'de'], ['en-us', 'de-de'], sep='-')
   172          Locale('de', territory='DE')
   173
   174          :param preferred: the list of locale identifiers preferred by the user
   175          :param available: the list of locale identifiers available
   176          :param aliases: a dictionary of aliases for locale identifiers
   177          """
   178          pass
   179
   180      @classmethod
   181      def parse(cls, identifier: str | Locale | None, sep: str='_', resolve_likely_subtags: bool=True) -> Locale:
   182          """Create a `Locale` instance for the given locale identifier.
   183
   184          >>> l = Locale.parse('de-DE', sep='-')
   185          >>> l.display_name
   186          u'Deutsch (Deutschland)'
   187
   188          If the `identifier` parameter is not a string, but actually a `Locale`
   189          object, that object is returned:
   190
   191          >>> Locale.parse(l)
   192          Locale('de', territory='DE')
   193
   194          If the `identifier` parameter is neither of these, such as `None`
   195          e.g. because a default locale identifier could not be determined,
   196          a `TypeError` is raised:
   197
   198          >>> Locale.parse(None)
   199          Traceback (most recent call last):
   200              ...
   201          TypeError: ...
   202
   203          This also can perform resolving of likely subtags which it does
   204          by default.  This is for instance useful to figure out the most
   205          likely locale for a territory you can use ``'und'`` as the
   206          language tag:
   207
   208          >>> Locale.parse('und_AT')
   209          Locale('de', territory='AT')
   210
   211          Modifiers are optional, and always at the end, separated by "@":
   212
   213          >>> Locale.parse('de_AT@euro')
   214          Locale('de', territory='AT', modifier='euro')
   215
   216          :param identifier: the locale identifier string
   217          :param sep: optional component separator
   218          :param resolve_likely_subtags: if this is specified then a locale will
   219                                         have its likely subtag resolved if the
   220                                         locale otherwise does not exist.  For
   221                                         instance ``zh_TW`` by itself is not a
   222                                         locale that exists but Babel can
   223                                         automatically expand it to the full
   224                                         form of ``zh_hant_TW``.  Note that this
   225                                         expansion is only taking place if no
   226                                         locale exists otherwise.  For instance
   227                                         there is a locale ``en`` that can exist
   228                                         by itself.
   229          :raise `ValueError`: if the string does not appear to be a valid locale
   230                               identifier
   231          :raise `UnknownLocaleError`: if no locale data is available for the
   232                                       requested locale
   233          :raise `TypeError`: if the identifier is not a string or a `Locale`
   234          """
   235          pass
   236
   237      def __eq__(self, other: object) -> bool:
   238          for key in ('language', 'territory', 'script', 'variant', 'modifier'):
   239              if not hasattr(other, key):
   240                  return False
   241          return self.language == getattr(other, 'language') and self.territory == getattr(other, 'territory') and (self.script == getattr(other, 'script')) and (self.variant == getattr(other, 'variant')) and (self.modifier == getattr(other, 'modifier'))
   242
   243      def __ne__(self, other: object) -> bool:
   244          return not self.__eq__(other)
   245
   246      def __hash__(self) -> int:
   247          return hash((self.language, self.territory, self.script, self.variant, self.modifier))
   248
   249      def __repr__(self) -> str:
   250          parameters = ['']
   251          for key in ('territory', 'script', 'variant', 'modifier'):
   252              value = getattr(self, key)
   253              if value is not None:
   254                  parameters.append(f'{key}={value!r}')
   255          return f'Locale({self.language!r}{', '.join(parameters)})'
   256
   257      def __str__(self) -> str:
   258          return get_locale_identifier((self.language, self.territory, self.script, self.variant, self.modifier))
   259
   260      def get_display_name(self, locale: Locale | str | None=None) -> str | None:
   261          """Return the display name of the locale using the given locale.
   262
   263          The display name will include the language, territory, script, and
   264          variant, if those are specified.
   265
   266          >>> Locale('zh', 'CN', script='Hans').get_display_name('en')
   267          u'Chinese (Simplified, China)'
   268
   269          Modifiers are currently passed through verbatim:
   270
   271          >>> Locale('it', 'IT', modifier='euro').get_display_name('en')
   272          u'Italian (Italy, euro)'
   273
   274          :param locale: the locale to use
   275          """
   276          pass
   277      display_name = property(get_display_name, doc="        The localized display name of the locale.\n\n        >>> Locale('en').display_name\n        u'English'\n        >>> Locale('en', 'US').display_name\n        u'English (United States)'\n        >>> Locale('sv').display_name\n        u'svenska'\n\n        :type: `unicode`\n        ")
   278
   279      def get_language_name(self, locale: Locale | str | None=None) -> str | None:
   280          """Return the language of this locale in the given locale.
   281
   282          >>> Locale('zh', 'CN', script='Hans').get_language_name('de')
   283          u'Chinesisch'
   284
   285          .. versionadded:: 1.0
   286
   287          :param locale: the locale to use
   288          """
   289          pass
   290      language_name = property(get_language_name, doc="        The localized language name of the locale.\n\n        >>> Locale('en', 'US').language_name\n        u'English'\n    ")
   291
   292      def get_territory_name(self, locale: Locale | str | None=None) -> str | None:
   293          """Return the territory name in the given locale."""
   294          pass
   295      territory_name = property(get_territory_name, doc="        The localized territory name of the locale if available.\n\n        >>> Locale('de', 'DE').territory_name\n        u'Deutschland'\n    ")
   296
   297      def get_script_name(self, locale: Locale | str | None=None) -> str | None:
   298          """Return the script name in the given locale."""
   299          pass
   300      script_name = property(get_script_name, doc="        The localized script name of the locale if available.\n\n        >>> <response clipped><NOTE>Due to the max output limit, only part of the full response has been shown to you.</NOTE>
   656
   657      This ID can be used as a key for the period name dictionary.
   658
   659      >>> from datetime import time
   660      >>> get_period_names(locale="de")[get_period_id(time(7, 42), locale="de")]
   661      u'Morgen'
   662
   663      >>> get_period_id(time(0), locale="en_US")
   664      u'midnight'
   665
   666      >>> get_period_id(time(0), type="selection", locale="en_US")
   667      u'night1'
   668
   669      :param time: The time to inspect.
   670      :param tzinfo: The timezone for the time. See ``format_time``.
   671      :param type: The period type to use. Either "selection" or None.
   672                   The selection type is used for selecting among phrases such as
   673                   “Your email arrived yesterday evening” or “Your email arrived last night”.
   674      :param locale: the `Locale` object, or a locale string
   675      :return: period ID. Something is always returned -- even if it's just "am" or "pm".
   676      """
   677      pass
   678
   679  class ParseError(ValueError):
   680      pass
   681
   682  def parse_date(string: str, locale: Locale | str | None=LC_TIME, format: _PredefinedTimeFormat='medium') -> datetime.date:
   683      """Parse a date from a string.
   684
   685      This function first tries to interpret the string as ISO-8601
   686      date format, then uses the date format for the locale as a hint to
   687      determine the order in which the date fields appear in the string.
   688
   689      >>> parse_date('4/1/04', locale='en_US')
   690      datetime.date(2004, 4, 1)
   691      >>> parse_date('01.04.2004', locale='de_DE')
   692      datetime.date(2004, 4, 1)
   693      >>> parse_date('2004-04-01', locale='en_US')
   694      datetime.date(2004, 4, 1)
   695      >>> parse_date('2004-04-01', locale='de_DE')
   696      datetime.date(2004, 4, 1)
   697
   698      :param string: the string containing the date
   699      :param locale: a `Locale` object or a locale identifier
   700      :param format: the format to use (see ``get_date_format``)
   701      """
   702      pass
   703
   704  def parse_time(string: str, locale: Locale | str | None=LC_TIME, format: _PredefinedTimeFormat='medium') -> datetime.time:
   705      """Parse a time from a string.
   706
   707      This function uses the time format for the locale as a hint to determine
   708      the order in which the time fields appear in the string.
   709
   710      >>> parse_time('15:30:00', locale='en_US')
   711      datetime.time(15, 30)
   712
   713      :param string: the string containing the time
   714      :param locale: a `Locale` object or a locale identifier
   715      :param format: the format to use (see ``get_time_format``)
   716      :return: the parsed time
   717      :rtype: `time`
   718      """
   719      pass
   720
   721  class DateTimePattern:
   722
   723      def __init__(self, pattern: str, format: DateTimeFormat):
   724          self.pattern = pattern
   725          self.format = format
   726
   727      def __repr__(self) -> str:
   728          return f'<{type(self).__name__} {self.pattern!r}>'
   729
   730      def __str__(self) -> str:
   731          pat = self.pattern
   732          return pat
   733
   734      def __mod__(self, other: DateTimeFormat) -> str:
   735          if not isinstance(other, DateTimeFormat):
   736              return NotImplemented
   737          return self.format % other
   738
   739  class DateTimeFormat:
   740
   741      def __init__(self, value: datetime.date | datetime.time, locale: Locale | str, reference_date: datetime.date | None=None) -> None:
   742          assert isinstance(value, (datetime.date, datetime.datetime, datetime.time))
   743          if isinstance(value, (datetime.datetime, datetime.time)) and value.tzinfo is None:
   744              value = value.replace(tzinfo=UTC)
   745          self.value = value
   746          self.locale = Locale.parse(locale)
   747          self.reference_date = reference_date
   748
   749      def __getitem__(self, name: str) -> str:
   750          char = name[0]
   751          num = len(name)
   752          if char == 'G':
   753              return self.format_era(char, num)
   754          elif char in ('y', 'Y', 'u'):
   755              return self.format_year(char, num)
   756          elif char in ('Q', 'q'):
   757              return self.format_quarter(char, num)
   758          elif char in ('M', 'L'):
   759              return self.format_month(char, num)
   760          elif char in ('w', 'W'):
   761              return self.format_week(char, num)
   762          elif char == 'd':
   763              return self.format(self.value.day, num)
   764          elif char == 'D':
   765              return self.format_day_of_year(num)
   766          elif char == 'F':
   767              return self.format_day_of_week_in_month()
   768          elif char in ('E', 'e', 'c'):
   769              return self.format_weekday(char, num)
   770          elif char in ('a', 'b', 'B'):
   771              return self.format_period(char, num)
   772          elif char == 'h':
   773              if self.value.hour % 12 == 0:
   774                  return self.format(12, num)
   775              else:
   776                  return self.format(self.value.hour % 12, num)
   777          elif char == 'H':
   778              return self.format(self.value.hour, num)
   779          elif char == 'K':
   780              return self.format(self.value.hour % 12, num)
   781          elif char == 'k':
   782              if self.value.hour == 0:
   783                  return self.format(24, num)
   784              else:
   785                  return self.format(self.value.hour, num)
   786          elif char == 'm':
   787              return self.format(self.value.minute, num)
   788          elif char == 's':
   789              return self.format(self.value.second, num)
   790          elif char == 'S':
   791              return self.format_frac_seconds(num)
   792          elif char == 'A':
   793              return self.format_milliseconds_in_day(num)
   794          elif char in ('z', 'Z', 'v', 'V', 'x', 'X', 'O'):
   795              return self.format_timezone(char, num)
   796          else:
   797              raise KeyError(f'Unsupported date/time field {char!r}')
   798
   799      def format_weekday(self, char: str='E', num: int=4) -> str:
   800          """
   801          Return weekday from parsed datetime according to format pattern.
   802
   803          >>> from datetime import date
   804          >>> format = DateTimeFormat(date(2016, 2, 28), Locale.parse('en_US'))
   805          >>> format.format_weekday()
   806          u'Sunday'
   807
   808          'E': Day of week - Use one through three letters for the abbreviated day name, four for the full (wide) name,
   809               five for the narrow name, or six for the short name.
   810          >>> format.format_weekday('E',2)
   811          u'Sun'
   812
   813          'e': Local day of week. Same as E except adds a numeric value that will depend on the local starting day of the
   814               week, using one or two letters. For this example, Monday is the first day of the week.
   815          >>> format.format_weekday('e',2)
   816          '01'
   817
   818          'c': Stand-Alone local day of week - Use one letter for the local numeric value (same as 'e'), three for the
   819               abbreviated day name, four for the full (wide) name, five for the narrow name, or six for the short name.
   820          >>> format.format_weekday('c',1)
   821          '1'
   822
   823          :param char: pattern format character ('e','E','c')
   824          :param num: count of format character
   825
   826          """
   827          pass
   828
   829      def format_period(self, char: str, num: int) -> str:
   830          """
   831          Return period from parsed datetime according to format pattern.
   832
   833          >>> from datetime import datetime, time
   834          >>> format = DateTimeFormat(time(13, 42), 'fi_FI')
   835          >>> format.format_period('a', 1)
   836          u'ip.'
   837          >>> format.format_period('b', 1)
   838          u'iltap.'
   839          >>> format.format_period('b', 4)
   840          u'iltapäivä'
   841          >>> format.format_period('B', 4)
   842          u'iltapäivällä'
   843          >>> format.format_period('B', 5)
   844          u'ip.'
   845
   846          >>> format = DateTimeFormat(datetime(2022, 4, 28, 6, 27), 'zh_Hant')
   847          >>> format.format_period('a', 1)
   848          u'上午'
   849          >>> format.format_period('b', 1)
   850          u'清晨'
   851          >>> format.format_period('B', 1)
   852          u'清晨'
   853
   854          :param char: pattern format character ('a', 'b', 'B')
   855          :param num: count of format character
   856
   857          """
   858          pass
   859
   860      def format_frac_seconds(self, num: int) -> str:
   861          """ Return fractional seconds.
   862
   863          Rounds the time's microseconds to the precision given by the number         of digits passed in.
   864          """
   865          pass
   866
   867      def get_week_number(self, day_of_period: int, day_of_week: int | None=None) -> int:
   868          """Return the number of the week of a day within a period. This may be
   869          the week number in a year or the week number in a month.
   870
   871          Usually this will return a value equal to or greater than 1, but if the
   872          first week of the period is so short that it actually counts as the last
   873          week of the previous period, this function will return 0.
   874
   875          >>> date = datetime.date(2006, 1, 8)
   876          >>> DateTimeFormat(date, 'de_DE').get_week_number(6)
   877          1
   878          >>> DateTimeFormat(date, 'en_US').get_week_number(6)
   879          2
   880
   881          :param day_of_period: the number of the day in the period (usually
   882                                either the day of month or the day of year)
   883          :param day_of_week: the week day; if omitted, the week day of the
   884                              current date is assumed
   885          """
   886          pass
   887  PATTERN_CHARS: dict[str, list[int] | None] = {'G': [1, 2, 3, 4, 5], 'y': None, 'Y': None, 'u': None, 'Q': [1, 2, 3, 4, 5], 'q': [1, 2, 3, 4, 5], 'M': [1, 2, 3, 4, 5], 'L': [1, 2, 3, 4, 5], 'w': [1, 2], 'W': [1], 'd': [1, 2], 'D': [1, 2, 3], 'F': [1], 'g': None, 'E': [1, 2, 3, 4, 5, 6], 'e': [1, 2, 3, 4, 5, 6], 'c': [1, 3, 4, 5, 6], 'a': [1, 2, 3, 4, 5], 'b': [1, 2, 3, 4, 5], 'B': [1, 2, 3, 4, 5], 'h': [1, 2], 'H': [1, 2], 'K': [1, 2], 'k': [1, 2], 'm': [1, 2], 's': [1, 2], 'S': None, 'A': None, 'z': [1, 2, 3, 4], 'Z': [1, 2, 3, 4, 5], 'O': [1, 4], 'v': [1, 4], 'V': [1, 2, 3, 4], 'x': [1, 2, 3, 4, 5], 'X': [1, 2, 3, 4, 5]}
   888  PATTERN_CHAR_ORDER = 'GyYuUQqMLlwWdDFgEecabBChHKkjJmsSAzZOvVXx'
   889
   890  def parse_pattern(pattern: str | DateTimePattern) -> DateTimePattern:
   891      """Parse date, time, and datetime format patterns.
   892
   893      >>> parse_pattern("MMMMd").format
   894      u'%(MMMM)s%(d)s'
   895      >>> parse_pattern("MMM d, yyyy").format
   896      u'%(MMM)s %(d)s, %(yyyy)s'
   897
   898      Pattern can contain literal strings in single quotes:
   899
   900      >>> parse_pattern("H:mm' Uhr 'z").format
   901      u'%(H)s:%(mm)s Uhr %(z)s'
   902
   903      An actual single quote can be used by using two adjacent single quote
   904      characters:
   905
   906      >>> parse_pattern("hh' o''clock'").format
   907      u"%(hh)s o'clock"
   908
   909      :param pattern: the formatting pattern to parse
   910      """
   911      pass
   912
   913  def tokenize_pattern(pattern: str) -> list[tuple[str, str | tuple[str, int]]]:
   914      """
   915      Tokenize date format patterns.
   916
   917      Returns a list of (token_type, token_value) tuples.
   918
   919      ``token_type`` may be either "chars" or "field".
   920
   921      For "chars" tokens, the value is the literal value.
   922
   923      For "field" tokens, the value is a tuple of (field character, repetition count).
   924
   925      :param pattern: Pattern string
   926      :type pattern: str
   927      :rtype: list[tuple]
   928      """
   929      pass
   930
   931  def untokenize_pattern(tokens: Iterable[tuple[str, str | tuple[str, int]]]) -> str:
   932      """
   933      Turn a date format pattern token stream back into a string.
   934
   935      This is the reverse operation of ``tokenize_pattern``.
   936
   937      :type tokens: Iterable[tuple]
   938      :rtype: str
   939      """
   940      pass
   941
   942  def split_interval_pattern(pattern: str) -> list[str]:
   943      """
   944      Split an interval-describing datetime pattern into multiple pieces.
   945
   946      > The pattern is then designed to be broken up into two pieces by determining the first repeating field.
   947      - https://www.unicode.org/reports/tr35/tr35-dates.html#intervalFormats
   948
   949      >>> split_interval_pattern(u'E d.M. – E d.M.')
   950      [u'E d.M. – ', 'E d.M.']
   951      >>> split_interval_pattern("Y 'text' Y 'more text'")
   952      ["Y 'text '", "Y 'more text'"]
   953      >>> split_interval_pattern(u"E, MMM d – E")
   954      [u'E, MMM d – ', u'E']
   955      >>> split_interval_pattern("MMM d")
   956      ['MMM d']
   957      >>> split_interval_pattern("y G")
   958      ['y G']
   959      >>> split_interval_pattern(u"MMM d – d")
   960      [u'MMM d – ', u'd']
   961
   962      :param pattern: Interval pattern string
   963      :return: list of "subpatterns"
   964      """
   965      pass
   966
   967  def match_skeleton(skeleton: str, options: Iterable[str], allow_different_fields: bool=False) -> str | None:
   968      """
   969      Find the closest match for the given datetime skeleton among the options given.
   970
   971      This uses the rules outlined in the TR35 document.
   972
   973      >>> match_skeleton('yMMd', ('yMd', 'yMMMd'))
   974      'yMd'
   975
   976      >>> match_skeleton('yMMd', ('jyMMd',), allow_different_fields=True)
   977      'jyMMd'
   978
   979      >>> match_skeleton('yMMd', ('qyMMd',), allow_different_fields=False)
   980
   981      >>> match_skeleton('hmz', ('hmv',))
   982      'hmv'
   983
   984      :param skeleton: The skeleton to match
   985      :type skeleton: str
   986      :param options: An iterable of other skeletons to match against
   987      :type options: Iterable[str]
   988      :return: The closest skeleton match, or if no match was found, None.
   989      :rtype: str|None
   990      """
   991      pass
[The command completed with exit code 0.]
[Current working directory: /workspace/babel]
[Python interpreter: /usr/bin/python]
[Command finished with exit code 0]