| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- from __future__ import annotations
- import atexit
- from contextlib import ExitStack
- import importlib
- import importlib.machinery
- import importlib.util
- import os
- import pathlib
- import re
- import tempfile
- from types import ModuleType
- from typing import Any
- from typing import Optional
- from typing import Union
- from mako import exceptions
- from mako.template import Template
- from . import compat
- from .exc import CommandError
- def template_to_file(
- template_file: Union[str, os.PathLike[str]],
- dest: Union[str, os.PathLike[str]],
- output_encoding: str,
- *,
- append_with_newlines: bool = False,
- **kw: Any,
- ) -> None:
- template = Template(filename=_preserving_path_as_str(template_file))
- try:
- output = template.render_unicode(**kw).encode(output_encoding)
- except:
- with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as ntf:
- ntf.write(
- exceptions.text_error_template()
- .render_unicode()
- .encode(output_encoding)
- )
- fname = ntf.name
- raise CommandError(
- "Template rendering failed; see %s for a "
- "template-oriented traceback." % fname
- )
- else:
- with open(dest, "ab" if append_with_newlines else "wb") as f:
- if append_with_newlines:
- f.write("\n\n".encode(output_encoding))
- f.write(output)
- def coerce_resource_to_filename(fname_or_resource: str) -> pathlib.Path:
- """Interpret a filename as either a filesystem location or as a package
- resource.
- Names that are non absolute paths and contain a colon
- are interpreted as resources and coerced to a file location.
- """
- # TODO: there seem to be zero tests for the package resource codepath
- if not os.path.isabs(fname_or_resource) and ":" in fname_or_resource:
- tokens = fname_or_resource.split(":")
- # from https://importlib-resources.readthedocs.io/en/latest/migration.html#pkg-resources-resource-filename # noqa E501
- file_manager = ExitStack()
- atexit.register(file_manager.close)
- ref = compat.importlib_resources.files(tokens[0])
- for tok in tokens[1:]:
- ref = ref / tok
- fname_or_resource = file_manager.enter_context( # type: ignore[assignment] # noqa: E501
- compat.importlib_resources.as_file(ref)
- )
- return pathlib.Path(fname_or_resource)
- def pyc_file_from_path(
- path: Union[str, os.PathLike[str]],
- ) -> Optional[pathlib.Path]:
- """Given a python source path, locate the .pyc."""
- pathpath = pathlib.Path(path)
- candidate = pathlib.Path(
- importlib.util.cache_from_source(pathpath.as_posix())
- )
- if candidate.exists():
- return candidate
- # even for pep3147, fall back to the old way of finding .pyc files,
- # to support sourceless operation
- ext = pathpath.suffix
- for ext in importlib.machinery.BYTECODE_SUFFIXES:
- if pathpath.with_suffix(ext).exists():
- return pathpath.with_suffix(ext)
- else:
- return None
- def load_python_file(
- dir_: Union[str, os.PathLike[str]], filename: Union[str, os.PathLike[str]]
- ) -> ModuleType:
- """Load a file from the given path as a Python module."""
- dir_ = pathlib.Path(dir_)
- filename_as_path = pathlib.Path(filename)
- filename = filename_as_path.name
- module_id = re.sub(r"\W", "_", filename)
- path = dir_ / filename
- ext = path.suffix
- if ext == ".py":
- if path.exists():
- module = load_module_py(module_id, path)
- else:
- pyc_path = pyc_file_from_path(path)
- if pyc_path is None:
- raise ImportError("Can't find Python file %s" % path)
- else:
- module = load_module_py(module_id, pyc_path)
- elif ext in (".pyc", ".pyo"):
- module = load_module_py(module_id, path)
- else:
- assert False
- return module
- def load_module_py(
- module_id: str, path: Union[str, os.PathLike[str]]
- ) -> ModuleType:
- spec = importlib.util.spec_from_file_location(module_id, path)
- assert spec
- module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(module) # type: ignore
- return module
- def _preserving_path_as_str(path: Union[str, os.PathLike[str]]) -> str:
- """receive str/pathlike and return a string.
- Does not convert an incoming string path to a Path first, to help with
- unit tests that are doing string path round trips without OS-specific
- processing if not necessary.
- """
- if isinstance(path, str):
- return path
- elif isinstance(path, pathlib.PurePath):
- return str(path)
- else:
- return str(pathlib.Path(path))
|