diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/alembic/script/write_hooks.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/alembic/script/write_hooks.py | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/alembic/script/write_hooks.py b/.venv/lib/python3.12/site-packages/alembic/script/write_hooks.py new file mode 100644 index 00000000..99771479 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/alembic/script/write_hooks.py @@ -0,0 +1,179 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import shlex +import subprocess +import sys +from typing import Any +from typing import Callable +from typing import Dict +from typing import List +from typing import Mapping +from typing import Optional +from typing import Union + +from .. import util +from ..util import compat + + +REVISION_SCRIPT_TOKEN = "REVISION_SCRIPT_FILENAME" + +_registry: dict = {} + + +def register(name: str) -> Callable: + """A function decorator that will register that function as a write hook. + + See the documentation linked below for an example. + + .. seealso:: + + :ref:`post_write_hooks_custom` + + + """ + + def decorate(fn): + _registry[name] = fn + return fn + + return decorate + + +def _invoke( + name: str, revision: str, options: Mapping[str, Union[str, int]] +) -> Any: + """Invokes the formatter registered for the given name. + + :param name: The name of a formatter in the registry + :param revision: A :class:`.MigrationRevision` instance + :param options: A dict containing kwargs passed to the + specified formatter. + :raises: :class:`alembic.util.CommandError` + """ + try: + hook = _registry[name] + except KeyError as ke: + raise util.CommandError( + f"No formatter with name '{name}' registered" + ) from ke + else: + return hook(revision, options) + + +def _run_hooks(path: str, hook_config: Mapping[str, str]) -> None: + """Invoke hooks for a generated revision.""" + + from .base import _split_on_space_comma + + names = _split_on_space_comma.split(hook_config.get("hooks", "")) + + for name in names: + if not name: + continue + opts = { + key[len(name) + 1 :]: hook_config[key] + for key in hook_config + if key.startswith(name + ".") + } + opts["_hook_name"] = name + try: + type_ = opts["type"] + except KeyError as ke: + raise util.CommandError( + f"Key {name}.type is required for post write hook {name!r}" + ) from ke + else: + with util.status( + f"Running post write hook {name!r}", newline=True + ): + _invoke(type_, path, opts) + + +def _parse_cmdline_options(cmdline_options_str: str, path: str) -> List[str]: + """Parse options from a string into a list. + + Also substitutes the revision script token with the actual filename of + the revision script. + + If the revision script token doesn't occur in the options string, it is + automatically prepended. + """ + if REVISION_SCRIPT_TOKEN not in cmdline_options_str: + cmdline_options_str = REVISION_SCRIPT_TOKEN + " " + cmdline_options_str + cmdline_options_list = shlex.split( + cmdline_options_str, posix=compat.is_posix + ) + cmdline_options_list = [ + option.replace(REVISION_SCRIPT_TOKEN, path) + for option in cmdline_options_list + ] + return cmdline_options_list + + +@register("console_scripts") +def console_scripts( + path: str, options: dict, ignore_output: bool = False +) -> None: + try: + entrypoint_name = options["entrypoint"] + except KeyError as ke: + raise util.CommandError( + f"Key {options['_hook_name']}.entrypoint is required for post " + f"write hook {options['_hook_name']!r}" + ) from ke + for entry in compat.importlib_metadata_get("console_scripts"): + if entry.name == entrypoint_name: + impl: Any = entry + break + else: + raise util.CommandError( + f"Could not find entrypoint console_scripts.{entrypoint_name}" + ) + cwd: Optional[str] = options.get("cwd", None) + cmdline_options_str = options.get("options", "") + cmdline_options_list = _parse_cmdline_options(cmdline_options_str, path) + + kw: Dict[str, Any] = {} + if ignore_output: + kw["stdout"] = kw["stderr"] = subprocess.DEVNULL + + subprocess.run( + [ + sys.executable, + "-c", + f"import {impl.module}; {impl.module}.{impl.attr}()", + ] + + cmdline_options_list, + cwd=cwd, + **kw, + ) + + +@register("exec") +def exec_(path: str, options: dict, ignore_output: bool = False) -> None: + try: + executable = options["executable"] + except KeyError as ke: + raise util.CommandError( + f"Key {options['_hook_name']}.executable is required for post " + f"write hook {options['_hook_name']!r}" + ) from ke + cwd: Optional[str] = options.get("cwd", None) + cmdline_options_str = options.get("options", "") + cmdline_options_list = _parse_cmdline_options(cmdline_options_str, path) + + kw: Dict[str, Any] = {} + if ignore_output: + kw["stdout"] = kw["stderr"] = subprocess.DEVNULL + + subprocess.run( + [ + executable, + *cmdline_options_list, + ], + cwd=cwd, + **kw, + ) |