main commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-16 16:30:25 +09:00
parent 91c7e04474
commit 537e7b363f
1146 changed files with 45926 additions and 77196 deletions

View File

@@ -1,9 +1,6 @@
# mypy: allow-untyped-defs, allow-untyped-calls
from __future__ import annotations
import os
import pathlib
from typing import List
from typing import Optional
from typing import TYPE_CHECKING
@@ -13,7 +10,6 @@ from . import autogenerate as autogen
from . import util
from .runtime.environment import EnvironmentContext
from .script import ScriptDirectory
from .util import compat
if TYPE_CHECKING:
from alembic.config import Config
@@ -22,7 +18,7 @@ if TYPE_CHECKING:
from .runtime.environment import ProcessRevisionDirectiveFn
def list_templates(config: Config) -> None:
def list_templates(config: Config):
"""List available templates.
:param config: a :class:`.Config` object.
@@ -30,10 +26,12 @@ def list_templates(config: Config) -> None:
"""
config.print_stdout("Available templates:\n")
for tempname in config._get_template_path().iterdir():
with (tempname / "README").open() as readme:
for tempname in os.listdir(config.get_template_directory()):
with open(
os.path.join(config.get_template_directory(), tempname, "README")
) as readme:
synopsis = next(readme).rstrip()
config.print_stdout("%s - %s", tempname.name, synopsis)
config.print_stdout("%s - %s", tempname, synopsis)
config.print_stdout("\nTemplates are used via the 'init' command, e.g.:")
config.print_stdout("\n alembic init --template generic ./scripts")
@@ -49,7 +47,7 @@ def init(
:param config: a :class:`.Config` object.
:param directory: string path of the target directory.
:param directory: string path of the target directory
:param template: string name of the migration environment template to
use.
@@ -59,136 +57,65 @@ def init(
"""
directory_path = pathlib.Path(directory)
if directory_path.exists() and list(directory_path.iterdir()):
if os.access(directory, os.F_OK) and os.listdir(directory):
raise util.CommandError(
"Directory %s already exists and is not empty" % directory_path
"Directory %s already exists and is not empty" % directory
)
template_path = config._get_template_path() / template
template_dir = os.path.join(config.get_template_directory(), template)
if not os.access(template_dir, os.F_OK):
raise util.CommandError("No such template %r" % template)
if not template_path.exists():
raise util.CommandError(f"No such template {template_path}")
# left as os.access() to suit unit test mocking
if not os.access(directory_path, os.F_OK):
if not os.access(directory, os.F_OK):
with util.status(
f"Creating directory {directory_path.absolute()}",
f"Creating directory {os.path.abspath(directory)!r}",
**config.messaging_opts,
):
os.makedirs(directory_path)
os.makedirs(directory)
versions = directory_path / "versions"
versions = os.path.join(directory, "versions")
with util.status(
f"Creating directory {versions.absolute()}",
f"Creating directory {os.path.abspath(versions)!r}",
**config.messaging_opts,
):
os.makedirs(versions)
if not directory_path.is_absolute():
# for non-absolute path, state config file in .ini / pyproject
# as relative to the %(here)s token, which is where the config
# file itself would be
script = ScriptDirectory(directory)
if config._config_file_path is not None:
rel_dir = compat.path_relative_to(
directory_path.absolute(),
config._config_file_path.absolute().parent,
walk_up=True,
)
ini_script_location_directory = ("%(here)s" / rel_dir).as_posix()
if config._toml_file_path is not None:
rel_dir = compat.path_relative_to(
directory_path.absolute(),
config._toml_file_path.absolute().parent,
walk_up=True,
)
toml_script_location_directory = ("%(here)s" / rel_dir).as_posix()
else:
ini_script_location_directory = directory_path.as_posix()
toml_script_location_directory = directory_path.as_posix()
script = ScriptDirectory(directory_path)
has_toml = False
config_file: pathlib.Path | None = None
for file_path in template_path.iterdir():
file_ = file_path.name
config_file: str | None = None
for file_ in os.listdir(template_dir):
file_path = os.path.join(template_dir, file_)
if file_ == "alembic.ini.mako":
assert config.config_file_name is not None
config_file = pathlib.Path(config.config_file_name).absolute()
if config_file.exists():
config_file = os.path.abspath(config.config_file_name)
if os.access(config_file, os.F_OK):
util.msg(
f"File {config_file} already exists, skipping",
f"File {config_file!r} already exists, skipping",
**config.messaging_opts,
)
else:
script._generate_template(
file_path,
config_file,
script_location=ini_script_location_directory,
file_path, config_file, script_location=directory
)
elif file_ == "pyproject.toml.mako":
has_toml = True
assert config._toml_file_path is not None
toml_path = config._toml_file_path.absolute()
if toml_path.exists():
# left as open() to suit unit test mocking
with open(toml_path, "rb") as f:
toml_data = compat.tomllib.load(f)
if "tool" in toml_data and "alembic" in toml_data["tool"]:
util.msg(
f"File {toml_path} already exists "
"and already has a [tool.alembic] section, "
"skipping",
)
continue
script._append_template(
file_path,
toml_path,
script_location=toml_script_location_directory,
)
else:
script._generate_template(
file_path,
toml_path,
script_location=toml_script_location_directory,
)
elif file_path.is_file():
output_file = directory_path / file_
elif os.path.isfile(file_path):
output_file = os.path.join(directory, file_)
script._copy_file(file_path, output_file)
if package:
for path in [
directory_path.absolute() / "__init__.py",
versions.absolute() / "__init__.py",
os.path.join(os.path.abspath(directory), "__init__.py"),
os.path.join(os.path.abspath(versions), "__init__.py"),
]:
with util.status(f"Adding {path!s}", **config.messaging_opts):
# left as open() to suit unit test mocking
with util.status(f"Adding {path!r}", **config.messaging_opts):
with open(path, "w"):
pass
assert config_file is not None
if has_toml:
util.msg(
f"Please edit configuration settings in {toml_path} and "
"configuration/connection/logging "
f"settings in {config_file} before proceeding.",
**config.messaging_opts,
)
else:
util.msg(
"Please edit configuration/connection/logging "
f"settings in {config_file} before proceeding.",
**config.messaging_opts,
)
util.msg(
"Please edit configuration/connection/logging "
f"settings in {config_file!r} before proceeding.",
**config.messaging_opts,
)
def revision(
@@ -199,7 +126,7 @@ def revision(
head: str = "head",
splice: bool = False,
branch_label: Optional[_RevIdType] = None,
version_path: Union[str, os.PathLike[str], None] = None,
version_path: Optional[str] = None,
rev_id: Optional[str] = None,
depends_on: Optional[str] = None,
process_revision_directives: Optional[ProcessRevisionDirectiveFn] = None,
@@ -245,7 +172,7 @@ def revision(
will be applied to the structure generated by the revision process
where it can be altered programmatically. Note that unlike all
the other parameters, this option is only available via programmatic
use of :func:`.command.revision`.
use of :func:`.command.revision`
"""
@@ -269,9 +196,7 @@ def revision(
process_revision_directives=process_revision_directives,
)
environment = util.asbool(
config.get_alembic_option("revision_environment")
)
environment = util.asbool(config.get_main_option("revision_environment"))
if autogenerate:
environment = True
@@ -365,15 +290,10 @@ def check(config: "Config") -> None:
# the revision_context now has MigrationScript structure(s) present.
migration_script = revision_context.generated_revisions[-1]
diffs = []
for upgrade_ops in migration_script.upgrade_ops_list:
diffs.extend(upgrade_ops.as_diffs())
diffs = migration_script.upgrade_ops.as_diffs()
if diffs:
raise util.AutogenerateDiffsDetected(
f"New upgrade operations detected: {diffs}",
revision_context=revision_context,
diffs=diffs,
f"New upgrade operations detected: {diffs}"
)
else:
config.print_stdout("No new upgrade operations detected.")
@@ -390,11 +310,9 @@ def merge(
:param config: a :class:`.Config` instance
:param revisions: The revisions to merge.
:param message: string message to apply to the revision
:param message: string message to apply to the revision.
:param branch_label: string label name to apply to the new revision.
:param branch_label: string label name to apply to the new revision
:param rev_id: hardcoded revision identifier instead of generating a new
one.
@@ -411,9 +329,7 @@ def merge(
# e.g. multiple databases
}
environment = util.asbool(
config.get_alembic_option("revision_environment")
)
environment = util.asbool(config.get_main_option("revision_environment"))
if environment:
@@ -449,10 +365,9 @@ def upgrade(
:param config: a :class:`.Config` instance.
:param revision: string revision target or range for --sql mode. May be
``"heads"`` to target the most recent revision(s).
:param revision: string revision target or range for --sql mode
:param sql: if True, use ``--sql`` mode.
:param sql: if True, use ``--sql`` mode
:param tag: an arbitrary "tag" that can be intercepted by custom
``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument`
@@ -493,10 +408,9 @@ def downgrade(
:param config: a :class:`.Config` instance.
:param revision: string revision target or range for --sql mode. May
be ``"base"`` to target the first revision.
:param revision: string revision target or range for --sql mode
:param sql: if True, use ``--sql`` mode.
:param sql: if True, use ``--sql`` mode
:param tag: an arbitrary "tag" that can be intercepted by custom
``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument`
@@ -530,13 +444,12 @@ def downgrade(
script.run_env()
def show(config: Config, rev: str) -> None:
def show(config, rev):
"""Show the revision(s) denoted by the given symbol.
:param config: a :class:`.Config` instance.
:param rev: string revision target. May be ``"current"`` to show the
revision(s) currently applied in the database.
:param revision: string revision target
"""
@@ -566,7 +479,7 @@ def history(
:param config: a :class:`.Config` instance.
:param rev_range: string revision range.
:param rev_range: string revision range
:param verbose: output in verbose mode.
@@ -586,7 +499,7 @@ def history(
base = head = None
environment = (
util.asbool(config.get_alembic_option("revision_environment"))
util.asbool(config.get_main_option("revision_environment"))
or indicate_current
)
@@ -625,9 +538,7 @@ def history(
_display_history(config, script, base, head)
def heads(
config: Config, verbose: bool = False, resolve_dependencies: bool = False
) -> None:
def heads(config, verbose=False, resolve_dependencies=False):
"""Show current available heads in the script directory.
:param config: a :class:`.Config` instance.
@@ -652,7 +563,7 @@ def heads(
)
def branches(config: Config, verbose: bool = False) -> None:
def branches(config, verbose=False):
"""Show current branch points.
:param config: a :class:`.Config` instance.
@@ -722,9 +633,7 @@ def stamp(
:param config: a :class:`.Config` instance.
:param revision: target revision or list of revisions. May be a list
to indicate stamping of multiple branch heads; may be ``"base"``
to remove all revisions from the table or ``"heads"`` to stamp the
most recent revision(s).
to indicate stamping of multiple branch heads.
.. note:: this parameter is called "revisions" in the command line
interface.
@@ -814,7 +723,7 @@ def ensure_version(config: Config, sql: bool = False) -> None:
:param config: a :class:`.Config` instance.
:param sql: use ``--sql`` mode.
:param sql: use ``--sql`` mode
.. versionadded:: 1.7.6