diff --git a/.coveragerc b/.coveragerc index 7c7a536..9d0ba33 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,8 @@ [run] source = picklefield branch = True + +[report] +exclude_lines = + pragma: no cover + except ImportError: diff --git a/.travis.yml b/.travis.yml index ce8b071..614c4b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,54 +1,23 @@ -dist: trusty +dist: bionic sudo: false language: python -env: - matrix: - - TOXENV=flake8 - - TOXENV=isort cache: pip -matrix: +python: + - 2.7 + - 3.6 + - 3.7 + - 3.8 +stages: + - lint + - test +jobs: fast_finish: true include: - - python: 2.7 - env: TOXENV=py27-1.11 - - python: 3.4 - env: TOXENV=py34-1.11 - - python: 3.4 - env: TOXENV=py34-2.0 - - python: 3.5 - env: TOXENV=py35-1.11 - - python: 3.5 - env: TOXENV=py35-2.0 - - python: 3.5 - env: TOXENV=py35-2.1 - - python: 3.5 - env: TOXENV=py35-master - - python: 3.6 - env: TOXENV=py36-1.11 - - python: 3.6 - env: TOXENV=py36-2.0 - - python: 3.6 - env: TOXENV=py36-2.1 - - python: 3.6 - env: TOXENV=py36-master - - python: 3.7 - env: TOXENV=py37-2.0 - dist: xenial - sudo: true - - python: 3.7 - env: TOXENV=py37-2.1 - dist: xenial - sudo: true - - python: 3.7 - env: TOXENV=py37-master - dist: xenial - sudo: true - allow_failures: - - env: TOXENV=py35-master - - env: TOXENV=py36-master - - env: TOXENV=py37-master + - { stage: lint, env: TOXENV=flake8, python: 3.6 } + - { stage: lint, env: TOXENV=isort, python: 3.6 } + install: -- pip install tox coveralls +- pip install tox coveralls tox-travis script: - tox after_success: diff --git a/picklefield/fields.py b/picklefield/fields.py index ef811d3..9fa18c1 100644 --- a/picklefield/fields.py +++ b/picklefield/fields.py @@ -4,7 +4,6 @@ from copy import deepcopy from zlib import compress, decompress -import django from django.core import checks from django.db import models from django.utils.encoding import force_text @@ -12,9 +11,9 @@ from .constants import DEFAULT_PROTOCOL try: - from cPickle import loads, dumps + from cPickle import loads, dumps # pragma: no cover except ImportError: - from pickle import loads, dumps + from pickle import loads, dumps # pragma: no cover class PickledObject(str): @@ -158,7 +157,7 @@ def to_python(self, value): value = dbsafe_decode(value, self.compress) except Exception: # If the value is a definite pickle; and an error is raised in - # de-pickling it should be allowed to propogate. + # de-pickling it should be allowed to propagate. if isinstance(value, PickledObject): raise else: @@ -170,12 +169,8 @@ def pre_save(self, model_instance, add): value = super(PickledObjectField, self).pre_save(model_instance, add) return wrap_conflictual_object(value) - if django.VERSION < (2, 0): - def from_db_value(self, value, expression, connection, context): - return self.to_python(value) - else: - def from_db_value(self, value, expression, connection): - return self.to_python(value) + def from_db_value(self, value, expression, connection, context=None): + return self.to_python(value) def get_db_prep_value(self, value, connection=None, prepared=False): """ diff --git a/setup.py b/setup.py index 68fa527..cd6af00 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ 'Framework :: Django :: 1.11', 'Framework :: Django :: 2.0', 'Framework :: Django :: 2.1', + 'Framework :: Django :: 2.2', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', diff --git a/tests/models.py b/tests/models.py index cc10344..3c80b6e 100644 --- a/tests/models.py +++ b/tests/models.py @@ -27,6 +27,7 @@ class TestingModel(models.Model): default_pickle_field = PickledObjectField(default=(D1, S1, T1, L1)) callable_pickle_field = PickledObjectField(default=date.today) non_copying_field = PickledObjectField(copy=False, default=TestCopyDataType('boom!')) + nullable_pickle_field = PickledObjectField(null=True) class MinimalTestingModel(models.Model): diff --git a/tests/tests.py b/tests/tests.py index 6afe920..d08bf9d 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -14,6 +14,11 @@ TestCustomDataType, TestingModel, ) +try: + from unittest.mock import patch # pragma: no cover +except ImportError: + from mock import patch # pragma: no cover + class PickledObjectFieldTests(TestCase): def setUp(self): @@ -35,6 +40,7 @@ def test_data_integrity(self): # the same data, even thought it's stored differently in the DB. self.assertEqual(value, model_test.pickle_field) self.assertEqual(value, model_test.compressed_pickle_field) + self.assertEqual(None, model_test.nullable_pickle_field) # Make sure we can also retrieve the model model_test.save() model_test.delete() @@ -181,6 +187,21 @@ def test_empty_strings_not_allowed(self): with self.assertRaises(IntegrityError): MinimalTestingModel.objects.create() + def test_decode_error(self): + def mock_decode_error(*args, **kwargs): + raise Exception() + + model = MinimalTestingModel.objects.create(pickle_field={'foo': 'bar'}) + model.save() + + self.assertEqual( + {'foo': 'bar'}, MinimalTestingModel.objects.get(pk=model.pk).pickle_field + ) + + with patch('picklefield.fields.dbsafe_decode', mock_decode_error): + encoded_value = dbsafe_encode({'foo': 'bar'}) + self.assertEqual(encoded_value, MinimalTestingModel.objects.get(pk=model.pk).pickle_field) + @isolate_apps('tests') class PickledObjectFieldCheckTests(SimpleTestCase): diff --git a/tox.ini b/tox.ini index fcd5535..81fa908 100644 --- a/tox.ini +++ b/tox.ini @@ -4,28 +4,32 @@ args_are_paths = false envlist = flake8, isort, - py27-1.11, - py34-{1.11,2.0}, - py35-{1.11,2.0,2.1,master}, - py36-{1.11,2.0,2.1,master}, - py37-{1.11,2.0,2.1,master} + py{27,36,37,38}-1.11, + py{36,37,38}-{2.0,2.1,2.2,master} + +[tox:travis] +2.7 = py27 +3.6 = py36 +3.7 = py37 +3.8 = py38 [testenv] basepython = py27: python2.7 - py34: python3.4 - py35: python3.5 py36: python3.6 py37: python3.7 + py38: python3.8 usedevelop = true commands = - {envpython} -R -Wonce {envbindir}/coverage run -m django test -v2 --settings=tests.settings {posargs} - coverage report + {envpython} -R -Wonce {envbindir}/coverage run --branch -m django test -v2 --settings=tests.settings {posargs} + coverage report -m deps = + py27: mock coverage 1.11: Django>=1.11,<2.0 2.0: Django>=2.0,<2.1 2.1: Django>=2.1,<2.2 + 2.2: Django>=2.2,<3.0 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8]