"""Utility code for constructing importers, etc."""

import importlib.abc
import importlib.machinery
import sys
import types
from _typeshed import ReadableBuffer, StrOrBytesPath
from _typeshed.importlib import LoaderProtocol
from collections.abc import Callable
from typing import Any
from typing_extensions import ParamSpec

_P = ParamSpec("_P")

if sys.version_info < (3, 12):
    def module_for_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ...
    def set_loader(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ...
    def set_package(fxn: Callable[_P, types.ModuleType]) -> Callable[_P, types.ModuleType]: ...

def resolve_name(name: str, package: str | None) -> str:
    """Resolve a relative module name to an absolute one."""
    ...

MAGIC_NUMBER: bytes

def cache_from_source(path: str, debug_override: bool | None = None, *, optimization: Any | None = None) -> str:
    """
    Given the path to a .py file, return the path to its .pyc file.

    The .py file does not need to exist; this simply returns the path to the
    .pyc file calculated as if the .py file were imported.

    The 'optimization' parameter controls the presumed optimization level of
    the bytecode file. If 'optimization' is not None, the string representation
    of the argument is taken and verified to be alphanumeric (else ValueError
    is raised).

    The debug_override parameter is deprecated. If debug_override is not None,
    a True value is the same as setting 'optimization' to the empty string
    while a False value is equivalent to setting 'optimization' to '1'.

    If sys.implementation.cache_tag is None then NotImplementedError is raised.
    """
    ...
def source_from_cache(path: str) -> str:
    """
    Given the path to a .pyc. file, return the path to its .py file.

    The .pyc file does not need to exist; this simply returns the path to
    the .py file calculated to correspond to the .pyc file.  If path does
    not conform to PEP 3147/488 format, ValueError will be raised. If
    sys.implementation.cache_tag is None then NotImplementedError is raised.
    """
    ...
def decode_source(source_bytes: ReadableBuffer) -> str:
    """
    Decode bytes representing source code and return the string.

    Universal newline support is used in the decoding.
    """
    ...
def find_spec(name: str, package: str | None = None) -> importlib.machinery.ModuleSpec | None:
    """
    Return the spec for the specified module.

    First, sys.modules is checked to see if the module was already imported. If
    so, then sys.modules[name].__spec__ is returned. If that happens to be
    set to None, then ValueError is raised. If the module is not in
    sys.modules, then sys.meta_path is searched for a suitable spec with the
    value of 'path' given to the finders. None is returned if no spec could
    be found.

    If the name is for submodule (contains a dot), the parent module is
    automatically imported.

    The name and package arguments work the same as importlib.import_module().
    In other words, relative module names (with leading dots) work.
    """
    ...
def spec_from_loader(
    name: str, loader: LoaderProtocol | None, *, origin: str | None = None, is_package: bool | None = None
) -> importlib.machinery.ModuleSpec | None:
    """Return a module spec based on various loader methods."""
    ...
def spec_from_file_location(
    name: str,
    location: StrOrBytesPath | None = None,
    *,
    loader: LoaderProtocol | None = None,
    submodule_search_locations: list[str] | None = ...,
) -> importlib.machinery.ModuleSpec | None:
    """
    Return a module spec based on a file location.

    To indicate that the module is a package, set
    submodule_search_locations to a list of directory paths.  An
    empty list is sufficient, though its not otherwise useful to the
    import system.

    The loader must take a spec as its only __init__() arg.
    """
    ...
def module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType:
    """Create a module based on the provided spec."""
    ...

class LazyLoader(importlib.abc.Loader):
    """A loader that creates a module which defers loading until attribute access."""
    def __init__(self, loader: importlib.abc.Loader) -> None: ...
    @classmethod
    def factory(cls, loader: importlib.abc.Loader) -> Callable[..., LazyLoader]:
        """Construct a callable which returns the eager loader made lazy."""
        ...
    def exec_module(self, module: types.ModuleType) -> None:
        """Make the module load lazily."""
        ...

def source_hash(source_bytes: ReadableBuffer) -> bytes:
    """Return the hash of *source_bytes* as used in hash-based pyc files."""
    ...
