-
Notifications
You must be signed in to change notification settings - Fork 19
Compare Enum types #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Makefile
Outdated
@@ -3,7 +3,7 @@ | |||
test: flake8 pylint pytest | |||
|
|||
pylint: | |||
pylint sqlalchemydiff -E | |||
pylint sqlalchemydiff -E --disable=E1102 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without:
************* Module sqlalchemydiff.comparer
E:137,22: inspector.get_table_names is not callable (not-callable)
E:200, 8: ignore_manager.get is not callable (not-callable)
E:207, 8: ignore_manager.get is not callable (not-callable)
E:214, 8: ignore_manager.get is not callable (not-callable)
E:221, 8: ignore_manager.get is not callable (not-callable)
E:228, 8: ignore_manager.get is not callable (not-callable)
E:289,11: inspector.get_foreign_keys is not callable (not-callable)
E:309,11: inspector.get_primary_keys is not callable (not-callable)
E:327,11: inspector.get_indexes is not callable (not-callable)
E:349,11: inspector.get_columns is not callable (not-callable)
E:373,15: inspector.get_check_constraints is not callable (not-callable)
E:394,15: inspector.get_enums is not callable (not-callable)
E:420,11: type_.compile is not callable (not-callable)
E:448,30: errors['tables_data'].setdefault is not callable (not-callable)
E:449,20: table_d.setdefault is not callable (not-callable)
************* Module sqlalchemydiff.util
E: 71,12: stream.write is not callable (not-callable)
E: 98,14: uri.rsplit is not callable (not-callable)
E:106, 4: sqlalchemy_base.metadata.create_all is not callable (not-callable)
E:124,27: data.strip is not callable (not-callable)
E:138,15: data.count is not callable (not-callable)
E:145,15: data.split is not callable (not-callable)
E:152,16: item.strip is not callable (not-callable)
E:152,41: data.split is not callable (not-callable)
@charness this looks awesome! Sorry to leave this PR hanging for such a long time. I've just updated the travis configuration so the tests run on their new infrastructure. Do you want to re-up this branch so we can get it merged? |
Compare enum types and/or check constraints where supported by the database.
Besides the test changes themselves, - Update mysql-connector-python to 2.1.6 that's currently available from Oracle. - Suppress "not-callable" errors from pylint. - Fix flake8 indentation errors.
Thanks for the update. It was an easy rebase. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@charness This looks great! Sorry it's taken me so long to get to it.
There's just a couple of suggestions and we have a failing test.
|
||
def _get_constraints_data(inspector, table_name): | ||
try: | ||
return inspector.get_check_constraints(table_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is new in sqlalchemy 1.1.0 - could you add the sqlalchemy>=1.1.0
requirement to setup.py?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, instead, catch AttributeError
alongside NotImplementedError
and return the same empty list? (A comment mentioning 1.1.0, similar to the one you request below for get_enums
, would make sense.)
I think better to leave the sqlalchemy version unconstrained if doing so requires so little.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yes - good idea! - let's do the error catching instead.
@@ -215,6 +248,8 @@ def test_errors_dict_catches_all_differences(uri_left, uri_right): | |||
} | |||
} | |||
}, | |||
'enums': { | |||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm seeing an error when running this test:
test/endtoend/test_example.py:261:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test/endtoend/test_example.py:297: in compare_error_dicts
assert_items_equal(walk_dict(err1, path), walk_dict(err2, path))
../../.pyenv/versions/3.4.4/lib/python3.4/unittest/case.py:1153: in assertCountEqual
self.fail(msg)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <unittest.case.TestCase testMethod=runTest>
msg = "Element counts were not equal:\nFirst has 1, Second has 0: {'key': 'name', 'right': {'type': 'VARCHAR(200)', 'name':...llable': True}, 'left': {'type': 'VARCHAR(200)', 'name': 'name', 'default': None, 'comment': None, 'nullable': False}}"
def fail(self, msg=None):
"""Fail immediately, with the given message."""
> raise self.failureException(msg)
E AssertionError: Element counts were not equal:
E First has 1, Second has 0: {'key': 'name', 'right': {'type': 'VARCHAR(200)', 'name': 'name', 'default': None, 'nullable': True}, 'left': {'type': 'VARCHAR(200)', 'name': 'name', 'default': None, 'nullable': False}}
E First has 0, Second has 1: {'key': 'name', 'right': {'type': 'VARCHAR(200)', 'name': 'name', 'default': None, 'comment': None, 'nullable': True}, 'left': {'type': 'VARCHAR(200)', 'name': 'name', 'default': None, 'comment': None, 'nullable': False}}
It looks like we are now getting an extra 'comment': None
field - is this expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(TIL) Comment support is new in SQLAlchemy 1.2.0: http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-1546
The change adds a dialect.supports_comments
.
I think the most straightforward way to go here is to key off that attribute (e.g., getattr(dialect, 'supports_comments', False)
) and add 'comment': None
to the expected errors when supported.
Perhaps, though, test_errors_dict_catches_all_differences
should compare diff
results more selectively? Check that a given key's value differs as expected between left and right, and that the rest match. That would ignore the matching, but unexpected, "comment"
.
That selectivity should also help make the tests work for a dialect other than mysql (e.g., postgresql), though left_only and right_only would still fail without further work.
Your thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree it would be better to be more selective in the assertions rather than comparing the whole result. But if that's too big a change, your first suggestion will do the job well enough for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, I've implemented the simpler change. I didn't want the more-directed tests to hold up this PR.
sqlalchemydiff/comparer.py
Outdated
|
||
def _get_enums_data(inspector): | ||
try: | ||
return inspector.get_enums() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps we could add a comment here mentioning that this is specific to the postgres inspector?
Explain the differences in comments and tolerate via try...except.
SQLAlchemy 1.2.0 added support for SQL comments. Since the test example models include no comments, these come back as None in the inspection output. When the dialect indicates it `supports_comments` (a new attribute in 1.2.0), add `"comment": None` to each column in the expected_errors.
SQLAlchemy supports PEP 435 Enum classes as of 1.1. In order to exercise get_check_constraints-related code aimed at < 1.1.0, adapt the Polarity (native enum) columns to the 1.0 Enum API when using 1.0.
Testing with sqlalchemy 1.2 and mysql raises neither the AttributeError nor the NotImplementedError in _get_constraints_data. Disable coverage checking for the except clause so tests can pass.
Thanks @charness for the contribution! I'll release this in the next cut |
Add checks for changes to SQLAlchemy Enums, implemented as native enum types and/or as check constraints.
In our use case, we failed to catch missing migrations for enum columns.
I've verified these changes locally against PostgreSQL, as well, but haven't yet found a clean way to abstract the differences in
expected_errors
for MySQL vs PostgreSQL.