This commit is contained in:
@@ -3,7 +3,6 @@ from __future__ import annotations
|
||||
from contextlib import contextmanager
|
||||
import datetime
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
@@ -12,6 +11,7 @@ from typing import Any
|
||||
from typing import cast
|
||||
from typing import Iterator
|
||||
from typing import List
|
||||
from typing import Mapping
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
from typing import Set
|
||||
@@ -23,9 +23,7 @@ from . import revision
|
||||
from . import write_hooks
|
||||
from .. import util
|
||||
from ..runtime import migration
|
||||
from ..util import compat
|
||||
from ..util import not_none
|
||||
from ..util.pyfiles import _preserving_path_as_str
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .revision import _GetRevArg
|
||||
@@ -33,28 +31,26 @@ if TYPE_CHECKING:
|
||||
from .revision import Revision
|
||||
from ..config import Config
|
||||
from ..config import MessagingOptions
|
||||
from ..config import PostWriteHookConfig
|
||||
from ..runtime.migration import RevisionStep
|
||||
from ..runtime.migration import StampStep
|
||||
|
||||
try:
|
||||
if compat.py39:
|
||||
from zoneinfo import ZoneInfo
|
||||
from zoneinfo import ZoneInfoNotFoundError
|
||||
else:
|
||||
from backports.zoneinfo import ZoneInfo # type: ignore[import-not-found,no-redef] # noqa: E501
|
||||
from backports.zoneinfo import ZoneInfoNotFoundError # type: ignore[no-redef] # noqa: E501
|
||||
from dateutil import tz
|
||||
except ImportError:
|
||||
ZoneInfo = None # type: ignore[assignment, misc]
|
||||
tz = None # type: ignore[assignment]
|
||||
|
||||
_sourceless_rev_file = re.compile(r"(?!\.\#|__init__)(.*\.py)(c|o)?$")
|
||||
_only_source_rev_file = re.compile(r"(?!\.\#|__init__)(.*\.py)$")
|
||||
_legacy_rev = re.compile(r"([a-f0-9]+)\.py$")
|
||||
_slug_re = re.compile(r"\w+")
|
||||
_default_file_template = "%(rev)s_%(slug)s"
|
||||
_split_on_space_comma = re.compile(r", *|(?: +)")
|
||||
|
||||
_split_on_space_comma_colon = re.compile(r", *|(?: +)|\:")
|
||||
|
||||
|
||||
class ScriptDirectory:
|
||||
|
||||
"""Provides operations upon an Alembic script directory.
|
||||
|
||||
This object is useful to get information as to current revisions,
|
||||
@@ -76,55 +72,40 @@ class ScriptDirectory:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
dir: Union[str, os.PathLike[str]], # noqa: A002
|
||||
dir: str, # noqa
|
||||
file_template: str = _default_file_template,
|
||||
truncate_slug_length: Optional[int] = 40,
|
||||
version_locations: Optional[
|
||||
Sequence[Union[str, os.PathLike[str]]]
|
||||
] = None,
|
||||
version_locations: Optional[List[str]] = None,
|
||||
sourceless: bool = False,
|
||||
output_encoding: str = "utf-8",
|
||||
timezone: Optional[str] = None,
|
||||
hooks: list[PostWriteHookConfig] = [],
|
||||
hook_config: Optional[Mapping[str, str]] = None,
|
||||
recursive_version_locations: bool = False,
|
||||
messaging_opts: MessagingOptions = cast(
|
||||
"MessagingOptions", util.EMPTY_DICT
|
||||
),
|
||||
) -> None:
|
||||
self.dir = _preserving_path_as_str(dir)
|
||||
self.version_locations = [
|
||||
_preserving_path_as_str(p) for p in version_locations or ()
|
||||
]
|
||||
self.dir = dir
|
||||
self.file_template = file_template
|
||||
self.version_locations = version_locations
|
||||
self.truncate_slug_length = truncate_slug_length or 40
|
||||
self.sourceless = sourceless
|
||||
self.output_encoding = output_encoding
|
||||
self.revision_map = revision.RevisionMap(self._load_revisions)
|
||||
self.timezone = timezone
|
||||
self.hooks = hooks
|
||||
self.hook_config = hook_config
|
||||
self.recursive_version_locations = recursive_version_locations
|
||||
self.messaging_opts = messaging_opts
|
||||
|
||||
if not os.access(dir, os.F_OK):
|
||||
raise util.CommandError(
|
||||
f"Path doesn't exist: {dir}. Please use "
|
||||
"Path doesn't exist: %r. Please use "
|
||||
"the 'init' command to create a new "
|
||||
"scripts folder."
|
||||
"scripts folder." % os.path.abspath(dir)
|
||||
)
|
||||
|
||||
@property
|
||||
def versions(self) -> str:
|
||||
"""return a single version location based on the sole path passed
|
||||
within version_locations.
|
||||
|
||||
If multiple version locations are configured, an error is raised.
|
||||
|
||||
|
||||
"""
|
||||
return str(self._singular_version_location)
|
||||
|
||||
@util.memoized_property
|
||||
def _singular_version_location(self) -> Path:
|
||||
loc = self._version_locations
|
||||
if len(loc) > 1:
|
||||
raise util.CommandError("Multiple version_locations present")
|
||||
@@ -132,31 +113,40 @@ class ScriptDirectory:
|
||||
return loc[0]
|
||||
|
||||
@util.memoized_property
|
||||
def _version_locations(self) -> Sequence[Path]:
|
||||
def _version_locations(self):
|
||||
if self.version_locations:
|
||||
return [
|
||||
util.coerce_resource_to_filename(location).absolute()
|
||||
os.path.abspath(util.coerce_resource_to_filename(location))
|
||||
for location in self.version_locations
|
||||
]
|
||||
else:
|
||||
return [Path(self.dir, "versions").absolute()]
|
||||
return (os.path.abspath(os.path.join(self.dir, "versions")),)
|
||||
|
||||
def _load_revisions(self) -> Iterator[Script]:
|
||||
paths = [vers for vers in self._version_locations if vers.exists()]
|
||||
if self.version_locations:
|
||||
paths = [
|
||||
vers
|
||||
for vers in self._version_locations
|
||||
if os.path.exists(vers)
|
||||
]
|
||||
else:
|
||||
paths = [self.versions]
|
||||
|
||||
dupes = set()
|
||||
for vers in paths:
|
||||
for file_path in Script._list_py_dir(self, vers):
|
||||
real_path = file_path.resolve()
|
||||
real_path = os.path.realpath(file_path)
|
||||
if real_path in dupes:
|
||||
util.warn(
|
||||
f"File {real_path} loaded twice! ignoring. "
|
||||
"Please ensure version_locations is unique."
|
||||
"File %s loaded twice! ignoring. Please ensure "
|
||||
"version_locations is unique." % real_path
|
||||
)
|
||||
continue
|
||||
dupes.add(real_path)
|
||||
|
||||
script = Script._from_path(self, real_path)
|
||||
filename = os.path.basename(real_path)
|
||||
dir_name = os.path.dirname(real_path)
|
||||
script = Script._from_filename(self, dir_name, filename)
|
||||
if script is None:
|
||||
continue
|
||||
yield script
|
||||
@@ -170,36 +160,74 @@ class ScriptDirectory:
|
||||
present.
|
||||
|
||||
"""
|
||||
script_location = config.get_alembic_option("script_location")
|
||||
script_location = config.get_main_option("script_location")
|
||||
if script_location is None:
|
||||
raise util.CommandError(
|
||||
"No 'script_location' key found in configuration."
|
||||
"No 'script_location' key " "found in configuration."
|
||||
)
|
||||
truncate_slug_length: Optional[int]
|
||||
tsl = config.get_alembic_option("truncate_slug_length")
|
||||
tsl = config.get_main_option("truncate_slug_length")
|
||||
if tsl is not None:
|
||||
truncate_slug_length = int(tsl)
|
||||
else:
|
||||
truncate_slug_length = None
|
||||
|
||||
prepend_sys_path = config.get_prepend_sys_paths_list()
|
||||
if prepend_sys_path:
|
||||
sys.path[:0] = prepend_sys_path
|
||||
version_locations_str = config.get_main_option("version_locations")
|
||||
version_locations: Optional[List[str]]
|
||||
if version_locations_str:
|
||||
version_path_separator = config.get_main_option(
|
||||
"version_path_separator"
|
||||
)
|
||||
|
||||
rvl = config.get_alembic_boolean_option("recursive_version_locations")
|
||||
split_on_path = {
|
||||
None: None,
|
||||
"space": " ",
|
||||
"os": os.pathsep,
|
||||
":": ":",
|
||||
";": ";",
|
||||
}
|
||||
|
||||
try:
|
||||
split_char: Optional[str] = split_on_path[
|
||||
version_path_separator
|
||||
]
|
||||
except KeyError as ke:
|
||||
raise ValueError(
|
||||
"'%s' is not a valid value for "
|
||||
"version_path_separator; "
|
||||
"expected 'space', 'os', ':', ';'" % version_path_separator
|
||||
) from ke
|
||||
else:
|
||||
if split_char is None:
|
||||
# legacy behaviour for backwards compatibility
|
||||
version_locations = _split_on_space_comma.split(
|
||||
version_locations_str
|
||||
)
|
||||
else:
|
||||
version_locations = [
|
||||
x for x in version_locations_str.split(split_char) if x
|
||||
]
|
||||
else:
|
||||
version_locations = None
|
||||
|
||||
prepend_sys_path = config.get_main_option("prepend_sys_path")
|
||||
if prepend_sys_path:
|
||||
sys.path[:0] = list(
|
||||
_split_on_space_comma_colon.split(prepend_sys_path)
|
||||
)
|
||||
|
||||
rvl = config.get_main_option("recursive_version_locations") == "true"
|
||||
return ScriptDirectory(
|
||||
util.coerce_resource_to_filename(script_location),
|
||||
file_template=config.get_alembic_option(
|
||||
file_template=config.get_main_option(
|
||||
"file_template", _default_file_template
|
||||
),
|
||||
truncate_slug_length=truncate_slug_length,
|
||||
sourceless=config.get_alembic_boolean_option("sourceless"),
|
||||
output_encoding=config.get_alembic_option(
|
||||
"output_encoding", "utf-8"
|
||||
),
|
||||
version_locations=config.get_version_locations_list(),
|
||||
timezone=config.get_alembic_option("timezone"),
|
||||
hooks=config.get_hooks_list(),
|
||||
sourceless=config.get_main_option("sourceless") == "true",
|
||||
output_encoding=config.get_main_option("output_encoding", "utf-8"),
|
||||
version_locations=version_locations,
|
||||
timezone=config.get_main_option("timezone"),
|
||||
hook_config=config.get_section("post_write_hooks", {}),
|
||||
recursive_version_locations=rvl,
|
||||
messaging_opts=config.messaging_opts,
|
||||
)
|
||||
@@ -269,22 +297,24 @@ class ScriptDirectory:
|
||||
):
|
||||
yield cast(Script, rev)
|
||||
|
||||
def get_revisions(self, id_: _GetRevArg) -> Tuple[Script, ...]:
|
||||
def get_revisions(self, id_: _GetRevArg) -> Tuple[Optional[Script], ...]:
|
||||
"""Return the :class:`.Script` instance with the given rev identifier,
|
||||
symbolic name, or sequence of identifiers.
|
||||
|
||||
"""
|
||||
with self._catch_revision_errors():
|
||||
return cast(
|
||||
Tuple[Script, ...],
|
||||
Tuple[Optional[Script], ...],
|
||||
self.revision_map.get_revisions(id_),
|
||||
)
|
||||
|
||||
def get_all_current(self, id_: Tuple[str, ...]) -> Set[Script]:
|
||||
def get_all_current(self, id_: Tuple[str, ...]) -> Set[Optional[Script]]:
|
||||
with self._catch_revision_errors():
|
||||
return cast(Set[Script], self.revision_map._get_all_current(id_))
|
||||
return cast(
|
||||
Set[Optional[Script]], self.revision_map._get_all_current(id_)
|
||||
)
|
||||
|
||||
def get_revision(self, id_: str) -> Script:
|
||||
def get_revision(self, id_: str) -> Optional[Script]:
|
||||
"""Return the :class:`.Script` instance with the given rev id.
|
||||
|
||||
.. seealso::
|
||||
@@ -294,7 +324,7 @@ class ScriptDirectory:
|
||||
"""
|
||||
|
||||
with self._catch_revision_errors():
|
||||
return cast(Script, self.revision_map.get_revision(id_))
|
||||
return cast(Optional[Script], self.revision_map.get_revision(id_))
|
||||
|
||||
def as_revision_number(
|
||||
self, id_: Optional[str]
|
||||
@@ -549,37 +579,24 @@ class ScriptDirectory:
|
||||
util.load_python_file(self.dir, "env.py")
|
||||
|
||||
@property
|
||||
def env_py_location(self) -> str:
|
||||
return str(Path(self.dir, "env.py"))
|
||||
def env_py_location(self):
|
||||
return os.path.abspath(os.path.join(self.dir, "env.py"))
|
||||
|
||||
def _append_template(self, src: Path, dest: Path, **kw: Any) -> None:
|
||||
def _generate_template(self, src: str, dest: str, **kw: Any) -> None:
|
||||
with util.status(
|
||||
f"Appending to existing {dest.absolute()}",
|
||||
**self.messaging_opts,
|
||||
):
|
||||
util.template_to_file(
|
||||
src,
|
||||
dest,
|
||||
self.output_encoding,
|
||||
append_with_newlines=True,
|
||||
**kw,
|
||||
)
|
||||
|
||||
def _generate_template(self, src: Path, dest: Path, **kw: Any) -> None:
|
||||
with util.status(
|
||||
f"Generating {dest.absolute()}", **self.messaging_opts
|
||||
f"Generating {os.path.abspath(dest)}", **self.messaging_opts
|
||||
):
|
||||
util.template_to_file(src, dest, self.output_encoding, **kw)
|
||||
|
||||
def _copy_file(self, src: Path, dest: Path) -> None:
|
||||
def _copy_file(self, src: str, dest: str) -> None:
|
||||
with util.status(
|
||||
f"Generating {dest.absolute()}", **self.messaging_opts
|
||||
f"Generating {os.path.abspath(dest)}", **self.messaging_opts
|
||||
):
|
||||
shutil.copy(src, dest)
|
||||
|
||||
def _ensure_directory(self, path: Path) -> None:
|
||||
path = path.absolute()
|
||||
if not path.exists():
|
||||
def _ensure_directory(self, path: str) -> None:
|
||||
path = os.path.abspath(path)
|
||||
if not os.path.exists(path):
|
||||
with util.status(
|
||||
f"Creating directory {path}", **self.messaging_opts
|
||||
):
|
||||
@@ -587,27 +604,25 @@ class ScriptDirectory:
|
||||
|
||||
def _generate_create_date(self) -> datetime.datetime:
|
||||
if self.timezone is not None:
|
||||
if ZoneInfo is None:
|
||||
if tz is None:
|
||||
raise util.CommandError(
|
||||
"Python >= 3.9 is required for timezone support or "
|
||||
"the 'backports.zoneinfo' package must be installed."
|
||||
"The library 'python-dateutil' is required "
|
||||
"for timezone support"
|
||||
)
|
||||
# First, assume correct capitalization
|
||||
try:
|
||||
tzinfo = ZoneInfo(self.timezone)
|
||||
except ZoneInfoNotFoundError:
|
||||
tzinfo = None
|
||||
tzinfo = tz.gettz(self.timezone)
|
||||
if tzinfo is None:
|
||||
try:
|
||||
tzinfo = ZoneInfo(self.timezone.upper())
|
||||
except ZoneInfoNotFoundError:
|
||||
raise util.CommandError(
|
||||
"Can't locate timezone: %s" % self.timezone
|
||||
) from None
|
||||
|
||||
create_date = datetime.datetime.now(
|
||||
tz=datetime.timezone.utc
|
||||
).astimezone(tzinfo)
|
||||
# Fall back to uppercase
|
||||
tzinfo = tz.gettz(self.timezone.upper())
|
||||
if tzinfo is None:
|
||||
raise util.CommandError(
|
||||
"Can't locate timezone: %s" % self.timezone
|
||||
)
|
||||
create_date = (
|
||||
datetime.datetime.utcnow()
|
||||
.replace(tzinfo=tz.tzutc())
|
||||
.astimezone(tzinfo)
|
||||
)
|
||||
else:
|
||||
create_date = datetime.datetime.now()
|
||||
return create_date
|
||||
@@ -619,8 +634,7 @@ class ScriptDirectory:
|
||||
head: Optional[_RevIdType] = None,
|
||||
splice: Optional[bool] = False,
|
||||
branch_labels: Optional[_RevIdType] = None,
|
||||
version_path: Union[str, os.PathLike[str], None] = None,
|
||||
file_template: Optional[str] = None,
|
||||
version_path: Optional[str] = None,
|
||||
depends_on: Optional[_RevIdType] = None,
|
||||
**kw: Any,
|
||||
) -> Optional[Script]:
|
||||
@@ -661,7 +675,7 @@ class ScriptDirectory:
|
||||
self.revision_map.get_revisions(head),
|
||||
)
|
||||
for h in heads:
|
||||
assert h != "base" # type: ignore[comparison-overlap]
|
||||
assert h != "base"
|
||||
|
||||
if len(set(heads)) != len(heads):
|
||||
raise util.CommandError("Duplicate head revisions specified")
|
||||
@@ -673,7 +687,7 @@ class ScriptDirectory:
|
||||
for head_ in heads:
|
||||
if head_ is not None:
|
||||
assert isinstance(head_, Script)
|
||||
version_path = head_._script_path.parent
|
||||
version_path = os.path.dirname(head_.path)
|
||||
break
|
||||
else:
|
||||
raise util.CommandError(
|
||||
@@ -681,19 +695,16 @@ class ScriptDirectory:
|
||||
"please specify --version-path"
|
||||
)
|
||||
else:
|
||||
version_path = self._singular_version_location
|
||||
else:
|
||||
version_path = Path(version_path)
|
||||
version_path = self.versions
|
||||
|
||||
assert isinstance(version_path, Path)
|
||||
norm_path = version_path.absolute()
|
||||
norm_path = os.path.normpath(os.path.abspath(version_path))
|
||||
for vers_path in self._version_locations:
|
||||
if vers_path.absolute() == norm_path:
|
||||
if os.path.normpath(vers_path) == norm_path:
|
||||
break
|
||||
else:
|
||||
raise util.CommandError(
|
||||
f"Path {version_path} is not represented in current "
|
||||
"version locations"
|
||||
"Path %s is not represented in current "
|
||||
"version locations" % version_path
|
||||
)
|
||||
|
||||
if self.version_locations:
|
||||
@@ -714,11 +725,9 @@ class ScriptDirectory:
|
||||
if depends_on:
|
||||
with self._catch_revision_errors():
|
||||
resolved_depends_on = [
|
||||
(
|
||||
dep
|
||||
if dep in rev.branch_labels # maintain branch labels
|
||||
else rev.revision
|
||||
) # resolve partial revision identifiers
|
||||
dep
|
||||
if dep in rev.branch_labels # maintain branch labels
|
||||
else rev.revision # resolve partial revision identifiers
|
||||
for rev, dep in [
|
||||
(not_none(self.revision_map.get_revision(dep)), dep)
|
||||
for dep in util.to_list(depends_on)
|
||||
@@ -728,7 +737,7 @@ class ScriptDirectory:
|
||||
resolved_depends_on = None
|
||||
|
||||
self._generate_template(
|
||||
Path(self.dir, "script.py.mako"),
|
||||
os.path.join(self.dir, "script.py.mako"),
|
||||
path,
|
||||
up_revision=str(revid),
|
||||
down_revision=revision.tuple_rev_as_scalar(
|
||||
@@ -742,7 +751,7 @@ class ScriptDirectory:
|
||||
**kw,
|
||||
)
|
||||
|
||||
post_write_hooks = self.hooks
|
||||
post_write_hooks = self.hook_config
|
||||
if post_write_hooks:
|
||||
write_hooks._run_hooks(path, post_write_hooks)
|
||||
|
||||
@@ -765,11 +774,11 @@ class ScriptDirectory:
|
||||
|
||||
def _rev_path(
|
||||
self,
|
||||
path: Union[str, os.PathLike[str]],
|
||||
path: str,
|
||||
rev_id: str,
|
||||
message: Optional[str],
|
||||
create_date: datetime.datetime,
|
||||
) -> Path:
|
||||
) -> str:
|
||||
epoch = int(create_date.timestamp())
|
||||
slug = "_".join(_slug_re.findall(message or "")).lower()
|
||||
if len(slug) > self.truncate_slug_length:
|
||||
@@ -788,10 +797,11 @@ class ScriptDirectory:
|
||||
"second": create_date.second,
|
||||
}
|
||||
)
|
||||
return Path(path) / filename
|
||||
return os.path.join(path, filename)
|
||||
|
||||
|
||||
class Script(revision.Revision):
|
||||
|
||||
"""Represent a single revision file in a ``versions/`` directory.
|
||||
|
||||
The :class:`.Script` instance is returned by methods
|
||||
@@ -799,17 +809,12 @@ class Script(revision.Revision):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
module: ModuleType,
|
||||
rev_id: str,
|
||||
path: Union[str, os.PathLike[str]],
|
||||
):
|
||||
def __init__(self, module: ModuleType, rev_id: str, path: str):
|
||||
self.module = module
|
||||
self.path = _preserving_path_as_str(path)
|
||||
self.path = path
|
||||
super().__init__(
|
||||
rev_id,
|
||||
module.down_revision,
|
||||
module.down_revision, # type: ignore[attr-defined]
|
||||
branch_labels=util.to_tuple(
|
||||
getattr(module, "branch_labels", None), default=()
|
||||
),
|
||||
@@ -824,10 +829,6 @@ class Script(revision.Revision):
|
||||
path: str
|
||||
"""Filesystem path of the script."""
|
||||
|
||||
@property
|
||||
def _script_path(self) -> Path:
|
||||
return Path(self.path)
|
||||
|
||||
_db_current_indicator: Optional[bool] = None
|
||||
"""Utility variable which when set will cause string output to indicate
|
||||
this is a "current" version in some database"""
|
||||
@@ -846,9 +847,9 @@ class Script(revision.Revision):
|
||||
if doc:
|
||||
if hasattr(self.module, "_alembic_source_encoding"):
|
||||
doc = doc.decode( # type: ignore[attr-defined]
|
||||
self.module._alembic_source_encoding
|
||||
self.module._alembic_source_encoding # type: ignore[attr-defined] # noqa
|
||||
)
|
||||
return doc.strip()
|
||||
return doc.strip() # type: ignore[union-attr]
|
||||
else:
|
||||
return ""
|
||||
|
||||
@@ -888,7 +889,7 @@ class Script(revision.Revision):
|
||||
)
|
||||
return entry
|
||||
|
||||
def __str__(self) -> str:
|
||||
def __str__(self):
|
||||
return "%s -> %s%s%s%s, %s" % (
|
||||
self._format_down_revision(),
|
||||
self.revision,
|
||||
@@ -922,11 +923,9 @@ class Script(revision.Revision):
|
||||
if head_indicators or tree_indicators:
|
||||
text += "%s%s%s" % (
|
||||
" (head)" if self._is_real_head else "",
|
||||
(
|
||||
" (effective head)"
|
||||
if self.is_head and not self._is_real_head
|
||||
else ""
|
||||
),
|
||||
" (effective head)"
|
||||
if self.is_head and not self._is_real_head
|
||||
else "",
|
||||
" (current)" if self._db_current_indicator else "",
|
||||
)
|
||||
if tree_indicators:
|
||||
@@ -960,33 +959,36 @@ class Script(revision.Revision):
|
||||
return util.format_as_comma(self._versioned_down_revisions)
|
||||
|
||||
@classmethod
|
||||
def _list_py_dir(
|
||||
cls, scriptdir: ScriptDirectory, path: Path
|
||||
) -> List[Path]:
|
||||
def _from_path(
|
||||
cls, scriptdir: ScriptDirectory, path: str
|
||||
) -> Optional[Script]:
|
||||
dir_, filename = os.path.split(path)
|
||||
return cls._from_filename(scriptdir, dir_, filename)
|
||||
|
||||
@classmethod
|
||||
def _list_py_dir(cls, scriptdir: ScriptDirectory, path: str) -> List[str]:
|
||||
paths = []
|
||||
for root, dirs, files in compat.path_walk(path, top_down=True):
|
||||
if root.name.endswith("__pycache__"):
|
||||
for root, dirs, files in os.walk(path, topdown=True):
|
||||
if root.endswith("__pycache__"):
|
||||
# a special case - we may include these files
|
||||
# if a `sourceless` option is specified
|
||||
continue
|
||||
|
||||
for filename in sorted(files):
|
||||
paths.append(root / filename)
|
||||
paths.append(os.path.join(root, filename))
|
||||
|
||||
if scriptdir.sourceless:
|
||||
# look for __pycache__
|
||||
py_cache_path = root / "__pycache__"
|
||||
if py_cache_path.exists():
|
||||
py_cache_path = os.path.join(root, "__pycache__")
|
||||
if os.path.exists(py_cache_path):
|
||||
# add all files from __pycache__ whose filename is not
|
||||
# already in the names we got from the version directory.
|
||||
# add as relative paths including __pycache__ token
|
||||
names = {
|
||||
Path(filename).name.split(".")[0] for filename in files
|
||||
}
|
||||
names = {filename.split(".")[0] for filename in files}
|
||||
paths.extend(
|
||||
py_cache_path / pyc
|
||||
for pyc in py_cache_path.iterdir()
|
||||
if pyc.name.split(".")[0] not in names
|
||||
os.path.join(py_cache_path, pyc)
|
||||
for pyc in os.listdir(py_cache_path)
|
||||
if pyc.split(".")[0] not in names
|
||||
)
|
||||
|
||||
if not scriptdir.recursive_version_locations:
|
||||
@@ -1001,13 +1003,9 @@ class Script(revision.Revision):
|
||||
return paths
|
||||
|
||||
@classmethod
|
||||
def _from_path(
|
||||
cls, scriptdir: ScriptDirectory, path: Union[str, os.PathLike[str]]
|
||||
def _from_filename(
|
||||
cls, scriptdir: ScriptDirectory, dir_: str, filename: str
|
||||
) -> Optional[Script]:
|
||||
|
||||
path = Path(path)
|
||||
dir_, filename = path.parent, path.name
|
||||
|
||||
if scriptdir.sourceless:
|
||||
py_match = _sourceless_rev_file.match(filename)
|
||||
else:
|
||||
@@ -1025,8 +1023,8 @@ class Script(revision.Revision):
|
||||
is_c = is_o = False
|
||||
|
||||
if is_o or is_c:
|
||||
py_exists = (dir_ / py_filename).exists()
|
||||
pyc_exists = (dir_ / (py_filename + "c")).exists()
|
||||
py_exists = os.path.exists(os.path.join(dir_, py_filename))
|
||||
pyc_exists = os.path.exists(os.path.join(dir_, py_filename + "c"))
|
||||
|
||||
# prefer .py over .pyc because we'd like to get the
|
||||
# source encoding; prefer .pyc over .pyo because we'd like to
|
||||
@@ -1042,14 +1040,14 @@ class Script(revision.Revision):
|
||||
m = _legacy_rev.match(filename)
|
||||
if not m:
|
||||
raise util.CommandError(
|
||||
"Could not determine revision id from "
|
||||
f"filename {filename}. "
|
||||
"Could not determine revision id from filename %s. "
|
||||
"Be sure the 'revision' variable is "
|
||||
"declared inside the script (please see 'Upgrading "
|
||||
"from Alembic 0.1 to 0.2' in the documentation)."
|
||||
% filename
|
||||
)
|
||||
else:
|
||||
revision = m.group(1)
|
||||
else:
|
||||
revision = module.revision
|
||||
return Script(module, revision, dir_ / filename)
|
||||
return Script(module, revision, os.path.join(dir_, filename))
|
||||
|
||||
Reference in New Issue
Block a user