diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-11 18:43:59 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2015-06-11 18:58:47 -0400 |
commit | 0c71f985e4885e4cd5c04eee46d73d7870bad6df (patch) | |
tree | 017d87272f4eec458f5fd3fcc9d8b191f741dab0 | |
parent | 541cbd26b5b86ce445f2065b60d28fdcbbb299a9 (diff) | |
download | alembic-0c71f985e4885e4cd5c04eee46d73d7870bad6df.tar.gz |
- refactor most files into packages. provide a degree of
backwards compat for major import targets. command and config
are too front-facing for a move like this so leave them
as is. first part of #302
41 files changed, 305 insertions, 269 deletions
@@ -10,3 +10,4 @@ alembic.ini .coverage coverage.xml .tox +*.patch diff --git a/alembic/__init__.py b/alembic/__init__.py index f429441..345bf26 100644 --- a/alembic/__init__.py +++ b/alembic/__init__.py @@ -1,9 +1,15 @@ from os import path -__version__ = '0.7.7' +__version__ = '0.8.0' package_dir = path.abspath(path.dirname(__file__)) from . import op # noqa from . import context # noqa + +import sys +from .runtime import environment +from .runtime import migration +sys.modules['alembic.migration'] = migration +sys.modules['alembic.environment'] = environment diff --git a/alembic/autogenerate/api.py b/alembic/autogenerate/api.py index 6281a6c..e912a5d 100644 --- a/alembic/autogenerate/api.py +++ b/alembic/autogenerate/api.py @@ -5,7 +5,7 @@ import logging import itertools import re -from ..compat import StringIO +from ..util.compat import StringIO from mako.pygen import PythonPrinter from sqlalchemy.engine.reflection import Inspector diff --git a/alembic/autogenerate/compare.py b/alembic/autogenerate/compare.py index 2aae962..9c04f80 100644 --- a/alembic/autogenerate/compare.py +++ b/alembic/autogenerate/compare.py @@ -1,7 +1,7 @@ from sqlalchemy import schema as sa_schema, types as sqltypes from sqlalchemy import event import logging -from .. import compat +from ..util import compat from sqlalchemy.util import OrderedSet import re from .render import _user_defined_render diff --git a/alembic/autogenerate/render.py b/alembic/autogenerate/render.py index 5007652..82f348b 100644 --- a/alembic/autogenerate/render.py +++ b/alembic/autogenerate/render.py @@ -1,9 +1,9 @@ from sqlalchemy import schema as sa_schema, types as sqltypes, sql import logging -from .. import compat +from ..util import compat from ..ddl.base import _table_for_constraint, _fk_spec import re -from ..compat import string_types +from ..util.compat import string_types log = logging.getLogger(__name__) diff --git a/alembic/command.py b/alembic/command.py index 5ba6d6a..fc80e38 100644 --- a/alembic/command.py +++ b/alembic/command.py @@ -1,8 +1,9 @@ import os from .script import ScriptDirectory -from .environment import EnvironmentContext -from . import util, autogenerate as autogen +from .runtime.environment import EnvironmentContext +from . import util +from . import autogenerate as autogen def list_templates(config): diff --git a/alembic/config.py b/alembic/config.py index 7f813d2..74b4d17 100644 --- a/alembic/config.py +++ b/alembic/config.py @@ -1,10 +1,13 @@ from argparse import ArgumentParser -from .compat import SafeConfigParser +from .util.compat import SafeConfigParser import inspect import os import sys -from . import command, util, package_dir, compat +from . import command +from . import util +from . import package_dir +from .util import compat class Config(object): diff --git a/alembic/context.py b/alembic/context.py index 9c0f676..1b1a3ab 100644 --- a/alembic/context.py +++ b/alembic/context.py @@ -1,4 +1,4 @@ -from .environment import EnvironmentContext +from .runtime.environment import EnvironmentContext from . import util # create proxy functions for diff --git a/alembic/ddl/impl.py b/alembic/ddl/impl.py index 3cca1ef..8c4f1d6 100644 --- a/alembic/ddl/impl.py +++ b/alembic/ddl/impl.py @@ -3,7 +3,7 @@ from sqlalchemy.ext.compiler import compiles from sqlalchemy import schema, text, sql from sqlalchemy import types as sqltypes -from ..compat import string_types, text_type, with_metaclass +from ..util.compat import string_types, text_type, with_metaclass from .. import util from . import base diff --git a/alembic/ddl/mysql.py b/alembic/ddl/mysql.py index 7956185..fe42145 100644 --- a/alembic/ddl/mysql.py +++ b/alembic/ddl/mysql.py @@ -2,7 +2,7 @@ from sqlalchemy.ext.compiler import compiles from sqlalchemy import types as sqltypes from sqlalchemy import schema -from ..compat import string_types +from ..util.compat import string_types from .. import util from .impl import DefaultImpl from .base import ColumnNullable, ColumnName, ColumnDefault, \ diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py index 9f97b34..ea423d7 100644 --- a/alembic/ddl/postgresql.py +++ b/alembic/ddl/postgresql.py @@ -1,6 +1,6 @@ import re -from .. import compat +from ..util import compat from .. import util from .base import compiles, alter_table, format_table_name, RenameTable from .impl import DefaultImpl diff --git a/alembic/op.py b/alembic/op.py index 8e5f777..615fd58 100644 --- a/alembic/op.py +++ b/alembic/op.py @@ -1,4 +1,4 @@ -from .operations import Operations +from .operations.base import Operations from . import util # create proxy functions for diff --git a/alembic/operations/__init__.py b/alembic/operations/__init__.py new file mode 100644 index 0000000..d7902ea --- /dev/null +++ b/alembic/operations/__init__.py @@ -0,0 +1,3 @@ +from .base import Operations, BatchOperations # noqa + +__all__ = ['Operations', 'BatchOperations']
\ No newline at end of file diff --git a/alembic/operations.py b/alembic/operations/base.py index 2bf8060..cda5aa2 100644 --- a/alembic/operations.py +++ b/alembic/operations/base.py @@ -3,9 +3,10 @@ from contextlib import contextmanager from sqlalchemy.types import NULLTYPE, Integer from sqlalchemy import schema as sa_schema -from . import util, batch -from .compat import string_types -from .ddl import impl +from .. import util +from . import batch +from ..util.compat import string_types +from ..ddl import impl __all__ = ('Operations', 'BatchOperations') @@ -58,7 +59,7 @@ class Operations(object): @classmethod @contextmanager def context(cls, migration_context): - from .op import _install_proxy, _remove_proxy + from ..op import _install_proxy, _remove_proxy op = Operations(migration_context) _install_proxy(op) yield op diff --git a/alembic/batch.py b/alembic/operations/batch.py index 1006739..9644a33 100644 --- a/alembic/batch.py +++ b/alembic/operations/batch.py @@ -3,8 +3,8 @@ from sqlalchemy import Table, MetaData, Index, select, Column, \ from sqlalchemy import types as sqltypes from sqlalchemy import schema as sql_schema from sqlalchemy.util import OrderedDict -from . import util -from .ddl.base import _columns_for_constraint, _is_type_bound +from .. import util +from ..ddl.base import _columns_for_constraint, _is_type_bound class BatchOperationsImpl(object): diff --git a/alembic/runtime/__init__.py b/alembic/runtime/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/alembic/runtime/__init__.py diff --git a/alembic/environment.py b/alembic/runtime/environment.py index 860315b..287d70a 100644 --- a/alembic/environment.py +++ b/alembic/runtime/environment.py @@ -1,6 +1,6 @@ -from .operations import Operations +from ..operations import Operations from .migration import MigrationContext -from . import util +from .. import util class EnvironmentContext(object): @@ -96,12 +96,12 @@ class EnvironmentContext(object): be made available as ``from alembic import context``. """ - from .context import _install_proxy + from ..context import _install_proxy _install_proxy(self) return self def __exit__(self, *arg, **kw): - from . import context, op + from .. import context, op context._remove_proxy() op._remove_proxy() diff --git a/alembic/migration.py b/alembic/runtime/migration.py index 9b46052..84a3c7f 100644 --- a/alembic/migration.py +++ b/alembic/runtime/migration.py @@ -6,8 +6,8 @@ from sqlalchemy import MetaData, Table, Column, String, literal_column from sqlalchemy.engine.strategies import MockEngineStrategy from sqlalchemy.engine import url as sqla_url -from .compat import callable, EncodedIO -from . import ddl, util +from ..util.compat import callable, EncodedIO +from .. import ddl, util log = logging.getLogger(__name__) diff --git a/alembic/script/__init__.py b/alembic/script/__init__.py new file mode 100644 index 0000000..cae294f --- /dev/null +++ b/alembic/script/__init__.py @@ -0,0 +1,3 @@ +from .base import ScriptDirectory, Script # noqa + +__all__ = ['ScriptDirectory', 'Script'] diff --git a/alembic/script.py b/alembic/script/base.py index 095a04b..e30c8b2 100644 --- a/alembic/script.py +++ b/alembic/script/base.py @@ -2,10 +2,10 @@ import datetime import os import re import shutil -from . import util -from . import compat +from .. import util +from ..util import compat from . import revision -from . import migration +from ..runtime import migration from contextlib import contextmanager diff --git a/alembic/revision.py b/alembic/script/revision.py index 4eea514..e9958b1 100644 --- a/alembic/revision.py +++ b/alembic/script/revision.py @@ -1,10 +1,9 @@ import re import collections -import itertools -from . import util +from .. import util from sqlalchemy import util as sqlautil -from . import compat +from ..util import compat _relative_destination = re.compile(r'(?:(.+?)@)?(\w+)?((?:\+|-)\d+)') diff --git a/alembic/testing/assertions.py b/alembic/testing/assertions.py index b3a5acd..6acca21 100644 --- a/alembic/testing/assertions.py +++ b/alembic/testing/assertions.py @@ -2,9 +2,9 @@ from __future__ import absolute_import import re -from alembic import util +from .. import util from sqlalchemy.engine import default -from alembic.compat import text_type, py3k +from ..util.compat import text_type, py3k import contextlib from sqlalchemy.util import decorator from sqlalchemy import exc as sa_exc diff --git a/alembic/testing/env.py b/alembic/testing/env.py index 9c53d5d..f8ad447 100644 --- a/alembic/testing/env.py +++ b/alembic/testing/env.py @@ -4,9 +4,9 @@ import os import shutil import textwrap -from alembic.compat import u -from alembic.script import Script, ScriptDirectory -from alembic import util +from ..util.compat import u +from ..script import Script, ScriptDirectory +from .. import util from . import engines from . import provision diff --git a/alembic/testing/exclusions.py b/alembic/testing/exclusions.py index 88df9fc..90f8bc6 100644 --- a/alembic/testing/exclusions.py +++ b/alembic/testing/exclusions.py @@ -14,11 +14,12 @@ from .plugin.plugin_base import SkipTest from sqlalchemy.util import decorator from . import config from sqlalchemy import util -from alembic import compat +from ..util import compat import inspect import contextlib from .compat import get_url_driver_name, get_url_backend_name + def skip_if(predicate, reason=None): rule = compound() pred = _as_predicate(predicate, reason) diff --git a/alembic/testing/fixtures.py b/alembic/testing/fixtures.py index ae25fd2..7e05525 100644 --- a/alembic/testing/fixtures.py +++ b/alembic/testing/fixtures.py @@ -5,13 +5,12 @@ import re from sqlalchemy import create_engine, text, MetaData import alembic -from alembic.compat import configparser -from alembic import util -from alembic.compat import string_types, text_type -from alembic.migration import MigrationContext -from alembic.environment import EnvironmentContext -from alembic.operations import Operations -from alembic.ddl.impl import _impls +from ..util.compat import configparser +from .. import util +from ..util.compat import string_types, text_type +from ..migration import MigrationContext +from ..environment import EnvironmentContext +from ..operations import Operations from contextlib import contextmanager from .plugin.plugin_base import SkipTest from .assertions import _get_dialect, eq_ diff --git a/alembic/testing/mock.py b/alembic/testing/mock.py index cdfcb88..b82a404 100644 --- a/alembic/testing/mock.py +++ b/alembic/testing/mock.py @@ -12,7 +12,7 @@ """ from __future__ import absolute_import -from alembic.compat import py33 +from ..util.compat import py33 if py33: from unittest.mock import MagicMock, Mock, call, patch diff --git a/alembic/testing/provision.py b/alembic/testing/provision.py index 801d36b..37ae141 100644 --- a/alembic/testing/provision.py +++ b/alembic/testing/provision.py @@ -3,9 +3,9 @@ """ from sqlalchemy.engine import url as sa_url from sqlalchemy import text -from alembic import compat -from alembic.testing import config, engines -from alembic.testing.compat import get_url_backend_name +from ..util import compat +from . import config, engines +from .compat import get_url_backend_name FOLLOWER_IDENT = None diff --git a/alembic/util/__init__.py b/alembic/util/__init__.py new file mode 100644 index 0000000..6cfc5d3 --- /dev/null +++ b/alembic/util/__init__.py @@ -0,0 +1,20 @@ +from .langhelpers import ( # noqa + create_module_class_proxy, asbool, rev_id, to_tuple, memoized_property, + immutabledict, _with_legacy_names) +from .messaging import ( # noqa + write_outstream, status, err, obfuscate_url_pw, warn, msg, format_as_comma) +from .pyfiles import ( # noqa + template_to_file, coerce_resource_to_filename, simple_pyc_file_from_path, + pyc_file_from_path, load_python_file) +from .sqla_compat import ( # noqa + sqla_07, sqla_079, sqla_08, sqla_083, sqla_084, sqla_09, sqla_092, + sqla_094, sqla_094, sqla_099, sqla_100, sqla_105) + + +class CommandError(Exception): + pass + + +if not sqla_07: + raise CommandError( + "SQLAlchemy 0.7.3 or greater is required. ") diff --git a/alembic/compat.py b/alembic/util/compat.py index a9e35f0..a9e35f0 100644 --- a/alembic/compat.py +++ b/alembic/util/compat.py diff --git a/alembic/util.py b/alembic/util/langhelpers.py index 2e0f731..04cb6e4 100644 --- a/alembic/util.py +++ b/alembic/util/langhelpers.py @@ -1,78 +1,14 @@ -import sys -import os import textwrap import warnings -import re import inspect import uuid import collections -from mako.template import Template -from sqlalchemy.engine import url -from sqlalchemy import __version__ - -from .compat import callable, exec_, load_module_py, load_module_pyc, \ - binary_type, string_types, py27 - - -class CommandError(Exception): - pass - - -def _safe_int(value): - try: - return int(value) - except: - return value -_vers = tuple( - [_safe_int(x) for x in re.findall(r'(\d+|[abc]\d)', __version__)]) -sqla_07 = _vers > (0, 7, 2) -sqla_079 = _vers >= (0, 7, 9) -sqla_08 = _vers >= (0, 8, 0) -sqla_083 = _vers >= (0, 8, 3) -sqla_084 = _vers >= (0, 8, 4) -sqla_09 = _vers >= (0, 9, 0) -sqla_092 = _vers >= (0, 9, 2) -sqla_094 = _vers >= (0, 9, 4) -sqla_094 = _vers >= (0, 9, 4) -sqla_099 = _vers >= (0, 9, 9) -sqla_100 = _vers >= (1, 0, 0) -sqla_105 = _vers >= (1, 0, 5) -if not sqla_07: - raise CommandError( - "SQLAlchemy 0.7.3 or greater is required. ") +from .compat import callable, exec_, string_types from sqlalchemy.util import format_argspec_plus, update_wrapper from sqlalchemy.util.compat import inspect_getfullargspec -import logging -log = logging.getLogger(__name__) - -if py27: - # disable "no handler found" errors - logging.getLogger('alembic').addHandler(logging.NullHandler()) - - -try: - import fcntl - import termios - import struct - ioctl = fcntl.ioctl(0, termios.TIOCGWINSZ, - struct.pack('HHHH', 0, 0, 0, 0)) - _h, TERMWIDTH, _hp, _wp = struct.unpack('HHHH', ioctl) - if TERMWIDTH <= 0: # can occur if running in emacs pseudo-tty - TERMWIDTH = None -except (ImportError, IOError): - TERMWIDTH = None - - -def template_to_file(template_file, dest, output_encoding, **kw): - with open(dest, 'wb') as f: - template = Template(filename=template_file) - f.write( - template.render_unicode(**kw).encode(output_encoding) - ) - def create_module_class_proxy(cls, globals_, locals_): """Create module level proxy functions for the @@ -157,135 +93,11 @@ def create_module_class_proxy(cls, globals_, locals_): attr_names.add(methname) -def write_outstream(stream, *text): - encoding = getattr(stream, 'encoding', 'ascii') or 'ascii' - for t in text: - if not isinstance(t, binary_type): - t = t.encode(encoding, 'replace') - t = t.decode(encoding) - try: - stream.write(t) - except IOError: - # suppress "broken pipe" errors. - # no known way to handle this on Python 3 however - # as the exception is "ignored" (noisily) in TextIOWrapper. - break - - -def coerce_resource_to_filename(fname): - """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. - - """ - if not os.path.isabs(fname) and ":" in fname: - import pkg_resources - fname = pkg_resources.resource_filename(*fname.split(':')) - return fname - - -def status(_statmsg, fn, *arg, **kw): - msg(_statmsg + " ...", False) - try: - ret = fn(*arg, **kw) - write_outstream(sys.stdout, " done\n") - return ret - except: - write_outstream(sys.stdout, " FAILED\n") - raise - - -def err(message): - log.error(message) - msg("FAILED: %s" % message) - sys.exit(-1) - - -def obfuscate_url_pw(u): - u = url.make_url(u) - if u.password: - u.password = 'XXXXX' - return str(u) - - def asbool(value): return value is not None and \ value.lower() == 'true' -def warn(msg): - warnings.warn(msg) - - -def msg(msg, newline=True): - if TERMWIDTH is None: - write_outstream(sys.stdout, msg) - if newline: - write_outstream(sys.stdout, "\n") - else: - # left indent output lines - lines = textwrap.wrap(msg, TERMWIDTH) - if len(lines) > 1: - for line in lines[0:-1]: - write_outstream(sys.stdout, " ", line, "\n") - write_outstream(sys.stdout, " ", lines[-1], ("\n" if newline else "")) - - -def load_python_file(dir_, filename): - """Load a file from the given path as a Python module.""" - - module_id = re.sub(r'\W', "_", filename) - path = os.path.join(dir_, filename) - _, ext = os.path.splitext(filename) - if ext == ".py": - if os.path.exists(path): - module = load_module_py(module_id, path) - elif os.path.exists(simple_pyc_file_from_path(path)): - # look for sourceless load - module = load_module_pyc( - module_id, simple_pyc_file_from_path(path)) - else: - raise ImportError("Can't find Python file %s" % path) - elif ext in (".pyc", ".pyo"): - module = load_module_pyc(module_id, path) - del sys.modules[module_id] - return module - - -def simple_pyc_file_from_path(path): - """Given a python source path, return the so-called - "sourceless" .pyc or .pyo path. - - This just a .pyc or .pyo file where the .py file would be. - - Even with PEP-3147, which normally puts .pyc/.pyo files in __pycache__, - this use case remains supported as a so-called "sourceless module import". - - """ - if sys.flags.optimize: - return path + "o" # e.g. .pyo - else: - return path + "c" # e.g. .pyc - - -def pyc_file_from_path(path): - """Given a python source path, locate the .pyc. - - See http://www.python.org/dev/peps/pep-3147/ - #detecting-pep-3147-availability - http://www.python.org/dev/peps/pep-3147/#file-extension-checks - - """ - import imp - has3147 = hasattr(imp, 'get_tag') - if has3147: - return imp.cache_from_source(path) - else: - return simple_pyc_file_from_path(path) - - def rev_id(): val = int(uuid.uuid4()) % 100000000000000 return hex(val)[2:-1] @@ -302,17 +114,6 @@ def to_tuple(x, default=None): raise ValueError("Don't know how to turn %r into a tuple" % x) -def format_as_comma(value): - if value is None: - return "" - elif isinstance(value, string_types): - return value - elif isinstance(value, collections.Iterable): - return ", ".join(value) - else: - raise ValueError("Don't know how to comma-format %r" % value) - - class memoized_property(object): """A read-only @property that is only evaluated once.""" diff --git a/alembic/util/messaging.py b/alembic/util/messaging.py new file mode 100644 index 0000000..c202e96 --- /dev/null +++ b/alembic/util/messaging.py @@ -0,0 +1,94 @@ +from .compat import py27, binary_type, string_types +import sys +from sqlalchemy.engine import url +import warnings +import textwrap +import collections +import logging + +log = logging.getLogger(__name__) + +if py27: + # disable "no handler found" errors + logging.getLogger('alembic').addHandler(logging.NullHandler()) + + +try: + import fcntl + import termios + import struct + ioctl = fcntl.ioctl(0, termios.TIOCGWINSZ, + struct.pack('HHHH', 0, 0, 0, 0)) + _h, TERMWIDTH, _hp, _wp = struct.unpack('HHHH', ioctl) + if TERMWIDTH <= 0: # can occur if running in emacs pseudo-tty + TERMWIDTH = None +except (ImportError, IOError): + TERMWIDTH = None + + +def write_outstream(stream, *text): + encoding = getattr(stream, 'encoding', 'ascii') or 'ascii' + for t in text: + if not isinstance(t, binary_type): + t = t.encode(encoding, 'replace') + t = t.decode(encoding) + try: + stream.write(t) + except IOError: + # suppress "broken pipe" errors. + # no known way to handle this on Python 3 however + # as the exception is "ignored" (noisily) in TextIOWrapper. + break + + +def status(_statmsg, fn, *arg, **kw): + msg(_statmsg + " ...", False) + try: + ret = fn(*arg, **kw) + write_outstream(sys.stdout, " done\n") + return ret + except: + write_outstream(sys.stdout, " FAILED\n") + raise + + +def err(message): + log.error(message) + msg("FAILED: %s" % message) + sys.exit(-1) + + +def obfuscate_url_pw(u): + u = url.make_url(u) + if u.password: + u.password = 'XXXXX' + return str(u) + + +def warn(msg): + warnings.warn(msg) + + +def msg(msg, newline=True): + if TERMWIDTH is None: + write_outstream(sys.stdout, msg) + if newline: + write_outstream(sys.stdout, "\n") + else: + # left indent output lines + lines = textwrap.wrap(msg, TERMWIDTH) + if len(lines) > 1: + for line in lines[0:-1]: + write_outstream(sys.stdout, " ", line, "\n") + write_outstream(sys.stdout, " ", lines[-1], ("\n" if newline else "")) + + +def format_as_comma(value): + if value is None: + return "" + elif isinstance(value, string_types): + return value + elif isinstance(value, collections.Iterable): + return ", ".join(value) + else: + raise ValueError("Don't know how to comma-format %r" % value) diff --git a/alembic/util/pyfiles.py b/alembic/util/pyfiles.py new file mode 100644 index 0000000..c51e187 --- /dev/null +++ b/alembic/util/pyfiles.py @@ -0,0 +1,80 @@ +import sys +import os +import re +from .compat import load_module_py, load_module_pyc +from mako.template import Template + + +def template_to_file(template_file, dest, output_encoding, **kw): + with open(dest, 'wb') as f: + template = Template(filename=template_file) + f.write( + template.render_unicode(**kw).encode(output_encoding) + ) + + +def coerce_resource_to_filename(fname): + """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. + + """ + if not os.path.isabs(fname) and ":" in fname: + import pkg_resources + fname = pkg_resources.resource_filename(*fname.split(':')) + return fname + + +def simple_pyc_file_from_path(path): + """Given a python source path, return the so-called + "sourceless" .pyc or .pyo path. + + This just a .pyc or .pyo file where the .py file would be. + + Even with PEP-3147, which normally puts .pyc/.pyo files in __pycache__, + this use case remains supported as a so-called "sourceless module import". + + """ + if sys.flags.optimize: + return path + "o" # e.g. .pyo + else: + return path + "c" # e.g. .pyc + + +def pyc_file_from_path(path): + """Given a python source path, locate the .pyc. + + See http://www.python.org/dev/peps/pep-3147/ + #detecting-pep-3147-availability + http://www.python.org/dev/peps/pep-3147/#file-extension-checks + + """ + import imp + has3147 = hasattr(imp, 'get_tag') + if has3147: + return imp.cache_from_source(path) + else: + return simple_pyc_file_from_path(path) + + +def load_python_file(dir_, filename): + """Load a file from the given path as a Python module.""" + + module_id = re.sub(r'\W', "_", filename) + path = os.path.join(dir_, filename) + _, ext = os.path.splitext(filename) + if ext == ".py": + if os.path.exists(path): + module = load_module_py(module_id, path) + elif os.path.exists(simple_pyc_file_from_path(path)): + # look for sourceless load + module = load_module_pyc( + module_id, simple_pyc_file_from_path(path)) + else: + raise ImportError("Can't find Python file %s" % path) + elif ext in (".pyc", ".pyo"): + module = load_module_pyc(module_id, path) + del sys.modules[module_id] + return module diff --git a/alembic/util/sqla_compat.py b/alembic/util/sqla_compat.py new file mode 100644 index 0000000..41badd4 --- /dev/null +++ b/alembic/util/sqla_compat.py @@ -0,0 +1,24 @@ +import re +from sqlalchemy import __version__ + + +def _safe_int(value): + try: + return int(value) + except: + return value +_vers = tuple( + [_safe_int(x) for x in re.findall(r'(\d+|[abc]\d)', __version__)]) +sqla_07 = _vers > (0, 7, 2) +sqla_079 = _vers >= (0, 7, 9) +sqla_08 = _vers >= (0, 8, 0) +sqla_083 = _vers >= (0, 8, 3) +sqla_084 = _vers >= (0, 8, 4) +sqla_09 = _vers >= (0, 9, 0) +sqla_092 = _vers >= (0, 9, 2) +sqla_094 = _vers >= (0, 9, 4) +sqla_094 = _vers >= (0, 9, 4) +sqla_099 = _vers >= (0, 9, 9) +sqla_100 = _vers >= (1, 0, 0) +sqla_105 = _vers >= (1, 0, 5) + diff --git a/docs/build/api.rst b/docs/build/api.rst index fea4e14..a22d333 100644 --- a/docs/build/api.rst +++ b/docs/build/api.rst @@ -56,13 +56,13 @@ current :class:`.EnvironmentContext` in use. In particular, the key method used within ``env.py`` is :meth:`.EnvironmentContext.configure`, which establishes all the details about how the database will be accessed. -.. automodule:: alembic.environment +.. automodule:: alembic.runtime.environment :members: The Migration Context ===================== -.. automodule:: alembic.migration +.. automodule:: alembic.runtime.migration :members: The Operations Object @@ -151,7 +151,7 @@ Revision The :class:`.RevisionMap` object serves as the basis for revision management, used exclusively by :class:`.ScriptDirectory`. -.. automodule:: alembic.revision +.. automodule:: alembic.script.revision :members: Autogeneration diff --git a/docs/build/ops.rst b/docs/build/ops.rst index 1df9d27..b4838aa 100644 --- a/docs/build/ops.rst +++ b/docs/build/ops.rst @@ -7,8 +7,8 @@ Operation Reference This file provides documentation on Alembic migration directives. The directives here are used within user-defined migration files, -within the ``upgrade()`` and ``downgrade()`` functions, as well as -any functions further invoked by those. +within the ``upgrade()`` and ``downgrade()`` functions, as well as +any functions further invoked by those. All directives exist as methods on a class called :class:`.Operations`. When migration scripts are run, this object is made available @@ -19,11 +19,11 @@ with individual proxies for each method on :class:`.Operations`, so symbols can be imported safely from the ``alembic.op`` namespace. A key design philosophy to the :mod:`alembic.operations` methods is that -to the greatest degree possible, they internally generate the +to the greatest degree possible, they internally generate the appropriate SQLAlchemy metadata, typically involving :class:`~sqlalchemy.schema.Table` and :class:`~sqlalchemy.schema.Constraint` -objects. This so that migration instructions can be -given in terms of just the string names and/or flags involved. +objects. This so that migration instructions can be +given in terms of just the string names and/or flags involved. The exceptions to this rule include the :meth:`~.Operations.add_column` and :meth:`~.Operations.create_table` directives, which require full :class:`~sqlalchemy.schema.Column` diff --git a/tests/test_autogen_render.py b/tests/test_autogen_render.py index 52f3601..32975f0 100644 --- a/tests/test_autogen_render.py +++ b/tests/test_autogen_render.py @@ -16,7 +16,8 @@ from sqlalchemy.sql import and_, column, literal_column, false from alembic.testing.mock import patch -from alembic import autogenerate, util, compat +from alembic import autogenerate, util +from alembic.util import compat from alembic.testing import eq_, eq_ignore_whitespace, config from alembic.testing.fixtures import op_fixture diff --git a/tests/test_batch.py b/tests/test_batch.py index a498c36..c827ac4 100644 --- a/tests/test_batch.py +++ b/tests/test_batch.py @@ -1,15 +1,13 @@ from contextlib import contextmanager import re -import io - from alembic.testing import exclusions from alembic.testing import TestBase, eq_, config from alembic.testing.fixtures import op_fixture from alembic.testing import mock from alembic.operations import Operations -from alembic.batch import ApplyBatchImpl -from alembic.migration import MigrationContext +from alembic.operations.batch import ApplyBatchImpl +from alembic.runtime.migration import MigrationContext from sqlalchemy import Integer, Table, Column, String, MetaData, ForeignKey, \ @@ -503,7 +501,7 @@ class BatchAPITest(TestBase): batch = op.batch_alter_table( 'tname', recreate='never', schema=schema).__enter__() - with mock.patch("alembic.operations.sa_schema") as mock_schema: + with mock.patch("alembic.operations.base.sa_schema") as mock_schema: yield batch batch.impl.flush() self.mock_schema = mock_schema diff --git a/tests/test_config.py b/tests/test_config.py index db37456..da0b413 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,9 +1,8 @@ #!coding: utf-8 -import os -import tempfile -from alembic import config, util, compat +from alembic import config, util +from alembic.util import compat from alembic.migration import MigrationContext from alembic.operations import Operations from alembic.script import ScriptDirectory diff --git a/tests/test_op.py b/tests/test_op.py index 7d5f83e..5be9aa8 100644 --- a/tests/test_op.py +++ b/tests/test_op.py @@ -524,7 +524,8 @@ class OpTest(TestBase): def test_add_foreign_key_dialect_kw(self): op_fixture() with mock.patch( - "alembic.operations.sa_schema.ForeignKeyConstraint") as fkc: + "alembic.operations.base.sa_schema.ForeignKeyConstraint" + ) as fkc: op.create_foreign_key('fk_test', 't1', 't2', ['foo', 'bar'], ['bat', 'hoho'], foobar_arg='xyz') diff --git a/tests/test_revision.py b/tests/test_revision.py index d73316d..0a515de 100644 --- a/tests/test_revision.py +++ b/tests/test_revision.py @@ -1,6 +1,6 @@ from alembic.testing.fixtures import TestBase from alembic.testing import eq_, assert_raises_message -from alembic.revision import RevisionMap, Revision, MultipleHeads, \ +from alembic.script.revision import RevisionMap, Revision, MultipleHeads, \ RevisionError diff --git a/tests/test_script_consumption.py b/tests/test_script_consumption.py index 11b8080..c2eef0a 100644 --- a/tests/test_script_consumption.py +++ b/tests/test_script_consumption.py @@ -3,7 +3,8 @@ import os import re -from alembic import command, util, compat +from alembic import command, util +from alembic.util import compat from alembic.script import ScriptDirectory, Script from alembic.testing.env import clear_staging_env, staging_env, \ _sqlite_testing_config, write_script, _sqlite_file_db, \ |