django-bitfield-1.6.4/0000755000076500000240000000000012054755645015321 5ustar dcramerstaff00000000000000django-bitfield-1.6.4/bitfield/0000755000076500000240000000000012054755645017103 5ustar dcramerstaff00000000000000django-bitfield-1.6.4/bitfield/__init__.py0000644000076500000240000000037612033675230021206 0ustar dcramerstaff00000000000000""" django-bitfield ~~~~~~~~~~~~~~~ """ try: VERSION = __import__('pkg_resources') \ .get_distribution('bitfield').version except Exception, e: VERSION = 'unknown' from bitfield.models import Bit, BitHandler, CompositeBitField, BitFielddjango-bitfield-1.6.4/bitfield/admin.py0000644000076500000240000000225612033675161020541 0ustar dcramerstaff00000000000000from django.utils.translation import ugettext_lazy as _ from django.contrib.admin import FieldListFilter from bitfield import Bit class BitFieldListFilter(FieldListFilter): """ BitField list filter. """ def __init__(self, field, request, params, model, model_admin, field_path): self.lookup_kwarg = field_path self.lookup_val = int(request.GET.get(self.lookup_kwarg, 0)) self.flags = field.flags super(BitFieldListFilter, self).__init__(field, request, params, model, model_admin, field_path) def expected_parameters(self): return [self.lookup_kwarg] def choices(self, cl): yield { 'selected': self.lookup_val == 0, 'query_string': cl.get_query_string({ }, [self.lookup_kwarg]), 'display': _('All'), } for number, flag in enumerate(self.flags): bit_mask = Bit(number).mask yield { 'selected': self.lookup_val == bit_mask, 'query_string': cl.get_query_string({ self.lookup_kwarg: bit_mask, }), 'display': flag, } django-bitfield-1.6.4/bitfield/forms.py0000644000076500000240000000306712033675201020573 0ustar dcramerstaff00000000000000from django.forms import CheckboxSelectMultiple, IntegerField, ValidationError from django.utils.encoding import force_unicode from bitfield.types import BitHandler class BitFieldCheckboxSelectMultiple(CheckboxSelectMultiple): def render(self, name, value, attrs=None, choices=()): if isinstance(value, BitHandler): value = [k for k, v in value if v] return super(BitFieldCheckboxSelectMultiple, self).render( name, value, attrs=attrs, choices=enumerate(choices)) def _has_changed(self, initial, data): if initial is None: initial = [] if data is None: data = [] if initial != data: return True initial_set = set([force_unicode(value) for value in initial]) data_set = set([force_unicode(value) for value in data]) return data_set != initial_set class BitFormField(IntegerField): def __init__(self, choices=(), widget=BitFieldCheckboxSelectMultiple, *args, **kwargs): self.widget = widget super(BitFormField, self).__init__(widget=widget, *args, **kwargs) self.choices = self.widget.choices = choices def clean(self, value): if not value: return 0 # Assume an iterable which contains an item per flag that's enabled result = BitHandler(0, [k for k, v in self.choices]) for k in value: try: setattr(result, str(k), True) except AttributeError: raise ValidationError('Unknown choice: %r' % (k,)) return int(result) django-bitfield-1.6.4/bitfield/models.py0000644000076500000240000002111212054755066020732 0ustar dcramerstaff00000000000000from django.db.models import signals from django.db.models.sql.expressions import SQLEvaluator from django.db.models.fields import Field, BigIntegerField from django.db.models.fields.subclassing import Creator try: from django.db.models.fields.subclassing import SubfieldBase except ImportError: # django 1.2 from django.db.models.fields.subclassing import LegacyConnection as SubfieldBase # NOQA from bitfield.forms import BitFormField from bitfield.query import BitQueryLookupWrapper from bitfield.types import BitHandler, Bit # Count binary capacity. Truncate "0b" prefix from binary form. # Twice faster than bin(i)[2:] or math.floor(math.log(i)) MAX_FLAG_COUNT = int(len(bin(BigIntegerField.MAX_BIGINT)) - 2) class BitFieldFlags(object): def __init__(self, flags): if len(flags) > MAX_FLAG_COUNT: raise ValueError('Too many flags') self._flags = flags def __repr__(self): return repr(self._flags) def __iter__(self): for flag in self._flags: yield flag def __getattr__(self, key): if key not in self._flags: raise AttributeError return Bit(self._flags.index(key)) def iteritems(self): for flag in self._flags: yield flag, Bit(self._flags.index(flag)) def iterkeys(self): for flag in self._flags: yield flag def itervalues(self): for flag in self._flags: yield Bit(self._flags.index(flag)) def items(self): return list(self.iteritems()) def keys(self): return list(self.iterkeys()) def values(self): return list(self.itervalues()) class BitFieldCreator(Creator): """ Descriptor for BitFields. Checks to make sure that all flags of the instance match the class. This is to handle the case when caching an older version of the instance and a newer version of the class is available (usually during deploys). """ def __get__(self, obj, type=None): if obj is None: return BitFieldFlags(self.field.flags) retval = obj.__dict__[self.field.name] if self.field.__class__ is BitField: # Update flags from class in case they've changed. retval._keys = self.field.flags return retval class BitFieldMeta(SubfieldBase): """ Modified SubFieldBase to use our contribute_to_class method (instead of monkey-patching make_contrib). This uses our BitFieldCreator descriptor in place of the default. NOTE: If we find ourselves needing custom descriptors for fields, we could make this generic. """ def __new__(cls, name, bases, attrs): def contribute_to_class(self, cls, name): BigIntegerField.contribute_to_class(self, cls, name) setattr(cls, self.name, BitFieldCreator(self)) new_class = super(BitFieldMeta, cls).__new__(cls, name, bases, attrs) new_class.contribute_to_class = contribute_to_class return new_class class BitField(BigIntegerField): __metaclass__ = BitFieldMeta def __init__(self, flags, default=None, *args, **kwargs): if isinstance(flags, dict): # Get only integer keys in correct range valid_keys = (k for k in flags.keys() if isinstance(k, int) and (0 <= k < MAX_FLAG_COUNT)) if not valid_keys: raise ValueError('Wrong keys or empty dictionary') # Fill list with values from dict or with empty values flags = [flags.get(i, '') for i in range(max(valid_keys) + 1)] if len(flags) > MAX_FLAG_COUNT: raise ValueError('Too many flags') if isinstance(default, (list, tuple, set, frozenset)): new_value = 0 for flag in default: new_value |= Bit(flags.index(flag)) default = new_value BigIntegerField.__init__(self, default=default, *args, **kwargs) self.flags = flags def south_field_triple(self): "Returns a suitable description of this field for South." from south.modelsinspector import introspector field_class = "django.db.models.fields.BigIntegerField" args, kwargs = introspector(self) return (field_class, args, kwargs) def formfield(self, form_class=BitFormField, **kwargs): return Field.formfield(self, form_class, choices=[(k, k) for k in self.flags], **kwargs) def pre_save(self, instance, add): value = getattr(instance, self.attname) return value def get_prep_value(self, value): if isinstance(value, (BitHandler, Bit)): value = value.mask return int(value) # def get_db_prep_save(self, value, connection): # if isinstance(value, Bit): # return BitQuerySaveWrapper(self.model._meta.db_table, self.name, value) # return super(BitField, self).get_db_prep_save(value, connection=connection) def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False): if isinstance(value, SQLEvaluator) and isinstance(value.expression, Bit): value = value.expression if isinstance(value, (BitHandler, Bit)): return BitQueryLookupWrapper(self.model._meta.db_table, self.db_column or self.name, value) return BigIntegerField.get_db_prep_lookup(self, lookup_type=lookup_type, value=value, connection=connection, prepared=prepared) def get_prep_lookup(self, lookup_type, value): if isinstance(value, SQLEvaluator) and isinstance(value.expression, Bit): value = value.expression if isinstance(value, Bit): if lookup_type in ('exact',): return value raise TypeError('Lookup type %r not supported with `Bit` type.' % lookup_type) return BigIntegerField.get_prep_lookup(self, lookup_type, value) def to_python(self, value): if isinstance(value, Bit): value = value.mask if not isinstance(value, BitHandler): # Regression for #1425: fix bad data that was created resulting # in negative values for flags. Compute the value that would # have been visible ot the application to preserve compatibility. if isinstance(value, (int, long)) and value < 0: new_value = 0 for bit_number, _ in enumerate(self.flags): new_value |= (value & (2 ** bit_number)) value = new_value value = BitHandler(value, self.flags) else: # Ensure flags are consistent for unpickling value._keys = self.flags return value class CompositeBitFieldWrapper(object): def __init__(self, fields): self.fields = fields def __getattr__(self, attr): if attr == 'fields': return super(CompositeBitFieldWrapper, self).__getattr__(attr) for field in self.fields: if hasattr(field, attr): return getattr(field, attr) raise AttributeError('%s is not a valid flag' % attr) def __hasattr__(self, attr): if attr == 'fields': return super(CompositeBitFieldWrapper, self).__hasattr__(attr) for field in self.fields: if hasattr(field, attr): return True return False def __setattr__(self, attr, value): if attr == 'fields': super(CompositeBitFieldWrapper, self).__setattr__(attr, value) return for field in self.fields: if hasattr(field, attr): setattr(field, attr, value) return raise AttributeError('%s is not a valid flag' % attr) class CompositeBitField(object): def __init__(self, fields): self.fields = fields def contribute_to_class(self, cls, name): self.name = name self.model = cls cls._meta.add_virtual_field(self) signals.class_prepared.connect(self.validate_fields, sender=cls) setattr(cls, name, self) def validate_fields(self, sender, **kwargs): cls = sender model_fields = dict([ (f.name, f) for f in cls._meta.fields if f.name in self.fields]) all_flags = sum([model_fields[f].flags for f in self.fields], ()) if len(all_flags) != len(set(all_flags)): raise ValueError('BitField flags must be unique.') def __get__(self, instance, instance_type=None): fields = [getattr(instance, f) for f in self.fields] return CompositeBitFieldWrapper(fields) def __set__(self, *args, **kwargs): raise NotImplementedError('CompositeBitField cannot be set.') django-bitfield-1.6.4/bitfield/query.py0000644000076500000240000000262012033675161020611 0ustar dcramerstaff00000000000000class BitQueryLookupWrapper(object): def __init__(self, alias, column, bit): self.table_alias = alias self.column = column self.bit = bit def as_sql(self, qn, connection=None): """ Create the proper SQL fragment. This inserts something like "(T0.flags & value) != 0". This will be called by Where.as_sql() """ if self.bit: return ("(%s.%s | %d)" % (qn(self.table_alias), qn(self.column), self.bit.mask), []) return ("(%s.%s & %d)" % (qn(self.table_alias), qn(self.column), self.bit.mask), []) class BitQuerySaveWrapper(BitQueryLookupWrapper): def as_sql(self, qn, connection): """ Create the proper SQL fragment. This inserts something like "(T0.flags & value) != 0". This will be called by Where.as_sql() """ engine = connection.settings_dict['ENGINE'].rsplit('.', -1)[-1] if engine.startswith('postgres'): XOR_OPERATOR = '#' elif engine.startswith('sqlite'): raise NotImplementedError else: XOR_OPERATOR = '^' if self.bit: return ("%s.%s | %d" % (qn(self.table_alias), qn(self.column), self.bit.mask), []) return ("%s.%s %s %d" % (qn(self.table_alias), qn(self.column), XOR_OPERATOR, self.bit.mask), []) django-bitfield-1.6.4/bitfield/tests/0000755000076500000240000000000012054755645020245 5ustar dcramerstaff00000000000000django-bitfield-1.6.4/bitfield/tests/__init__.py0000644000076500000240000000007412033675161022346 0ustar dcramerstaff00000000000000from forms import * from models import * from tests import *django-bitfield-1.6.4/bitfield/tests/forms.py0000644000076500000240000000031712033675161021735 0ustar dcramerstaff00000000000000from django import forms from bitfield.tests.models import BitFieldTestModel, CompositeBitFieldTestModel class BitFieldTestModelForm(forms.ModelForm): class Meta: model = BitFieldTestModel django-bitfield-1.6.4/bitfield/tests/models.py0000644000076500000240000000117212054754762022102 0ustar dcramerstaff00000000000000from django.db import models from bitfield import BitField, CompositeBitField class BitFieldTestModel(models.Model): flags = BitField(flags=( 'FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3', ), default=3, db_column='another_name') class CompositeBitFieldTestModel(models.Model): flags_1 = BitField(flags=( 'FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3', ), default=0) flags_2 = BitField(flags=( 'FLAG_4', 'FLAG_5', 'FLAG_6', 'FLAG_7', ), default=0) flags = CompositeBitField(( 'flags_1', 'flags_2', )) django-bitfield-1.6.4/bitfield/tests/tests.py0000644000076500000240000004006612054755025021757 0ustar dcramerstaff00000000000000import pickle from django.db import connection, models from django.db.models import F from django.test import TestCase from bitfield import BitHandler, Bit, BitField from bitfield.tests import BitFieldTestModel, CompositeBitFieldTestModel, BitFieldTestModelForm class BitHandlerTest(TestCase): def test_defaults(self): bithandler = BitHandler(0, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) # Default value of 0. self.assertEquals(int(bithandler), 0) # Test bit numbers. self.assertEquals(int(bithandler.FLAG_0.number), 0) self.assertEquals(int(bithandler.FLAG_1.number), 1) self.assertEquals(int(bithandler.FLAG_2.number), 2) self.assertEquals(int(bithandler.FLAG_3.number), 3) # Negative test non-existant key. self.assertRaises(AttributeError, lambda: bithandler.FLAG_4) # Test bool(). self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) def test_nonzero_default(self): bithandler = BitHandler(1, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(2, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), True) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(3, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), True) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(4, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), True) self.assertEquals(bool(bithandler.FLAG_3), False) def test_mutation(self): bithandler = BitHandler(0, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(bithandler | 1, bithandler._keys) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler ^= 3 self.assertEquals(int(bithandler), 2) self.assertEquals(bool(bithandler & 1), False) bithandler.FLAG_0 = False self.assertEquals(bithandler.FLAG_0, False) bithandler.FLAG_1 = True self.assertEquals(bithandler.FLAG_0, False) self.assertEquals(bithandler.FLAG_1, True) bithandler.FLAG_2 = False self.assertEquals(bithandler.FLAG_0, False) self.assertEquals(bithandler.FLAG_1, True) self.assertEquals(bithandler.FLAG_2, False) class BitTest(TestCase): def test_int(self): bit = Bit(0) self.assertEquals(int(bit), 1) self.assertEquals(bool(bit), True) self.assertFalse(not bit) def test_comparison(self): self.assertEquals(Bit(0), Bit(0)) self.assertNotEquals(Bit(1), Bit(0)) self.assertNotEquals(Bit(0, 0), Bit(0, 1)) self.assertEquals(Bit(0, 1), Bit(0, 1)) self.assertEquals(Bit(0), 1) def test_and(self): self.assertEquals(1 & Bit(2), 0) self.assertEquals(1 & Bit(0), 1) self.assertEquals(1 & ~Bit(0), 0) self.assertEquals(Bit(0) & Bit(2), 0) self.assertEquals(Bit(0) & Bit(0), 1) self.assertEquals(Bit(0) & ~Bit(0), 0) def test_or(self): self.assertEquals(1 | Bit(2), 5) self.assertEquals(1 | Bit(5), 33) self.assertEquals(1 | ~Bit(2), -5) self.assertEquals(Bit(0) | Bit(2), 5) self.assertEquals(Bit(0) | Bit(5), 33) self.assertEquals(Bit(0) | ~Bit(2), -5) def test_xor(self): self.assertEquals(1 ^ Bit(2), 5) self.assertEquals(1 ^ Bit(0), 0) self.assertEquals(1 ^ Bit(1), 3) self.assertEquals(1 ^ Bit(5), 33) self.assertEquals(1 ^ ~Bit(2), -6) self.assertEquals(Bit(0) ^ Bit(2), 5) self.assertEquals(Bit(0) ^ Bit(0), 0) self.assertEquals(Bit(0) ^ Bit(1), 3) self.assertEquals(Bit(0) ^ Bit(5), 33) self.assertEquals(Bit(0) ^ ~Bit(2), -6) class BitFieldTest(TestCase): def test_basic(self): # Create instance and make sure flags are working properly. instance = BitFieldTestModel.objects.create(flags=1) self.assertTrue(instance.flags.FLAG_0) self.assertFalse(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_2) self.assertFalse(instance.flags.FLAG_3) def test_regression_1425(self): # Creating new instances shouldn't allow negative values. instance = BitFieldTestModel.objects.create(flags=-1) self.assertEqual(instance.flags._value, 15) self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_2) self.assertTrue(instance.flags.FLAG_3) cursor = connection.cursor() flags_field = BitFieldTestModel._meta.get_field_by_name('flags')[0] flags_db_column = flags_field.db_column or flags_field.name cursor.execute("INSERT INTO %s (%s) VALUES (-1)" % (BitFieldTestModel._meta.db_table, flags_db_column)) # There should only be the one row we inserted through the cursor. instance = BitFieldTestModel.objects.get(flags=-1) self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_2) self.assertTrue(instance.flags.FLAG_3) instance.save() self.assertEqual(BitFieldTestModel.objects.filter(flags=15).count(), 2) self.assertEqual(BitFieldTestModel.objects.filter(flags__lt=0).count(), 0) def test_select(self): BitFieldTestModel.objects.create(flags=3) self.assertTrue(BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_1).exists()) self.assertTrue(BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_0).exists()) self.assertFalse(BitFieldTestModel.objects.exclude(flags=BitFieldTestModel.flags.FLAG_0).exists()) self.assertFalse(BitFieldTestModel.objects.exclude(flags=BitFieldTestModel.flags.FLAG_1).exists()) def test_update(self): instance = BitFieldTestModel.objects.create(flags=0) self.assertFalse(instance.flags.FLAG_0) BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=F('flags') | BitFieldTestModel.flags.FLAG_1) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertTrue(instance.flags.FLAG_1) BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=F('flags') | ((~BitFieldTestModel.flags.FLAG_0 | BitFieldTestModel.flags.FLAG_3))) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertFalse(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_3) self.assertFalse(BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_0).exists()) BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=F('flags') & ~BitFieldTestModel.flags.FLAG_3) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertFalse(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_3) def test_update_with_handler(self): instance = BitFieldTestModel.objects.create(flags=0) self.assertFalse(instance.flags.FLAG_0) instance.flags.FLAG_1 = True BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=F('flags') | instance.flags) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertTrue(instance.flags.FLAG_1) def test_negate(self): BitFieldTestModel.objects.create(flags=BitFieldTestModel.flags.FLAG_0 | BitFieldTestModel.flags.FLAG_1) BitFieldTestModel.objects.create(flags=BitFieldTestModel.flags.FLAG_1) self.assertEqual(BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_0).count(), 1) self.assertEqual(BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_1).count(), 0) self.assertEqual(BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_2).count(), 2) def test_default_value(self): instance = BitFieldTestModel.objects.create() self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_2) self.assertFalse(instance.flags.FLAG_3) def test_binary_capacity(self): import math from django.db.models.fields import BigIntegerField # Local maximum value, slow canonical algorithm MAX_COUNT = int(math.floor(math.log(BigIntegerField.MAX_BIGINT, 2))) # Big flags list flags = ['f' + str(i) for i in range(100)] try: BitField(flags=flags[:MAX_COUNT]) except ValueError: self.fail("It should work well with these flags") self.assertRaises(ValueError, BitField, flags=flags[:(MAX_COUNT + 1)]) def test_dictionary_init(self): flags = { 0: 'zero', 1: 'first', 10: 'tenth', 2: 'second', 'wrongkey': 'wrongkey', 100: 'bigkey', -100: 'smallkey', } try: bf = BitField(flags) except ValueError: self.fail("It should work well with these flags") self.assertEquals(bf.flags, ['zero', 'first', 'second', '', '', '', '', '', '', '', 'tenth']) self.assertRaises(ValueError, BitField, flags={}) self.assertRaises(ValueError, BitField, flags={'wrongkey': 'wrongkey'}) self.assertRaises(ValueError, BitField, flags={'1': 'non_int_key'}) def test_defaults_as_key_names(self): class TestModel(models.Model): flags = BitField(flags=( 'FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3', ), default=('FLAG_1', 'FLAG_2')) field = TestModel._meta.get_field('flags') self.assertEquals(field.default, TestModel.flags.FLAG_1 | TestModel.flags.FLAG_2) class BitFieldSerializationTest(TestCase): def test_can_unserialize_bithandler(self): data = "cdjango.db.models.base\nmodel_unpickle\np0\n(cbitfield.tests.models\nBitFieldTestModel\np1\n(lp2\ncdjango.db.models.base\nsimple_class_factory\np3\ntp4\nRp5\n(dp6\nS'flags'\np7\nccopy_reg\n_reconstructor\np8\n(cbitfield.types\nBitHandler\np9\nc__builtin__\nobject\np10\nNtp11\nRp12\n(dp13\nS'_value'\np14\nI1\nsS'_keys'\np15\n(S'FLAG_0'\np16\nS'FLAG_1'\np17\nS'FLAG_2'\np18\nS'FLAG_3'\np19\ntp20\nsbsS'_state'\np21\ng8\n(cdjango.db.models.base\nModelState\np22\ng10\nNtp23\nRp24\n(dp25\nS'adding'\np26\nI00\nsS'db'\np27\nS'default'\np28\nsbsS'id'\np29\nI1\nsb." inst = pickle.loads(data) self.assertTrue(inst.flags.FLAG_0) self.assertFalse(inst.flags.FLAG_1) def test_pickle_integration(self): inst = BitFieldTestModel.objects.create(flags=1) data = pickle.dumps(inst) inst = pickle.loads(data) self.assertEquals(type(inst.flags), BitHandler) self.assertEquals(int(inst.flags), 1) def test_added_field(self): data = "cdjango.db.models.base\nmodel_unpickle\np0\n(cbitfield.tests.models\nBitFieldTestModel\np1\n(lp2\ncdjango.db.models.base\nsimple_class_factory\np3\ntp4\nRp5\n(dp6\nS'flags'\np7\nccopy_reg\n_reconstructor\np8\n(cbitfield.types\nBitHandler\np9\nc__builtin__\nobject\np10\nNtp11\nRp12\n(dp13\nS'_value'\np14\nI1\nsS'_keys'\np15\n(S'FLAG_0'\np16\nS'FLAG_1'\np17\nS'FLAG_2'\np18\ntp19\nsbsS'_state'\np20\ng8\n(cdjango.db.models.base\nModelState\np21\ng10\nNtp22\nRp23\n(dp24\nS'adding'\np25\nI00\nsS'db'\np27\nS'default'\np27\nsbsS'id'\np28\nI1\nsb." inst = pickle.loads(data) self.assertTrue('FLAG_3' in inst.flags.keys()) class CompositeBitFieldTest(TestCase): def test_get_flag(self): inst = CompositeBitFieldTestModel() self.assertEqual(inst.flags.FLAG_0, inst.flags_1.FLAG_0) self.assertEqual(inst.flags.FLAG_4, inst.flags_2.FLAG_4) self.assertRaises(AttributeError, lambda: inst.flags.flag_NA) def test_set_flag(self): inst = CompositeBitFieldTestModel() flag_0_original = bool(inst.flags.FLAG_0) self.assertEqual(bool(inst.flags_1.FLAG_0), flag_0_original) flag_4_original = bool(inst.flags.FLAG_4) self.assertEqual(bool(inst.flags_2.FLAG_4), flag_4_original) # flip flags' bits inst.flags.FLAG_0 = not flag_0_original inst.flags.FLAG_4 = not flag_4_original # check to make sure the bit flips took effect self.assertNotEqual(bool(inst.flags.FLAG_0), flag_0_original) self.assertNotEqual(bool(inst.flags_1.FLAG_0), flag_0_original) self.assertNotEqual(bool(inst.flags.FLAG_4), flag_4_original) self.assertNotEqual(bool(inst.flags_2.FLAG_4), flag_4_original) def set_flag(): inst.flags.flag_NA = False self.assertRaises(AttributeError, set_flag) def test_hasattr(self): inst = CompositeBitFieldTestModel() self.assertEqual(hasattr(inst.flags, 'flag_0'), hasattr(inst.flags_1, 'flag_0')) self.assertEqual(hasattr(inst.flags, 'flag_4'), hasattr(inst.flags_2, 'flag_4')) class BitFormFieldTest(TestCase): def test_form_new_invalid(self): invalid_data_dicts = [ {'flags': ['FLAG_0', 'FLAG_FLAG']}, {'flags': ['FLAG_4']}, {'flags': [1, 2]} ] for invalid_data in invalid_data_dicts: form = BitFieldTestModelForm(data=invalid_data) self.assertFalse(form.is_valid()) def test_form_new(self): data_dicts = [ {'flags': ['FLAG_0', 'FLAG_1']}, {'flags': ['FLAG_3']}, {'flags': []}, {} ] for data in data_dicts: form = BitFieldTestModelForm(data=data) self.failUnless(form.is_valid()) instance = form.save() flags = data['flags'] if 'flags' in data else [] for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in flags) def test_form_update(self): instance = BitFieldTestModel.objects.create(flags=0) for k in BitFieldTestModel.flags: self.assertFalse(bool(getattr(instance.flags, k))) data = {'flags': ['FLAG_0', 'FLAG_1']} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in data['flags']) data = {'flags': ['FLAG_2', 'FLAG_3']} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in data['flags']) data = {'flags': []} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertFalse(bool(getattr(instance.flags, k))) django-bitfield-1.6.4/bitfield/types.py0000644000076500000240000001324712054754540020621 0ustar dcramerstaff00000000000000class Bit(object): """ Represents a single Bit. """ def __init__(self, number, is_set=True): self.number = number self.is_set = bool(is_set) self.mask = 2 ** int(number) if not self.is_set: self.mask = ~self.mask def __repr__(self): return '<%s: number=%d, is_set=%s>' % (self.__class__.__name__, self.number, self.is_set) # def __str__(self): # if self.is_set: # return 'Yes' # return 'No' def __int__(self): return self.mask def __nonzero__(self): return self.is_set def __eq__(self, value): if isinstance(value, Bit): return value.number == self.number and value.is_set == self.is_set elif isinstance(value, bool): return value == self.is_set elif isinstance(value, int): return value == self.mask return value == self.is_set def __ne__(self, value): return not self == value def __coerce__(self, value): return (self.is_set, bool(value)) def __invert__(self): return self.__class__(self.number, not self.is_set) def __and__(self, value): if isinstance(value, Bit): value = value.mask return value & self.mask def __rand__(self, value): if isinstance(value, Bit): value = value.mask return self.mask & value def __or__(self, value): if isinstance(value, Bit): value = value.mask return value | self.mask def __ror__(self, value): if isinstance(value, Bit): value = value.mask return self.mask | value def __lshift__(self, value): if isinstance(value, Bit): value = value.mask return value << self.mask def __rlshift__(self, value): if isinstance(value, Bit): value = value.mask return self.mask << value def __rshift__(self, value): if isinstance(value, Bit): value = value.mask return value >> self.mask def __rrshift__(self, value): if isinstance(value, Bit): value = value.mask return self.mask >> value def __xor__(self, value): if isinstance(value, Bit): value = value.mask return value ^ self.mask def __rxor__(self, value): if isinstance(value, Bit): value = value.mask return self.mask ^ value def __sentry__(self): return repr(self) def prepare(self, evaluator, query, allow_joins): return self def evaluate(self, evaluator, qn, connection): return self.mask, [] class BitHandler(object): """ Represents an array of bits, each as a ``Bit`` object. """ def __init__(self, value, keys): # TODO: change to bitarray? if value: self._value = int(value) else: self._value = 0 self._keys = keys def __eq__(self, other): if not isinstance(other, BitHandler): return False return self._value == other._value def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, ', '.join('%s=%s' % (k, self.get_bit(n).is_set) for n, k in enumerate(self._keys)),) def __str__(self): return str(self._value) def __int__(self): return self._value def __nonzero__(self): return bool(self._value) def __and__(self, value): return BitHandler(self._value & int(value), self._keys) def __or__(self, value): return BitHandler(self._value | int(value), self._keys) def __add__(self, value): return BitHandler(self._value + int(value), self._keys) def __sub__(self, value): return BitHandler(self._value - int(value), self._keys) def __lshift__(self, value): return BitHandler(self._value << int(value), self._keys) def __rshift__(self, value): return BitHandler(self._value >> int(value), self._keys) def __xor__(self, value): return BitHandler(self._value ^ int(value), self._keys) def __contains__(self, key): bit_number = self._keys.index(key) return bool(self.get_bit(bit_number)) def __getattr__(self, key): if key.startswith('_'): return object.__getattribute__(self, key) if key not in self._keys: raise AttributeError('%s is not a valid flag' % key) return self.get_bit(self._keys.index(key)) def __setattr__(self, key, value): if key.startswith('_'): return object.__setattr__(self, key, value) if key not in self._keys: raise AttributeError('%s is not a valid flag' % key) self.set_bit(self._keys.index(key), value) def __iter__(self): return self.iteritems() def __sentry__(self): return repr(self) def _get_mask(self): return self._value mask = property(_get_mask) def prepare(self, evaluator, query, allow_joins): return self def evaluate(self, evaluator, qn, connection): return self.mask, [] def get_bit(self, bit_number): mask = 2 ** int(bit_number) return Bit(bit_number, self._value & mask != 0) def set_bit(self, bit_number, true_or_false): mask = 2 ** int(bit_number) if true_or_false: self._value |= mask else: self._value &= (~mask) return Bit(bit_number, self._value & mask != 0) def keys(self): return self._keys def iterkeys(self): return iter(self._keys) def items(self): return list(self.iteritems()) def iteritems(self): for k in self._keys: yield (k, getattr(self, k).is_set) django-bitfield-1.6.4/django_bitfield.egg-info/0000755000076500000240000000000012054755645022117 5ustar dcramerstaff00000000000000django-bitfield-1.6.4/django_bitfield.egg-info/dependency_links.txt0000644000076500000240000000000112054755645026165 0ustar dcramerstaff00000000000000 django-bitfield-1.6.4/django_bitfield.egg-info/not-zip-safe0000644000076500000240000000000112033675311024331 0ustar dcramerstaff00000000000000 django-bitfield-1.6.4/django_bitfield.egg-info/PKG-INFO0000644000076500000240000000072212054755645023215 0ustar dcramerstaff00000000000000Metadata-Version: 1.0 Name: django-bitfield Version: 1.6.4 Summary: BitField in Django Home-page: http://github.com/disqus/django-bitfield Author: DISQUS Author-email: opensource@disqus.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development django-bitfield-1.6.4/django_bitfield.egg-info/requires.txt0000644000076500000240000000001312054755645024511 0ustar dcramerstaff00000000000000Django>=1.2django-bitfield-1.6.4/django_bitfield.egg-info/SOURCES.txt0000644000076500000240000000074312054755645024007 0ustar dcramerstaff00000000000000LICENSE MANIFEST.in README.rst setup.py bitfield/__init__.py bitfield/admin.py bitfield/forms.py bitfield/models.py bitfield/query.py bitfield/types.py bitfield/tests/__init__.py bitfield/tests/forms.py bitfield/tests/models.py bitfield/tests/tests.py django_bitfield.egg-info/PKG-INFO django_bitfield.egg-info/SOURCES.txt django_bitfield.egg-info/dependency_links.txt django_bitfield.egg-info/not-zip-safe django_bitfield.egg-info/requires.txt django_bitfield.egg-info/top_level.txtdjango-bitfield-1.6.4/django_bitfield.egg-info/top_level.txt0000644000076500000240000000001112054755645024641 0ustar dcramerstaff00000000000000bitfield django-bitfield-1.6.4/LICENSE0000644000076500000240000002511512033675161016321 0ustar dcramerstaff00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2010 DISQUS Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.django-bitfield-1.6.4/MANIFEST.in0000644000076500000240000000007512033675161017050 0ustar dcramerstaff00000000000000include setup.py README MANIFEST.in LICENSE global-exclude *~django-bitfield-1.6.4/PKG-INFO0000644000076500000240000000072212054755645016417 0ustar dcramerstaff00000000000000Metadata-Version: 1.0 Name: django-bitfield Version: 1.6.4 Summary: BitField in Django Home-page: http://github.com/disqus/django-bitfield Author: DISQUS Author-email: opensource@disqus.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development django-bitfield-1.6.4/README.rst0000644000076500000240000000454112033675161017003 0ustar dcramerstaff00000000000000django-bitfield --------------- .. image:: https://secure.travis-ci.org/ahref/django-bitfield.png?branch=master Provides a BitField like class (using a BigIntegerField) for your Django models. (If you're upgrading from a version before 1.2 the API has changed greatly and is backwards incompatible!) Requirements ============ * Django >= 1.2 Note: SQLite does not support save operations using a ``Bit`` (per the example under Usage) Installation ============ Install it with pip (or easy_install):: pip install django-bitfield Usage ===== First you'll need to attach a BitField to your class. This acts as a BigIntegerField (BIGINT) in your database:: from bitfield import BitField class MyModel(models.Model): flags = BitField(flags=( 'awesome_flag', 'flaggy_foo', 'baz_bar', )) Now you can use the field using very familiar Django operations:: # Create the model o = MyModel.objects.create(flags=0) # Add awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags') | MyModel.flags.awesome_flag) # Set flags manually to [awesome_flag, flaggy_foo] MyModel.objects.filter(pk=o.pk).update(flags=MyModel.flags.awesome_flag | MyModel.flags.flaggy_foo) # Remove awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags') & ~MyModel.flags.awesome_flag) # Find by awesome_flag MyModel.objects.filter(flags=MyModel.flags.awesome_flag) # Exclude by awesome_flag MyModel.objects.filter(flags=~MyModel.flags.awesome_flag) # Test awesome_flag if o.flags.awesome_flag: print "Happy times!" # List all flags on the field for f in o.flags: print f Enjoy! Admin ===== To use the widget in the admin, you'll need to update your ModelAdmin. Add the following lines to your ModelAdmin:: formfield_overrides = { BitField: {'widget': BitFieldCheckboxSelectMultiple}, } Make sure you've imported the classes by adding these lines to the top of the file:: from bitfield import BitField from bitfield.forms import BitFieldCheckboxSelectMultiple There is also a ``BitFieldListFilter`` list filter (Django 1.4 or newer). To use it set ``list_filter`` ModelAdmin option:: list_filter = ( ('flags', BitFieldListFilter,) ) BitFieldListFilter is in ``bitfield.admin`` module:: from bitfield.admin import BitFieldListFilter django-bitfield-1.6.4/setup.cfg0000644000076500000240000000007312054755645017142 0ustar dcramerstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 django-bitfield-1.6.4/setup.py0000644000076500000240000000146312054755132017026 0ustar dcramerstaff00000000000000#!/usr/bin/env python from setuptools import setup, find_packages setup( name='django-bitfield', version='1.6.4', author='DISQUS', author_email='opensource@disqus.com', url='http://github.com/disqus/django-bitfield', description='BitField in Django', packages=find_packages(), zip_safe=False, install_requires=[ 'Django>=1.2', ], setup_requires=[ 'nose>=1.0', ], tests_require=[ 'django-nose>=0.1.3', 'psycopg2>=2.3', ], test_suite='runtests.runtests', include_package_data=True, classifiers=[ 'Framework :: Django', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Operating System :: OS Independent', 'Topic :: Software Development' ], )