(root)/
Python-3.12.0/
Lib/
zoneinfo/
_tzpath.py
       1  import os
       2  import sysconfig
       3  
       4  
       5  def reset_tzpath(to=None):
       6      global TZPATH
       7  
       8      tzpaths = to
       9      if tzpaths is not None:
      10          if isinstance(tzpaths, (str, bytes)):
      11              raise TypeError(
      12                  f"tzpaths must be a list or tuple, "
      13                  + f"not {type(tzpaths)}: {tzpaths!r}"
      14              )
      15  
      16          if not all(map(os.path.isabs, tzpaths)):
      17              raise ValueError(_get_invalid_paths_message(tzpaths))
      18          base_tzpath = tzpaths
      19      else:
      20          env_var = os.environ.get("PYTHONTZPATH", None)
      21          if env_var is not None:
      22              base_tzpath = _parse_python_tzpath(env_var)
      23          else:
      24              base_tzpath = _parse_python_tzpath(
      25                  sysconfig.get_config_var("TZPATH")
      26              )
      27  
      28      TZPATH = tuple(base_tzpath)
      29  
      30  
      31  def _parse_python_tzpath(env_var):
      32      if not env_var:
      33          return ()
      34  
      35      raw_tzpath = env_var.split(os.pathsep)
      36      new_tzpath = tuple(filter(os.path.isabs, raw_tzpath))
      37  
      38      # If anything has been filtered out, we will warn about it
      39      if len(new_tzpath) != len(raw_tzpath):
      40          import warnings
      41  
      42          msg = _get_invalid_paths_message(raw_tzpath)
      43  
      44          warnings.warn(
      45              "Invalid paths specified in PYTHONTZPATH environment variable. "
      46              + msg,
      47              InvalidTZPathWarning,
      48          )
      49  
      50      return new_tzpath
      51  
      52  
      53  def _get_invalid_paths_message(tzpaths):
      54      invalid_paths = (path for path in tzpaths if not os.path.isabs(path))
      55  
      56      prefix = "\n    "
      57      indented_str = prefix + prefix.join(invalid_paths)
      58  
      59      return (
      60          "Paths should be absolute but found the following relative paths:"
      61          + indented_str
      62      )
      63  
      64  
      65  def find_tzfile(key):
      66      """Retrieve the path to a TZif file from a key."""
      67      _validate_tzfile_path(key)
      68      for search_path in TZPATH:
      69          filepath = os.path.join(search_path, key)
      70          if os.path.isfile(filepath):
      71              return filepath
      72  
      73      return None
      74  
      75  
      76  _TEST_PATH = os.path.normpath(os.path.join("_", "_"))[:-1]
      77  
      78  
      79  def _validate_tzfile_path(path, _base=_TEST_PATH):
      80      if os.path.isabs(path):
      81          raise ValueError(
      82              f"ZoneInfo keys may not be absolute paths, got: {path}"
      83          )
      84  
      85      # We only care about the kinds of path normalizations that would change the
      86      # length of the key - e.g. a/../b -> a/b, or a/b/ -> a/b. On Windows,
      87      # normpath will also change from a/b to a\b, but that would still preserve
      88      # the length.
      89      new_path = os.path.normpath(path)
      90      if len(new_path) != len(path):
      91          raise ValueError(
      92              f"ZoneInfo keys must be normalized relative paths, got: {path}"
      93          )
      94  
      95      resolved = os.path.normpath(os.path.join(_base, new_path))
      96      if not resolved.startswith(_base):
      97          raise ValueError(
      98              f"ZoneInfo keys must refer to subdirectories of TZPATH, got: {path}"
      99          )
     100  
     101  
     102  del _TEST_PATH
     103  
     104  
     105  def available_timezones():
     106      """Returns a set containing all available time zones.
     107  
     108      .. caution::
     109  
     110          This may attempt to open a large number of files, since the best way to
     111          determine if a given file on the time zone search path is to open it
     112          and check for the "magic string" at the beginning.
     113      """
     114      from importlib import resources
     115  
     116      valid_zones = set()
     117  
     118      # Start with loading from the tzdata package if it exists: this has a
     119      # pre-assembled list of zones that only requires opening one file.
     120      try:
     121          with resources.files("tzdata").joinpath("zones").open("r") as f:
     122              for zone in f:
     123                  zone = zone.strip()
     124                  if zone:
     125                      valid_zones.add(zone)
     126      except (ImportError, FileNotFoundError):
     127          pass
     128  
     129      def valid_key(fpath):
     130          try:
     131              with open(fpath, "rb") as f:
     132                  return f.read(4) == b"TZif"
     133          except Exception:  # pragma: nocover
     134              return False
     135  
     136      for tz_root in TZPATH:
     137          if not os.path.exists(tz_root):
     138              continue
     139  
     140          for root, dirnames, files in os.walk(tz_root):
     141              if root == tz_root:
     142                  # right/ and posix/ are special directories and shouldn't be
     143                  # included in the output of available zones
     144                  if "right" in dirnames:
     145                      dirnames.remove("right")
     146                  if "posix" in dirnames:
     147                      dirnames.remove("posix")
     148  
     149              for file in files:
     150                  fpath = os.path.join(root, file)
     151  
     152                  key = os.path.relpath(fpath, start=tz_root)
     153                  if os.sep != "/":  # pragma: nocover
     154                      key = key.replace(os.sep, "/")
     155  
     156                  if not key or key in valid_zones:
     157                      continue
     158  
     159                  if valid_key(fpath):
     160                      valid_zones.add(key)
     161  
     162      if "posixrules" in valid_zones:
     163          # posixrules is a special symlink-only time zone where it exists, it
     164          # should not be included in the output
     165          valid_zones.remove("posixrules")
     166  
     167      return valid_zones
     168  
     169  
     170  class ESC[4;38;5;81mInvalidTZPathWarning(ESC[4;38;5;149mRuntimeWarning):
     171      """Warning raised if an invalid path is specified in PYTHONTZPATH."""
     172  
     173  
     174  TZPATH = ()
     175  reset_tzpath()