Skip to content

Commit

Permalink
[g_sorcery/serialization] fix serialization for collections
Browse files Browse the repository at this point in the history
  • Loading branch information
jauhien committed Apr 18, 2015
1 parent 84e5836 commit 4b58d04
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 73 deletions.
13 changes: 8 additions & 5 deletions g_sorcery/g_collections.py
Expand Up @@ -4,14 +4,14 @@
"""
g_collections.py
~~~~~~~~~~~~~~~~
Customized classes of standard python data types
for use withing g-sorcery for custom formatted string output
substitution in our ebuild templates and classes for storing
information about packages and dependencies.
:copyright: (c) 2013 by Brian Dolbec
:copyright: (c) 2013 by Jauhien Piatlicki
:copyright: (c) 2013-2015 by Jauhien Piatlicki
:license: GPL-2, see LICENSE for more details.
"""

Expand Down Expand Up @@ -52,14 +52,17 @@ class serializable_elist(object):
"""

__slots__ = ('data')

def __init__(self, iterable=None, separator=' '):
'''
iterable: initialize from iterable's items
separator: string used to join list members with for __str__()
'''
self.data = elist(iterable or [], separator)

def __eq__(self, other):
return self.data == other.data

def __iter__(self):
return iter(self.data)

Expand Down Expand Up @@ -122,7 +125,7 @@ class Dependency(object):

def __init__(self, category, package, version="", operator=""):
atom_str = operator + category + "/" + package
if version:
if version:
atom_str += "-" + str(version)
object.__setattr__(self, "atom", portage.dep.Atom(atom_str))
object.__setattr__(self, "category", category)
Expand Down
15 changes: 10 additions & 5 deletions g_sorcery/serialization.py
Expand Up @@ -14,6 +14,8 @@
import json
import importlib

from .compatibility import basestring

def step_to_raw_serializable(obj):
"""
Make one step of convertion of object
Expand All @@ -40,19 +42,22 @@ def to_raw_serializable(obj):
Convert object to the raw serializable type.
Logic is the same as in the standard json encoder.
"""
if isinstance(obj, str) \
if isinstance(obj, basestring) \
or obj is None \
or obj is True \
or obj is False \
or isinstance(obj, int) \
or isinstance(obj, float) \
or isinstance(obj, (list, tuple)) \
or isinstance(obj, dict):
or isinstance(obj, float):
return obj
elif isinstance(obj, dict):
return {k: to_raw_serializable(v) for k, v in obj.items()}
elif isinstance(obj, (list, tuple)):
return [to_raw_serializable(item) for item in obj]

else:
sobj = step_to_raw_serializable(obj)
if not sobj:
raise TypeError('Non serializable object: ', sobj)
raise TypeError('Non serializable object: ', obj)
return to_raw_serializable(sobj)


Expand Down
38 changes: 38 additions & 0 deletions tests/serializable.py
@@ -0,0 +1,38 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
serializable.py
~~~~~~~~~~~~~~~
test classes for serialization
:copyright: (c) 2013-2015 by Jauhien Piatlicki
:license: GPL-2, see LICENSE for more details.
"""

class NonSerializableClass(object):
pass


class SerializableClass(object):

__slots__ = ("field1", "field2")

def __init__(self, field1, field2):
self.field1 = field1
self.field2 = field2

def __eq__(self, other):
return self.field1 == other.field1 \
and self.field2 == other.field2

def serialize(self):
return {"field1": self.field1, "field2": self.field2}


class DeserializableClass(SerializableClass):

@classmethod
def deserialize(cls, value):
return DeserializableClass(value["field1"], value["field2"])
40 changes: 13 additions & 27 deletions tests/test_FileBSON.py
Expand Up @@ -14,7 +14,10 @@
import os
import unittest

from g_sorcery.g_collections import serializable_elist

from tests.base import BaseTest
from tests.serializable import NonSerializableClass, SerializableClass, DeserializableClass

BSON_INSTALLED = False

Expand All @@ -24,33 +27,6 @@
except ImportError as e:
pass

class NonSerializableClass(object):
pass


class SerializableClass(object):

__slots__ = ("field1", "field2")

def __init__(self, field1, field2):
self.field1 = field1
self.field2 = field2

def __eq__(self, other):
return self.field1 == other.field1 \
and self.field2 == other.field2

def serialize(self):
return {"field1": self.field1, "field2": self.field2}


class DeserializableClass(SerializableClass):

@classmethod
def deserialize(cls, value):
return DeserializableClass(value["field1"], value["field2"])


if BSON_INSTALLED:

class TestFileJSON(BaseTest):
Expand Down Expand Up @@ -82,11 +58,21 @@ def test_deserializable(self):
content_r = fj.read()
self.assertEqual(content, content_r)

def test_deserializable_collection(self):
fj = FileBSON(self.directory, self.name, [])
content1 = DeserializableClass("1", "2")
content2 = DeserializableClass("3", "4")
content = serializable_elist([content1, content2])
fj.write(content)
content_r = fj.read()
self.assertEqual(content, content_r)

def suite():
suite = unittest.TestSuite()
suite.addTest(TestFileJSON('test_write_read'))
suite.addTest(TestFileJSON('test_serializable'))
suite.addTest(TestFileJSON('test_deserializable'))
suite.addTest(TestFileJSON('test_deserializable_collection'))
return suite

else:
Expand Down
50 changes: 17 additions & 33 deletions tests/test_FileJSON.py
Expand Up @@ -4,10 +4,10 @@
"""
test_FileJSON.py
~~~~~~~~~~~~~~~~
FileJSON test suite
:copyright: (c) 2013 by Jauhien Piatlicki
:copyright: (c) 2013-2015 by Jauhien Piatlicki
:license: GPL-2, see LICENSE for more details.
"""

Expand All @@ -17,44 +17,18 @@

from g_sorcery.fileutils import FileJSON
from g_sorcery.exceptions import FileJSONError
from g_sorcery.g_collections import serializable_elist

from tests.base import BaseTest


class NonSerializableClass(object):
pass


class SerializableClass(object):

__slots__ = ("field1", "field2")

def __init__(self, field1, field2):
self.field1 = field1
self.field2 = field2

def __eq__(self, other):
return self.field1 == other.field1 \
and self.field2 == other.field2

def serialize(self):
return {"field1": self.field1, "field2": self.field2}


class DeserializableClass(SerializableClass):

@classmethod
def deserialize(cls, value):
return DeserializableClass(value["field1"], value["field2"])

from tests.serializable import NonSerializableClass, SerializableClass, DeserializableClass

class TestFileJSON(BaseTest):
def setUp(self):
super(TestFileJSON, self).setUp()
self.directory = os.path.join(self.tempdir.name, 'tst')
self.name = 'tst.json'
self.path = os.path.join(self.directory, self.name)

def test_read_nonexistent(self):
fj = FileJSON(self.directory, self.name, [])
content = fj.read()
Expand Down Expand Up @@ -99,7 +73,16 @@ def test_deserializable(self):
fj.write(content)
content_r = fj.read()
self.assertEqual(content, content_r)


def test_deserializable_collection(self):
fj = FileJSON(self.directory, self.name, [])
content1 = DeserializableClass("1", "2")
content2 = DeserializableClass("3", "4")
content = serializable_elist([content1, content2])
fj.write(content)
content_r = fj.read()
self.assertEqual(content, content_r)

def suite():
suite = unittest.TestSuite()
suite.addTest(TestFileJSON('test_read_nonexistent'))
Expand All @@ -109,4 +92,5 @@ def suite():
suite.addTest(TestFileJSON('test_write_read'))
suite.addTest(TestFileJSON('test_serializable'))
suite.addTest(TestFileJSON('test_deserializable'))
suite.addTest(TestFileJSON('test_deserializable_collection'))
return suite
11 changes: 8 additions & 3 deletions tests/test_PackageDB.py
Expand Up @@ -18,10 +18,11 @@
from g_sorcery.compatibility import TemporaryDirectory
from g_sorcery.db_layout import JSON_FILE_SUFFIX, BSON_FILE_SUFFIX
from g_sorcery.exceptions import IntegrityError, InvalidKeyError, SyncError
from g_sorcery.g_collections import Package
from g_sorcery.g_collections import Package, serializable_elist
from g_sorcery.package_db import PackageDB

from tests.base import BaseTest
from tests.serializable import DeserializableClass
from tests.server import Server

SUPPORTED_FILE_FORMATS = [JSON_FILE_SUFFIX]
Expand Down Expand Up @@ -50,8 +51,12 @@ def test_functionality(self):
orig_db = PackageDB(orig_path, preferred_category_format=fmt)
orig_db.add_category("app-test1")
orig_db.add_category("app-test2")
ebuild_data = {"test1": "tst1", "test2": "tst2"}
common_data = {"common1": "cmn1", "common2": "cmn2"}
ebuild_data = {"test1": "tst1", "test2": "tst2",
"test3": serializable_elist([DeserializableClass("1", "2"),
DeserializableClass("3", "4")])}
common_data = {"common1": "cmn1", "common2": "cmn2",
"common3": serializable_elist([DeserializableClass("c1", "c2"),
DeserializableClass("c3", "c4")])}
packages = [Package("app-test1", "test", "1"), Package("app-test1", "test", "2"),
Package("app-test1", "test1", "1"), Package("app-test2", "test2", "1")]
for package in packages:
Expand Down

0 comments on commit 4b58d04

Please sign in to comment.