From 6c687e99a3f87b00d4e438e2ee3196591231418b Mon Sep 17 00:00:00 2001 From: Matthew Schinckel Date: Mon, 8 Nov 2010 14:49:26 +1030 Subject: [PATCH 01/85] Added connection argument to get_db_prep_value --- src/picklefield/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 0e2de16..61d26da 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -108,7 +108,7 @@ def to_python(self, value): raise return value - def get_db_prep_value(self, value): + def get_db_prep_value(self, value, connection=None): """ Pickle and b64encode the object, optionally compressing it. From e43faf4d3f963c1a7632688ed45732ab2e724485 Mon Sep 17 00:00:00 2001 From: Matthew Schinckel Date: Mon, 8 Nov 2010 14:52:42 +1030 Subject: [PATCH 02/85] Added prepared argument to get_db_prep_value --- src/picklefield/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 61d26da..38d19ce 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -108,7 +108,7 @@ def to_python(self, value): raise return value - def get_db_prep_value(self, value, connection=None): + def get_db_prep_value(self, value, connection=None, prepared=None): """ Pickle and b64encode the object, optionally compressing it. From 044a83db56d6df12021daf22e344067dcf6e5bfb Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Wed, 10 Nov 2010 08:59:19 +0200 Subject: [PATCH 03/85] Cosmetic fix: the interface default for `prepared` in get_db_prep_value is False. --- src/picklefield/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 38d19ce..3bc78bf 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -108,7 +108,7 @@ def to_python(self, value): raise return value - def get_db_prep_value(self, value, connection=None, prepared=None): + def get_db_prep_value(self, value, connection=None, prepared=False): """ Pickle and b64encode the object, optionally compressing it. From 23fbafc2825cbc89063625a3b96bdcf9bda52f72 Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Wed, 10 Nov 2010 09:01:13 +0200 Subject: [PATCH 04/85] Updated changelog, bumped version to 0.1.9. --- README | 6 ++++++ setup.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README b/README index fb3f937..077c4c8 100644 --- a/README +++ b/README @@ -114,6 +114,12 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 0.1.9 +======================== + + * Added `connection` and `prepared` parameters to `get_db_prep_value()` too + (thanks to Matthew Schinckel). + Changes in version 0.1.8 ======================== diff --git a/setup.py b/setup.py index 1655ef8..1b5ef3a 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ from setuptools import setup, find_packages setup(name='django-picklefield', - version='0.1.8', + version='0.1.9', description='Pickled object field for Django', long_description=open('README').read(), author='Gintautas Miliauskas', From d64acf36386457a836474e7ee4aad80f5a8cd1b8 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 2 Feb 2012 16:26:27 -0500 Subject: [PATCH 05/85] Make sure objects such as `django.db.Model` subclasses can be pickled correctly and that `callable` objects can be wrapped to be retreived using the ORM. Prior to this patch attempting to update a Model instance with a `PickledObjectField` referencing a `django.db.Model` subclass (or any object with the `prepare_database_save`) property raised a `TypeError` and there was no way to retreive a stored object that was `callable` since the ORM would try to filter on the actual return value of the object instead of the object instead (this is a desired and documented behavior to allow `Model.objects.filter(datetime_field__gte=datetime.now)` to work correctly and is especially usefull when defining `limit_choices_to`). --- src/picklefield/fields.py | 24 ++++++++++++++++++++++++ src/picklefield/tests.py | 12 +++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 3bc78bf..5f7d7dc 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -27,6 +27,23 @@ class PickledObject(str): """ +class ObjectWrapper(object): + """ + A class used to wrap object that have properties that may clash with the + ORM internals. + + For example, objects with the `prepare_database_save` property such as + `django.db.Model` subclasses won't work under certain conditions and the + same apply for trying to retrieve any `callable` object. + """ + + def __init__(self, obj): + self._obj = obj + +def wrap_conflictual_object(obj): + if hasattr(obj, 'prepare_database_save') or callable(obj): + obj = ObjectWrapper(obj) + return obj def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL): # We use deepcopy() here to avoid a problem with cPickle, where dumps @@ -106,8 +123,15 @@ def to_python(self, value): # de-pickling it should be allowed to propogate. if isinstance(value, PickledObject): raise + else: + if isinstance(value, ObjectWrapper): + return value._obj return value + def pre_save(self, model_instance, add): + value = super(PickledObjectField, self).pre_save(model_instance, add) + return wrap_conflictual_object(value) + def get_db_prep_value(self, value, connection=None, prepared=False): """ Pickle and b64encode the object, optionally compressing it. diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index a18bb68..ebb98a3 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -3,7 +3,7 @@ from django.test import TestCase from django.db import models from django.core import serializers -from picklefield.fields import PickledObjectField +from picklefield.fields import PickledObjectField, wrap_conflictual_object class TestingModel(models.Model): pickle_field = PickledObjectField() @@ -24,6 +24,7 @@ def setUp(self): (1, 2, 3, 4, 5), [1, 2, 3, 4, 5], TestCustomDataType('Hello World'), + MinimalTestingModel, ) return super(PickledObjectFieldTests, self).setUp() @@ -41,6 +42,8 @@ def testDataIntegriry(self): # the same data, even thought it's stored differently in the DB. self.assertEquals(value, model_test.pickle_field) self.assertEquals(value, model_test.compressed_pickle_field) + # Make sure we can also retreive the model + model_test.save() model_test.delete() # Make sure the default value for default_pickled_field gets stored @@ -110,11 +113,14 @@ def testLookups(self): model_test.save() # Make sure that we can do an ``exact`` lookup by both the # pickle_field and the compressed_pickle_field. - model_test = TestingModel.objects.get(pickle_field__exact=value, compressed_pickle_field__exact=value) + wrapped_value = wrap_conflictual_object(value) + model_test = TestingModel.objects.get(pickle_field__exact=wrapped_value, + compressed_pickle_field__exact=wrapped_value) self.assertEquals(value, model_test.pickle_field) self.assertEquals(value, model_test.compressed_pickle_field) # Make sure that ``in`` lookups also work correctly. - model_test = TestingModel.objects.get(pickle_field__in=[value], compressed_pickle_field__in=[value]) + model_test = TestingModel.objects.get(pickle_field__in=[wrapped_value], + compressed_pickle_field__in=[wrapped_value]) self.assertEquals(value, model_test.pickle_field) self.assertEquals(value, model_test.compressed_pickle_field) # Make sure that ``is_null`` lookups are working. From 6e501763650d581dd29f63f7965b73523cee66d1 Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Sat, 18 Feb 2012 01:10:30 +0100 Subject: [PATCH 06/85] Use optimize() instead of deepcopy. Updated tests. --- src/picklefield/fields.py | 42 ++++++++++++++++++++++++++++++++++++--- src/picklefield/tests.py | 21 +++++--------------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 5f7d7dc..df9c0e9 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -3,14 +3,46 @@ from copy import deepcopy from base64 import b64encode, b64decode from zlib import compress, decompress +import pickletools try: from cPickle import loads, dumps except ImportError: from pickle import loads, dumps +try: + from pickletools import optimize +except ImportError: + # python2.5 does not provide pickletools.optimize + from pickletools import genops + def optimize(p): + 'Optimize a pickle string by removing unused PUT opcodes' + gets = set() # set of args used by a GET opcode + puts = [] # (arg, startpos, stoppos) for the PUT opcodes + prevpos = None # set to pos if previous opcode was a PUT + for opcode, arg, pos in genops(p): + if prevpos is not None: + puts.append((prevarg, prevpos, pos)) + prevpos = None + if 'PUT' in opcode.name: + prevarg, prevpos = arg, pos + elif 'GET' in opcode.name: + gets.add(arg) + + # Copy the pickle string except for PUTS without a corresponding GET + s = [] + i = 0 + for arg, start, stop in puts: + j = stop if (arg in gets) else start + s.append(p[i:j]) + i = stop + s.append(p[i:]) + return ''.join(s) + from django.db import models from django.utils.encoding import force_unicode + + from picklefield import DEFAULT_PROTOCOL class PickledObject(str): @@ -46,16 +78,16 @@ def wrap_conflictual_object(obj): return obj def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL): - # We use deepcopy() here to avoid a problem with cPickle, where dumps + # We use pickletools.optimize() here to avoid a problem with cPickle, where dumps # can generate different character streams for same lookup value if # they are referenced differently. # The reason this is important is because we do all of our lookups as # simple string matches, thus the character streams must be the same # for the lookups to work properly. See tests.py for more information. if not compress_object: - value = b64encode(dumps(deepcopy(value), pickle_protocol)) + value = b64encode(optimize(dumps(value, pickle_protocol))) else: - value = b64encode(compress(dumps(deepcopy(value), pickle_protocol))) + value = b64encode(compress(optimize(dumps(value, pickle_protocol)))) return PickledObject(value) @@ -182,3 +214,7 @@ def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False pass else: add_introspection_rules([], [r"^picklefield\.fields\.PickledObjectField"]) + + + +####################################################################### diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index ebb98a3..5b8e3a9 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -82,30 +82,19 @@ def testLookups(self): >>> # But with cPickle the two dumps() are not the same! >>> # Both will generate the same object when loads() is called though. - We can solve this by calling deepcopy() on the value before + We can solve this by calling optimize() on the value after pickling it, as this copies everything to a brand new data structure. >>> from cPickle import dumps - >>> from copy import deepcopy + >>> from picklefield.fields import optimize >>> t = ({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]) - >>> dumps(deepcopy(({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]))) + >>> optimize(dumps(({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5])))) "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." >>> dumps(deepcopy(t)) "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." - >>> # Using deepcopy() beforehand means that now both dumps() are idential. - >>> # It may not be necessary, but deepcopy() ensures that lookups will always work. - - Unfortunately calling copy() alone doesn't seem to fix the - problem as it lies primarily with complex data types. - - >>> from cPickle import dumps - >>> from copy import copy - >>> t = ({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]) - >>> dumps(copy(({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]))) - "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." - >>> dumps(copy(t)) - "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\n(I1\nI2\nI3\nI4\nI5\nt(lp2\nI1\naI2\naI3\naI4\naI5\natp3\n." + >>> # Using optimize() means that now both dumps() are idential. + >>> # It may not be necessary, but optimize() ensures that lookups will always work. """ for value in self.testing_data: From 671886f9ba5e29dd9cd5e5d2be516c19474f9b54 Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Sat, 18 Feb 2012 01:13:00 +0100 Subject: [PATCH 07/85] Updated changelog. --- README | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README b/README index 077c4c8..4dde18f 100644 --- a/README +++ b/README @@ -114,6 +114,12 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 0.2.0 +======================== + + * Allow pickling of subclasses of django.db.models.Model (thanks to Simon + Charette) + Changes in version 0.1.9 ======================== From 24ef54321a9327e9139c681d03bf404424399781 Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Sat, 18 Feb 2012 01:24:21 +0100 Subject: [PATCH 08/85] Revert "Use optimize() instead of deepcopy." This reverts commit 6e501763650d581dd29f63f7965b73523cee66d1. The change breaks tests and, by implication, backwards compatibility. --- src/picklefield/fields.py | 42 +++------------------------------------ src/picklefield/tests.py | 21 +++++++++++++++----- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index df9c0e9..5f7d7dc 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -3,46 +3,14 @@ from copy import deepcopy from base64 import b64encode, b64decode from zlib import compress, decompress -import pickletools try: from cPickle import loads, dumps except ImportError: from pickle import loads, dumps -try: - from pickletools import optimize -except ImportError: - # python2.5 does not provide pickletools.optimize - from pickletools import genops - def optimize(p): - 'Optimize a pickle string by removing unused PUT opcodes' - gets = set() # set of args used by a GET opcode - puts = [] # (arg, startpos, stoppos) for the PUT opcodes - prevpos = None # set to pos if previous opcode was a PUT - for opcode, arg, pos in genops(p): - if prevpos is not None: - puts.append((prevarg, prevpos, pos)) - prevpos = None - if 'PUT' in opcode.name: - prevarg, prevpos = arg, pos - elif 'GET' in opcode.name: - gets.add(arg) - - # Copy the pickle string except for PUTS without a corresponding GET - s = [] - i = 0 - for arg, start, stop in puts: - j = stop if (arg in gets) else start - s.append(p[i:j]) - i = stop - s.append(p[i:]) - return ''.join(s) - from django.db import models from django.utils.encoding import force_unicode - - from picklefield import DEFAULT_PROTOCOL class PickledObject(str): @@ -78,16 +46,16 @@ def wrap_conflictual_object(obj): return obj def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL): - # We use pickletools.optimize() here to avoid a problem with cPickle, where dumps + # We use deepcopy() here to avoid a problem with cPickle, where dumps # can generate different character streams for same lookup value if # they are referenced differently. # The reason this is important is because we do all of our lookups as # simple string matches, thus the character streams must be the same # for the lookups to work properly. See tests.py for more information. if not compress_object: - value = b64encode(optimize(dumps(value, pickle_protocol))) + value = b64encode(dumps(deepcopy(value), pickle_protocol)) else: - value = b64encode(compress(optimize(dumps(value, pickle_protocol)))) + value = b64encode(compress(dumps(deepcopy(value), pickle_protocol))) return PickledObject(value) @@ -214,7 +182,3 @@ def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False pass else: add_introspection_rules([], [r"^picklefield\.fields\.PickledObjectField"]) - - - -####################################################################### diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index 5b8e3a9..ebb98a3 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -82,19 +82,30 @@ def testLookups(self): >>> # But with cPickle the two dumps() are not the same! >>> # Both will generate the same object when loads() is called though. - We can solve this by calling optimize() on the value after + We can solve this by calling deepcopy() on the value before pickling it, as this copies everything to a brand new data structure. >>> from cPickle import dumps - >>> from picklefield.fields import optimize + >>> from copy import deepcopy >>> t = ({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]) - >>> optimize(dumps(({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5])))) + >>> dumps(deepcopy(({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]))) "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." >>> dumps(deepcopy(t)) "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." - >>> # Using optimize() means that now both dumps() are idential. - >>> # It may not be necessary, but optimize() ensures that lookups will always work. + >>> # Using deepcopy() beforehand means that now both dumps() are idential. + >>> # It may not be necessary, but deepcopy() ensures that lookups will always work. + + Unfortunately calling copy() alone doesn't seem to fix the + problem as it lies primarily with complex data types. + + >>> from cPickle import dumps + >>> from copy import copy + >>> t = ({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]) + >>> dumps(copy(({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]))) + "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." + >>> dumps(copy(t)) + "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\n(I1\nI2\nI3\nI4\nI5\nt(lp2\nI1\naI2\naI3\naI4\naI5\natp3\n." """ for value in self.testing_data: From f28a7c444b910da7ee4d0ccc998891035fdbb9c6 Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Sat, 18 Feb 2012 01:13:16 +0100 Subject: [PATCH 09/85] Bumped version to 0.2.0. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1b5ef3a..280c95e 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ from setuptools import setup, find_packages setup(name='django-picklefield', - version='0.1.9', + version='0.2.0', description='Pickled object field for Django', long_description=open('README').read(), author='Gintautas Miliauskas', From 4cdc56b6c735009d31fd2867e35b5403bba932d3 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 6 Apr 2012 14:49:20 -0400 Subject: [PATCH 10/85] Make sure ObjectWrapper is considered internal and is optimized it with slots --- src/picklefield/fields.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 5f7d7dc..702ddae 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -27,7 +27,7 @@ class PickledObject(str): """ -class ObjectWrapper(object): +class _ObjectWrapper(object): """ A class used to wrap object that have properties that may clash with the ORM internals. @@ -36,13 +36,14 @@ class ObjectWrapper(object): `django.db.Model` subclasses won't work under certain conditions and the same apply for trying to retrieve any `callable` object. """ - + __slots__ = ('_obj',) + def __init__(self, obj): self._obj = obj def wrap_conflictual_object(obj): if hasattr(obj, 'prepare_database_save') or callable(obj): - obj = ObjectWrapper(obj) + obj = _ObjectWrapper(obj) return obj def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL): @@ -124,7 +125,7 @@ def to_python(self, value): if isinstance(value, PickledObject): raise else: - if isinstance(value, ObjectWrapper): + if isinstance(value, _ObjectWrapper): return value._obj return value From 0c501991b777d89af38b916a0b8290128300a6df Mon Sep 17 00:00:00 2001 From: Rafal Stozek Date: Mon, 3 Dec 2012 21:04:57 +0100 Subject: [PATCH 11/85] Added classifiers and requirements to setup.py --- setup.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 280c95e..eb027b7 100644 --- a/setup.py +++ b/setup.py @@ -19,21 +19,35 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. - +import codecs from setuptools import setup, find_packages + +DESC=codecs.open('README', encoding='utf-8').read() + + setup(name='django-picklefield', version='0.2.0', description='Pickled object field for Django', - long_description=open('README').read(), + long_description=DESC, author='Gintautas Miliauskas', author_email='gintautas@miliauskas.lt', url='http://github.com/gintas/django-picklefield', packages=find_packages('src'), package_dir={'' : 'src'}, + install_requires=[ + 'six', + ], classifiers=[ 'Development Status :: 4 - Beta', 'Framework :: Django', + 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', ] ) From bf3a43202a266458bfc6514d47e19b27c779348b Mon Sep 17 00:00:00 2001 From: Rafal Stozek Date: Tue, 4 Dec 2012 21:05:01 +0100 Subject: [PATCH 12/85] Added settings and script for running tests --- runtests.sh | 2 ++ test_settings.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100755 runtests.sh create mode 100644 test_settings.py diff --git a/runtests.sh b/runtests.sh new file mode 100755 index 0000000..5b65b6c --- /dev/null +++ b/runtests.sh @@ -0,0 +1,2 @@ +#!/bin/sh +DJANGO_SETTINGS_MODULE=test_settings django-admin.py test --pythonpath `pwd` --settings test_settings picklefield diff --git a/test_settings.py b/test_settings.py new file mode 100644 index 0000000..f133309 --- /dev/null +++ b/test_settings.py @@ -0,0 +1,21 @@ +SITE_ID = 1 + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:', + } +} + +DATABASE_ENGINE = 'django.db.backends.sqlite3' +DATABASE_NAME = ':memory:' + +INSTALLED_APPS = [ + 'django.contrib.contenttypes', + 'django.contrib.sites', + 'django.contrib.auth', + 'django.contrib.admin', + 'picklefield', + ] + +SECRET_KEY = 'local' From fe74ffb96d596fb6f63acff0077b9d5ec1fddf3b Mon Sep 17 00:00:00 2001 From: Rafal Stozek Date: Tue, 4 Dec 2012 21:05:19 +0100 Subject: [PATCH 13/85] Python 3 support --- src/picklefield/compat.py | 15 +++++++++ src/picklefield/fields.py | 53 +++++++++++++++++-------------- src/picklefield/tests.py | 67 ++++++++++++++++++++++++--------------- 3 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 src/picklefield/compat.py diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py new file mode 100644 index 0000000..b52e507 --- /dev/null +++ b/src/picklefield/compat.py @@ -0,0 +1,15 @@ +# django 1.5 introduces force_text instead of force_unicode +try: + from django.utils.encoding import force_text +except ImportError: + from django.utils.encoding import force_unicode as force_text + +# python 3.x does not have cPickle module +try: + from cPickle import loads, dumps # cpython 2.x +except ImportError: + from pickle import loads, dumps # cpython 3.x, other interpreters +try: + from django.utils import simplejson as json +except ImportError: + import json diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 702ddae..4bc6eeb 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -1,17 +1,14 @@ """Pickle field implementation for Django.""" - from copy import deepcopy from base64 import b64encode, b64decode from zlib import compress, decompress -try: - from cPickle import loads, dumps -except ImportError: - from pickle import loads, dumps - +import six +import django from django.db import models -from django.utils.encoding import force_unicode from picklefield import DEFAULT_PROTOCOL +from picklefield.compat import force_text, loads, dumps + class PickledObject(str): """ @@ -24,9 +21,9 @@ class PickledObject(str): remove PickledObject and its references, you won't be able to pass in pre-encoded values anymore, but you can always just pass in the python objects themselves. - """ + class _ObjectWrapper(object): """ A class used to wrap object that have properties that may clash with the @@ -41,11 +38,13 @@ class _ObjectWrapper(object): def __init__(self, obj): self._obj = obj + def wrap_conflictual_object(obj): if hasattr(obj, 'prepare_database_save') or callable(obj): obj = _ObjectWrapper(obj) return obj + def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL): # We use deepcopy() here to avoid a problem with cPickle, where dumps # can generate different character streams for same lookup value if @@ -53,22 +52,31 @@ def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL # The reason this is important is because we do all of our lookups as # simple string matches, thus the character streams must be the same # for the lookups to work properly. See tests.py for more information. - if not compress_object: - value = b64encode(dumps(deepcopy(value), pickle_protocol)) - else: - value = b64encode(compress(dumps(deepcopy(value), pickle_protocol))) + value = dumps(deepcopy(value), protocol=pickle_protocol) + if compress_object: + value = compress(value) + value = b64encode(value).decode() # decode bytes to str return PickledObject(value) def dbsafe_decode(value, compress_object=False): - if not compress_object: - value = loads(b64decode(value)) - else: - value = loads(decompress(b64decode(value))) - return value + value = value.encode() # encode str to bytes + value = b64decode(value) + if compress_object: + value = decompress(value) + return loads(value) -class PickledObjectField(models.Field): +def _get_subfield_superclass(): + # hardcore trick to support django < 1.3 - there was something wrong with + # inheritance and SubfieldBase before django 1.3 + # see https://github.com/django/django/commit/222c73261650201f5ce99e8dd4b1ce0d30a69eb4 + if django.VERSION < (1,3): + return models.Field + return six.with_metaclass(models.SubfieldBase, models.Field) + + +class PickledObjectField(_get_subfield_superclass()): """ A field that will accept *any* python object and store it in the database. PickledObjectField will optionally compress its values if @@ -78,8 +86,7 @@ class PickledObjectField(models.Field): can still do lookups using None). This way, it is still possible to use the ``isnull`` lookup type correctly. """ - - __metaclass__ = models.SubfieldBase + __metaclass__ = models.SubfieldBase # for django < 1.3 def __init__(self, *args, **kwargs): self.compress = kwargs.pop('compress', False) @@ -111,7 +118,7 @@ def to_python(self, value): B64decode and unpickle the object, optionally decompressing it. If an error is raised in de-pickling and we're sure the value is - a definite pickle, the error is allowed to propogate. If we + a definite pickle, the error is allowed to propagate. If we aren't sure if the value is a pickle or not, then we catch the error and return the original value instead. @@ -145,13 +152,13 @@ def get_db_prep_value(self, value, connection=None, prepared=False): """ if value is not None and not isinstance(value, PickledObject): - # We call force_unicode here explicitly, so that the encoded string + # We call force_text here explicitly, so that the encoded string # isn't rejected by the postgresql_psycopg2 backend. Alternatively, # we could have just registered PickledObject with the psycopg # marshaller (telling it to store it like it would a string), but # since both of these methods result in the same value being stored, # doing things this way is much easier. - value = force_unicode(dbsafe_encode(value, self.compress, self.protocol)) + value = force_text(dbsafe_encode(value, self.compress, self.protocol)) return value def value_to_string(self, obj): diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index ebb98a3..ea766f4 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -3,46 +3,54 @@ from django.test import TestCase from django.db import models from django.core import serializers -from picklefield.fields import PickledObjectField, wrap_conflictual_object +from picklefield.compat import json +from picklefield.fields import (PickledObjectField, wrap_conflictual_object, + dbsafe_encode) + + +S1 = 'Hello World' +T1 = (1, 2, 3, 4, 5) +L1 = [1, 2, 3, 4, 5] +D1 = {1: 1, 2: 4, 3: 6, 4: 8, 5: 10} +D2 = {1: 2, 2: 4, 3: 6, 4: 8, 5: 10} + class TestingModel(models.Model): pickle_field = PickledObjectField() compressed_pickle_field = PickledObjectField(compress=True) - default_pickle_field = PickledObjectField(default=({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5])) + default_pickle_field = PickledObjectField(default=(D1, S1, T1, L1)) + class MinimalTestingModel(models.Model): pickle_field = PickledObjectField() + class TestCustomDataType(str): pass + class PickledObjectFieldTests(TestCase): def setUp(self): - self.testing_data = ( - {1:2, 2:4, 3:6, 4:8, 5:10}, - 'Hello World', - (1, 2, 3, 4, 5), - [1, 2, 3, 4, 5], - TestCustomDataType('Hello World'), - MinimalTestingModel, - ) + self.testing_data = (D2, S1, T1, L1, + TestCustomDataType(S1), + MinimalTestingModel) return super(PickledObjectFieldTests, self).setUp() - def testDataIntegriry(self): + def testDataIntegrity(self): """ Tests that data remains the same when saved to and fetched from the database, whether compression is enabled or not. - """ for value in self.testing_data: - model_test = TestingModel(pickle_field=value, compressed_pickle_field=value) + model_test = TestingModel(pickle_field=value, + compressed_pickle_field=value) model_test.save() model_test = TestingModel.objects.get(id__exact=model_test.id) # Make sure that both the compressed and uncompressed fields return # the same data, even thought it's stored differently in the DB. self.assertEquals(value, model_test.pickle_field) self.assertEquals(value, model_test.compressed_pickle_field) - # Make sure we can also retreive the model + # Make sure we can also retrieve the model model_test.save() model_test.delete() @@ -51,7 +59,8 @@ def testDataIntegriry(self): model_test = TestingModel() model_test.save() model_test = TestingModel.objects.get(id__exact=model_test.id) - self.assertEquals(({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]), model_test.default_pickle_field) + self.assertEquals((D1, S1, T1, L1), + model_test.default_pickle_field) def testLookups(self): """ @@ -130,7 +139,7 @@ def testLookups(self): # Make sure that lookups of the same value work, even when referenced # differently. See the above docstring for more info on the issue. - value = ({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]) + value = (D1, S1, T1, L1) model_test = TestingModel(pickle_field=value, compressed_pickle_field=value) model_test.save() # Test lookup using an assigned variable. @@ -138,19 +147,25 @@ def testLookups(self): self.assertEquals(value, model_test.pickle_field) # Test lookup using direct input of a matching value. model_test = TestingModel.objects.get( - pickle_field__exact = ({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]), - compressed_pickle_field__exact = ({1: 1, 2: 4, 3: 6, 4: 8, 5: 10}, 'Hello World', (1, 2, 3, 4, 5), [1, 2, 3, 4, 5]), + pickle_field__exact = (D1, S1, T1, L1), + compressed_pickle_field__exact = (D1, S1, T1, L1), ) self.assertEquals(value, model_test.pickle_field) model_test.delete() def testSerialization(self): - model_test = MinimalTestingModel(pickle_field={'foo': 'bar'}) - json_test = serializers.serialize('json', [model_test]) - self.assertEquals(json_test, - '[{"pk": null,' - ' "model": "picklefield.minimaltestingmodel",' - ' "fields": {"pickle_field": "gAJ9cQFVA2Zvb3ECVQNiYXJxA3Mu"}}]') - for deserialized_test in serializers.deserialize('json', json_test): + model = MinimalTestingModel(pickle_field={'foo': 'bar'}) + serialized = serializers.serialize('json', [model]) + data = json.loads(serialized) + + # determine output at runtime, because pickle output in python 3 + # is different (but compatible with python 2) + p = dbsafe_encode({'foo': 'bar'}) + + self.assertEquals(data, + [{'pk': None, 'model': 'picklefield.minimaltestingmodel', + 'fields': {"pickle_field": p}}]) + + for deserialized_test in serializers.deserialize('json', serialized): self.assertEquals(deserialized_test.object, - model_test) + model) From 44e7e4eddabccda8b0751de43493bce13ffae19a Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Sun, 9 Dec 2012 10:18:56 +0100 Subject: [PATCH 14/85] Bumped version to 0.3, updated changelog. --- README | 5 +++++ setup.py | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README b/README index 4dde18f..20c8a55 100644 --- a/README +++ b/README @@ -114,6 +114,11 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 0.3.0 +======================== + + * Python 3 support (thanks to Rafal Stozek). + Changes in version 0.2.0 ======================== diff --git a/setup.py b/setup.py index eb027b7..5ee1a32 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup(name='django-picklefield', - version='0.2.0', + version='0.3.0', description='Pickled object field for Django', long_description=DESC, author='Gintautas Miliauskas', @@ -39,7 +39,7 @@ 'six', ], classifiers=[ - 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', 'Framework :: Django', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', From 05c6c0f7c6116a5de7cc877041124ac6ab8c0f4c Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Mon, 28 Jan 2013 19:23:37 -0500 Subject: [PATCH 15/85] Favor the built-in json module. The django's bundled simplejson module is pending deprecation as of version 1.5 and will be totally removed in 1.7. We should favor the built-in version in all cases. --- src/picklefield/compat.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index b52e507..e2d617a 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -9,7 +9,10 @@ from cPickle import loads, dumps # cpython 2.x except ImportError: from pickle import loads, dumps # cpython 3.x, other interpreters + +# django 1.6 will deprecate django.utils.simple_json try: - from django.utils import simplejson as json -except ImportError: import json +except ImportError: + from django.utils import simplejson as json + From 871421051b428cf048065e625e528637da9c12b3 Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Fri, 15 Nov 2013 22:19:49 +0100 Subject: [PATCH 16/85] Bumped version to 0.3.1 --- README | 7 ++++++- setup.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README b/README index 20c8a55..8ac6bc6 100644 --- a/README +++ b/README @@ -114,6 +114,11 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 0.3.1 +======================== + + * Favor the built in json module (thanks to Simon Charette). + Changes in version 0.3.0 ======================== @@ -123,7 +128,7 @@ Changes in version 0.2.0 ======================== * Allow pickling of subclasses of django.db.models.Model (thanks to Simon - Charette) + Charette). Changes in version 0.1.9 ======================== diff --git a/setup.py b/setup.py index 5ee1a32..1bd0b53 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup(name='django-picklefield', - version='0.3.0', + version='0.3.1', description='Pickled object field for Django', long_description=DESC, author='Gintautas Miliauskas', From 857ff99db2fddb0475d6503ccf5afaddc96390e7 Mon Sep 17 00:00:00 2001 From: Gintautas Miliauskas Date: Fri, 15 Nov 2013 22:26:46 +0100 Subject: [PATCH 17/85] Updated runtests script. --- runtests.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtests.sh b/runtests.sh index 5b65b6c..153e6c9 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,2 +1,4 @@ #!/bin/sh -DJANGO_SETTINGS_MODULE=test_settings django-admin.py test --pythonpath `pwd` --settings test_settings picklefield +ln -s src/picklefield . +DJANGO_SETTINGS_MODULE=test_settings django-admin test --pythonpath `pwd` --settings test_settings picklefield +rm -f picklefield From 50b7fac84460f924673fc92c5b92db21697015ed Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 14:21:08 -0400 Subject: [PATCH 18/85] Used Tox to test against supported Django versions. --- .gitignore | 2 +- README | 11 +++++++++++ runtests.sh | 4 ---- tox.ini | 24 ++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) delete mode 100755 runtests.sh create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 1340094..70df000 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ /dist /temp /src/django_picklefield.egg-info - +.tox diff --git a/README b/README index 8ac6bc6..265da04 100644 --- a/README +++ b/README @@ -44,6 +44,17 @@ base64-encoded pickles, which allows reliable deserialization, but such a format is not convenient for parsing in the browser. By overriding ``value_to_string()`` you can choose a more convenient serialization format. +------------- +Running tests +------------- + +The full test suite can be run with `Tox`_:: + + >>> pip install tox + >>> tox + +.. _Tox: https://testrun.org/tox/latest/ + -------------- Original notes -------------- diff --git a/runtests.sh b/runtests.sh deleted file mode 100755 index 153e6c9..0000000 --- a/runtests.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -ln -s src/picklefield . -DJANGO_SETTINGS_MODULE=test_settings django-admin test --pythonpath `pwd` --settings test_settings picklefield -rm -f picklefield diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..b05e77e --- /dev/null +++ b/tox.ini @@ -0,0 +1,24 @@ +[tox] +envlist = + py26-1.4 + py27-{1.4,1.7,1.8,master}, + py32-{1.7,1.8}, + py33-{1.7,1.8}, + py34-{1.7,1.8,master} + +[testenv] +basepython = + py26: python2.6 + py27: python2.7 + py32: python3.2 + py33: python3.3 + py34: python3.4 +usedevelop = true +commands = + {envpython} -R -Wonce {envbindir}/django-admin.py test -v2 --pythonpath=. --settings test_settings picklefield +deps = + six + 1.4: Django>=1.4,<1.5 + 1.7: Django>=1.7,<1.8 + 1.8: Django>=1.8,<1.9 + master: https://github.com/django/django/archive/master.tar.gz From 90bf54cbe1f20996816bb67848a3f02f98b1f985 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 14:25:52 -0400 Subject: [PATCH 19/85] Added a TravisCI configuration file. --- .travis.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..105d7a1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +sudo: no + +language: python + +env: + - TOXENV=py26-1.4 + - TOXENV=py27-1.4 + - TOXENV=py27-1.7 + - TOXENV=py27-1.8 + - TOXENV=py27-master + - TOXENV=py32-1.7 + - TOXENV=py32-1.8 + - TOXENV=py33-1.7 + - TOXENV=py33-1.8 + - TOXENV=py34-1.7 + - TOXENV=py34-1.8 + - TOXENV=py34-master + +matrix: + fast_finish: true + allow_failures: + - env: TOXENV=py27-master + - env: TOXENV=py34-master + +install: + - pip install tox + +script: + - tox From 474849395c0b0d0c0b0924cd65faf4df9b30d2a7 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 14:47:14 -0400 Subject: [PATCH 20/85] Fixed a test failure against Django 1.7+. Model instances with no primary-key are not equal to each other anymore. --- src/picklefield/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index ea766f4..26b338a 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -154,7 +154,7 @@ def testLookups(self): model_test.delete() def testSerialization(self): - model = MinimalTestingModel(pickle_field={'foo': 'bar'}) + model = MinimalTestingModel(pk=1, pickle_field={'foo': 'bar'}) serialized = serializers.serialize('json', [model]) data = json.loads(serialized) @@ -163,7 +163,7 @@ def testSerialization(self): p = dbsafe_encode({'foo': 'bar'}) self.assertEquals(data, - [{'pk': None, 'model': 'picklefield.minimaltestingmodel', + [{'pk': 1, 'model': 'picklefield.minimaltestingmodel', 'fields': {"pickle_field": p}}]) for deserialized_test in serializers.deserialize('json', serialized): From ad0d5e624e794747581891c7883628df65920f69 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 14:41:17 -0400 Subject: [PATCH 21/85] Dropped Django 1.3 support. --- src/picklefield/fields.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 4bc6eeb..bccd223 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -3,7 +3,6 @@ from base64 import b64encode, b64decode from zlib import compress, decompress import six -import django from django.db import models from picklefield import DEFAULT_PROTOCOL @@ -67,16 +66,7 @@ def dbsafe_decode(value, compress_object=False): return loads(value) -def _get_subfield_superclass(): - # hardcore trick to support django < 1.3 - there was something wrong with - # inheritance and SubfieldBase before django 1.3 - # see https://github.com/django/django/commit/222c73261650201f5ce99e8dd4b1ce0d30a69eb4 - if django.VERSION < (1,3): - return models.Field - return six.with_metaclass(models.SubfieldBase, models.Field) - - -class PickledObjectField(_get_subfield_superclass()): +class PickledObjectField(six.with_metaclass(models.SubfieldBase, models.Field)): """ A field that will accept *any* python object and store it in the database. PickledObjectField will optionally compress its values if @@ -86,7 +76,6 @@ class PickledObjectField(_get_subfield_superclass()): can still do lookups using None). This way, it is still possible to use the ``isnull`` lookup type correctly. """ - __metaclass__ = models.SubfieldBase # for django < 1.3 def __init__(self, *args, **kwargs): self.compress = kwargs.pop('compress', False) From 7ec0333b2ca52193de641d03daa62a61fff0eba1 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 15:01:09 -0400 Subject: [PATCH 22/85] Fixed #8 -- Removed dependency on six. --- setup.py | 3 --- src/picklefield/compat.py | 12 +++++++++++- src/picklefield/fields.py | 6 ++---- tox.ini | 1 - 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index 1bd0b53..4b36019 100644 --- a/setup.py +++ b/setup.py @@ -35,9 +35,6 @@ url='http://github.com/gintas/django-picklefield', packages=find_packages('src'), package_dir={'' : 'src'}, - install_requires=[ - 'six', - ], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Framework :: Django', diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index e2d617a..378a58b 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -1,3 +1,5 @@ +from django.db import models + # django 1.5 introduces force_text instead of force_unicode try: from django.utils.encoding import force_text @@ -15,4 +17,12 @@ import json except ImportError: from django.utils import simplejson as json - + +# django 1.4 doesn't ship with six +try: + from django.utils import six +except ImportError: + class _PickledObjectField(models.Field): + __metaclass__ = models.SubfieldBase +else: + _PickledObjectField = six.with_metaclass(models.SubfieldBase, models.Field) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index bccd223..c9673a5 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -2,11 +2,9 @@ from copy import deepcopy from base64 import b64encode, b64decode from zlib import compress, decompress -import six -from django.db import models from picklefield import DEFAULT_PROTOCOL -from picklefield.compat import force_text, loads, dumps +from picklefield.compat import force_text, loads, dumps, _PickledObjectField class PickledObject(str): @@ -66,7 +64,7 @@ def dbsafe_decode(value, compress_object=False): return loads(value) -class PickledObjectField(six.with_metaclass(models.SubfieldBase, models.Field)): +class PickledObjectField(_PickledObjectField): """ A field that will accept *any* python object and store it in the database. PickledObjectField will optionally compress its values if diff --git a/tox.ini b/tox.ini index b05e77e..ec3e797 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,6 @@ usedevelop = true commands = {envpython} -R -Wonce {envbindir}/django-admin.py test -v2 --pythonpath=. --settings test_settings picklefield deps = - six 1.4: Django>=1.4,<1.5 1.7: Django>=1.7,<1.8 1.8: Django>=1.8,<1.9 From 9cabb40191e4fda1f5246da46ca1f59dd5bef362 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 15:22:01 -0400 Subject: [PATCH 23/85] Fixed #15 -- Stopped using models.SubfieldBase on Django 1.8+ --- src/picklefield/compat.py | 18 +++++++++++------- src/picklefield/fields.py | 3 +++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index 378a58b..8a7317d 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -1,3 +1,4 @@ +import django from django.db import models # django 1.5 introduces force_text instead of force_unicode @@ -18,11 +19,14 @@ except ImportError: from django.utils import simplejson as json -# django 1.4 doesn't ship with six -try: - from django.utils import six -except ImportError: - class _PickledObjectField(models.Field): - __metaclass__ = models.SubfieldBase +if django.VERSION >= (1, 8): + _PickledObjectField = models.Field else: - _PickledObjectField = six.with_metaclass(models.SubfieldBase, models.Field) + # django 1.4 doesn't ship with six + try: + from django.utils import six + except ImportError: + class _PickledObjectField(models.Field): + __metaclass__ = models.SubfieldBase + else: + _PickledObjectField = six.with_metaclass(models.SubfieldBase, models.Field) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index c9673a5..d9460a6 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -127,6 +127,9 @@ def pre_save(self, model_instance, add): value = super(PickledObjectField, self).pre_save(model_instance, add) return wrap_conflictual_object(value) + def from_db_value(self, value, expression, connection, context): + return self.to_python(value) + def get_db_prep_value(self, value, connection=None, prepared=False): """ Pickle and b64encode the object, optionally compressing it. From 231b65cfc48ff9992442c62b9e3b4c39a2d63b50 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 15:27:28 -0400 Subject: [PATCH 24/85] Removed a Python 2.5 comptability shim. --- setup.py | 1 - src/picklefield/compat.py | 6 ------ src/picklefield/tests.py | 2 +- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 4b36019..40af8b1 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,6 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index 8a7317d..b754425 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -13,12 +13,6 @@ except ImportError: from pickle import loads, dumps # cpython 3.x, other interpreters -# django 1.6 will deprecate django.utils.simple_json -try: - import json -except ImportError: - from django.utils import simplejson as json - if django.VERSION >= (1, 8): _PickledObjectField = models.Field else: diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index 26b338a..e58a794 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -1,9 +1,9 @@ """Unit tests for django-picklefield.""" +import json from django.test import TestCase from django.db import models from django.core import serializers -from picklefield.compat import json from picklefield.fields import (PickledObjectField, wrap_conflictual_object, dbsafe_encode) From 8788164a2635407a880b52c0f354a6c3fc7db0ec Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 15:33:10 -0400 Subject: [PATCH 25/85] Added the .rst extension to the README. --- README => README.rst | 0 setup.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename README => README.rst (100%) diff --git a/README b/README.rst similarity index 100% rename from README rename to README.rst diff --git a/setup.py b/setup.py index 40af8b1..823cbb1 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ from setuptools import setup, find_packages -DESC=codecs.open('README', encoding='utf-8').read() +DESC=codecs.open('README.rst', encoding='utf-8').read() setup(name='django-picklefield', From 7323e7569fed35b369c1ec17aa113d316f13f5cd Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 15:35:31 -0400 Subject: [PATCH 26/85] Stopped using the deprecated assertEquals method in tests. --- src/picklefield/tests.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index e58a794..8d941da 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -48,8 +48,8 @@ def testDataIntegrity(self): model_test = TestingModel.objects.get(id__exact=model_test.id) # Make sure that both the compressed and uncompressed fields return # the same data, even thought it's stored differently in the DB. - self.assertEquals(value, model_test.pickle_field) - self.assertEquals(value, model_test.compressed_pickle_field) + self.assertEqual(value, model_test.pickle_field) + self.assertEqual(value, model_test.compressed_pickle_field) # Make sure we can also retrieve the model model_test.save() model_test.delete() @@ -59,7 +59,7 @@ def testDataIntegrity(self): model_test = TestingModel() model_test.save() model_test = TestingModel.objects.get(id__exact=model_test.id) - self.assertEquals((D1, S1, T1, L1), + self.assertEqual((D1, S1, T1, L1), model_test.default_pickle_field) def testLookups(self): @@ -125,16 +125,16 @@ def testLookups(self): wrapped_value = wrap_conflictual_object(value) model_test = TestingModel.objects.get(pickle_field__exact=wrapped_value, compressed_pickle_field__exact=wrapped_value) - self.assertEquals(value, model_test.pickle_field) - self.assertEquals(value, model_test.compressed_pickle_field) + self.assertEqual(value, model_test.pickle_field) + self.assertEqual(value, model_test.compressed_pickle_field) # Make sure that ``in`` lookups also work correctly. model_test = TestingModel.objects.get(pickle_field__in=[wrapped_value], compressed_pickle_field__in=[wrapped_value]) - self.assertEquals(value, model_test.pickle_field) - self.assertEquals(value, model_test.compressed_pickle_field) + self.assertEqual(value, model_test.pickle_field) + self.assertEqual(value, model_test.compressed_pickle_field) # Make sure that ``is_null`` lookups are working. - self.assertEquals(1, TestingModel.objects.filter(pickle_field__isnull=False).count()) - self.assertEquals(0, TestingModel.objects.filter(pickle_field__isnull=True).count()) + self.assertEqual(1, TestingModel.objects.filter(pickle_field__isnull=False).count()) + self.assertEqual(0, TestingModel.objects.filter(pickle_field__isnull=True).count()) model_test.delete() # Make sure that lookups of the same value work, even when referenced @@ -144,13 +144,13 @@ def testLookups(self): model_test.save() # Test lookup using an assigned variable. model_test = TestingModel.objects.get(pickle_field__exact=value) - self.assertEquals(value, model_test.pickle_field) + self.assertEqual(value, model_test.pickle_field) # Test lookup using direct input of a matching value. model_test = TestingModel.objects.get( pickle_field__exact = (D1, S1, T1, L1), compressed_pickle_field__exact = (D1, S1, T1, L1), ) - self.assertEquals(value, model_test.pickle_field) + self.assertEqual(value, model_test.pickle_field) model_test.delete() def testSerialization(self): @@ -162,10 +162,10 @@ def testSerialization(self): # is different (but compatible with python 2) p = dbsafe_encode({'foo': 'bar'}) - self.assertEquals(data, + self.assertEqual(data, [{'pk': 1, 'model': 'picklefield.minimaltestingmodel', 'fields': {"pickle_field": p}}]) for deserialized_test in serializers.deserialize('json', serialized): - self.assertEquals(deserialized_test.object, + self.assertEqual(deserialized_test.object, model) From c3f1f44d9b3e81a9d8559bc5f441e1e6809d1bcc Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 15:58:34 -0400 Subject: [PATCH 27/85] Silenced a pesky system check on Django 1.7 --- test_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_settings.py b/test_settings.py index f133309..0ed3815 100644 --- a/test_settings.py +++ b/test_settings.py @@ -19,3 +19,5 @@ ] SECRET_KEY = 'local' + +SILENCED_SYSTEM_CHECKS = ['1_7.W001'] From ad71fe99b29dd050726ef492fd0a2271002e193d Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 16:03:58 -0400 Subject: [PATCH 28/85] Simplified test settings to avoid unnecessary table creation. --- test_settings.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/test_settings.py b/test_settings.py index 0ed3815..9e70d61 100644 --- a/test_settings.py +++ b/test_settings.py @@ -1,22 +1,12 @@ -SITE_ID = 1 - DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:', - } + }, } -DATABASE_ENGINE = 'django.db.backends.sqlite3' -DATABASE_NAME = ':memory:' - INSTALLED_APPS = [ - 'django.contrib.contenttypes', - 'django.contrib.sites', - 'django.contrib.auth', - 'django.contrib.admin', 'picklefield', - ] +] SECRET_KEY = 'local' From 9f8b2cc85fbd75cf9194b99dbef8856b3f1278ee Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 16:05:49 -0400 Subject: [PATCH 29/85] Avoid using the deprecated _get_val_from_obj method. --- src/picklefield/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index d9460a6..8aaedbd 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -152,7 +152,7 @@ def get_db_prep_value(self, value, connection=None, prepared=False): return value def value_to_string(self, obj): - value = self._get_val_from_obj(obj) + value = self.value_from_object(obj) return self.get_db_prep_value(value) def get_internal_type(self): From e8ce88ef7c1cf99c937561297a8b534534a7d87c Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 16:11:49 -0400 Subject: [PATCH 30/85] Adjusted the release not and the actual version. --- README.rst | 7 +++++++ setup.py | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 265da04..a5ae7fb 100644 --- a/README.rst +++ b/README.rst @@ -125,6 +125,13 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 0.3.2 +======================== + +* Dropped support for Django 1.3. +* Dropped support for Python 2.5. +* Silenced deprecation warnings on Django 1.8+. + Changes in version 0.3.1 ======================== diff --git a/setup.py b/setup.py index 823cbb1..6d218c9 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ setup(name='django-picklefield', - version='0.3.1', + version='0.3.2', description='Pickled object field for Django', long_description=DESC, author='Gintautas Miliauskas', @@ -45,5 +45,6 @@ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', ] ) From 4345979ca0c50c410c33c94771b393d1109c1a91 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 16:17:20 -0400 Subject: [PATCH 31/85] Fixed some formating issues in the README. --- README.rst | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/README.rst b/README.rst index a5ae7fb..fc32a26 100644 --- a/README.rst +++ b/README.rst @@ -5,13 +5,13 @@ About **django-picklefield** provides an implementation of a pickled object field. Such fields can contain any picklable objects. -The implementation is taken and adopted from Django snippet #1694 - by Taavi Taijala, which is in -turn based on Django snippet #513 -by Oliver Beattie. +The implementation is taken and adopted from `Django snippet #1694`_ by Taavi +Taijala, which is in turn based on `Django snippet #513`_ by Oliver Beattie. django-picklefield is available under the MIT license. +.. _Django snippet #1694: http://www.djangosnippets.org/snippets/1694/ +.. _Django snippet #513: http://www.djangosnippets.org/snippets/513/ ----- Usage @@ -135,74 +135,74 @@ Changes in version 0.3.2 Changes in version 0.3.1 ======================== - * Favor the built in json module (thanks to Simon Charette). +* Favor the built in json module (thanks to Simon Charette). Changes in version 0.3.0 ======================== - * Python 3 support (thanks to Rafal Stozek). +* Python 3 support (thanks to Rafal Stozek). Changes in version 0.2.0 ======================== - * Allow pickling of subclasses of django.db.models.Model (thanks to Simon - Charette). +* Allow pickling of subclasses of django.db.models.Model (thanks to Simon + Charette). Changes in version 0.1.9 ======================== - * Added `connection` and `prepared` parameters to `get_db_prep_value()` too - (thanks to Matthew Schinckel). +* Added `connection` and `prepared` parameters to `get_db_prep_value()` too + (thanks to Matthew Schinckel). Changes in version 0.1.8 ======================== - * Updated link to code repository. +* Updated link to code repository. Changes in version 0.1.7 ======================== - * Added `connection` and `prepared` parameters to `get_db_prep_lookup()` to - get rid of deprecation warnings in Django 1.2. +* Added `connection` and `prepared` parameters to `get_db_prep_lookup()` to + get rid of deprecation warnings in Django 1.2. Changes in version 0.1.6 ======================== - * Fixed South support (thanks aehlke@github). +* Fixed South support (thanks aehlke@github). Changes in version 0.1.5 ======================== - * Added support for South. - * Changed default to null=False, as is common throughout Django. +* Added support for South. +* Changed default to null=False, as is common throughout Django. Changes in version 0.1.4 ======================== - * Updated copyright statements. +* Updated copyright statements. Changes in version 0.1.3 ======================== - * Updated serialization tests (thanks to Michael Fladischer). +* Updated serialization tests (thanks to Michael Fladischer). Changes in version 0.1.2 ======================== - * Added Simplified BSD licence. +* Added Simplified BSD licence. Changes in version 0.1.1 ======================== - * Added test for serialization. - * Added note about JSON serialization for browser. - * Added support for different pickle protocol versions (thanks to Michael - Fladischer). +* Added test for serialization. +* Added note about JSON serialization for browser. +* Added support for different pickle protocol versions (thanks to Michael + Fladischer). Changes in version 0.1 ====================== - * First public release. +* First public release. -------- From f1867441b4e9155d7d18b5140520267c3879ef3f Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 16:44:41 -0400 Subject: [PATCH 32/85] Removed some dead code. --- src/picklefield/fields.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 8aaedbd..42d7927 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -163,14 +163,9 @@ def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False raise TypeError('Lookup type %s is not supported.' % lookup_type) # The Field model already calls get_db_prep_value before doing the # actual lookup, so all we need to do is limit the lookup types. - try: - return super(PickledObjectField, self).get_db_prep_lookup( - lookup_type, value, connection=connection, prepared=prepared) - except TypeError: - # Try not to break on older versions of Django, where the - # `connection` and `prepared` parameters are not available. - return super(PickledObjectField, self).get_db_prep_lookup( - lookup_type, value) + return super(PickledObjectField, self).get_db_prep_lookup( + lookup_type, value, connection=connection, prepared=prepared + ) # South support; see http://south.aeracode.org/docs/tutorial/part4.html#simple-inheritance From 6ab88592fb289717c38525d2702b5ced33612761 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 19:26:21 -0400 Subject: [PATCH 33/85] Tracked coverage with coveralls --- .coveragerc | 3 +++ .gitignore | 1 + .travis.yml | 5 ++++- tox.ini | 4 +++- 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..9e2ae7c --- /dev/null +++ b/.coveragerc @@ -0,0 +1,3 @@ +[run] +source = src/picklefield +branch = True diff --git a/.gitignore b/.gitignore index 70df000..36d129b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /temp /src/django_picklefield.egg-info .tox +.coverage \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 105d7a1..2cade97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,10 @@ matrix: - env: TOXENV=py34-master install: - - pip install tox + - pip install tox coveralls script: - tox + +after_success: + - coveralls diff --git a/tox.ini b/tox.ini index ec3e797..abc2ff7 100644 --- a/tox.ini +++ b/tox.ini @@ -15,8 +15,10 @@ basepython = py34: python3.4 usedevelop = true commands = - {envpython} -R -Wonce {envbindir}/django-admin.py test -v2 --pythonpath=. --settings test_settings picklefield + {envpython} -R -Wonce {envbindir}/coverage run {envbindir}/django-admin.py test --pythonpath=. --settings test_settings picklefield + coverage report deps = + coverage 1.4: Django>=1.4,<1.5 1.7: Django>=1.7,<1.8 1.8: Django>=1.8,<1.9 From 5329100b4d83df68e7be20f6910752e62dcf7066 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 19:32:37 -0400 Subject: [PATCH 34/85] Added TravisCI and Coveralls badges to the README. --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index fc32a26..dfce781 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,9 @@ +.. image:: https://travis-ci.org/gintas/django-picklefield.svg?branch=master + :target: https://travis-ci.org/gintas/django-picklefield + +.. image:: https://coveralls.io/repos/gintas/django-picklefield/badge.svg?branch=master&service=github + :target: https://coveralls.io/github/gintas/django-picklefield?branch=master + ----- About ----- From b435b31ded1a0d1452990a8983fb1d08e3457894 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 19:35:54 -0400 Subject: [PATCH 35/85] Django 1.4 actually ships with six. --- src/picklefield/compat.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index b754425..bd67fb4 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -1,5 +1,6 @@ import django from django.db import models +from django.utils import six # django 1.5 introduces force_text instead of force_unicode try: @@ -16,11 +17,4 @@ if django.VERSION >= (1, 8): _PickledObjectField = models.Field else: - # django 1.4 doesn't ship with six - try: - from django.utils import six - except ImportError: - class _PickledObjectField(models.Field): - __metaclass__ = models.SubfieldBase - else: - _PickledObjectField = six.with_metaclass(models.SubfieldBase, models.Field) + _PickledObjectField = six.with_metaclass(models.SubfieldBase, models.Field) From 9261104817a9c416f3f2e529077724183561cfba Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 19:40:01 -0400 Subject: [PATCH 36/85] The force_text method is also available from Django 1.4+. --- src/picklefield/compat.py | 6 ------ src/picklefield/fields.py | 4 +++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index bd67fb4..088c0ae 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -2,12 +2,6 @@ from django.db import models from django.utils import six -# django 1.5 introduces force_text instead of force_unicode -try: - from django.utils.encoding import force_text -except ImportError: - from django.utils.encoding import force_unicode as force_text - # python 3.x does not have cPickle module try: from cPickle import loads, dumps # cpython 2.x diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 42d7927..6073f56 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -3,8 +3,10 @@ from base64 import b64encode, b64decode from zlib import compress, decompress +from django.utils.encoding import force_text + from picklefield import DEFAULT_PROTOCOL -from picklefield.compat import force_text, loads, dumps, _PickledObjectField +from picklefield.compat import loads, dumps, _PickledObjectField class PickledObject(str): From 1f0177292a54dced6b02f2ff47123a33bc8e6728 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 19:42:48 -0400 Subject: [PATCH 37/85] Install South when testing against 1.4 to increase coverage. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index abc2ff7..495b928 100644 --- a/tox.ini +++ b/tox.ini @@ -20,6 +20,7 @@ commands = deps = coverage 1.4: Django>=1.4,<1.5 + 1.4: South 1.7: Django>=1.7,<1.8 1.8: Django>=1.8,<1.9 master: https://github.com/django/django/archive/master.tar.gz From 7a9d83d9fa3aecef6b146f4e2d1f575142d51c35 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 19:56:55 -0400 Subject: [PATCH 38/85] Run flake8 as part of the CI to detect pep8 violations. --- .travis.yml | 1 + setup.cfg | 2 ++ setup.py | 11 +++++------ src/picklefield/__init__.py | 2 +- src/picklefield/compat.py | 5 +++-- src/picklefield/fields.py | 10 +++++----- src/picklefield/tests.py | 23 +++++++++++------------ tox.ini | 10 +++++++++- 8 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 setup.cfg diff --git a/.travis.yml b/.travis.yml index 2cade97..6e36cae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ sudo: no language: python env: + - TOXENV=flake8 - TOXENV=py26-1.4 - TOXENV=py27-1.4 - TOXENV=py27-1.7 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..791f075 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 119 diff --git a/setup.py b/setup.py index 6d218c9..51c3145 100644 --- a/setup.py +++ b/setup.py @@ -22,19 +22,18 @@ import codecs from setuptools import setup, find_packages +long_description = codecs.open('README.rst', encoding='utf-8').read() -DESC=codecs.open('README.rst', encoding='utf-8').read() - - -setup(name='django-picklefield', +setup( + name='django-picklefield', version='0.3.2', description='Pickled object field for Django', - long_description=DESC, + long_description=long_description, author='Gintautas Miliauskas', author_email='gintautas@miliauskas.lt', url='http://github.com/gintas/django-picklefield', packages=find_packages('src'), - package_dir={'' : 'src'}, + package_dir={'': 'src'}, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Framework :: Django', diff --git a/src/picklefield/__init__.py b/src/picklefield/__init__.py index d8269fd..c5098e2 100644 --- a/src/picklefield/__init__.py +++ b/src/picklefield/__init__.py @@ -2,4 +2,4 @@ DEFAULT_PROTOCOL = 2 -from picklefield.fields import PickledObjectField # reexport +from picklefield.fields import PickledObjectField # noqa diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index 088c0ae..e38997d 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -4,9 +4,10 @@ # python 3.x does not have cPickle module try: - from cPickle import loads, dumps # cpython 2.x + # cpython 2.x + from cPickle import loads, dumps # noqa except ImportError: - from pickle import loads, dumps # cpython 3.x, other interpreters + from pickle import loads, dumps # noqa if django.VERSION >= (1, 8): _PickledObjectField = models.Field diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 6073f56..86ae045 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -25,10 +25,10 @@ class PickledObject(str): class _ObjectWrapper(object): """ - A class used to wrap object that have properties that may clash with the + A class used to wrap object that have properties that may clash with the ORM internals. - - For example, objects with the `prepare_database_save` property such as + + For example, objects with the `prepare_database_save` property such as `django.db.Model` subclasses won't work under certain conditions and the same apply for trying to retrieve any `callable` object. """ @@ -54,12 +54,12 @@ def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL value = dumps(deepcopy(value), protocol=pickle_protocol) if compress_object: value = compress(value) - value = b64encode(value).decode() # decode bytes to str + value = b64encode(value).decode() # decode bytes to str return PickledObject(value) def dbsafe_decode(value, compress_object=False): - value = value.encode() # encode str to bytes + value = value.encode() # encode str to bytes value = b64decode(value) if compress_object: value = decompress(value) diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index 8d941da..b9b70e4 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -42,8 +42,7 @@ def testDataIntegrity(self): the database, whether compression is enabled or not. """ for value in self.testing_data: - model_test = TestingModel(pickle_field=value, - compressed_pickle_field=value) + model_test = TestingModel(pickle_field=value, compressed_pickle_field=value) model_test.save() model_test = TestingModel.objects.get(id__exact=model_test.id) # Make sure that both the compressed and uncompressed fields return @@ -59,8 +58,7 @@ def testDataIntegrity(self): model_test = TestingModel() model_test.save() model_test = TestingModel.objects.get(id__exact=model_test.id) - self.assertEqual((D1, S1, T1, L1), - model_test.default_pickle_field) + self.assertEqual((D1, S1, T1, L1), model_test.default_pickle_field) def testLookups(self): """ @@ -116,7 +114,7 @@ def testLookups(self): >>> dumps(copy(t)) "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\n(I1\nI2\nI3\nI4\nI5\nt(lp2\nI1\naI2\naI3\naI4\naI5\natp3\n." - """ + """ # noqa for value in self.testing_data: model_test = TestingModel(pickle_field=value, compressed_pickle_field=value) model_test.save() @@ -147,8 +145,8 @@ def testLookups(self): self.assertEqual(value, model_test.pickle_field) # Test lookup using direct input of a matching value. model_test = TestingModel.objects.get( - pickle_field__exact = (D1, S1, T1, L1), - compressed_pickle_field__exact = (D1, S1, T1, L1), + pickle_field__exact=(D1, S1, T1, L1), + compressed_pickle_field__exact=(D1, S1, T1, L1), ) self.assertEqual(value, model_test.pickle_field) model_test.delete() @@ -162,10 +160,11 @@ def testSerialization(self): # is different (but compatible with python 2) p = dbsafe_encode({'foo': 'bar'}) - self.assertEqual(data, - [{'pk': 1, 'model': 'picklefield.minimaltestingmodel', - 'fields': {"pickle_field": p}}]) + self.assertEqual(data, [{ + 'pk': 1, + 'model': 'picklefield.minimaltestingmodel', + 'fields': {"pickle_field": p}}, + ]) for deserialized_test in serializers.deserialize('json', serialized): - self.assertEqual(deserialized_test.object, - model) + self.assertEqual(deserialized_test.object, model) diff --git a/tox.ini b/tox.ini index 495b928..282a130 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [tox] envlist = - py26-1.4 + flake8, + py26-1.4, py27-{1.4,1.7,1.8,master}, py32-{1.7,1.8}, py33-{1.7,1.8}, @@ -24,3 +25,10 @@ deps = 1.7: Django>=1.7,<1.8 1.8: Django>=1.8,<1.9 master: https://github.com/django/django/archive/master.tar.gz + +[testenv:flake8] +basepython = python2.7 +commands = + flake8 +deps = + flake8==2.4.1 From bd8ecc799c39a8189ab428c98fa540bcc96965ea Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 3 Sep 2015 20:03:47 -0400 Subject: [PATCH 39/85] Run isort as part of the CI to define an expected import order. --- .travis.yml | 1 + setup.cfg | 7 +++++++ src/picklefield/fields.py | 6 +++--- src/picklefield/tests.py | 7 +++---- tox.ini | 7 +++++++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e36cae..a66b43c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ language: python env: - TOXENV=flake8 + - TOXENV=isort - TOXENV=py26-1.4 - TOXENV=py27-1.4 - TOXENV=py27-1.7 diff --git a/setup.cfg b/setup.cfg index 791f075..aed2773 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,9 @@ [flake8] max-line-length = 119 + +[isort] +combine_as_imports=true +include_trailing_comma=true +known_third_party=picklefield,south +multi_line_output=5 +not_skip=__init__.py diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 86ae045..c53f491 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -1,12 +1,12 @@ """Pickle field implementation for Django.""" +from base64 import b64decode, b64encode from copy import deepcopy -from base64 import b64encode, b64decode from zlib import compress, decompress from django.utils.encoding import force_text -from picklefield import DEFAULT_PROTOCOL -from picklefield.compat import loads, dumps, _PickledObjectField +from . import DEFAULT_PROTOCOL +from .compat import _PickledObjectField, dumps, loads class PickledObject(str): diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index b9b70e4..2430b21 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -1,12 +1,11 @@ """Unit tests for django-picklefield.""" import json -from django.test import TestCase -from django.db import models from django.core import serializers -from picklefield.fields import (PickledObjectField, wrap_conflictual_object, - dbsafe_encode) +from django.db import models +from django.test import TestCase +from .fields import PickledObjectField, dbsafe_encode, wrap_conflictual_object S1 = 'Hello World' T1 = (1, 2, 3, 4, 5) diff --git a/tox.ini b/tox.ini index 282a130..118c706 100644 --- a/tox.ini +++ b/tox.ini @@ -32,3 +32,10 @@ commands = flake8 deps = flake8==2.4.1 + +[testenv:isort] +basepython = python2.7 +commands = + isort --recursive --check-only --diff src/picklefield +deps = + isort==4.1.2 From a300ebc1b308509831647f29b6ec306ade8c6de7 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 6 Sep 2015 13:43:07 -0400 Subject: [PATCH 40/85] Made sure to use universal wheels. --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index aed2773..ebd8cb3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,3 +7,6 @@ include_trailing_comma=true known_third_party=picklefield,south multi_line_output=5 not_skip=__init__.py + +[wheel] +universal = 1 From 855e1a77e51620858df8982d070c071fa49c8865 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 6 Oct 2016 21:00:14 +0200 Subject: [PATCH 41/85] Added Django 1.9, 1.10 to tox. Dropped support for Python 2.6. --- .travis.yml | 7 +++++-- tox.ini | 11 ++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a66b43c..19fd215 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,11 @@ language: python env: - TOXENV=flake8 - TOXENV=isort - - TOXENV=py26-1.4 - TOXENV=py27-1.4 - TOXENV=py27-1.7 - TOXENV=py27-1.8 + - TOXENV=py27-1.9 + - TOXENV=py27-1.10 - TOXENV=py27-master - TOXENV=py32-1.7 - TOXENV=py32-1.8 @@ -16,6 +17,8 @@ env: - TOXENV=py33-1.8 - TOXENV=py34-1.7 - TOXENV=py34-1.8 + - TOXENV=py34-1.9 + - TOXENV=py34-1.10 - TOXENV=py34-master matrix: @@ -25,7 +28,7 @@ matrix: - env: TOXENV=py34-master install: - - pip install tox coveralls + - pip install tox coveralls "virtualenv<14.0" script: - tox diff --git a/tox.ini b/tox.ini index 118c706..bc14498 100644 --- a/tox.ini +++ b/tox.ini @@ -1,15 +1,13 @@ [tox] envlist = flake8, - py26-1.4, - py27-{1.4,1.7,1.8,master}, + py27-{1.4,1.7,1.8,1.9,1.10,master}, py32-{1.7,1.8}, py33-{1.7,1.8}, - py34-{1.7,1.8,master} + py34-{1.7,1.8,1.9,1.10,master} [testenv] basepython = - py26: python2.6 py27: python2.7 py32: python3.2 py33: python3.3 @@ -19,11 +17,14 @@ commands = {envpython} -R -Wonce {envbindir}/coverage run {envbindir}/django-admin.py test --pythonpath=. --settings test_settings picklefield coverage report deps = - coverage + py32: coverage<4.0 + {py27,py33,py34,py35}: coverage 1.4: Django>=1.4,<1.5 1.4: South 1.7: Django>=1.7,<1.8 1.8: Django>=1.8,<1.9 + 1.9: Django>=1.9,<1.10 + 1.10: Django>=1.10,<1.11 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8] From fcc608c5f4c727bd6b0acccb85d4bd9eeef26ac2 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Sat, 22 Oct 2016 11:17:23 +0200 Subject: [PATCH 42/85] Fixed #21 -- Used get_lookup instead get_db_prep_lookup --- src/picklefield/fields.py | 16 +++++++--------- src/picklefield/tests.py | 10 ++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index c53f491..a49c2b1 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -160,15 +160,13 @@ def value_to_string(self, obj): def get_internal_type(self): return 'TextField' - def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False): - if lookup_type not in ['exact', 'in', 'isnull']: - raise TypeError('Lookup type %s is not supported.' % lookup_type) - # The Field model already calls get_db_prep_value before doing the - # actual lookup, so all we need to do is limit the lookup types. - return super(PickledObjectField, self).get_db_prep_lookup( - lookup_type, value, connection=connection, prepared=prepared - ) - + def get_lookup(self, lookup_name): + """ + We need to limit the lookup types. + """ + if lookup_name not in ['exact', 'in', 'isnull']: + raise TypeError('Lookup type %s is not supported.' % lookup_name) + return super(PickledObjectField, self).get_lookup(lookup_name) # South support; see http://south.aeracode.org/docs/tutorial/part4.html#simple-inheritance try: diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index 2430b21..ad57404 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -1,6 +1,8 @@ """Unit tests for django-picklefield.""" import json +from unittest import skipIf +import django from django.core import serializers from django.db import models from django.test import TestCase @@ -150,6 +152,14 @@ def testLookups(self): self.assertEqual(value, model_test.pickle_field) model_test.delete() + @skipIf(django.VERSION[:2] < (1, 5), 'Skip for older Django versions') + def testLimitLookupsType(self): + """ + Test that picklefield supports lookup type limit + """ + with self.assertRaisesMessage(TypeError, 'Lookup type gte is not supported'): + TestingModel.objects.filter(pickle_field__gte=1) + def testSerialization(self): model = MinimalTestingModel(pk=1, pickle_field={'foo': 'bar'}) serialized = serializers.serialize('json', [model]) From bd98749f314f904521e9247ad78c20c1aafc5d16 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Sat, 22 Oct 2016 17:33:30 +0200 Subject: [PATCH 43/85] Increased fields.py test coverage --- src/picklefield/tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index ad57404..fb60cfc 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -1,5 +1,6 @@ """Unit tests for django-picklefield.""" import json +from datetime import date from unittest import skipIf import django @@ -20,6 +21,7 @@ class TestingModel(models.Model): pickle_field = PickledObjectField() compressed_pickle_field = PickledObjectField(compress=True) default_pickle_field = PickledObjectField(default=(D1, S1, T1, L1)) + callable_pickle_field = PickledObjectField(default=date.today) class MinimalTestingModel(models.Model): @@ -60,6 +62,7 @@ def testDataIntegrity(self): model_test.save() model_test = TestingModel.objects.get(id__exact=model_test.id) self.assertEqual((D1, S1, T1, L1), model_test.default_pickle_field) + self.assertEqual(date.today(), model_test.callable_pickle_field) def testLookups(self): """ From 252fe4d621eddece766f292743285c7643556595 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Sun, 22 Jan 2017 21:10:27 +0100 Subject: [PATCH 44/85] Added Django 1.11 to tox an travis configurations. --- .travis.yml | 6 ++++-- tox.ini | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 19fd215..5a7791d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: - TOXENV=py27-1.8 - TOXENV=py27-1.9 - TOXENV=py27-1.10 - - TOXENV=py27-master + - TOXENV=py27-1.11 - TOXENV=py32-1.7 - TOXENV=py32-1.8 - TOXENV=py33-1.7 @@ -19,12 +19,14 @@ env: - TOXENV=py34-1.8 - TOXENV=py34-1.9 - TOXENV=py34-1.10 + - TOXENV=py34-1.11 - TOXENV=py34-master matrix: fast_finish: true allow_failures: - - env: TOXENV=py27-master + - env: TOXENV=py27-1.11 + - env: TOXENV=py34-1.11 - env: TOXENV=py34-master install: diff --git a/tox.ini b/tox.ini index bc14498..00fc3fd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ [tox] envlist = flake8, - py27-{1.4,1.7,1.8,1.9,1.10,master}, + py27-{1.4,1.7,1.8,1.9,1.10,1.11}, py32-{1.7,1.8}, py33-{1.7,1.8}, - py34-{1.7,1.8,1.9,1.10,master} + py34-{1.7,1.8,1.9,1.10,1.11,master} [testenv] basepython = @@ -25,6 +25,7 @@ deps = 1.8: Django>=1.8,<1.9 1.9: Django>=1.9,<1.10 1.10: Django>=1.10,<1.11 + 1.11: Django>=1.11a1,<2.0 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8] From a0e3aff2ab3f267df7f0f3efd6060c786285c109 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Sat, 6 May 2017 14:55:51 +0200 Subject: [PATCH 45/85] Removed Django 1.11 from the allowed failures. --- .travis.yml | 2 -- tox.ini | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5a7791d..8547b7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,6 @@ env: matrix: fast_finish: true allow_failures: - - env: TOXENV=py27-1.11 - - env: TOXENV=py34-1.11 - env: TOXENV=py34-master install: diff --git a/tox.ini b/tox.ini index 00fc3fd..0dd7a0b 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,7 @@ deps = 1.8: Django>=1.8,<1.9 1.9: Django>=1.9,<1.10 1.10: Django>=1.10,<1.11 - 1.11: Django>=1.11a1,<2.0 + 1.11: Django>=1.11,<2.0 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8] From f5bb1278f27e46be251cdf4f55d07e5a3b0f134a Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Sat, 6 May 2017 15:05:34 +0200 Subject: [PATCH 46/85] Removed Python 2.6 from setup.py. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 51c3145..a2f48de 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,6 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', From ed33b3c0325d749f9574f3224ac4b6ad2f8669d3 Mon Sep 17 00:00:00 2001 From: Josh Smeaton Date: Sun, 25 Jun 2017 15:43:08 +1000 Subject: [PATCH 47/85] Allow users to skip the copy step --- README.rst | 11 +++++++++++ src/picklefield/fields.py | 12 +++++++++--- src/picklefield/tests.py | 20 ++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index dfce781..8e9b84b 100644 --- a/README.rst +++ b/README.rst @@ -50,6 +50,12 @@ base64-encoded pickles, which allows reliable deserialization, but such a format is not convenient for parsing in the browser. By overriding ``value_to_string()`` you can choose a more convenient serialization format. +Fields now accept the boolean key word argument `copy`, which defaults to +`True`. The `copy` is necessary for lookups to work correctly. If you don't +care about performing lookups on the picklefield, you can set `copy=False` to +save on some memory usage. This an be especially beneficial for very large +object trees. + ------------- Running tests ------------- @@ -131,6 +137,11 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 0.3.3 +======================== + +* Added a new option to prevent a copy of the object before pickling: `copy=True` + Changes in version 0.3.2 ======================== diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index a49c2b1..a66936b 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -44,14 +44,18 @@ def wrap_conflictual_object(obj): return obj -def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL): +def dbsafe_encode(value, compress_object=False, pickle_protocol=DEFAULT_PROTOCOL, copy=True): # We use deepcopy() here to avoid a problem with cPickle, where dumps # can generate different character streams for same lookup value if # they are referenced differently. # The reason this is important is because we do all of our lookups as # simple string matches, thus the character streams must be the same # for the lookups to work properly. See tests.py for more information. - value = dumps(deepcopy(value), protocol=pickle_protocol) + if copy: + # Copy can be very expensive if users aren't going to perform lookups + # on the value anyway. + value = deepcopy(value) + value = dumps(value, protocol=pickle_protocol) if compress_object: value = compress(value) value = b64encode(value).decode() # decode bytes to str @@ -80,6 +84,7 @@ class PickledObjectField(_PickledObjectField): def __init__(self, *args, **kwargs): self.compress = kwargs.pop('compress', False) self.protocol = kwargs.pop('protocol', DEFAULT_PROTOCOL) + self.copy = kwargs.pop('copy', True) kwargs.setdefault('editable', False) super(PickledObjectField, self).__init__(*args, **kwargs) @@ -150,7 +155,7 @@ def get_db_prep_value(self, value, connection=None, prepared=False): # marshaller (telling it to store it like it would a string), but # since both of these methods result in the same value being stored, # doing things this way is much easier. - value = force_text(dbsafe_encode(value, self.compress, self.protocol)) + value = force_text(dbsafe_encode(value, self.compress, self.protocol, self.copy)) return value def value_to_string(self, obj): @@ -168,6 +173,7 @@ def get_lookup(self, lookup_name): raise TypeError('Lookup type %s is not supported.' % lookup_name) return super(PickledObjectField, self).get_lookup(lookup_name) + # South support; see http://south.aeracode.org/docs/tutorial/part4.html#simple-inheritance try: from south.modelsinspector import add_introspection_rules diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index fb60cfc..c357569 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -17,11 +17,17 @@ D2 = {1: 2, 2: 4, 3: 6, 4: 8, 5: 10} +class TestCopyDataType(str): + def __deepcopy__(self, memo): + raise ValueError('Please dont copy me') + + class TestingModel(models.Model): pickle_field = PickledObjectField() compressed_pickle_field = PickledObjectField(compress=True) 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!')) class MinimalTestingModel(models.Model): @@ -180,3 +186,17 @@ def testSerialization(self): for deserialized_test in serializers.deserialize('json', serialized): self.assertEqual(deserialized_test.object, model) + + def testNoCopy(self): + TestingModel.objects.create( + pickle_field='Copy Me', + compressed_pickle_field='Copy Me', + non_copying_field=TestCopyDataType('Dont Copy Me') + ) + + with self.assertRaises(ValueError): + TestingModel.objects.create( + pickle_field=TestCopyDataType('BOOM!'), + compressed_pickle_field='Copy Me', + non_copying_field='Dont copy me' + ) From 297d7cc5273ca12acfbd6f8b1c38cc7e3243b34d Mon Sep 17 00:00:00 2001 From: Josh Smeaton Date: Sun, 25 Jun 2017 15:46:08 +1000 Subject: [PATCH 48/85] Dropped support for old Python and Django versions. Added support for Python 3.6 --- .travis.yml | 56 +++++++++++++++++++++++---------------- README.rst | 4 +++ src/picklefield/fields.py | 9 ------- tox.ini | 22 +++++++-------- 4 files changed, 47 insertions(+), 44 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8547b7b..d06a969 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,41 @@ -sudo: no - +sudo: false language: python - -env: - - TOXENV=flake8 - - TOXENV=isort - - TOXENV=py27-1.4 - - TOXENV=py27-1.7 - - TOXENV=py27-1.8 - - TOXENV=py27-1.9 - - TOXENV=py27-1.10 - - TOXENV=py27-1.11 - - TOXENV=py32-1.7 - - TOXENV=py32-1.8 - - TOXENV=py33-1.7 - - TOXENV=py33-1.8 - - TOXENV=py34-1.7 - - TOXENV=py34-1.8 - - TOXENV=py34-1.9 - - TOXENV=py34-1.10 - - TOXENV=py34-1.11 - - TOXENV=py34-master - matrix: + include: + - env: TOXENV=flake8 + - env: TOXENV=isort + - env: TOXENV=py27-1.8 + - env: TOXENV=py27-1.9 + - env: TOXENV=py27-1.10 + - env: TOXENV=py27-1.11 + - env: TOXENV=py33-1.8 + python: 3.3 + - env: TOXENV=py34-1.8 + python: 3.4 + - env: TOXENV=py34-1.9 + python: 3.4 + - env: TOXENV=py34-1.10 + python: 3.4 + - env: TOXENV=py34-1.11 + python: 3.4 + - env: TOXENV=py34-master + python: 3.4 + - env: TOXENV=py35-1.11 + python: 3.5 + - env: TOXENV=py35-master + python: 3.5 + - env: TOXENV=py36-1.11 + python: 3.6 + - env: TOXENV=py36-master + python: 3.6 fast_finish: true allow_failures: - env: TOXENV=py34-master + python: 3.4 + - env: TOXENV=py35-master + python: 3.5 + - env: TOXENV=py36-master + python: 3.6 install: - pip install tox coveralls "virtualenv<14.0" diff --git a/README.rst b/README.rst index 8e9b84b..3556779 100644 --- a/README.rst +++ b/README.rst @@ -141,6 +141,10 @@ Changes in version 0.3.3 ======================== * Added a new option to prevent a copy of the object before pickling: `copy=True` +* Dropped support for Django 1.4 +* Dropped support for Django 1.7 +* Dropped support for Python 3.2 +* Added support for Python 3.6 Changes in version 0.3.2 ======================== diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index a66936b..7072915 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -172,12 +172,3 @@ def get_lookup(self, lookup_name): if lookup_name not in ['exact', 'in', 'isnull']: raise TypeError('Lookup type %s is not supported.' % lookup_name) return super(PickledObjectField, self).get_lookup(lookup_name) - - -# South support; see http://south.aeracode.org/docs/tutorial/part4.html#simple-inheritance -try: - from south.modelsinspector import add_introspection_rules -except ImportError: - pass -else: - add_introspection_rules([], [r"^picklefield\.fields\.PickledObjectField"]) diff --git a/tox.ini b/tox.ini index 0dd7a0b..f5bee57 100644 --- a/tox.ini +++ b/tox.ini @@ -1,27 +1,25 @@ [tox] envlist = flake8, - py27-{1.4,1.7,1.8,1.9,1.10,1.11}, - py32-{1.7,1.8}, - py33-{1.7,1.8}, - py34-{1.7,1.8,1.9,1.10,1.11,master} + py27-{1.8,1.9,1.10,1.11}, + py33-{1.8}, + py34-{1.8,1.9,1.10,1.11,master} + py35-{1.8,1.9,1.10,1.11,master} + py36-{1.11,master} [testenv] basepython = py27: python2.7 - py32: python3.2 py33: python3.3 py34: python3.4 + py35: python3.5 + py36: python3.6 usedevelop = true commands = {envpython} -R -Wonce {envbindir}/coverage run {envbindir}/django-admin.py test --pythonpath=. --settings test_settings picklefield coverage report deps = - py32: coverage<4.0 - {py27,py33,py34,py35}: coverage - 1.4: Django>=1.4,<1.5 - 1.4: South - 1.7: Django>=1.7,<1.8 + coverage 1.8: Django>=1.8,<1.9 1.9: Django>=1.9,<1.10 1.10: Django>=1.10,<1.11 @@ -33,11 +31,11 @@ basepython = python2.7 commands = flake8 deps = - flake8==2.4.1 + flake8==3.3.0 [testenv:isort] basepython = python2.7 commands = isort --recursive --check-only --diff src/picklefield deps = - isort==4.1.2 + isort==4.2.5 From 87a55086071a307f40235b921bbfe8f2043f9cb1 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 29 Jun 2017 19:31:42 -0400 Subject: [PATCH 49/85] Remove SubfieldBase shim now that we only support Django 1.8+. --- src/picklefield/compat.py | 9 --------- src/picklefield/fields.py | 5 +++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py index e38997d..924894b 100644 --- a/src/picklefield/compat.py +++ b/src/picklefield/compat.py @@ -1,15 +1,6 @@ -import django -from django.db import models -from django.utils import six - # python 3.x does not have cPickle module try: # cpython 2.x from cPickle import loads, dumps # noqa except ImportError: from pickle import loads, dumps # noqa - -if django.VERSION >= (1, 8): - _PickledObjectField = models.Field -else: - _PickledObjectField = six.with_metaclass(models.SubfieldBase, models.Field) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 7072915..968b66d 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -3,10 +3,11 @@ from copy import deepcopy from zlib import compress, decompress +from django.db import models from django.utils.encoding import force_text from . import DEFAULT_PROTOCOL -from .compat import _PickledObjectField, dumps, loads +from .compat import dumps, loads class PickledObject(str): @@ -70,7 +71,7 @@ def dbsafe_decode(value, compress_object=False): return loads(value) -class PickledObjectField(_PickledObjectField): +class PickledObjectField(models.Field): """ A field that will accept *any* python object and store it in the database. PickledObjectField will optionally compress its values if From 0dcdb064a430f96ef4393db2642b639495ee4e37 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 29 Jun 2017 19:45:34 -0400 Subject: [PATCH 50/85] Bumped version to 1.0.0. --- README.rst | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 3556779..b37bd40 100644 --- a/README.rst +++ b/README.rst @@ -137,7 +137,7 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- -Changes in version 0.3.3 +Changes in version 1.0.0 ======================== * Added a new option to prevent a copy of the object before pickling: `copy=True` diff --git a/setup.py b/setup.py index a2f48de..05d4290 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name='django-picklefield', - version='0.3.2', + version='1.0.0', description='Pickled object field for Django', long_description=long_description, author='Gintautas Miliauskas', From 26e39b10cd87a66a9ee2eea7a116155962e9481f Mon Sep 17 00:00:00 2001 From: Grey Panther Date: Thu, 17 May 2018 16:13:12 +0300 Subject: [PATCH 51/85] Add Django 2 explicitly to the list of tested versions --- .travis.yml | 6 ++++++ tox.ini | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d06a969..35612b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,14 +18,20 @@ matrix: python: 3.4 - env: TOXENV=py34-1.11 python: 3.4 + - env: TOXENV=py34-2.0 + python: 3.4 - env: TOXENV=py34-master python: 3.4 - env: TOXENV=py35-1.11 python: 3.5 + - env: TOXENV=py35-2.0 + python: 3.5 - env: TOXENV=py35-master python: 3.5 - env: TOXENV=py36-1.11 python: 3.6 + - env: TOXENV=py36-2.0 + python: 3.6 - env: TOXENV=py36-master python: 3.6 fast_finish: true diff --git a/tox.ini b/tox.ini index f5bee57..0fe3a7a 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,9 @@ envlist = flake8, py27-{1.8,1.9,1.10,1.11}, py33-{1.8}, - py34-{1.8,1.9,1.10,1.11,master} - py35-{1.8,1.9,1.10,1.11,master} - py36-{1.11,master} + py34-{1.8,1.9,1.10,1.11,2.0,master} + py35-{1.8,1.9,1.10,1.11,2.0,master} + py36-{1.11,2.0,master} [testenv] basepython = @@ -24,6 +24,7 @@ deps = 1.9: Django>=1.9,<1.10 1.10: Django>=1.10,<1.11 1.11: Django>=1.11,<2.0 + 2.0: Django>=2.0,<3.0 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8] From c587b65d4051d2b523d7b7fae5b59020e70bb422 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 18 May 2018 09:55:32 -0400 Subject: [PATCH 52/85] Adjust Django 2.0 tox target. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 0fe3a7a..133a622 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = 1.9: Django>=1.9,<1.10 1.10: Django>=1.10,<1.11 1.11: Django>=1.11,<2.0 - 2.0: Django>=2.0,<3.0 + 2.0: Django>=2.0,<2.1 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8] From cb65ac35e16348c8f7e0761247d4d41612262c3d Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 18 May 2018 10:01:10 -0400 Subject: [PATCH 53/85] Run CI against Django 2.1. --- .travis.yml | 4 ++++ tox.ini | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35612b3..61093c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,12 +26,16 @@ matrix: 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.5 - 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.6 fast_finish: true diff --git a/tox.ini b/tox.ini index 133a622..2d6eea8 100644 --- a/tox.ini +++ b/tox.ini @@ -4,8 +4,8 @@ envlist = py27-{1.8,1.9,1.10,1.11}, py33-{1.8}, py34-{1.8,1.9,1.10,1.11,2.0,master} - py35-{1.8,1.9,1.10,1.11,2.0,master} - py36-{1.11,2.0,master} + py35-{1.8,1.9,1.10,1.11,2.0,2.1,master} + py36-{1.11,2.0,2.1,master} [testenv] basepython = @@ -25,6 +25,7 @@ deps = 1.10: Django>=1.10,<1.11 1.11: Django>=1.11,<2.0 2.0: Django>=2.0,<2.1 + 2.1: Django>=2.1a1,<2.2 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8] From 220f6b3875e5e79e14ef96b6876420f3be3f9687 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 18 May 2018 10:05:36 -0400 Subject: [PATCH 54/85] Drop support for unsupported versions of Django. --- .travis.yml | 13 +------------ setup.py | 4 ++-- src/picklefield/tests.py | 3 --- tox.ini | 7 +++---- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 61093c6..122a2c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,18 +4,7 @@ matrix: include: - env: TOXENV=flake8 - env: TOXENV=isort - - env: TOXENV=py27-1.8 - - env: TOXENV=py27-1.9 - - env: TOXENV=py27-1.10 - env: TOXENV=py27-1.11 - - env: TOXENV=py33-1.8 - python: 3.3 - - env: TOXENV=py34-1.8 - python: 3.4 - - env: TOXENV=py34-1.9 - python: 3.4 - - env: TOXENV=py34-1.10 - python: 3.4 - env: TOXENV=py34-1.11 python: 3.4 - env: TOXENV=py34-2.0 @@ -48,7 +37,7 @@ matrix: python: 3.6 install: - - pip install tox coveralls "virtualenv<14.0" + - pip install tox coveralls script: - tox diff --git a/setup.py b/setup.py index 05d4290..25d2f05 100644 --- a/setup.py +++ b/setup.py @@ -41,8 +41,8 @@ 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', ] ) diff --git a/src/picklefield/tests.py b/src/picklefield/tests.py index c357569..78a9850 100644 --- a/src/picklefield/tests.py +++ b/src/picklefield/tests.py @@ -1,9 +1,7 @@ """Unit tests for django-picklefield.""" import json from datetime import date -from unittest import skipIf -import django from django.core import serializers from django.db import models from django.test import TestCase @@ -161,7 +159,6 @@ def testLookups(self): self.assertEqual(value, model_test.pickle_field) model_test.delete() - @skipIf(django.VERSION[:2] < (1, 5), 'Skip for older Django versions') def testLimitLookupsType(self): """ Test that picklefield supports lookup type limit diff --git a/tox.ini b/tox.ini index 2d6eea8..b2ed24a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,9 @@ [tox] envlist = flake8, - py27-{1.8,1.9,1.10,1.11}, - py33-{1.8}, - py34-{1.8,1.9,1.10,1.11,2.0,master} - py35-{1.8,1.9,1.10,1.11,2.0,2.1,master} + py27-1.11, + py34-{1.11,2.0,master} + py35-{1.11,2.0,2.1,master} py36-{1.11,2.0,2.1,master} [testenv] From fc43e9fae74ffa45c9499b8841876e72bcb7a343 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 18 May 2018 10:09:35 -0400 Subject: [PATCH 55/85] Stop running CI against Django master/Python 3.4. --- .travis.yml | 4 ---- tox.ini | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 122a2c4..5efae06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,6 @@ matrix: python: 3.4 - env: TOXENV=py34-2.0 python: 3.4 - - env: TOXENV=py34-master - python: 3.4 - env: TOXENV=py35-1.11 python: 3.5 - env: TOXENV=py35-2.0 @@ -29,8 +27,6 @@ matrix: python: 3.6 fast_finish: true allow_failures: - - env: TOXENV=py34-master - python: 3.4 - env: TOXENV=py35-master python: 3.5 - env: TOXENV=py36-master diff --git a/tox.ini b/tox.ini index b2ed24a..71947c4 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = flake8, py27-1.11, - py34-{1.11,2.0,master} + py34-{1.11,2.0} py35-{1.11,2.0,2.1,master} py36-{1.11,2.0,2.1,master} From 8ff8e98f059d76470ac407e2e063eaa79581838c Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Fri, 18 May 2018 10:21:53 -0400 Subject: [PATCH 56/85] Bumped version number to 1.1.0. --- README.rst | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b37bd40..2df1ad4 100644 --- a/README.rst +++ b/README.rst @@ -137,6 +137,11 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 1.1.0 +======================== + +* Added support for Django 2.1 and dropped support for Django < 1.11. + Changes in version 1.0.0 ======================== diff --git a/setup.py b/setup.py index 25d2f05..1d938ce 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name='django-picklefield', - version='1.0.0', + version='1.1.0', description='Pickled object field for Django', long_description=long_description, author='Gintautas Miliauskas', From 2c188c4815c868fce2ed38e25bc9d8d994b3d5ae Mon Sep 17 00:00:00 2001 From: Renaud Canarduck Date: Thu, 8 Nov 2018 14:00:26 +0100 Subject: [PATCH 57/85] Fix RemovedInDjango30Warning on Django > 2.0 --- src/picklefield/fields.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/picklefield/fields.py b/src/picklefield/fields.py index 968b66d..e3f19f6 100644 --- a/src/picklefield/fields.py +++ b/src/picklefield/fields.py @@ -3,6 +3,7 @@ from copy import deepcopy from zlib import compress, decompress +import django from django.db import models from django.utils.encoding import force_text @@ -135,8 +136,12 @@ def pre_save(self, model_instance, add): value = super(PickledObjectField, self).pre_save(model_instance, add) return wrap_conflictual_object(value) - def from_db_value(self, value, expression, connection, context): - return self.to_python(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 get_db_prep_value(self, value, connection=None, prepared=False): """ From d019903fa8ced00c5fcde287f22c1754651ca958 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 07:38:12 -0500 Subject: [PATCH 58/85] Removed unused models module. --- src/picklefield/models.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/picklefield/models.py diff --git a/src/picklefield/models.py b/src/picklefield/models.py deleted file mode 100644 index e69de29..0000000 From 3669520f3be77eceb28121eb078f8399ea1b245f Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 07:56:56 -0500 Subject: [PATCH 59/85] Removed stale South reference. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index ebd8cb3..f1690eb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ max-line-length = 119 [isort] combine_as_imports=true include_trailing_comma=true -known_third_party=picklefield,south +known_third_party=picklefield multi_line_output=5 not_skip=__init__.py From 00fc121d5da82d2bebd47c36dac0e770ecb4168c Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 07:59:15 -0500 Subject: [PATCH 60/85] Updated license and ownership details. --- LICENSE | 1 + setup.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index 19459ea..c97bbdd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright (c) 2009-2010 Gintautas Miliauskas +Copyright (c) 2011-2018 Simon Charette Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/setup.py b/setup.py index 1d938ce..9332239 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +# Copyright (c) 2011-2018 Simon Charette # Copyright (c) 2009-2010 Gintautas Miliauskas # Copyright (c) 2009, Taavi Taijala # Copyright (c) 2007, Oliver Beattie @@ -29,11 +30,10 @@ version='1.1.0', description='Pickled object field for Django', long_description=long_description, - author='Gintautas Miliauskas', - author_email='gintautas@miliauskas.lt', + author='Simon Charette', + author_email='charette.s+django-picklefiel@gmail.com', url='http://github.com/gintas/django-picklefield', - packages=find_packages('src'), - package_dir={'': 'src'}, + license='MIT', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Framework :: Django', From 93bc477cf9e914578db998791d1d3ab4b7b9a527 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:00:26 -0500 Subject: [PATCH 61/85] Fixed #25 -- Included README.rst and the LICENSE in the distributed package. --- MANIFEST.in | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..9c8317c --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE +include README.rst \ No newline at end of file From 414c50d977f0de4326dfaa36f9c2028242134551 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:00:55 -0500 Subject: [PATCH 62/85] Massive restructuration of the project layout for easier maintenance. This is the layout used for all the third-party apps I maintain. --- .coveragerc | 2 +- .gitignore | 13 ++-- .travis.yml | 80 +++++++++++++--------- picklefield/__init__.py | 6 ++ picklefield/constants.py | 3 + {src/picklefield => picklefield}/fields.py | 11 ++- setup.py | 18 ++++- src/picklefield/__init__.py | 5 -- src/picklefield/compat.py | 6 -- tests/__init__.py | 0 tests/models.py | 33 +++++++++ test_settings.py => tests/settings.py | 12 ++-- {src/picklefield => tests}/tests.py | 37 ++-------- tox.ini | 33 +++++---- 14 files changed, 148 insertions(+), 111 deletions(-) create mode 100644 picklefield/__init__.py create mode 100644 picklefield/constants.py rename {src/picklefield => picklefield}/fields.py (97%) delete mode 100644 src/picklefield/__init__.py delete mode 100644 src/picklefield/compat.py create mode 100644 tests/__init__.py create mode 100644 tests/models.py rename test_settings.py => tests/settings.py (54%) rename {src/picklefield => tests}/tests.py (90%) diff --git a/.coveragerc b/.coveragerc index 9e2ae7c..7c7a536 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,3 @@ [run] -source = src/picklefield +source = picklefield branch = True diff --git a/.gitignore b/.gitignore index 36d129b..4bd06b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ -*.pyc -*.pyo -/build -/dist -/temp -/src/django_picklefield.egg-info +syntax:glob +*.py[co] +dist/ +django_picklefield.egg-info/* +build/ +.coverage .tox -.coverage \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 5efae06..19c8609 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,42 +1,54 @@ +dist: trusty sudo: false language: python +env: +- TOXENV=flake8 +- TOXENV=isort +cache: pip matrix: - include: - - env: TOXENV=flake8 - - env: TOXENV=isort - - env: TOXENV=py27-1.11 - - env: TOXENV=py34-1.11 - python: 3.4 - - env: TOXENV=py34-2.0 - python: 3.4 - - 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.5 - - 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.6 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 - python: 3.5 - - env: TOXENV=py36-master - python: 3.6 - + - env: TOXENV=py35-master + - env: TOXENV=py36-master + - env: TOXENV=py37-master install: - - pip install tox coveralls - +- pip install tox coveralls script: - - tox - +- tox after_success: - - coveralls +- if [ -f .coverage ]; then coveralls; fi diff --git a/picklefield/__init__.py b/picklefield/__init__.py new file mode 100644 index 0000000..2255686 --- /dev/null +++ b/picklefield/__init__.py @@ -0,0 +1,6 @@ +from __future__ import unicode_literals + +from picklefield.constants import DEFAULT_PROTOCOL +from picklefield.fields import PickledObjectField + +__all__ = 'DEFAULT_PROTOCOL', 'PickledObjectField' \ No newline at end of file diff --git a/picklefield/constants.py b/picklefield/constants.py new file mode 100644 index 0000000..d3c1743 --- /dev/null +++ b/picklefield/constants.py @@ -0,0 +1,3 @@ +from __future__ import unicode_literals + +DEFAULT_PROTOCOL = 2 diff --git a/src/picklefield/fields.py b/picklefield/fields.py similarity index 97% rename from src/picklefield/fields.py rename to picklefield/fields.py index e3f19f6..5a13634 100644 --- a/src/picklefield/fields.py +++ b/picklefield/fields.py @@ -1,4 +1,5 @@ -"""Pickle field implementation for Django.""" +from __future__ import unicode_literals + from base64 import b64decode, b64encode from copy import deepcopy from zlib import compress, decompress @@ -7,8 +8,12 @@ from django.db import models from django.utils.encoding import force_text -from . import DEFAULT_PROTOCOL -from .compat import dumps, loads +from .constants import DEFAULT_PROTOCOL + +try: + from cPickle import loads, dumps +except ImportError: + from pickle import loads, dumps class PickledObject(str): diff --git a/setup.py b/setup.py index 9332239..7145cd8 100644 --- a/setup.py +++ b/setup.py @@ -36,13 +36,29 @@ license='MIT', classifiers=[ 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', 'Framework :: Django', + 'Framework :: Django :: 1.11', + 'Framework :: Django :: 2.0', + 'Framework :: Django :: 2.1', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', - ] + 'Programming Language :: Python :: 3.7', + 'Topic :: Software Development :: Libraries :: Application Frameworks', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], + keywords=['django pickle model field'], + packages=find_packages(exclude=['tests', 'tests.*']), + install_requires=['Django>=1.11'], + extras_require={ + 'tests': ['tox'], + }, ) diff --git a/src/picklefield/__init__.py b/src/picklefield/__init__.py deleted file mode 100644 index c5098e2..0000000 --- a/src/picklefield/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Pickle field implementation for Django.""" - -DEFAULT_PROTOCOL = 2 - -from picklefield.fields import PickledObjectField # noqa diff --git a/src/picklefield/compat.py b/src/picklefield/compat.py deleted file mode 100644 index 924894b..0000000 --- a/src/picklefield/compat.py +++ /dev/null @@ -1,6 +0,0 @@ -# python 3.x does not have cPickle module -try: - # cpython 2.x - from cPickle import loads, dumps # noqa -except ImportError: - from pickle import loads, dumps # noqa diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models.py b/tests/models.py new file mode 100644 index 0000000..cc10344 --- /dev/null +++ b/tests/models.py @@ -0,0 +1,33 @@ +from __future__ import unicode_literals + +from datetime import date + +from django.db import models +from picklefield import PickledObjectField + +S1 = 'Hello World' +T1 = (1, 2, 3, 4, 5) +L1 = [1, 2, 3, 4, 5] +D1 = {1: 1, 2: 4, 3: 6, 4: 8, 5: 10} +D2 = {1: 2, 2: 4, 3: 6, 4: 8, 5: 10} + + +class TestCopyDataType(str): + def __deepcopy__(self, memo): + raise ValueError('Please dont copy me') + + +class TestCustomDataType(str): + pass + + +class TestingModel(models.Model): + pickle_field = PickledObjectField() + compressed_pickle_field = PickledObjectField(compress=True) + 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!')) + + +class MinimalTestingModel(models.Model): + pickle_field = PickledObjectField() diff --git a/test_settings.py b/tests/settings.py similarity index 54% rename from test_settings.py rename to tests/settings.py index 9e70d61..e2a67fa 100644 --- a/test_settings.py +++ b/tests/settings.py @@ -1,13 +1,13 @@ +from __future__ import unicode_literals + +SECRET_KEY = 'not-anymore' + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', - }, + } } INSTALLED_APPS = [ - 'picklefield', + 'tests', ] - -SECRET_KEY = 'local' - -SILENCED_SYSTEM_CHECKS = ['1_7.W001'] diff --git a/src/picklefield/tests.py b/tests/tests.py similarity index 90% rename from src/picklefield/tests.py rename to tests/tests.py index 78a9850..9675620 100644 --- a/src/picklefield/tests.py +++ b/tests/tests.py @@ -1,39 +1,14 @@ -"""Unit tests for django-picklefield.""" import json from datetime import date from django.core import serializers -from django.db import models from django.test import TestCase +from picklefield.fields import dbsafe_encode, wrap_conflictual_object -from .fields import PickledObjectField, dbsafe_encode, wrap_conflictual_object - -S1 = 'Hello World' -T1 = (1, 2, 3, 4, 5) -L1 = [1, 2, 3, 4, 5] -D1 = {1: 1, 2: 4, 3: 6, 4: 8, 5: 10} -D2 = {1: 2, 2: 4, 3: 6, 4: 8, 5: 10} - - -class TestCopyDataType(str): - def __deepcopy__(self, memo): - raise ValueError('Please dont copy me') - - -class TestingModel(models.Model): - pickle_field = PickledObjectField() - compressed_pickle_field = PickledObjectField(compress=True) - 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!')) - - -class MinimalTestingModel(models.Model): - pickle_field = PickledObjectField() - - -class TestCustomDataType(str): - pass +from .models import ( + D1, D2, L1, S1, T1, MinimalTestingModel, TestCopyDataType, + TestCustomDataType, TestingModel, +) class PickledObjectFieldTests(TestCase): @@ -177,7 +152,7 @@ def testSerialization(self): self.assertEqual(data, [{ 'pk': 1, - 'model': 'picklefield.minimaltestingmodel', + 'model': 'tests.minimaltestingmodel', 'fields': {"pickle_field": p}}, ]) diff --git a/tox.ini b/tox.ini index 71947c4..fcd5535 100644 --- a/tox.ini +++ b/tox.ini @@ -1,42 +1,41 @@ [tox] +skipsdist = true +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} + 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} [testenv] basepython = py27: python2.7 - py33: python3.3 py34: python3.4 py35: python3.5 py36: python3.6 + py37: python3.7 usedevelop = true commands = - {envpython} -R -Wonce {envbindir}/coverage run {envbindir}/django-admin.py test --pythonpath=. --settings test_settings picklefield + {envpython} -R -Wonce {envbindir}/coverage run -m django test -v2 --settings=tests.settings {posargs} coverage report deps = coverage - 1.8: Django>=1.8,<1.9 - 1.9: Django>=1.9,<1.10 - 1.10: Django>=1.10,<1.11 1.11: Django>=1.11,<2.0 2.0: Django>=2.0,<2.1 - 2.1: Django>=2.1a1,<2.2 + 2.1: Django>=2.1,<2.2 master: https://github.com/django/django/archive/master.tar.gz [testenv:flake8] +usedevelop = false basepython = python2.7 -commands = - flake8 -deps = - flake8==3.3.0 +commands = flake8 +deps = flake8 [testenv:isort] +usedevelop = false basepython = python2.7 -commands = - isort --recursive --check-only --diff src/picklefield -deps = - isort==4.2.5 +commands = isort --recursive --check-only --diff picklefield tests +deps = isort==4.2.5 From f3a9260444840326e5080ecf6bc672a9927ab287 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:03:00 -0500 Subject: [PATCH 63/85] Use snake case for test method names. --- tests/tests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/tests.py b/tests/tests.py index 9675620..84f5bdb 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -18,7 +18,7 @@ def setUp(self): MinimalTestingModel) return super(PickledObjectFieldTests, self).setUp() - def testDataIntegrity(self): + def test_data_integrity(self): """ Tests that data remains the same when saved to and fetched from the database, whether compression is enabled or not. @@ -43,7 +43,7 @@ def testDataIntegrity(self): self.assertEqual((D1, S1, T1, L1), model_test.default_pickle_field) self.assertEqual(date.today(), model_test.callable_pickle_field) - def testLookups(self): + def test_lookups(self): """ Tests that lookups can be performed on data once stored in the database, whether compression is enabled or not. @@ -134,14 +134,14 @@ def testLookups(self): self.assertEqual(value, model_test.pickle_field) model_test.delete() - def testLimitLookupsType(self): + def test_limit_lookups_type(self): """ Test that picklefield supports lookup type limit """ with self.assertRaisesMessage(TypeError, 'Lookup type gte is not supported'): TestingModel.objects.filter(pickle_field__gte=1) - def testSerialization(self): + def test_serialization(self): model = MinimalTestingModel(pk=1, pickle_field={'foo': 'bar'}) serialized = serializers.serialize('json', [model]) data = json.loads(serialized) @@ -159,7 +159,7 @@ def testSerialization(self): for deserialized_test in serializers.deserialize('json', serialized): self.assertEqual(deserialized_test.object, model) - def testNoCopy(self): + def test_no_copy(self): TestingModel.objects.create( pickle_field='Copy Me', compressed_pickle_field='Copy Me', From 7dcef232808bc88a653088df84aaa32e44da19f7 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:08:08 -0500 Subject: [PATCH 64/85] Exposed package version metadata. --- picklefield/__init__.py | 8 +++++++- setup.py | 12 ++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/picklefield/__init__.py b/picklefield/__init__.py index 2255686..b80dd05 100644 --- a/picklefield/__init__.py +++ b/picklefield/__init__.py @@ -3,4 +3,10 @@ from picklefield.constants import DEFAULT_PROTOCOL from picklefield.fields import PickledObjectField -__all__ = 'DEFAULT_PROTOCOL', 'PickledObjectField' \ No newline at end of file +import django.utils.version + +__all__ = 'VERSION', '__version__', 'DEFAULT_PROTOCOL', 'PickledObjectField' + +VERSION = (1, 1, 0, 'final', 0) + +__version__ = django.utils.version.get_version(VERSION) diff --git a/setup.py b/setup.py index 7145cd8..419dd44 100644 --- a/setup.py +++ b/setup.py @@ -20,14 +20,18 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -import codecs -from setuptools import setup, find_packages +from __future__ import unicode_literals -long_description = codecs.open('README.rst', encoding='utf-8').read() +from setuptools import find_packages, setup + +import picklefield + +with open('README.rst') as file_: + long_description = file_.read() setup( name='django-picklefield', - version='1.1.0', + version=picklefield.__version__, description='Pickled object field for Django', long_description=long_description, author='Simon Charette', From d3e2055b438609fd3346a948768c009dae26715d Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:17:39 -0500 Subject: [PATCH 65/85] Prepared 2.0 release. --- README.rst | 6 ++++++ picklefield/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 2df1ad4..2a83bb1 100644 --- a/README.rst +++ b/README.rst @@ -137,6 +137,12 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 2.0.0 +======================== + +* Silenced RemovedInDjango30Warning warnings on Django 2.0+. +* Restructured project directories. + Changes in version 1.1.0 ======================== diff --git a/picklefield/__init__.py b/picklefield/__init__.py index b80dd05..1a672dc 100644 --- a/picklefield/__init__.py +++ b/picklefield/__init__.py @@ -7,6 +7,6 @@ __all__ = 'VERSION', '__version__', 'DEFAULT_PROTOCOL', 'PickledObjectField' -VERSION = (1, 1, 0, 'final', 0) +VERSION = (2, 0, 0, 'alpha', 0) __version__ = django.utils.version.get_version(VERSION) From 98be5f73944f8a60c8f6c3ee54d3c9ba7de96e4d Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:19:48 -0500 Subject: [PATCH 66/85] Removed bare exception catching. --- picklefield/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picklefield/fields.py b/picklefield/fields.py index 5a13634..80c364a 100644 --- a/picklefield/fields.py +++ b/picklefield/fields.py @@ -127,7 +127,7 @@ def to_python(self, value): if value is not None: try: value = dbsafe_decode(value, self.compress) - except: + except Exception: # If the value is a definite pickle; and an error is raised in # de-pickling it should be allowed to propogate. if isinstance(value, PickledObject): From 7dc7695e8968a749dfd54b8d6ec2e1e89c0b1e35 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:21:23 -0500 Subject: [PATCH 67/85] Adjusted imports and isort configuration. --- picklefield/__init__.py | 6 +++--- setup.cfg | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/picklefield/__init__.py b/picklefield/__init__.py index 1a672dc..c592fd6 100644 --- a/picklefield/__init__.py +++ b/picklefield/__init__.py @@ -1,10 +1,10 @@ from __future__ import unicode_literals -from picklefield.constants import DEFAULT_PROTOCOL -from picklefield.fields import PickledObjectField - import django.utils.version +from .constants import DEFAULT_PROTOCOL +from .fields import PickledObjectField + __all__ = 'VERSION', '__version__', 'DEFAULT_PROTOCOL', 'PickledObjectField' VERSION = (2, 0, 0, 'alpha', 0) diff --git a/setup.cfg b/setup.cfg index f1690eb..b4b3ce2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,6 @@ max-line-length = 119 [isort] combine_as_imports=true include_trailing_comma=true -known_third_party=picklefield multi_line_output=5 not_skip=__init__.py From 4aa0ea67564a8240d5739f8a29a0d23f73f7d2db Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:22:46 -0500 Subject: [PATCH 68/85] Removed duplicated LICENSE header in setup.py. --- setup.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/setup.py b/setup.py index 419dd44..68fa527 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,3 @@ -# Copyright (c) 2011-2018 Simon Charette -# Copyright (c) 2009-2010 Gintautas Miliauskas -# Copyright (c) 2009, Taavi Taijala -# Copyright (c) 2007, Oliver Beattie -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. from __future__ import unicode_literals from setuptools import find_packages, setup From 500ab1f3a3f1ef75945918983b35fa85f0ebac00 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:27:10 -0500 Subject: [PATCH 69/85] Added extra badges to the README. --- README.rst | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 2a83bb1..ac48ef9 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,29 @@ -.. image:: https://travis-ci.org/gintas/django-picklefield.svg?branch=master - :target: https://travis-ci.org/gintas/django-picklefield +django-picklefield +================== -.. image:: https://coveralls.io/repos/gintas/django-picklefield/badge.svg?branch=master&service=github - :target: https://coveralls.io/github/gintas/django-picklefield?branch=master +.. image:: https://img.shields.io/pypi/l/django-picklefield.svg?style=flat + :target: https://pypi.python.org/pypi/django-picklefield/ + :alt: License + +.. image:: https://img.shields.io/pypi/v/django-picklefield.svg?style=flat + :target: https://pypi.python.org/pypi/django-picklefield/ + :alt: Latest Version + +.. image:: https://travis-ci.org/charettes/django-picklefield.svg?branch=master + :target: https://travis-ci.org/charettes/django-picklefield + :alt: Build Status + +.. image:: https://coveralls.io/repos/charettes/django-picklefield/badge.svg?branch=master + :target: https://coveralls.io/r/charettes/django-picklefield?branch=master + :alt: Coverage Status + +.. image:: https://img.shields.io/pypi/pyversions/django-picklefield.svg?style=flat + :target: https://pypi.python.org/pypi/django-picklefield/ + :alt: Supported Python Versions + +.. image:: https://img.shields.io/pypi/wheel/django-picklefield.svg?style=flat + :target: https://pypi.python.org/pypi/django-picklefield/ + :alt: Wheel Status ----- About From dfa4f6e63f3b7a496ea200c3bff9104211707dcd Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:35:21 -0500 Subject: [PATCH 70/85] Made some adjustments to the README. --- README.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ac48ef9..ef3329a 100644 --- a/README.rst +++ b/README.rst @@ -49,12 +49,16 @@ convenience, recent versions should be available from PyPI. To use, just define a field in your model:: +.. code:: python + >>> from picklefield.fields import PickledObjectField ... class SomeObject(models.Model): ... args = PickledObjectField() and assign whatever you like (as long as it's picklable) to the field:: +.. code:: python + >>> obj = SomeObject() >>> obj.args = ['fancy', {'objects': 'inside'}] >>> obj.save() @@ -161,7 +165,8 @@ Changes Changes in version 2.0.0 ======================== -* Silenced RemovedInDjango30Warning warnings on Django 2.0+. +* Silenced ``RemovedInDjango30Warning`` warnings on Django 2.0+ (thanks to + canarduck). * Restructured project directories. Changes in version 1.1.0 From 46e229e4f01a8ee724d6810a643139bc0ce06aa0 Mon Sep 17 00:00:00 2001 From: Grey Panther Date: Thu, 17 May 2018 16:12:03 +0300 Subject: [PATCH 71/85] Disallowed empty strings for PickledObjectField. That enforces the usage of `null=True` to store empty values. --- README.rst | 4 ++++ picklefield/fields.py | 1 + tests/tests.py | 7 ++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ef3329a..1f24c77 100644 --- a/README.rst +++ b/README.rst @@ -168,6 +168,10 @@ Changes in version 2.0.0 * Silenced ``RemovedInDjango30Warning`` warnings on Django 2.0+ (thanks to canarduck). * Restructured project directories. +* Disallowed the usage of empty strings for ``PickledObjectField``. That makes + ``.save()``, ``.create()``, etc. raise ``IntegrityError`` if `null` is not + ``True`` and no default value was specified like built-in fields do + (thanks to Attila-Mihaly Balazs). Changes in version 1.1.0 ======================== diff --git a/picklefield/fields.py b/picklefield/fields.py index 80c364a..745df70 100644 --- a/picklefield/fields.py +++ b/picklefield/fields.py @@ -87,6 +87,7 @@ class PickledObjectField(models.Field): can still do lookups using None). This way, it is still possible to use the ``isnull`` lookup type correctly. """ + empty_strings_allowed = False def __init__(self, *args, **kwargs): self.compress = kwargs.pop('compress', False) diff --git a/tests/tests.py b/tests/tests.py index 84f5bdb..f36f3f4 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -2,6 +2,7 @@ from datetime import date from django.core import serializers +from django.db import IntegrityError from django.test import TestCase from picklefield.fields import dbsafe_encode, wrap_conflictual_object @@ -37,7 +38,7 @@ def test_data_integrity(self): # Make sure the default value for default_pickled_field gets stored # correctly and that it isn't converted to a string. - model_test = TestingModel() + model_test = TestingModel(pickle_field=1, compressed_pickle_field=1) model_test.save() model_test = TestingModel.objects.get(id__exact=model_test.id) self.assertEqual((D1, S1, T1, L1), model_test.default_pickle_field) @@ -172,3 +173,7 @@ def test_no_copy(self): compressed_pickle_field='Copy Me', non_copying_field='Dont copy me' ) + + def test_empty_strings_not_allowed(self): + with self.assertRaises(IntegrityError): + MinimalTestingModel.objects.create() From 05fcbd191db56875cb908added0e54990c914a94 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 09:02:07 -0500 Subject: [PATCH 72/85] Adjusted code examples in the README. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 1f24c77..75c4ac1 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,7 @@ Usage First of all, you need to have **django-picklefield** installed; for your convenience, recent versions should be available from PyPI. -To use, just define a field in your model:: +To use, just define a field in your model: .. code:: python @@ -55,7 +55,7 @@ To use, just define a field in your model:: ... class SomeObject(models.Model): ... args = PickledObjectField() -and assign whatever you like (as long as it's picklable) to the field:: +and assign whatever you like (as long as it's picklable) to the field: .. code:: python From 81323f2a9180390117e2a58d03e05482770bed7e Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 08:59:18 -0500 Subject: [PATCH 73/85] Fixed #34 -- Added a check for mutable PickledObjectField default. --- README.rst | 1 + picklefield/fields.py | 28 ++++++++++++++++++++++ tests/tests.py | 54 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 75c4ac1..9b9b306 100644 --- a/README.rst +++ b/README.rst @@ -172,6 +172,7 @@ Changes in version 2.0.0 ``.save()``, ``.create()``, etc. raise ``IntegrityError`` if `null` is not ``True`` and no default value was specified like built-in fields do (thanks to Attila-Mihaly Balazs). +* Added a check for mutable default values to ``PickledObjectField``. Changes in version 1.1.0 ======================== diff --git a/picklefield/fields.py b/picklefield/fields.py index 745df70..ef811d3 100644 --- a/picklefield/fields.py +++ b/picklefield/fields.py @@ -5,6 +5,7 @@ from zlib import compress, decompress import django +from django.core import checks from django.db import models from django.utils.encoding import force_text @@ -115,6 +116,33 @@ def get_default(self): # If the field doesn't have a default, then we punt to models.Field. return super(PickledObjectField, self).get_default() + def _check_default(self): + if self.has_default() and isinstance(self.default, (list, dict, set)): + return [ + checks.Warning( + "%s default should be a callable instead of a mutable instance so " + "that it's not shared between all field instances." % ( + self.__class__.__name__, + ), + hint=( + 'Use a callable instead, e.g., use `%s` instead of ' + '`%r`.' % ( + type(self.default).__name__, + self.default, + ) + ), + obj=self, + id='picklefield.E001', + ) + ] + else: + return [] + + def check(self, **kwargs): + errors = super(PickledObjectField, self).check(**kwargs) + errors.extend(self._check_default()) + return errors + def to_python(self, value): """ B64decode and unpickle the object, optionally decompressing it. diff --git a/tests/tests.py b/tests/tests.py index f36f3f4..6afe920 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,10 +1,13 @@ import json from datetime import date -from django.core import serializers -from django.db import IntegrityError -from django.test import TestCase -from picklefield.fields import dbsafe_encode, wrap_conflictual_object +from django.core import checks, serializers +from django.db import IntegrityError, models +from django.test import SimpleTestCase, TestCase +from django.test.utils import isolate_apps +from picklefield.fields import ( + PickledObjectField, dbsafe_encode, wrap_conflictual_object, +) from .models import ( D1, D2, L1, S1, T1, MinimalTestingModel, TestCopyDataType, @@ -177,3 +180,46 @@ def test_no_copy(self): def test_empty_strings_not_allowed(self): with self.assertRaises(IntegrityError): MinimalTestingModel.objects.create() + + +@isolate_apps('tests') +class PickledObjectFieldCheckTests(SimpleTestCase): + def test_mutable_default_check(self): + class Model(models.Model): + list_field = PickledObjectField(default=[]) + dict_field = PickledObjectField(default={}) + set_field = PickledObjectField(default=set()) + + msg = ( + "PickledObjectField default should be a callable instead of a mutable instance so " + "that it's not shared between all field instances." + ) + + self.assertEqual(Model().check(), [ + checks.Warning( + msg=msg, + hint='Use a callable instead, e.g., use `list` instead of `[]`.', + obj=Model._meta.get_field('list_field'), + id='picklefield.E001', + ), + checks.Warning( + msg=msg, + hint='Use a callable instead, e.g., use `dict` instead of `{}`.', + obj=Model._meta.get_field('dict_field'), + id='picklefield.E001', + ), + checks.Warning( + msg=msg, + hint='Use a callable instead, e.g., use `set` instead of `%s`.' % repr(set()), + obj=Model._meta.get_field('set_field'), + id='picklefield.E001', + ) + ]) + + def test_non_mutable_default_check(self): + class Model(models.Model): + list_field = PickledObjectField(default=list) + dict_field = PickledObjectField(default=dict) + set_field = PickledObjectField(default=set) + + self.assertEqual(Model().check(), []) From 7d1ebfe24956554821d0a3e766214c7e3af9d89d Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 09:25:33 -0500 Subject: [PATCH 74/85] Allowed TravisCI to upload Pypi package on tags. --- .travis.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 19c8609..ce8b071 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,9 @@ dist: trusty sudo: false language: python env: -- TOXENV=flake8 -- TOXENV=isort + matrix: + - TOXENV=flake8 + - TOXENV=isort cache: pip matrix: fast_finish: true @@ -52,3 +53,13 @@ script: - tox after_success: - if [ -f .coverage ]; then coveralls; fi +before_deploy: "pip install django" +deploy: + provider: pypi + user: charettes + password: + secure: Ix/iLGKu+usjaNGPIKRzvgdZz8TVxReiMFLorgMpHb9P29tgIBQSDzVmo8gzUi+LF4SQs2AdnuYJY15ap68JX7iLfCuWWYIvIqgMLb3W64jfUDg9KpLJQYCv6cTKQHoDVtVTg0jXJmauEYMMYuqmdrSgZ6rSfpsbajMrveCzVuStb7kd5H4WPX3/j4JfCmr07CyAi85rLAnaIfmCYADWC4nrYll7+0Ugke64C4KstY0l6HYBNTT5ZwJvXSNd0VQd31b5okv8bwjGXzeVrbgIAe+c1p0lS11hsNqfLL/rrnlvgC7P7568TxGwWVmIDJm131r1K7skLscEfCuPbJJ3oqljxLQMQIJ4JbR12HtRwB93dOnJOgCbqySAJAZHUvkm2r3VAO8BJK3xYnJg/2IGrxolDQxwJi2SMEgwqQZx3thTFKXvviiWn5dnLjiNRpSOHUzayCVYga7WAgVksKVMbmtcNrJWwrAHtWLxo9vnM2W+6JNFeXiGIIwGu7qJ/Sz/qCuv/uKibbFqhUJfATXf5HhSJ3yIM/Ef04/SSpBqkYcFcrGLdzzW2Bpv4jscv4EcNyGDxnzeJtvXWVceoW0g7XweUn4b5P+NvOSV4YCjJ04BU4r/upV7+ulkQxnCWot2MD0zhvlodldn+6tGfdBbYJ/KlBHQjwdjFFKaxqRkuyA= + distributions: sdist bdist_wheel + on: + tags: true + condition: "$TOXENV = py27-1.11" From 65afd8893dba9d4cdbeeabe5bd93db842be0c866 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 25 Nov 2018 09:42:18 -0500 Subject: [PATCH 75/85] Adjusted README paths of build and coverage statuses. --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 9b9b306..55f8d4d 100644 --- a/README.rst +++ b/README.rst @@ -9,12 +9,12 @@ django-picklefield :target: https://pypi.python.org/pypi/django-picklefield/ :alt: Latest Version -.. image:: https://travis-ci.org/charettes/django-picklefield.svg?branch=master - :target: https://travis-ci.org/charettes/django-picklefield +.. image:: https://travis-ci.org/gintas/django-picklefield.svg?branch=master + :target: https://travis-ci.org/gintas/django-picklefield :alt: Build Status -.. image:: https://coveralls.io/repos/charettes/django-picklefield/badge.svg?branch=master - :target: https://coveralls.io/r/charettes/django-picklefield?branch=master +.. image:: https://coveralls.io/repos/gintas/django-picklefield/badge.svg?branch=master + :target: https://coveralls.io/r/gintas/django-picklefield?branch=master :alt: Coverage Status .. image:: https://img.shields.io/pypi/pyversions/django-picklefield.svg?style=flat From 3775860565d43a77cbf2ba7d46a6ee03d1926175 Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Thu, 23 Jan 2020 21:52:04 +0100 Subject: [PATCH 76/85] Improve travis script & switch to bionic beaver (python 3.8) --- .travis.yml | 61 ++++++++++++++--------------------------------------- tox.ini | 16 +++++++++++--- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce8b071..0319e3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,54 +1,25 @@ -dist: trusty +dist: bionic sudo: false language: python -env: - matrix: - - TOXENV=flake8 - - TOXENV=isort cache: pip -matrix: +python: + - 2.7 + - 3.4 + - 3.5 + - 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/tox.ini b/tox.ini index fcd5535..75db51b 100644 --- a/tox.ini +++ b/tox.ini @@ -6,9 +6,18 @@ envlist = isort, py27-1.11, py34-{1.11,2.0}, - py35-{1.11,2.0,2.1,master}, + py35-{1.11,2.0,2.1}, py36-{1.11,2.0,2.1,master}, - py37-{1.11,2.0,2.1,master} + py37-{1.11,2.0,2.1,master}, + py38-{2.1,master}, + +[tox:travis] +2.7 = py27 +3.4 = py34 +3.5 = py35 +3.6 = py36 +3.7 = py37 +3.8 = py38 [testenv] basepython = @@ -17,9 +26,10 @@ basepython = 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} + {envpython} -R -Wonce {envbindir}/coverage run --branch django test -v2 --settings=tests.settings {posargs} coverage report deps = coverage From 728d084b2d3fae88f2d8be21e05040e7c0302c55 Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Thu, 23 Jan 2020 21:54:09 +0100 Subject: [PATCH 77/85] Improve coverage reporting --- .coveragerc | 5 +++++ picklefield/fields.py | 18 +++++++++--------- tests/models.py | 1 + tests/tests.py | 21 +++++++++++++++++++++ tox.ini | 5 +++-- 5 files changed, 39 insertions(+), 11 deletions(-) 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/picklefield/fields.py b/picklefield/fields.py index ef811d3..45a4f31 100644 --- a/picklefield/fields.py +++ b/picklefield/fields.py @@ -4,7 +4,7 @@ from copy import deepcopy from zlib import compress, decompress -import django +from django import VERSION as DJANGO_VERSION from django.core import checks from django.db import models from django.utils.encoding import force_text @@ -12,9 +12,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 +158,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 +170,12 @@ 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) + if DJANGO_VERSION < (2, 0): + def from_db_value(self, value, expression, connection, context): # pragma: no cover + return self.to_python(value) # pragma: no cover else: - def from_db_value(self, value, expression, connection): - return self.to_python(value) + def from_db_value(self, value, expression, connection): # pragma: no cover + return self.to_python(value) # pragma: no cover def get_db_prep_value(self, value, connection=None, prepared=False): """ 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..1bb42f8 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.assertIsNone(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 75db51b..3d6e98f 100644 --- a/tox.ini +++ b/tox.ini @@ -29,9 +29,10 @@ basepython = py38: python3.8 usedevelop = true commands = - {envpython} -R -Wonce {envbindir}/coverage run --branch 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 From e0da79bf9cd8aa12265f8d2eea140a34f677fca9 Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Thu, 23 Jan 2020 21:57:39 +0100 Subject: [PATCH 78/85] Add support for Django 2.2 --- setup.py | 2 ++ tox.ini | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 68fa527..9d4a7ae 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', @@ -34,6 +35,7 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Software Development :: Libraries :: Application Frameworks', 'Topic :: Software Development :: Libraries :: Python Modules', ], diff --git a/tox.ini b/tox.ini index 3d6e98f..736934d 100644 --- a/tox.ini +++ b/tox.ini @@ -6,10 +6,10 @@ envlist = isort, py27-1.11, py34-{1.11,2.0}, - py35-{1.11,2.0,2.1}, - py36-{1.11,2.0,2.1,master}, - py37-{1.11,2.0,2.1,master}, - py38-{2.1,master}, + py35-{1.11,2.0,2.1,2.2}, + py36-{1.11,2.0,2.1,2.2,master}, + py37-{1.11,2.0,2.1,2.2,master}, + py38-{2.1,2.2,master}, [tox:travis] 2.7 = py27 @@ -37,6 +37,7 @@ deps = 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] From e21c854b833fa44d0c314c3099311d72f16e23b8 Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Thu, 23 Jan 2020 22:00:09 +0100 Subject: [PATCH 79/85] Drop support for Django 2.0 & 2.1 --- setup.py | 2 -- tox.ini | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 9d4a7ae..df67a5b 100644 --- a/setup.py +++ b/setup.py @@ -21,8 +21,6 @@ 'Environment :: Web Environment', 'Framework :: Django', 'Framework :: Django :: 1.11', - 'Framework :: Django :: 2.0', - 'Framework :: Django :: 2.1', 'Framework :: Django :: 2.2', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', diff --git a/tox.ini b/tox.ini index 736934d..14ea5ba 100644 --- a/tox.ini +++ b/tox.ini @@ -5,11 +5,11 @@ envlist = flake8, isort, py27-1.11, - py34-{1.11,2.0}, - py35-{1.11,2.0,2.1,2.2}, - py36-{1.11,2.0,2.1,2.2,master}, - py37-{1.11,2.0,2.1,2.2,master}, - py38-{2.1,2.2,master}, + py34-1.11, + py35-{1.11,2.2}, + py36-{1.11,2.2,master}, + py37-{1.11,2.2,master}, + py38-{2.2,master}, [tox:travis] 2.7 = py27 From 3745f2f34bbb9d95b7db28f4c5d2c62b83e07cc6 Mon Sep 17 00:00:00 2001 From: Fabre Florian Date: Fri, 24 Jan 2020 06:23:28 +0100 Subject: [PATCH 80/85] Drop support of python 3.4 --- .travis.yml | 1 - tox.ini | 3 --- 2 files changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0319e3d..ec59a30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ language: python cache: pip python: - 2.7 - - 3.4 - 3.5 - 3.6 - 3.7 diff --git a/tox.ini b/tox.ini index 14ea5ba..72304b2 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,6 @@ envlist = flake8, isort, py27-1.11, - py34-1.11, py35-{1.11,2.2}, py36-{1.11,2.2,master}, py37-{1.11,2.2,master}, @@ -13,7 +12,6 @@ envlist = [tox:travis] 2.7 = py27 -3.4 = py34 3.5 = py35 3.6 = py36 3.7 = py37 @@ -22,7 +20,6 @@ envlist = [testenv] basepython = py27: python2.7 - py34: python3.4 py35: python3.5 py36: python3.6 py37: python3.7 From ebdbc7b37e59aa8d28c559e28238d183e1dda6c4 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Tue, 28 Jan 2020 09:44:24 -0500 Subject: [PATCH 81/85] Release 2.1.0. --- README.rst | 7 +++++++ picklefield/__init__.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 55f8d4d..6639493 100644 --- a/README.rst +++ b/README.rst @@ -162,6 +162,13 @@ since it is never a good idea to have a PickledObjectField be user editable. Changes ------- +Changes in version 2.1.0 +======================== + +* Added official support for Django 2.2 (thanks to joehybird). +* Dropped support for Django 2.0 and 2.1 (thanks to joehybird). +* Dropped support for Python 3.4 (thanks to joehybidd). + Changes in version 2.0.0 ======================== diff --git a/picklefield/__init__.py b/picklefield/__init__.py index c592fd6..004fd32 100644 --- a/picklefield/__init__.py +++ b/picklefield/__init__.py @@ -7,6 +7,6 @@ __all__ = 'VERSION', '__version__', 'DEFAULT_PROTOCOL', 'PickledObjectField' -VERSION = (2, 0, 0, 'alpha', 0) +VERSION = (2, 1, 0, 'final', 0) __version__ = django.utils.version.get_version(VERSION) From 0532d1662fa179e3294ac79c8fef997b4c9a4cc2 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Tue, 28 Jan 2020 09:57:02 -0500 Subject: [PATCH 82/85] Adjust package building logic to account for recent changes. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ec59a30..aae3926 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,4 +32,4 @@ deploy: distributions: sdist bdist_wheel on: tags: true - condition: "$TOXENV = py27-1.11" + condition: "$TRAVIS_PYTHON_VERSION = 2.7" From 6af2ec72cc9202bc67808d31d96f36a63a76014e Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Tue, 28 Jan 2020 10:00:26 -0500 Subject: [PATCH 83/85] Remove Python 3.4 classifier from supported Python versions. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index df67a5b..24ec734 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,6 @@ 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', From 2405a2afcd4cd2161e1393eaae525779ff6801b3 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Tue, 28 Jan 2020 10:01:12 -0500 Subject: [PATCH 84/85] Release 2.1.1 to account for packaging issues. --- picklefield/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picklefield/__init__.py b/picklefield/__init__.py index 004fd32..2f0479c 100644 --- a/picklefield/__init__.py +++ b/picklefield/__init__.py @@ -7,6 +7,6 @@ __all__ = 'VERSION', '__version__', 'DEFAULT_PROTOCOL', 'PickledObjectField' -VERSION = (2, 1, 0, 'final', 0) +VERSION = (2, 1, 1, 'final', 0) __version__ = django.utils.version.get_version(VERSION) From 1abc2f698bc41e05767dd2864d7bfb8e3d907ce9 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Sun, 8 Mar 2020 15:58:16 +1100 Subject: [PATCH 85/85] docs: Fix simple typo, idential -> identical There is a small typo in tests/tests.py. Should read `identical` rather than `idential`. --- tests/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index 1bb42f8..f162588 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -93,7 +93,7 @@ def test_lookups(self): "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." >>> dumps(deepcopy(t)) "((dp1\nI1\nI1\nsI2\nI4\nsI3\nI6\nsI4\nI8\nsI5\nI10\nsS'Hello World'\np2\n(I1\nI2\nI3\nI4\nI5\ntp3\n(lp4\nI1\naI2\naI3\naI4\naI5\nat." - >>> # Using deepcopy() beforehand means that now both dumps() are idential. + >>> # Using deepcopy() beforehand means that now both dumps() are identical. >>> # It may not be necessary, but deepcopy() ensures that lookups will always work. Unfortunately calling copy() alone doesn't seem to fix the