summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alembic/__init__.py2
-rw-r--r--alembic/autogenerate/compare.py2
-rw-r--r--alembic/autogenerate/render.py2
-rw-r--r--alembic/ddl/base.py9
-rw-r--r--alembic/ddl/mysql.py9
-rw-r--r--alembic/ddl/postgresql.py51
-rw-r--r--alembic/ddl/sqlite.py26
-rw-r--r--alembic/testing/assertions.py55
-rw-r--r--alembic/testing/fixtures.py45
-rw-r--r--alembic/testing/plugin/bootstrap.py5
-rw-r--r--alembic/testing/plugin/noseplugin.py106
-rw-r--r--alembic/testing/plugin/plugin_base.py21
-rw-r--r--alembic/testing/requirements.py79
-rw-r--r--alembic/testing/runner.py49
-rw-r--r--alembic/testing/warnings.py5
-rw-r--r--alembic/util/__init__.py12
-rw-r--r--alembic/util/compat.py5
-rw-r--r--alembic/util/sqla_compat.py36
-rw-r--r--docs/build/changelog.rst2
-rw-r--r--docs/build/unreleased/bump.rst6
-rw-r--r--setup.py2
-rw-r--r--tests/_autogen_fixtures.py4
-rw-r--r--tests/requirements.py27
-rw-r--r--tests/test_autogen_diffs.py6
-rw-r--r--tests/test_autogen_indexes.py4
-rw-r--r--tests/test_autogen_render.py25
-rw-r--r--tests/test_batch.py1
-rw-r--r--tests/test_mssql.py5
-rw-r--r--tests/test_op.py4
-rw-r--r--tests/test_op_naming_convention.py2
-rw-r--r--tests/test_postgresql.py47
-rw-r--r--tox.ini8
32 files changed, 80 insertions, 582 deletions
diff --git a/alembic/__init__.py b/alembic/__init__.py
index 78d16cd..5396c6c 100644
--- a/alembic/__init__.py
+++ b/alembic/__init__.py
@@ -6,7 +6,7 @@ from . import op # noqa
from .runtime import environment
from .runtime import migration
-__version__ = '1.0.12'
+__version__ = "1.1.0"
package_dir = path.abspath(path.dirname(__file__))
diff --git a/alembic/autogenerate/compare.py b/alembic/autogenerate/compare.py
index 37371eb..49ceb21 100644
--- a/alembic/autogenerate/compare.py
+++ b/alembic/autogenerate/compare.py
@@ -809,7 +809,7 @@ def _setup_autoincrement(
if metadata_col.table._autoincrement_column is metadata_col:
alter_column_op.kw["autoincrement"] = True
- elif util.sqla_110 and metadata_col.autoincrement is True:
+ elif metadata_col.autoincrement is True:
alter_column_op.kw["autoincrement"] = True
elif metadata_col.autoincrement is False:
alter_column_op.kw["autoincrement"] = False
diff --git a/alembic/autogenerate/render.py b/alembic/autogenerate/render.py
index 0e3b2d8..24eeb4e 100644
--- a/alembic/autogenerate/render.py
+++ b/alembic/autogenerate/render.py
@@ -476,7 +476,7 @@ def _ident(name):
"""
if name is None:
return name
- elif util.sqla_09 and isinstance(name, sql.elements.quoted_name):
+ elif isinstance(name, sql.elements.quoted_name):
if compat.py2k:
# the attempt to encode to ascii here isn't super ideal,
# however we are trying to cut down on an explosion of
diff --git a/alembic/ddl/base.py b/alembic/ddl/base.py
index 90573cf..c654c83 100644
--- a/alembic/ddl/base.py
+++ b/alembic/ddl/base.py
@@ -5,19 +5,14 @@ from sqlalchemy import types as sqltypes
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.schema import Column
from sqlalchemy.schema import DDLElement
+from sqlalchemy.sql.elements import quoted_name
-from .. import util
from ..util.sqla_compat import _columns_for_constraint # noqa
from ..util.sqla_compat import _find_columns # noqa
from ..util.sqla_compat import _fk_spec # noqa
from ..util.sqla_compat import _is_type_bound # noqa
from ..util.sqla_compat import _table_for_constraint # noqa
-# backwards compat
-
-if util.sqla_09:
- from sqlalchemy.sql.elements import quoted_name
-
class AlterTable(DDLElement):
@@ -169,7 +164,7 @@ def visit_column_default(element, compiler, **kw):
def quote_dotted(name, quote):
"""quote the elements of a dotted name"""
- if util.sqla_09 and isinstance(name, quoted_name):
+ if isinstance(name, quoted_name):
return quote(name)
result = ".".join([quote(x) for x in name.split(".")])
return result
diff --git a/alembic/ddl/mysql.py b/alembic/ddl/mysql.py
index b514149..17ae2de 100644
--- a/alembic/ddl/mysql.py
+++ b/alembic/ddl/mysql.py
@@ -18,7 +18,6 @@ from ..autogenerate import compare
from ..util.compat import string_types
from ..util.sqla_compat import _is_mariadb
from ..util.sqla_compat import _is_type_bound
-from ..util.sqla_compat import sqla_100
class MySQLImpl(DefaultImpl):
@@ -212,14 +211,6 @@ class MySQLImpl(DefaultImpl):
if idx.name in removed:
metadata_indexes.remove(idx)
- if not sqla_100:
- self._legacy_correct_for_dupe_uq_uix(
- conn_unique_constraints,
- conn_indexes,
- metadata_unique_constraints,
- metadata_indexes,
- )
-
def _legacy_correct_for_dupe_uq_uix(
self,
conn_unique_constraints,
diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py
index 255c7e6..d5bcd17 100644
--- a/alembic/ddl/postgresql.py
+++ b/alembic/ddl/postgresql.py
@@ -6,6 +6,7 @@ from sqlalchemy import Numeric
from sqlalchemy import text
from sqlalchemy import types as sqltypes
from sqlalchemy.dialects.postgresql import BIGINT
+from sqlalchemy.dialects.postgresql import ExcludeConstraint
from sqlalchemy.dialects.postgresql import INTEGER
from sqlalchemy.sql.expression import ColumnClause
from sqlalchemy.sql.expression import UnaryExpression
@@ -29,9 +30,6 @@ from ..operations.base import Operations
from ..util import compat
from ..util import sqla_compat
-if util.sqla_100:
- from sqlalchemy.dialects.postgresql import ExcludeConstraint
-
log = logging.getLogger(__name__)
@@ -76,8 +74,8 @@ class PostgresqlImpl(DefaultImpl):
# check for unquoted string and quote for PG String types
if (
- not isinstance(inspector_column.type, Numeric) and
- metadata_column.server_default is not None
+ not isinstance(inspector_column.type, Numeric)
+ and metadata_column.server_default is not None
and isinstance(
metadata_column.server_default.arg, compat.string_types
)
@@ -187,24 +185,13 @@ class PostgresqlImpl(DefaultImpl):
metadata_indexes,
):
- conn_uniques_by_name = dict(
- (c.name, c) for c in conn_unique_constraints
- )
conn_indexes_by_name = dict((c.name, c) for c in conn_indexes)
- if not util.sqla_100:
- doubled_constraints = set(
- conn_indexes_by_name[name]
- for name in set(conn_uniques_by_name).intersection(
- conn_indexes_by_name
- )
- )
- else:
- doubled_constraints = set(
- index
- for index in conn_indexes
- if index.info.get("duplicates_constraint")
- )
+ doubled_constraints = set(
+ index
+ for index in conn_indexes
+ if index.info.get("duplicates_constraint")
+ )
for ix in doubled_constraints:
conn_indexes.remove(ix)
@@ -344,10 +331,6 @@ class CreateExcludeConstraintOp(ops.AddConstraintOp):
)
def to_constraint(self, migration_context=None):
- if not util.sqla_100:
- raise NotImplementedError(
- "ExcludeConstraint not supported until SQLAlchemy 1.0"
- )
if self._orig_constraint is not None:
return self._orig_constraint
schema_obj = schemaobj.SchemaObjects(migration_context)
@@ -435,17 +418,15 @@ def _add_exclude_constraint(autogen_context, op):
return _exclude_constraint(op.to_constraint(), autogen_context, alter=True)
-if util.sqla_100:
-
- @render._constraint_renderers.dispatch_for(ExcludeConstraint)
- def _render_inline_exclude_constraint(constraint, autogen_context):
- rendered = render._user_defined_render(
- "exclude", constraint, autogen_context
- )
- if rendered is not False:
- return rendered
+@render._constraint_renderers.dispatch_for(ExcludeConstraint)
+def _render_inline_exclude_constraint(constraint, autogen_context):
+ rendered = render._user_defined_render(
+ "exclude", constraint, autogen_context
+ )
+ if rendered is not False:
+ return rendered
- return _exclude_constraint(constraint, autogen_context, False)
+ return _exclude_constraint(constraint, autogen_context, False)
def _postgresql_autogenerate_prefix(autogen_context):
diff --git a/alembic/ddl/sqlite.py b/alembic/ddl/sqlite.py
index 95f814a..5a69778 100644
--- a/alembic/ddl/sqlite.py
+++ b/alembic/ddl/sqlite.py
@@ -70,32 +70,6 @@ class SQLiteImpl(DefaultImpl):
return rendered_inspector_default != rendered_metadata_default
- def correct_for_autogen_constraints(
- self,
- conn_unique_constraints,
- conn_indexes,
- metadata_unique_constraints,
- metadata_indexes,
- ):
-
- if util.sqla_100:
- return
-
- # adjustments to accommodate for SQLite unnamed unique constraints
- # not being reported from the backend; this was updated in
- # SQLA 1.0.
-
- def uq_sig(uq):
- return tuple(sorted(uq.columns.keys()))
-
- conn_unique_sigs = set(uq_sig(uq) for uq in conn_unique_constraints)
-
- for idx in list(metadata_unique_constraints):
- # SQLite backend can't report on unnamed UNIQUE constraints,
- # so remove these, unless we see an exact signature match
- if idx.name is None and uq_sig(idx) not in conn_unique_sigs:
- metadata_unique_constraints.remove(idx)
-
def _guess_if_default_is_unparenthesized_sql_expr(self, expr):
"""Determine if a server default is a SQL expression or a constant.
diff --git a/alembic/testing/assertions.py b/alembic/testing/assertions.py
index 8654083..2ad9dba 100644
--- a/alembic/testing/assertions.py
+++ b/alembic/testing/assertions.py
@@ -6,6 +6,12 @@ import warnings
from sqlalchemy import exc as sa_exc
from sqlalchemy.engine import default
+from sqlalchemy.testing.assertions import assert_raises # noqa
+from sqlalchemy.testing.assertions import assert_raises_message # noqa
+from sqlalchemy.testing.assertions import eq_ # noqa
+from sqlalchemy.testing.assertions import is_ # noqa
+from sqlalchemy.testing.assertions import is_not_ # noqa
+from sqlalchemy.testing.assertions import ne_ # noqa
from sqlalchemy.util import decorator
from . import config
@@ -16,55 +22,6 @@ from ..util.compat import py3k
from ..util.compat import text_type
-if not util.sqla_094:
-
- def eq_(a, b, msg=None):
- """Assert a == b, with repr messaging on failure."""
- assert a == b, msg or "%r != %r" % (a, b)
-
- def ne_(a, b, msg=None):
- """Assert a != b, with repr messaging on failure."""
- assert a != b, msg or "%r == %r" % (a, b)
-
- def is_(a, b, msg=None):
- """Assert a is b, with repr messaging on failure."""
- assert a is b, msg or "%r is not %r" % (a, b)
-
- def is_not_(a, b, msg=None):
- """Assert a is not b, with repr messaging on failure."""
- assert a is not b, msg or "%r is %r" % (a, b)
-
- def assert_raises(except_cls, callable_, *args, **kw):
- try:
- callable_(*args, **kw)
- success = False
- except except_cls:
- success = True
-
- # assert outside the block so it works for AssertionError too !
- assert success, "Callable did not raise an exception"
-
- def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
- try:
- callable_(*args, **kwargs)
- assert False, "Callable did not raise an exception"
- except except_cls as e:
- assert re.search(msg, text_type(e), re.UNICODE), "%r !~ %s" % (
- msg,
- e,
- )
- print(text_type(e).encode("utf-8"))
-
-
-else:
- from sqlalchemy.testing.assertions import assert_raises # noqa
- from sqlalchemy.testing.assertions import assert_raises_message # noqa
- from sqlalchemy.testing.assertions import eq_ # noqa
- from sqlalchemy.testing.assertions import is_ # noqa
- from sqlalchemy.testing.assertions import is_not_ # noqa
- from sqlalchemy.testing.assertions import ne_ # noqa
-
-
def eq_ignore_whitespace(a, b, msg=None):
a = re.sub(r"^\s+?|\n", "", a)
a = re.sub(r" {2,}", " ", a)
diff --git a/alembic/testing/fixtures.py b/alembic/testing/fixtures.py
index 6243467..46a77ca 100644
--- a/alembic/testing/fixtures.py
+++ b/alembic/testing/fixtures.py
@@ -10,14 +10,13 @@ from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import text
+from sqlalchemy.testing.fixtures import TestBase # noqa
import alembic
from . import config
from . import mock
from .assertions import _get_dialect
from .assertions import eq_
-from .plugin.plugin_base import SkipTest
-from .. import util
from ..environment import EnvironmentContext
from ..migration import MigrationContext
from ..operations import Operations
@@ -29,44 +28,6 @@ testing_config = configparser.ConfigParser()
testing_config.read(["test.cfg"])
-if not util.sqla_094:
-
- class TestBase(object):
- # A sequence of database names to always run, regardless of the
- # constraints below.
- __whitelist__ = ()
-
- # A sequence of requirement names matching testing.requires decorators
- __requires__ = ()
-
- # A sequence of dialect names to exclude from the test class.
- __unsupported_on__ = ()
-
- # If present, test class is only runnable for the *single* specified
- # dialect. If you need multiple, use __unsupported_on__ and invert.
- __only_on__ = None
-
- # A sequence of no-arg callables. If any are True, the entire
- # testcase is skipped.
- __skip_if__ = None
-
- def assert_(self, val, msg=None):
- assert val, msg
-
- # apparently a handful of tests are doing this....OK
- def setup(self):
- if hasattr(self, "setUp"):
- self.setUp()
-
- def teardown(self):
- if hasattr(self, "tearDown"):
- self.tearDown()
-
-
-else:
- from sqlalchemy.testing.fixtures import TestBase # noqa
-
-
def capture_db():
buf = []
@@ -108,10 +69,6 @@ def op_fixture(
opts = {}
if naming_convention:
- if not util.sqla_092:
- raise SkipTest(
- "naming_convention feature requires " "sqla 0.9.2 or greater"
- )
opts["target_metadata"] = MetaData(naming_convention=naming_convention)
class buffer_(object):
diff --git a/alembic/testing/plugin/bootstrap.py b/alembic/testing/plugin/bootstrap.py
index 4bd415d..ed8fa82 100644
--- a/alembic/testing/plugin/bootstrap.py
+++ b/alembic/testing/plugin/bootstrap.py
@@ -1,5 +1,5 @@
"""
-Bootstrapper for nose/pytest plugins.
+Bootstrapper for test framework plugins.
The entire rationale for this system is to get the modules in plugin/
imported without importing all of the supporting library, so that we can
@@ -40,8 +40,5 @@ def load_file_as_module(name):
if to_bootstrap == "pytest":
sys.modules["alembic_plugin_base"] = load_file_as_module("plugin_base")
sys.modules["alembic_pytestplugin"] = load_file_as_module("pytestplugin")
-elif to_bootstrap == "nose":
- sys.modules["alembic_plugin_base"] = load_file_as_module("plugin_base")
- sys.modules["alembic_noseplugin"] = load_file_as_module("noseplugin")
else:
raise Exception("unknown bootstrap: %s" % to_bootstrap) # noqa
diff --git a/alembic/testing/plugin/noseplugin.py b/alembic/testing/plugin/noseplugin.py
deleted file mode 100644
index fafb9e1..0000000
--- a/alembic/testing/plugin/noseplugin.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# plugin/noseplugin.py
-# Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-
-"""
-Enhance nose with extra options and behaviors for running SQLAlchemy tests.
-
-
-NOTE: copied/adapted from SQLAlchemy master for backwards compatibility;
-this should be removable when Alembic targets SQLAlchemy 1.0.0.
-
-"""
-
-try:
- # installed by bootstrap.py
- import alembic_plugin_base as plugin_base
-except ImportError:
- # assume we're a package, use traditional import
- from . import plugin_base
-
-import os
-import sys
-
-from nose.plugins import Plugin
-
-fixtures = None
-
-py3k = sys.version_info.major >= 3
-
-
-class NoseSQLAlchemy(Plugin):
- enabled = True
-
- name = "sqla_testing"
- score = 100
-
- def options(self, parser, env=os.environ):
- Plugin.options(self, parser, env)
- opt = parser.add_option
-
- def make_option(name, **kw):
- callback_ = kw.pop("callback", None)
- if callback_:
-
- def wrap_(option, opt_str, value, parser):
- callback_(opt_str, value, parser)
-
- kw["callback"] = wrap_
- opt(name, **kw)
-
- plugin_base.setup_options(make_option)
- plugin_base.read_config()
-
- def configure(self, options, conf):
- super(NoseSQLAlchemy, self).configure(options, conf)
- plugin_base.pre_begin(options)
-
- plugin_base.set_coverage_flag(options.enable_plugin_coverage)
-
- def begin(self):
- global fixtures
- from alembic.testing import fixtures # noqa
-
- plugin_base.post_begin()
-
- def describeTest(self, test):
- return ""
-
- def wantFunction(self, fn):
- return False
-
- def wantMethod(self, fn):
- if py3k:
- if not hasattr(fn.__self__, "cls"):
- return False
- cls = fn.__self__.cls
- else:
- cls = fn.im_class
- return plugin_base.want_method(cls, fn)
-
- def wantClass(self, cls):
- return plugin_base.want_class(cls)
-
- def beforeTest(self, test):
- plugin_base.before_test(
- test,
- test.test.cls.__module__,
- test.test.cls,
- test.test.method.__name__,
- )
-
- def afterTest(self, test):
- plugin_base.after_test(test)
-
- def startContext(self, ctx):
- if not isinstance(ctx, type) or not issubclass(ctx, fixtures.TestBase):
- return
- plugin_base.start_test_class(ctx)
-
- def stopContext(self, ctx):
- if not isinstance(ctx, type) or not issubclass(ctx, fixtures.TestBase):
- return
- plugin_base.stop_test_class(ctx)
diff --git a/alembic/testing/plugin/plugin_base.py b/alembic/testing/plugin/plugin_base.py
index 44930c8..20aa34e 100644
--- a/alembic/testing/plugin/plugin_base.py
+++ b/alembic/testing/plugin/plugin_base.py
@@ -7,11 +7,7 @@
"""Testing extensions.
this module is designed to work as a testing-framework-agnostic library,
-so that we can continue to support nose and also begin adding new
-functionality via py.test.
-
-NOTE: copied/adapted from SQLAlchemy master for backwards compatibility;
-this should be removable when Alembic targets SQLAlchemy 1.0.0
+however it currenty targets only py.test.
"""
@@ -21,14 +17,9 @@ from __future__ import absolute_import
import re
import sys
-try:
- # unitttest has a SkipTest also but pytest doesn't
- # honor it unless nose is imported too...
- from nose import SkipTest
-except ImportError:
- from pytest import skip
+from pytest import skip
- SkipTest = skip.Exception
+SkipTest = skip.Exception
py3k = sys.version_info.major >= 3
@@ -234,8 +225,6 @@ def post_begin():
for fn in post_configure:
fn(options, file_config)
- # late imports, has to happen after config as well
- # as nose plugins like coverage
global util, fixtures, engines, exclusions, assertions
global warnings, profiling, config, testing
from alembic.testing import config, warnings, exclusions # noqa
@@ -373,8 +362,6 @@ def _prep_testing_database(options, file_config):
from alembic.testing import config
from alembic.testing.exclusions import against
from sqlalchemy import schema
- from alembic import util
-
from sqlalchemy import inspect
if options.dropfirst:
@@ -431,7 +418,7 @@ def _prep_testing_database(options, file_config):
)
)
- if against(cfg, "postgresql") and util.sqla_100:
+ if against(cfg, "postgresql"):
from sqlalchemy.dialects import postgresql
for enum in inspector.get_enums("*"):
diff --git a/alembic/testing/requirements.py b/alembic/testing/requirements.py
index b4c147b..bbb2ca2 100644
--- a/alembic/testing/requirements.py
+++ b/alembic/testing/requirements.py
@@ -1,16 +1,11 @@
import sys
+from sqlalchemy.testing.requirements import Requirements
+
from alembic import util
from alembic.util import sqla_compat
from . import exclusions
-if util.sqla_094:
- from sqlalchemy.testing.requirements import Requirements
-else:
-
- class Requirements(object):
- pass
-
class SuiteRequirements(Requirements):
@property
@@ -77,76 +72,6 @@ class SuiteRequirements(Requirements):
)
@property
- def sqlalchemy_10(self):
- return exclusions.skip_if(
- lambda config: not util.sqla_100,
- "SQLAlchemy 1.0.0 or greater required",
- )
-
- @property
- def fail_before_sqla_100(self):
- return exclusions.fails_if(
- lambda config: not util.sqla_100,
- "SQLAlchemy 1.0.0 or greater required",
- )
-
- @property
- def fail_before_sqla_1010(self):
- return exclusions.fails_if(
- lambda config: not util.sqla_1010,
- "SQLAlchemy 1.0.10 or greater required",
- )
-
- @property
- def fail_before_sqla_099(self):
- return exclusions.fails_if(
- lambda config: not util.sqla_099,
- "SQLAlchemy 0.9.9 or greater required",
- )
-
- @property
- def fail_before_sqla_110(self):
- return exclusions.fails_if(
- lambda config: not util.sqla_110,
- "SQLAlchemy 1.1.0 or greater required",
- )
-
- @property
- def sqlalchemy_092(self):
- return exclusions.skip_if(
- lambda config: not util.sqla_092,
- "SQLAlchemy 0.9.2 or greater required",
- )
-
- @property
- def sqlalchemy_094(self):
- return exclusions.skip_if(
- lambda config: not util.sqla_094,
- "SQLAlchemy 0.9.4 or greater required",
- )
-
- @property
- def sqlalchemy_099(self):
- return exclusions.skip_if(
- lambda config: not util.sqla_099,
- "SQLAlchemy 0.9.9 or greater required",
- )
-
- @property
- def sqlalchemy_100(self):
- return exclusions.skip_if(
- lambda config: not util.sqla_100,
- "SQLAlchemy 1.0.0 or greater required",
- )
-
- @property
- def sqlalchemy_1014(self):
- return exclusions.skip_if(
- lambda config: not util.sqla_1014,
- "SQLAlchemy 1.0.14 or greater required",
- )
-
- @property
def sqlalchemy_1115(self):
return exclusions.skip_if(
lambda config: not util.sqla_1115,
diff --git a/alembic/testing/runner.py b/alembic/testing/runner.py
deleted file mode 100644
index da5e0f4..0000000
--- a/alembic/testing/runner.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-# testing/runner.py
-# Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-"""
-Nose test runner module.
-
-This script is a front-end to "nosetests" which
-installs SQLAlchemy's testing plugin into the local environment.
-
-The script is intended to be used by third-party dialects and extensions
-that run within SQLAlchemy's testing framework. The runner can
-be invoked via::
-
- python -m alembic.testing.runner
-
-The script is then essentially the same as the "nosetests" script, including
-all of the usual Nose options. The test environment requires that a
-setup.cfg is locally present including various required options.
-
-Note that when using this runner, Nose's "coverage" plugin will not be
-able to provide coverage for SQLAlchemy itself, since SQLAlchemy is
-imported into sys.modules before coverage is started. The special
-script sqla_nose.py is provided as a top-level script which loads the
-plugin in a special (somewhat hacky) way so that coverage against
-SQLAlchemy itself is possible.
-
-"""
-import nose
-
-from .plugin.noseplugin import NoseSQLAlchemy
-
-
-def main():
- nose.main(addplugins=[NoseSQLAlchemy()])
-
-
-def setup_py_test():
- """Runner to use for the 'test_suite' entry of your setup.py.
-
- Prevents any name clash shenanigans from the command line
- argument "test" that the "setup.py test" command sends
- to nose.
-
- """
- nose.main(addplugins=[NoseSQLAlchemy()], argv=["runner"])
diff --git a/alembic/testing/warnings.py b/alembic/testing/warnings.py
index 27ba706..7689e17 100644
--- a/alembic/testing/warnings.py
+++ b/alembic/testing/warnings.py
@@ -25,6 +25,11 @@ def setup_filters():
warnings.filterwarnings("error", category=sa_exc.SAWarning)
warnings.filterwarnings("error", category=DeprecationWarning)
+ # temporary to allow SQLAlchemy 1.1 to pass under python 3
+ warnings.filterwarnings(
+ "ignore", category=DeprecationWarning, message=".*formatargspec"
+ )
+
def assert_warnings(fn, warning_msgs, regex=False):
"""Assert that each of the given warnings are emitted by fn."""
diff --git a/alembic/util/__init__.py b/alembic/util/__init__.py
index 88b7431..5a765fe 100644
--- a/alembic/util/__init__.py
+++ b/alembic/util/__init__.py
@@ -21,19 +21,11 @@ from .pyfiles import edit # noqa
from .pyfiles import load_python_file # noqa
from .pyfiles import pyc_file_from_path # noqa
from .pyfiles import template_to_file # noqa
-from .sqla_compat import sqla_09 # noqa
-from .sqla_compat import sqla_092 # noqa
-from .sqla_compat import sqla_094 # noqa
-from .sqla_compat import sqla_099 # noqa
-from .sqla_compat import sqla_100 # noqa
-from .sqla_compat import sqla_1010 # noqa
-from .sqla_compat import sqla_1014 # noqa
-from .sqla_compat import sqla_105 # noqa
from .sqla_compat import sqla_110 # noqa
from .sqla_compat import sqla_1115 # noqa
from .sqla_compat import sqla_120 # noqa
from .sqla_compat import sqla_1216 # noqa
-if not sqla_09:
- raise CommandError("SQLAlchemy 0.9.0 or greater is required. ")
+if not sqla_110:
+ raise CommandError("SQLAlchemy 1.1.0 or greater is required. ")
diff --git a/alembic/util/compat.py b/alembic/util/compat.py
index e0269b7..a08b101 100644
--- a/alembic/util/compat.py
+++ b/alembic/util/compat.py
@@ -3,9 +3,6 @@ import inspect
import io
import sys
-if sys.version_info < (2, 7):
- raise NotImplementedError("Python 2.7 or greater is required.")
-
py27 = sys.version_info >= (2, 7)
py2k = sys.version_info.major < 3
py3k = sys.version_info.major >= 3
@@ -327,7 +324,7 @@ class EncodedIO(io.TextIOWrapper):
if py2k:
# in Py2K, the io.* package is awkward because it does not
# easily wrap the file type (e.g. sys.stdout) and I can't
- # figure out at all how to wrap StringIO.StringIO (used by nosetests)
+ # figure out at all how to wrap StringIO.StringIO
# and also might be user specified too. So create a full
# adapter.
diff --git a/alembic/util/sqla_compat.py b/alembic/util/sqla_compat.py
index 52d4e01..5fa4332 100644
--- a/alembic/util/sqla_compat.py
+++ b/alembic/util/sqla_compat.py
@@ -25,25 +25,13 @@ def _safe_int(value):
_vers = tuple(
[_safe_int(x) for x in re.findall(r"(\d+|[abc]\d)", __version__)]
)
-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)
-sqla_1010 = _vers >= (1, 0, 10)
sqla_110 = _vers >= (1, 1, 0)
-sqla_1014 = _vers >= (1, 0, 14)
sqla_1115 = _vers >= (1, 1, 15)
sqla_120 = _vers >= (1, 2, 0)
sqla_1216 = _vers >= (1, 2, 16)
-if sqla_110:
- AUTOINCREMENT_DEFAULT = "auto"
-else:
- AUTOINCREMENT_DEFAULT = True
+AUTOINCREMENT_DEFAULT = "auto"
def _table_for_constraint(constraint):
@@ -63,14 +51,9 @@ def _columns_for_constraint(constraint):
def _fk_spec(constraint):
- if sqla_100:
- source_columns = [
- constraint.columns[key].name for key in constraint.column_keys
- ]
- else:
- source_columns = [
- element.parent.name for element in constraint.elements
- ]
+ source_columns = [
+ constraint.columns[key].name for key in constraint.column_keys
+ ]
source_table = constraint.parent.name
source_schema = constraint.parent.schema
@@ -106,15 +89,8 @@ def _fk_is_self_referential(constraint):
def _is_type_bound(constraint):
# this deals with SQLAlchemy #3260, don't copy CHECK constraints
# that will be generated by the type.
- if sqla_100:
- # new feature added for #3260
- return constraint._type_bound
- else:
- # old way, look at what we know Boolean/Enum to use
- return constraint._create_rule is not None and isinstance(
- getattr(constraint._create_rule, "target", None),
- sqltypes.SchemaType,
- )
+ # new feature added for #3260
+ return constraint._type_bound
def _find_columns(clause):
diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst
index b05177a..a242b85 100644
--- a/docs/build/changelog.rst
+++ b/docs/build/changelog.rst
@@ -4,7 +4,7 @@ Changelog
==========
.. changelog::
- :version: 1.0.12
+ :version: 1.1.0
:include_notes_from: unreleased
.. changelog::
diff --git a/docs/build/unreleased/bump.rst b/docs/build/unreleased/bump.rst
new file mode 100644
index 0000000..7e04c9c
--- /dev/null
+++ b/docs/build/unreleased/bump.rst
@@ -0,0 +1,6 @@
+.. change::
+ :tags: change
+
+ Alembic 1.1 bumps the minimum version of SQLAlchemy to 1.1. As was the
+ case before, Python requirements remain at Python 2.7, or in the 3.x series
+ Python 3.4.
diff --git a/setup.py b/setup.py
index b1555fe..ae87576 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@ v.close()
readme = os.path.join(os.path.dirname(__file__), "README.rst")
requires = [
- "SQLAlchemy>=0.9.0",
+ "SQLAlchemy>=1.1.0",
"Mako",
"python-editor>=0.3",
"python-dateutil",
diff --git a/tests/_autogen_fixtures.py b/tests/_autogen_fixtures.py
index e9047f6..a417a0e 100644
--- a/tests/_autogen_fixtures.py
+++ b/tests/_autogen_fixtures.py
@@ -186,9 +186,7 @@ class _ComparesFKs(object):
eq_([elem.column.name for elem in diff[1].elements], target_columns)
if conditional_name is not None:
- if config.requirements.no_fk_names.enabled:
- eq_(diff[1].name, None)
- elif conditional_name == "servergenerated":
+ if conditional_name == "servergenerated":
fks = Inspector.from_engine(self.bind).get_foreign_keys(
source_table
)
diff --git a/tests/requirements.py b/tests/requirements.py
index a4e7f59..bcfadbd 100644
--- a/tests/requirements.py
+++ b/tests/requirements.py
@@ -1,4 +1,3 @@
-from alembic import util
from alembic.testing import exclusions
from alembic.testing.requirements import SuiteRequirements
from alembic.util import sqla_compat
@@ -33,15 +32,6 @@ class DefaultRequirements(SuiteRequirements):
)
@property
- def no_fk_names(self):
- """foreign key constraints *never* have names in the DB"""
-
- return exclusions.only_if(
- lambda config: exclusions.against(config, "sqlite")
- and not util.sqla_100
- )
-
- @property
def check_constraints_w_enforcement(self):
return exclusions.fails_on("mysql")
@@ -63,14 +53,7 @@ class DefaultRequirements(SuiteRequirements):
@property
def reflects_fk_options(self):
- return exclusions.only_on(
- [
- "postgresql",
- "mysql",
- lambda config: util.sqla_110
- and exclusions.against(config, "sqlite"),
- ]
- )
+ return exclusions.only_on(["postgresql", "mysql", "sqlite"])
@property
def fk_initially(self):
@@ -100,13 +83,7 @@ class DefaultRequirements(SuiteRequirements):
"""Target driver reflects the name of primary key constraints."""
return exclusions.fails_on_everything_except(
- "postgresql",
- "oracle",
- "mssql",
- "sybase",
- lambda config: (
- util.sqla_110 and exclusions.against(config, "sqlite")
- ),
+ "postgresql", "oracle", "mssql", "sybase", "sqlite"
)
@property
diff --git a/tests/test_autogen_diffs.py b/tests/test_autogen_diffs.py
index d7d0430..f02dbb8 100644
--- a/tests/test_autogen_diffs.py
+++ b/tests/test_autogen_diffs.py
@@ -751,11 +751,6 @@ class AutogenSystemColTest(AutogenTest, TestBase):
class AutogenerateVariantCompareTest(AutogenTest, TestBase):
__backend__ = True
- # 1.0.13 and lower fail on Postgresql due to variant / bigserial issue
- # #3739
-
- __requires__ = ("sqlalchemy_1014",)
-
@classmethod
def _get_db_schema(cls):
m = MetaData()
@@ -1544,7 +1539,6 @@ class AutoincrementTest(AutogenFixtureTest, TestBase):
ops = self._fixture(m1, m2, return_ops=True)
assert "autoincrement" not in ops.ops[0].ops[0].kw
- @config.requirements.fail_before_sqla_110
def test_alter_column_autoincrement_nonpk_explicit_true(self):
m1 = MetaData()
m2 = MetaData()
diff --git a/tests/test_autogen_indexes.py b/tests/test_autogen_indexes.py
index 12f705f..c8eaffb 100644
--- a/tests/test_autogen_indexes.py
+++ b/tests/test_autogen_indexes.py
@@ -536,9 +536,6 @@ class AutogenerateUniqueIndexTest(AutogenFixtureTest, TestBase):
set([diffs[0][1].name, diffs[1][1].name]), set(["xy_idx", "y_idx"])
)
- # this simply doesn't fully work before we had
- # effective deduping of indexes/uniques.
- @config.requirements.sqlalchemy_100
def test_drop_table_w_uq_constraint(self):
m1 = MetaData()
m2 = MetaData()
@@ -830,7 +827,6 @@ class PGUniqueIndexTest(AutogenerateUniqueIndexTest):
diffs = self._fixture(m1, m2, include_schemas=True)
eq_(diffs, [])
- @config.requirements.sqlalchemy_100
@config.requirements.btree_gist
def test_exclude_const_unchanged(self):
from sqlalchemy.dialects.postgresql import TSRANGE, ExcludeConstraint
diff --git a/tests/test_autogen_render.py b/tests/test_autogen_render.py
index d17ed28..037a05f 100644
--- a/tests/test_autogen_render.py
+++ b/tests/test_autogen_render.py
@@ -181,18 +181,11 @@ class AutogenRenderTest(TestBase):
idx = Index("test_lower_code_idx", cast(t.c.code, String))
op_obj = ops.CreateIndexOp.from_index(idx)
- if config.requirements.sqlalchemy_110.enabled:
- eq_ignore_whitespace(
- autogenerate.render_op_text(self.autogen_context, op_obj),
- "op.create_index('test_lower_code_idx', 'test', "
- "[sa.text(!U'CAST(code AS VARCHAR)')], unique=False)",
- )
- else:
- eq_ignore_whitespace(
- autogenerate.render_op_text(self.autogen_context, op_obj),
- "op.create_index('test_lower_code_idx', 'test', "
- "[sa.text(!U'CAST(code AS VARCHAR)')], unique=False)",
- )
+ eq_ignore_whitespace(
+ autogenerate.render_op_text(self.autogen_context, op_obj),
+ "op.create_index('test_lower_code_idx', 'test', "
+ "[sa.text(!U'CAST(code AS VARCHAR)')], unique=False)",
+ )
def test_render_add_index_desc(self):
m = MetaData()
@@ -1035,7 +1028,6 @@ class AutogenRenderTest(TestBase):
"sa.PrimaryKeyConstraint('x'))",
)
- @config.requirements.fail_before_sqla_110
def test_render_table_w_autoincrement(self):
m = MetaData()
t = Table(
@@ -1164,7 +1156,7 @@ class AutogenRenderTest(TestBase):
result,
"sa.Column('some_key', sa.Integer(), "
"nullable=True, "
- "comment=\"This is a john's comment\")",
+ 'comment="This is a john\'s comment")',
)
def test_render_col_autoinc_false_mysql(self):
@@ -1587,7 +1579,6 @@ class AutogenRenderTest(TestBase):
"sa.Enum('one', 'two', 'three')",
)
- @config.requirements.sqlalchemy_099
def test_render_non_native_enum(self):
eq_ignore_whitespace(
autogenerate.render._repr_type(
@@ -1604,7 +1595,6 @@ class AutogenRenderTest(TestBase):
"sa.Integer()",
)
- @config.requirements.sqlalchemy_110
def test_generic_array_type(self):
eq_ignore_whitespace(
@@ -1621,7 +1611,6 @@ class AutogenRenderTest(TestBase):
"sa.ARRAY(sa.DateTime(timezone=True))",
)
- @config.requirements.sqlalchemy_110
def test_render_array_no_context(self):
uo = ops.UpgradeOps(
ops=[
@@ -1908,8 +1897,6 @@ class AutogenRenderTest(TestBase):
class RenderNamingConventionTest(TestBase):
- __requires__ = ("sqlalchemy_094",)
-
def setUp(self):
convention = {
diff --git a/tests/test_batch.py b/tests/test_batch.py
index c8c5b33..5454771 100644
--- a/tests/test_batch.py
+++ b/tests/test_batch.py
@@ -1438,7 +1438,6 @@ class BatchRoundTripTest(TestBase):
{"id": 6, "data": 5, "x": -2},
)
- @config.requirements.sqlalchemy_094
@config.requirements.unnamed_constraints
def test_drop_foreign_key(self):
bar = Table(
diff --git a/tests/test_mssql.py b/tests/test_mssql.py
index 23577b0..9418271 100644
--- a/tests/test_mssql.py
+++ b/tests/test_mssql.py
@@ -21,10 +21,7 @@ class FullEnvironmentTests(TestBase):
@classmethod
def setup_class(cls):
staging_env()
- if util.sqla_105:
- directives = "sqlalchemy.legacy_schema_aliasing=false"
- else:
- directives = ""
+ directives = "sqlalchemy.legacy_schema_aliasing=false"
cls.cfg = cfg = _no_sql_testing_config("mssql", directives)
cls.a, cls.b, cls.c = three_rev_fixture(cfg)
diff --git a/tests/test_op.py b/tests/test_op.py
index 0312790..26feeb4 100644
--- a/tests/test_op.py
+++ b/tests/test_op.py
@@ -10,6 +10,7 @@ from sqlalchemy import Table
from sqlalchemy.sql import column
from sqlalchemy.sql import func
from sqlalchemy.sql import text
+from sqlalchemy.sql.schema import quoted_name
from alembic import op
from alembic.operations import ops
@@ -53,7 +54,6 @@ class OpTest(TestBase):
)
def test_add_column_schema_hard_quoting(self):
- from sqlalchemy.sql.schema import quoted_name
context = op_fixture("postgresql")
op.add_column(
@@ -67,7 +67,6 @@ class OpTest(TestBase):
)
def test_rename_table_schema_hard_quoting(self):
- from sqlalchemy.sql.schema import quoted_name
context = op_fixture("postgresql")
op.rename_table(
@@ -77,7 +76,6 @@ class OpTest(TestBase):
context.assert_('ALTER TABLE "some.schema".t1 RENAME TO t2')
def test_add_constraint_schema_hard_quoting(self):
- from sqlalchemy.sql.schema import quoted_name
context = op_fixture("postgresql")
op.create_check_constraint(
diff --git a/tests/test_op_naming_convention.py b/tests/test_op_naming_convention.py
index 88c761f..b83b3af 100644
--- a/tests/test_op_naming_convention.py
+++ b/tests/test_op_naming_convention.py
@@ -13,8 +13,6 @@ from alembic.testing.fixtures import TestBase
class AutoNamingConventionTest(TestBase):
- __requires__ = ("sqlalchemy_094",)
-
def test_add_check_constraint(self):
context = op_fixture(
naming_convention={"ck": "ck_%(table_name)s_%(constraint_name)s"}
diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py
index 6c9838a..13ced30 100644
--- a/tests/test_postgresql.py
+++ b/tests/test_postgresql.py
@@ -16,6 +16,9 @@ from sqlalchemy import text
from sqlalchemy import types
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.dialects.postgresql import BYTEA
+from sqlalchemy.dialects.postgresql import HSTORE
+from sqlalchemy.dialects.postgresql import JSON
+from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.engine.reflection import Inspector
from sqlalchemy.sql import column
@@ -47,10 +50,6 @@ from alembic.testing.fixtures import op_fixture
from alembic.testing.fixtures import TestBase
-if util.sqla_09:
- from sqlalchemy.dialects.postgresql import JSON, JSONB, HSTORE
-
-
class PostgresqlOpTest(TestBase):
def test_rename_table_postgresql(self):
context = op_fixture("postgresql")
@@ -88,7 +87,6 @@ class PostgresqlOpTest(TestBase):
"WHERE locations.coordinates != Null"
)
- @config.requirements.fail_before_sqla_099
def test_create_index_postgresql_concurrently(self):
context = op_fixture("postgresql")
op.create_index(
@@ -101,7 +99,6 @@ class PostgresqlOpTest(TestBase):
"CREATE INDEX CONCURRENTLY geocoded ON locations (coordinates)"
)
- @config.requirements.fail_before_sqla_110
def test_drop_index_postgresql_concurrently(self):
context = op_fixture("postgresql")
op.drop_index("geocoded", "locations", postgresql_concurrently=True)
@@ -119,7 +116,6 @@ class PostgresqlOpTest(TestBase):
op.add_column("some_table", Column("q", Integer, primary_key=True))
context.assert_("ALTER TABLE some_table ADD COLUMN q SERIAL NOT NULL")
- @config.requirements.fail_before_sqla_100
def test_create_exclude_constraint(self):
context = op_fixture("postgresql")
op.create_exclude_constraint(
@@ -130,7 +126,6 @@ class PostgresqlOpTest(TestBase):
"WHERE (x > 5)"
)
- @config.requirements.fail_before_sqla_100
def test_create_exclude_constraint_quoted_literal(self):
context = op_fixture("postgresql")
op.create_exclude_constraint(
@@ -145,7 +140,6 @@ class PostgresqlOpTest(TestBase):
'("SomeColumn" WITH >) WHERE ("SomeColumn" > 5)'
)
- @config.requirements.fail_before_sqla_1010
def test_create_exclude_constraint_quoted_column(self):
context = op_fixture("postgresql")
op.create_exclude_constraint(
@@ -535,7 +529,6 @@ class PostgresqlDefaultCompareTest(TestBase):
DateTime(), text("TIMEZONE('utc', CURRENT_TIMESTAMP)")
)
- @config.requirements.sqlalchemy_10
def test_compare_current_timestamp_fn_w_binds(self):
self._compare_default_roundtrip(
DateTime(), func.timezone("utc", func.current_timestamp())
@@ -799,7 +792,6 @@ class PostgresqlAutogenRenderTest(TestBase):
in self.autogen_context.imports
)
- @config.requirements.sqlalchemy_110
def test_postgresql_hstore_subtypes(self):
eq_ignore_whitespace(
autogenerate.render._repr_type(HSTORE(), self.autogen_context),
@@ -825,7 +817,6 @@ class PostgresqlAutogenRenderTest(TestBase):
in self.autogen_context.imports
)
- @config.requirements.sqlalchemy_110
def test_generic_array_type(self):
eq_ignore_whitespace(
@@ -876,7 +867,6 @@ class PostgresqlAutogenRenderTest(TestBase):
"postgresql.ARRAY(foobar.MYVARCHAR)",
)
- @config.requirements.fail_before_sqla_100
def test_add_exclude_constraint(self):
from sqlalchemy.dialects.postgresql import ExcludeConstraint
@@ -898,7 +888,6 @@ class PostgresqlAutogenRenderTest(TestBase):
"where=sa.text(!U'x != 2'), using='gist')",
)
- @config.requirements.fail_before_sqla_100
def test_add_exclude_constraint_case_sensitive(self):
from sqlalchemy.dialects.postgresql import ExcludeConstraint
@@ -925,7 +914,6 @@ class PostgresqlAutogenRenderTest(TestBase):
"where=sa.text(!U'\"XColumn\" != 2'), using='gist')",
)
- @config.requirements.fail_before_sqla_100
def test_inline_exclude_constraint(self):
from sqlalchemy.dialects.postgresql import ExcludeConstraint
@@ -956,7 +944,6 @@ class PostgresqlAutogenRenderTest(TestBase):
")",
)
- @config.requirements.fail_before_sqla_100
def test_inline_exclude_constraint_case_sensitive(self):
from sqlalchemy.dialects.postgresql import ExcludeConstraint
@@ -986,25 +973,13 @@ class PostgresqlAutogenRenderTest(TestBase):
)
def test_json_type(self):
- if config.requirements.sqlalchemy_110.enabled:
- eq_ignore_whitespace(
- autogenerate.render._repr_type(JSON(), self.autogen_context),
- "postgresql.JSON(astext_type=sa.Text())",
- )
- else:
- eq_ignore_whitespace(
- autogenerate.render._repr_type(JSON(), self.autogen_context),
- "postgresql.JSON()",
- )
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(JSON(), self.autogen_context),
+ "postgresql.JSON(astext_type=sa.Text())",
+ )
def test_jsonb_type(self):
- if config.requirements.sqlalchemy_110.enabled:
- eq_ignore_whitespace(
- autogenerate.render._repr_type(JSONB(), self.autogen_context),
- "postgresql.JSONB(astext_type=sa.Text())",
- )
- else:
- eq_ignore_whitespace(
- autogenerate.render._repr_type(JSONB(), self.autogen_context),
- "postgresql.JSONB()",
- )
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(JSONB(), self.autogen_context),
+ "postgresql.JSONB(astext_type=sa.Text())",
+ )
diff --git a/tox.ini b/tox.ini
index bfe1d6a..a6fd78c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,8 +1,6 @@
[tox]
-# current mysqlclient fails with <=SQLA 0.9 on py3k due to
-# old unicode statements flag
-envlist = py{27,34,35,36,37}-sqla{10,11,12,13,master}, py{27}-sqla{079,084,09}
+envlist = py{27,34,35,36,37}-sqla{11,12,13,master}
SQLA_REPO = {env:SQLA_REPO:git+https://github.com/sqlalchemy/sqlalchemy.git}
@@ -12,10 +10,6 @@ cov_args=--cov=alembic --cov-report term --cov-report xml
deps=pytest!=3.9.1,!=3.9.2
pytest-xdist
mock
- sqla079: {[tox]SQLA_REPO}@rel_0_7_9
- sqla084: {[tox]SQLA_REPO}@rel_0_8_4
- sqla09: {[tox]SQLA_REPO}@rel_0_9
- sqla10: {[tox]SQLA_REPO}@rel_1_0
sqla11: {[tox]SQLA_REPO}@rel_1_1
sqla12: {[tox]SQLA_REPO}@rel_1_2
sqla13: {[tox]SQLA_REPO}@rel_1_3