Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit.

  • Loading branch information...
commit 19695858c7ad5506f1b1dfd9421e36f143efddc3 1 parent 771425c
John Wehr authored
View
2  .gitignore
@@ -0,0 +1,2 @@
+*.pyc
+*.DS_Store*
View
1  AUTHORS
@@ -0,0 +1 @@
+John Wehr <johnwehr@gmail.com>
View
20 LICENSE
@@ -0,0 +1,20 @@
+Everything else:
+http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2011 John Wehr
+
+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.
View
0  README.mkd
No changes.
View
5 cassorm/__init__.py
@@ -0,0 +1,5 @@
+from .list import CassandraList
+from .model import CassandraModel
+from .dict import CassandraDict
+from .types import DateTime, DateTimeString, Float64, FloatString, Int64, IntString, String
+from .tests import CassandraListTest, CassandraModelTest, CassandraDictTest
View
123 cassorm/dict.py
@@ -0,0 +1,123 @@
+from django.conf import settings
+import pycassa
+from pycassa.system_manager import SystemManager
+
+
+MANAGER_MAPS = {}
+
+
+class CassandraKeyError(KeyError):
+ pass
+
+
+class CassandraDictManager(pycassa.ColumnFamily):
+ """Manager for CassandraDict, provides schema creation."""
+
+ def __init__(self, cls):
+ self.cls = cls
+ super(CassandraDictManager, self).__init__(
+ settings.CASSANDRA_POOL,
+ self.cls.__name__.lower())
+
+
+class CassandraMetaDict(type):
+ """Metaclass for CassandraDict, provides access to CassandraDictManager"""
+
+ @property
+ def objects(cls):
+ """CassandraListManager Singletons"""
+ if cls.__name__ not in MANAGER_MAPS:
+ MANAGER_MAPS[id(cls)] = CassandraDictManager(cls)
+ return MANAGER_MAPS[id(cls)]
+
+ def sync(cls, default_validation_class=None, destructive=False):
+ """Create the Cassandra List schema."""
+ if default_validation_class is None:
+ default_validation_class = cls.default_validation_class
+ sys = SystemManager(settings.CASSANDRA_SERVERS[0])
+ if destructive:
+ try:
+ sys.drop_column_family(settings.CASSANDRA_KEYSPACE, cls.__name__.lower())
+ except:
+ pass
+ sys.create_column_family(
+ settings.CASSANDRA_KEYSPACE,
+ cls.__name__.lower(),
+ comparator_type=pycassa.UTF8_TYPE,
+ default_validation_class=default_validation_class)
+ sys.close()
+
+
+class CassandraDict(object):
+ "A dictionary that stores its data persistantly in Cassandra"
+
+ default_validation_class = pycassa.UTF8_TYPE
+
+ __metaclass__ = CassandraMetaDict
+
+ def __init__(self, row_key):
+ self.row_key = row_key
+ self.cf = self.__class__.objects
+
+ def get(self, key, default=None):
+ try:
+ return self.cf.get(self.row_key, columns=[key]).values()[0]
+ except pycassa.NotFoundException, e:
+ if default is None:
+ raise CassandraKeyError(key)
+ else:
+ return default
+
+ def __getitem__(self, key):
+ try:
+ return self.cf.get(self.row_key, columns=[key]).values()[0]
+ except pycassa.NotFoundException, e:
+ raise CassandraKeyError(key)
+
+ def __setitem__(self, key, value):
+ return self.cf.insert(self.row_key, {key:value})
+
+ def __delitem__(self, key):
+ return self.cf.remove(self.row_key, columns=[key])
+
+ def update(self, d):
+ return self.cf.insert(self.row_key, d)
+
+ def keys(self):
+ try:
+ return self.cf.get(self.row_key).keys()
+ except pycassa.NotFoundException, e:
+ return []
+
+ def values(self):
+ try:
+ return self.cf.get(self.row_key).values()
+ except pycassa.NotFoundException, e:
+ return []
+
+ def items(self):
+ try:
+ return self.cf.get(self.row_key)
+ except pycassa.NotFoundException, e:
+ return []
+
+ def iterkeys(self):
+ return self.keys()
+
+ def itervalues(self):
+ return self.values()
+
+ def iteritems(self):
+ return self.items()
+
+ def has_key(self, key):
+ return key in dict(self.items())
+
+ def __contains__(self, key):
+ return self.has_key(key)
+
+ def __len__(self):
+ return self.cf.get_count(self.row_key)
+
+ def __del__(self):
+ self.cf.remove(self.row_key)
View
146 cassorm/list.py
@@ -0,0 +1,146 @@
+import time
+import uuid
+from django.conf import settings
+import pycassa
+from pycassa.util import convert_time_to_uuid, convert_uuid_to_time
+from pycassa.system_manager import SystemManager
+from .exceptions import CassandraIndexError
+
+MANAGER_MAPS = {}
+
+class CassandraIndexError(IndexError):
+ pass
+
+class CassandraListManager(pycassa.ColumnFamily):
+ """Manager for CassandraList."""
+
+ def __init__(self, cls):
+ self.cls = cls
+ super(CassandraListManager, self).__init__(
+ settings.CASSANDRA_POOL,
+ self.cls.__name__.lower())
+
+
+class CassandraMetaList(type):
+ """Metaclass for CassandraList, provides schema creation,
+ access to CassandraListManager"""
+
+ @property
+ def objects(cls):
+ """CassandraListManager Singletons"""
+ if cls.__name__ not in MANAGER_MAPS:
+ MANAGER_MAPS[id(cls)] = CassandraListManager(cls)
+ return MANAGER_MAPS[id(cls)]
+
+ def sync(cls, default_validation_class=None, destructive=False):
+ """Create the Cassandra List schema."""
+ if default_validation_class is None:
+ default_validation_class = cls.default_validation_class
+ sys = SystemManager(settings.CASSANDRA_SERVERS[0])
+ if destructive:
+ try:
+ sys.drop_column_family(settings.CASSANDRA_KEYSPACE, cls.__name__.lower())
+ except:
+ pass
+ sys.create_column_family(
+ settings.CASSANDRA_KEYSPACE,
+ cls.__name__.lower(),
+ comparator_type=pycassa.TIME_UUID_TYPE,
+ default_validation_class=default_validation_class)
+ sys.close()
+
+
+class CassandraList(object):
+
+ default_validation_class = pycassa.UTF8_TYPE
+
+ __metaclass__ = CassandraMetaList
+
+ def __init__(self, row_key):
+ self.row_key = row_key
+ self.cf = self.__class__.objects
+
+ def append(self, x):
+ self.cf.insert(self.row_key, {time.time():x})
+
+ def extend(self, seq):
+ rows = {}
+ start = uuid.UUID("{%s}" % convert_time_to_uuid(time.time()))
+ i = 0
+ for x in seq:
+ rows[uuid.UUID(int=start.int + i)] = unicode(x)
+ i += 10
+ self.cf.insert(self.row_key, rows)
+
+ def insert(self, i, x):
+ try:
+ seq = self.cf.get(self.row_key, column_count=i + 1)
+ except pycassa.NotFoundException, e:
+ self.append(x)
+ return
+ if i >= len(seq):
+ self.append(x)
+ return
+ old_key = seq.keys().pop()
+ old_key_time = convert_uuid_to_time(old_key)
+ low, high = convert_time_to_uuid(old_key_time, randomize=True), \
+ convert_time_to_uuid(old_key_time, randomize=True)
+ if low > high:
+ high, low = low, high
+ while high > old_key:
+ low, high = convert_time_to_uuid(old_key_time, randomize=True), \
+ convert_time_to_uuid(old_key_time, randomize=True)
+ if low > high:
+ high, low = low, high
+ old_value = self.cf.get(self.row_key, columns=[old_key]).values().pop()
+ self.cf.insert(self.row_key, {high:old_value})
+ self.cf.insert(self.row_key, {low:x})
+ self.cf.remove(self.row_key, columns=[old_key])
+
+ def pop(self):
+ try:
+ item = self.cf.get(self.row_key, column_count=1, column_reversed=True)
+ except pycassa.NotFoundException, e:
+ raise CassandraIndexError("pop from empty list")
+ self.cf.remove(self.row_key, columns=item.keys())
+ return item.values()[0]
+
+ def remove(self, x):
+ column_start = ""
+ while 1:
+ try:
+ columns = self.cf.get(self.row_key, column_start=column_start, column_count=100)
+ except pycassa.NotFoundException, e:
+ return
+ for key in columns:
+ if columns[key] == x:
+ self.cf.remove(self.row_key, columns=[key])
+ key = uuid.UUID("{%s}" % key)
+ column_start = uuid.UUID(int=key.int + 1)
+
+ def delete(self):
+ self.cf.remove(self.row_key)
+
+ def __len__(self):
+ return self.cf.get_count(self.row_key)
+
+ def __str__(self):
+ try:
+ return str([x[1] for x in self.cf.get(self.row_key).items()])
+ except pycassa.NotFoundException, e:
+ return str([])
+
+ def __getitem__(self, val):
+ if isinstance(val, slice):
+ if (val.step is None or val.step > 0) and val.stop is not None:
+ seq = [x[1] for x in self.cf.get(self.row_key, column_count=val.stop).items()]
+ return seq[val.start:val.stop:val.step]
+ seq = [x[1] for x in self.cf.get(self.row_key).items()]
+ return seq[val.start:val.stop:val.step]
+ elif isinstance(val, int):
+ try:
+ return self[val:val + 1].pop()
+ except Exception, e:
+ raise CassandraIndexError("List index out of range.")
+ else:
+ return super(CassandraList, self).__getitem__(val)
View
115 cassorm/model.py
@@ -0,0 +1,115 @@
+import time
+from django.conf import settings
+import pycassa
+from pycassa.util import convert_time_to_uuid
+from pycassa.system_manager import SystemManager
+from .exceptions import CassandraIndexError
+from .types import DateTime, DateTimeString, Float64, FloatString, Int64, IntString, String
+from pycassa import BYTES_TYPE, LONG_TYPE, INT_TYPE, ASCII_TYPE, UTF8_TYPE, TIME_UUID_TYPE, LEXICAL_UUID_TYPE
+
+
+MANAGER_MAPS = {}
+
+
+class CassandraMetaModel(type):
+
+ @property
+ def objects(cls):
+ if cls.__name__ not in MANAGER_MAPS:
+ MANAGER_MAPS[id(cls)] = CassandraModelManager(cls)
+ return MANAGER_MAPS[id(cls)]
+
+ def sync(cls, default_validation_class=None, destructive=False):
+ if default_validation_class is None:
+ default_validation_class = cls.default_validation_class
+ columns = []
+ indexes = []
+ column_family = cls.__name__.lower()
+ for i in cls.__dict__:
+ attr = cls.__dict__[i]
+ if isinstance(attr, DateTime) or \
+ isinstance(attr, DateTimeString) or isinstance(attr, Float64) or \
+ isinstance(attr, FloatString) or isinstance(attr, Int64) or \
+ isinstance(attr, IntString) or isinstance(attr, String):
+ columns.append(i)
+ if attr.index:
+ indexes.append(i)
+ sys = SystemManager(settings.CASSANDRA_SERVERS[0])
+ if destructive:
+ try:
+ sys.drop_column_family(settings.CASSANDRA_KEYSPACE, cls.__name__.lower())
+ except:
+ pass
+ sys.create_column_family(
+ settings.CASSANDRA_KEYSPACE,
+ cls.__name__.lower(),
+ comparator_type=UTF8_TYPE,
+ default_validation_class=default_validation_class)
+ for column in columns:
+ attr = cls.__dict__[column]
+ if isinstance(attr, DateTime):
+ type = ASCII_TYPE
+ elif isinstance(attr, DateTimeString):
+ type = ASCII_TYPE
+ elif isinstance(attr, Float64):
+ type = BYTES_TYPE
+ elif isinstance(attr, FloatString):
+ type = ASCII_TYPE
+ elif isinstance(attr, Int64):
+ type = BYTES_TYPE
+ elif isinstance(attr, IntString):
+ type = ASCII_TYPE
+ elif isinstance(attr, String):
+ type = UTF8_TYPE
+ if column in indexes:
+ sys.create_index(
+ settings.CASSANDRA_KEYSPACE,
+ column_family,
+ column.lower(),
+ type,
+ index_name="%s_index" % column)
+ else:
+ sys.alter_column(
+ settings.CASSANDRA_KEYSPACE,
+ column_family,
+ column.lower(),
+ type)
+ sys.close()
+
+
+class CassandraModel(object):
+
+ default_validation_class = UTF8_TYPE
+
+ __metaclass__ = CassandraMetaModel
+
+ def __init__(self, **kwargs):
+ for key in kwargs:
+ setattr(self, key, kwargs[key])
+
+ def save(self):
+ if not hasattr(self, "key"):
+ self.key = str(convert_time_to_uuid(time.time()))
+ self.__class__.objects.insert(self)
+
+
+class CassandraModelManager(pycassa.ColumnFamilyMap):
+
+ def __init__(self, cls, create=False):
+ self.cls = cls
+ try:
+ super(CassandraModelManager, self).__init__(
+ self.cls,
+ pycassa.ColumnFamily(
+ settings.CASSANDRA_POOL,
+ self.cls.__name__.lower()))
+ except:
+ pass
+
+ def __getitem__(self, key):
+ return self.get(key)
+
+ def __setitem__(self, key, value):
+ value.key = key
+ self.insert(value)
+
View
3  cassorm/tests/__init__.py
@@ -0,0 +1,3 @@
+from .list import CassandraListTest
+from .model import CassandraModelTest
+from .dict import CassandraDictTest
View
129 cassorm/tests/dict.py
@@ -0,0 +1,129 @@
+from django.utils import unittest
+from ..dict import CassandraDict
+from .randomseq import random_utf8_seq
+from ..dict import CassandraKeyError
+
+
+class TestDict(CassandraDict):
+ pass
+
+
+class CassandraDictTest(unittest.TestCase):
+
+ def setUp(self):
+ TestDict.sync(destructive=True)
+ self.d = TestDict("Test")
+
+ def test_get(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.assertEqual(self.d.get(key_1, value_2), value_1)
+ self.assertEqual(self.d.get(key_2, value_2), value_2)
+ self.assertNotEqual(self.d.get(key_2, value_2), value_1)
+
+ def test__getitem__(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ self.assertEqual(self.d[key_1], value_1)
+ self.assertEqual(self.d[key_2], value_2)
+ self.assertNotEqual(self.d[key_1], value_2)
+ self.assertNotEqual(self.d[key_2], value_1)
+
+ def test__delitem__(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ self.assertEqual(self.d[key_1], value_1)
+ del self.d[key_1]
+ with self.assertRaises(CassandraKeyError):
+ x = self.d[key_1]
+ self.assertEqual(self.d[key_2], value_2)
+ del self.d[key_2]
+ with self.assertRaises(CassandraKeyError):
+ x = self.d[key_2]
+
+ def testupdate(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ x = {key_1:value_1, key_2:value_2}
+ self.d.update(x)
+ self.assertEqual(self.d[key_1], value_1)
+ self.assertEqual(self.d[key_2], value_2)
+
+ def test_keys(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ self.assertTrue(key_1 in self.d.keys())
+ self.assertTrue(key_2 in self.d.keys())
+
+ def test_values(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ self.assertTrue(value_1 in self.d.values())
+ self.assertTrue(value_2 in self.d.values())
+
+ def test_items(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ x = dict(self.d.items())
+ self.assertEqual(x[key_1], value_1)
+ self.assertEqual(x[key_2], value_2)
+
+ def test_has_key(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ self.assertTrue(self.d.has_key(key_1))
+ self.assertTrue(self.d.has_key(key_2))
+
+ def test__contains__(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ self.assertTrue(key_1 in self.d)
+ self.assertTrue(key_2 in self.d)
+
+ def test__len__(self):
+ key_1 = "".join(random_utf8_seq() for i in range(10))
+ key_2 = "".join(random_utf8_seq() for i in range(10))
+ value_1 = "".join(random_utf8_seq() for i in range(10))
+ value_2 = "".join(random_utf8_seq() for i in range(10))
+ self.d[key_1] = value_1
+ self.d[key_2] = value_2
+ self.assertEqual(len(self.d), 2)
+ del self.d[key_1]
+ self.assertEqual(len(self.d), 1)
+ del self.d[key_2]
+ self.assertEqual(len(self.d), 0)
+
+
View
61 cassorm/tests/list.py
@@ -0,0 +1,61 @@
+from django.utils import unittest
+from ..list import CassandraList
+
+class TestList(CassandraList):
+ pass
+
+class CassandraListTest(unittest.TestCase):
+
+ def setUp(self):
+ TestList.sync(destructive=True)
+ self.seq = TestList("Test")
+
+ def tearDown(self):
+ self.seq.delete()
+
+ def test_append(self):
+ self.seq.append("a")
+ self.seq.append("b")
+ self.assertEqual(len(self.seq), 2)
+ self.assertNotEqual(len(self.seq), 9)
+ self.assertEqual(self.seq.pop(), "b")
+ self.assertEqual(self.seq.pop(), "a")
+ self.assertEqual(len(self.seq), 0)
+ self.seq.append("c")
+ self.assertEqual(self.seq.pop(), "c")
+
+ def test_extend(self):
+ self.seq.extend(["a", "b", "c"])
+ self.assertEqual(len(self.seq), 3)
+ self.assertEqual(self.seq.pop(), "c")
+ self.seq.extend(["d", "e", "f"])
+ self.assertEqual(len(self.seq), 5)
+ self.assertEqual(self.seq.pop(), "f")
+
+ def test_insert(self):
+ self.seq.insert(0, "a")
+ self.seq.insert(0, "b")
+ self.seq.insert(2, "c")
+ self.assertEqual(len(self.seq), 3)
+ self.assertEqual(self.seq.pop(), "c")
+ self.assertEqual(self.seq.pop(), "a")
+ self.assertEqual(self.seq.pop(), "b")
+ self.assertEqual(len(self.seq), 0)
+
+ def test_remove(self):
+ self.seq.extend(["a", "b", "c"])
+ self.seq.remove("b")
+ self.assertEqual(len(self.seq), 2)
+ self.assertEqual(self.seq.pop(), "c")
+ self.assertEqual(self.seq.pop(), "a")
+
+ def test_delete(self):
+ self.seq.delete()
+
+ def test_get(self):
+ self.seq.append("a")
+ self.seq.append("b")
+ self.assertEqual(self.seq[0], "a")
+ self.assertEqual(self.seq[1], "b")
+
+
View
112 cassorm/tests/model.py
@@ -0,0 +1,112 @@
+from datetime import datetime
+import time
+import random
+from django.utils import unittest
+from .randomseq import random_utf8_seq
+from ..model import CassandraModel
+from ..types import *
+
+
+class TestModel(CassandraModel):
+ a = DateTime()
+ b = DateTimeString()
+ c = Float64()
+ d = FloatString()
+ e = Int64()
+ f = IntString()
+ g = String()
+
+class CassandraModelTest(unittest.TestCase):
+
+ def setUp(self):
+ TestModel.sync(destructive=True)
+
+ def test_a(self):
+ tm_1 = TestModel()
+ now = datetime.now()
+ tm_1.a = now
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertAlmostEqual(
+ time.mktime(tm_2.a.timetuple()),
+ time.mktime(now.timetuple()))
+
+ def test_b(self):
+ tm_1 = TestModel()
+ now = datetime.now()
+ tm_1.b = now
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertAlmostEqual(
+ time.mktime(tm_2.b.timetuple()),
+ time.mktime(now.timetuple()))
+
+ def test_c(self):
+ value = random.random()
+ tm_1 = TestModel()
+ tm_1.c = value
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertAlmostEqual(tm_2.c, value)
+
+ def test_d(self):
+ value = random.random()
+ tm_1 = TestModel()
+ tm_1.d = value
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertAlmostEqual(tm_2.d, value)
+
+ def test_e(self):
+ value = random.randint(0, 100000)
+ tm_1 = TestModel()
+ tm_1.e = value
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertEqual(tm_2.e, value)
+
+ def test_f(self):
+ value = random.randint(0, 100000)
+ tm_1 = TestModel()
+ tm_1.f = value
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertEqual(tm_2.f, value)
+
+ def test_g(self):
+ value = "".join(random_utf8_seq() for i in range(10))
+ tm_1 = TestModel()
+ tm_1.g = value
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertEqual(tm_2.g, value)
+
+ def test_all(self):
+ now_a = datetime.now()
+ now_b = datetime.now()
+ value_c = random.random()
+ value_d = random.random()
+ value_e = random.randint(0, 100000)
+ value_f = random.randint(0, 100000)
+ value_g = "".join(random_utf8_seq() for i in range(10))
+ tm_1 = TestModel(
+ a=now_a,
+ b=now_b,
+ c=value_c,
+ d=value_d,
+ e=value_e,
+ f=value_f,
+ g=value_g)
+ tm_1.save()
+ tm_2 = TestModel.objects[tm_1.key]
+ self.assertAlmostEqual(
+ time.mktime(tm_2.a.timetuple()),
+ time.mktime(now_a.timetuple()))
+ self.assertAlmostEqual(
+ time.mktime(tm_2.b.timetuple()),
+ time.mktime(now_a.timetuple()))
+ self.assertAlmostEqual(tm_2.c, value_c)
+ self.assertAlmostEqual(tm_2.d, value_d)
+ self.assertEqual(tm_2.e, value_e)
+ self.assertEqual(tm_2.f, value_f)
+ self.assertEqual(tm_2.g, value_g)
View
28 cassorm/tests/randomseq.py
@@ -0,0 +1,28 @@
+import random
+
+""" From Table 3-7 of the Unicode Standard 5.0.0 """
+
+def byte_range(first, last):
+ return list(range(first, last+1))
+
+first_values = byte_range(0x00, 0x7F) + byte_range(0xC2, 0xF4)
+trailing_values = byte_range(0x80, 0xBF)
+
+def random_utf8_seq():
+ first = random.choice(first_values)
+ if first <= 0x7F:
+ return bytes([first])
+ elif first <= 0xDF:
+ return bytes([first, random.choice(trailing_values)])
+ elif first == 0xE0:
+ return bytes([first, random.choice(byte_range(0xA0, 0xBF)), random.choice(trailing_values)])
+ elif first == 0xED:
+ return bytes([first, random.choice(byte_range(0x80, 0x9F)), random.choice(trailing_values)])
+ elif first <= 0xEF:
+ return bytes([first, random.choice(trailing_values), random.choice(trailing_values)])
+ elif first == 0xF0:
+ return bytes([first, random.choice(byte_range(0x90, 0xBF)), random.choice(trailing_values), random.choice(trailing_values)])
+ elif first <= 0xF3:
+ return bytes([first, random.choice(trailing_values), random.choice(trailing_values), random.choice(trailing_values)])
+ elif first == 0xF4:
+ return bytes([first, random.choice(byte_range(0x80, 0x8F)), random.choice(trailing_values), random.choice(trailing_values)])
View
39 cassorm/types.py
@@ -0,0 +1,39 @@
+import pycassa
+
+def _pycassa_type_init(self, *args, **kwargs):
+ """Stores an index kwarg, then passes to standard pycassa Types"""
+ if "index" in kwargs:
+ self.index = kwargs["index"]
+ del kwargs["index"]
+ else:
+ self.index = False
+ super(self.__class__, self).__init__(*args, **kwargs)
+
+
+class DateTime(pycassa.DateTime):
+ __init__ = _pycassa_type_init
+
+
+class DateTimeString(pycassa.DateTimeString):
+ __init__ = _pycassa_type_init
+
+
+class Float64(pycassa.Float64):
+ __init__ = _pycassa_type_init
+
+
+class FloatString(pycassa.FloatString):
+ __init__ = _pycassa_type_init
+
+
+class Int64(pycassa.Int64):
+ __init__ = _pycassa_type_init
+
+
+class IntString(pycassa.IntString):
+ __init__ = _pycassa_type_init
+
+
+class String(pycassa.String):
+ __init__ = _pycassa_type_init
+
View
275 ez_setup.py
@@ -0,0 +1,275 @@
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from ez_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c9"
+DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+ 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+ 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+ 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+ 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+ 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+ 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+ 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+ 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+ 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+ 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+ 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+ 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+ 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+ 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+ 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+ 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+ 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+ 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+ 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+ 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+ 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+ 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+ 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+ 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+ 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+ 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+ 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+ 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+ 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+ 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+ 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+ 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+ 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+ 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+ if egg_name in md5_data:
+ digest = md5(data).hexdigest()
+ if digest != md5_data[egg_name]:
+ print >>sys.stderr, (
+ "md5 validation of %s failed! (Possible download problem?)"
+ % egg_name
+ )
+ sys.exit(2)
+ return data
+
+def use_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ download_delay=15
+):
+ """Automatically find/download setuptools and make it available on sys.path
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end with
+ a '/'). `to_dir` is the directory where setuptools will be downloaded, if
+ it is not already available. If `download_delay` is specified, it should
+ be the number of seconds that will be paused before initiating a download,
+ should one be required. If an older version of setuptools is installed,
+ this routine will print a message to ``sys.stderr`` and raise SystemExit in
+ an attempt to abort the calling script.
+ """
+ was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+ def do_download():
+ egg = download_setuptools(version, download_base, to_dir, download_delay)
+ sys.path.insert(0, egg)
+ import setuptools; setuptools.bootstrap_install_from = egg
+ try:
+ import pkg_resources
+ except ImportError:
+ return do_download()
+ try:
+ pkg_resources.require("setuptools>="+version); return
+ except pkg_resources.VersionConflict, e:
+ if was_imported:
+ print >>sys.stderr, (
+ "The required version of setuptools (>=%s) is not available, and\n"
+ "can't be installed while this script is running. Please install\n"
+ " a more recent version first, using 'easy_install -U setuptools'."
+ "\n\n(Currently using %r)"
+ ) % (version, e.args[0])
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return do_download()
+ except pkg_resources.DistributionNotFound:
+ return do_download()
+
+def download_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ delay = 15
+):
+ """Download setuptools from a specified location and return its filename
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download attempt.
+ """
+ import urllib2, shutil
+ egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+ url = download_base + egg_name
+ saveto = os.path.join(to_dir, egg_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ from distutils import log
+ if delay:
+ log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help). I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+ %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+ version, download_base, delay, url
+ ); from time import sleep; sleep(delay)
+ log.warn("Downloading %s", url)
+ src = urllib2.urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = _validate_md5(egg_name, src.read())
+ dst = open(saveto,"wb"); dst.write(data)
+ finally:
+ if src: src.close()
+ if dst: dst.close()
+ return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ try:
+ import setuptools
+ except ImportError:
+ egg = None
+ try:
+ egg = download_setuptools(version, delay=0)
+ sys.path.insert(0,egg)
+ from setuptools.command.easy_install import main
+ return main(list(argv)+[egg]) # we're done here
+ finally:
+ if egg and os.path.exists(egg):
+ os.unlink(egg)
+ else:
+ if setuptools.__version__ == '0.0.1':
+ print >>sys.stderr, (
+ "You have an obsolete version of setuptools installed. Please\n"
+ "remove it from your system entirely before rerunning this script."
+ )
+ sys.exit(2)
+
+ req = "setuptools>="+version
+ import pkg_resources
+ try:
+ pkg_resources.require(req)
+ except pkg_resources.VersionConflict:
+ try:
+ from setuptools.command.easy_install import main
+ except ImportError:
+ from easy_install import main
+ main(list(argv)+[download_setuptools(delay=0)])
+ sys.exit(0) # try to force an exit
+ else:
+ if argv:
+ from setuptools.command.easy_install import main
+ main(argv)
+ else:
+ print "Setuptools version",version,"or greater has been installed."
+ print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+ """Update our built-in md5 registry"""
+
+ import re
+
+ for name in filenames:
+ base = os.path.basename(name)
+ f = open(name,'rb')
+ md5_data[base] = md5(f.read()).hexdigest()
+ f.close()
+
+ data = [" %r: %r,\n" % it for it in md5_data.items()]
+ data.sort()
+ repl = "".join(data)
+
+ import inspect
+ srcfile = inspect.getsourcefile(sys.modules[__name__])
+ f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+ match = re.search("\nmd5_data = {\n([^}]+)}", src)
+ if not match:
+ print >>sys.stderr, "Internal error!"
+ sys.exit(2)
+
+ src = src[:match.start(1)] + repl + src[match.end(1):]
+ f = open(srcfile,'w')
+ f.write(src)
+ f.close()
+
+
+if __name__=='__main__':
+ if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+ update_md5(sys.argv[2:])
+ else:
+ main(sys.argv[1:])
+
+
+
+
+
View
36 setup.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+
+from distutils.core import setup
+
+__version_info__ = (0, 0, 1)
+__version__ = '.'.join([str(v) for v in __version_info__])
+
+setup(
+ name = 'cassorm',
+ version = __version__,
+ author = 'John Wehr',
+ author_email = 'johnwehr@gmail.com',
+ maintainer = 'John Wehr',
+ maintainer_email = 'johnwehr@gmail.com',
+ description = 'Django ORM for use with Apache Cassandra',
+ url = 'https://github.com/wehriam/cassorm/',
+ download_url = 'https://github.com/wehriam/cassorm/tarball/master',
+ keywords = 'cassandra client db distributed thrift pycassa django',
+ packages = ['cassorm'],
+ py_modules = ['ez_setup'],
+ requires = ['pycassa'],
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: MIT License',
+ 'Natural Language :: English',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python :: 2.4',
+ 'Programming Language :: Python :: 2.5',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Topic :: Software Development :: Libraries :: Python Modules'
+ ]
+ )
Please sign in to comment.
Something went wrong with that request. Please try again.