diff --git a/setup.py b/setup.py index 171d271..8edebe2 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ extras_require = { 'Jinja2>=2.3', 'docutils>=0.10', 'flexmock>=0.9.7', + 'mock==2.0.0', 'psycopg2>=2.5.1', 'pytz>=2014.2', 'python-dateutil>=2.2', diff --git a/sqlalchemy_utils/types/password.py b/sqlalchemy_utils/types/password.py index aa39c3d..020d107 100644 --- a/sqlalchemy_utils/types/password.py +++ b/sqlalchemy_utils/types/password.py @@ -155,11 +155,15 @@ class PasswordType(types.TypeDecorator, ScalarCoercible): # Construct the passlib crypt context. self.context = LazyCryptContext(**kwargs) - if max_length is None: - max_length = self.calculate_max_length() + self._max_length = max_length - # Set the length to the now-calculated max length. - self.length = max_length + @property + def length(self): + """Get column length.""" + if self._max_length is None: + self._max_length = self.calculate_max_length() + + return self._max_length def calculate_max_length(self): # Calculate the largest possible encoded password. diff --git a/tests/types/test_password.py b/tests/types/test_password.py index 641f5af..f26f8d7 100644 --- a/tests/types/test_password.py +++ b/tests/types/test_password.py @@ -1,3 +1,4 @@ +import mock import pytest import sqlalchemy as sa from sqlalchemy import inspect @@ -217,3 +218,19 @@ class TestPasswordType(object): obj = User() obj.password = b'b' assert obj.password.hash.decode('utf8').startswith('$pbkdf2-sha256$') + + @pytest.mark.parametrize('max_length', [1, 103]) + def test_constant_length(self, max_length): + """ + Test that constant max_length is applied. + """ + typ = PasswordType(max_length=max_length) + assert typ.length == max_length + + def test_context_is_lazy(self): + """ + Make sure the init doesn't evaluate the lazy context. + """ + onload = mock.Mock(return_value={}) + PasswordType(onload=onload) + assert not onload.called