mirror of
https://github.com/carrotquest/django-clickhouse.git
synced 2025-07-06 12:53:17 +03:00
Added namedtuple with defaults compatible.
This commit is contained in:
parent
29a251b892
commit
27c9ca4dfc
47
src/django_clickhouse/compatibility.py
Normal file
47
src/django_clickhouse/compatibility.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import sys
|
||||||
|
from collections import namedtuple as basenamedtuple
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
||||||
|
class NamedTuple:
|
||||||
|
__slots__ = ('_data', '_data_iterator')
|
||||||
|
_defaults = {}
|
||||||
|
_data_cls = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@lru_cache(maxsize=32)
|
||||||
|
def _get_defaults(cls, exclude):
|
||||||
|
res = cls._defaults
|
||||||
|
for k in exclude:
|
||||||
|
res.pop(k, None)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
new_kwargs = deepcopy(self._get_defaults(self._data_cls._fields[:len(args)]))
|
||||||
|
new_kwargs.update(kwargs)
|
||||||
|
self._data = self._data_cls(*args, **new_kwargs)
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return getattr(self._data, item)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self._data_iterator = iter(self._data)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
return next(self._data_iterator)
|
||||||
|
|
||||||
|
|
||||||
|
def namedtuple(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Changes namedtuple to support defaults parameter as python 3.7 does
|
||||||
|
https://docs.python.org/3.7/library/collections.html#collections.namedtuple
|
||||||
|
:return: namedtuple class
|
||||||
|
"""
|
||||||
|
if sys.version_info < (3, 7):
|
||||||
|
defaults = kwargs.pop('defaults', {})
|
||||||
|
return type('namedtuple', (NamedTuple,), {'_defaults': defaults, '_data_cls': basenamedtuple(*args, **kwargs)})
|
||||||
|
else:
|
||||||
|
return basenamedtuple(*args, **kwargs)
|
36
tests/test_compatibility.py
Normal file
36
tests/test_compatibility.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from django_clickhouse.compatibility import namedtuple
|
||||||
|
|
||||||
|
|
||||||
|
class NamedTupleTest(TestCase):
|
||||||
|
def test_defaults(self):
|
||||||
|
TestTuple = namedtuple('TestTuple', ('a', 'b', 'c'), defaults={'c': 3})
|
||||||
|
self.assertTupleEqual((1, 2, 3), tuple(TestTuple(1, b=2)))
|
||||||
|
self.assertTupleEqual((1, 2, 4), tuple(TestTuple(1, 2, 4)))
|
||||||
|
self.assertTupleEqual((1, 2, 4), tuple(TestTuple(a=1, b=2, c=4)))
|
||||||
|
|
||||||
|
def test_exceptions(self):
|
||||||
|
TestTuple = namedtuple('TestTuple', ('a', 'b', 'c'), defaults={'c': 3})
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
TestTuple(b=1, c=4)
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
TestTuple(1, 2, 3, c=4)
|
||||||
|
|
||||||
|
def test_different_defaults(self):
|
||||||
|
# Test that 2 tuple type defaults don't affect each other
|
||||||
|
TestTuple = namedtuple('TestTuple', ('a', 'b', 'c'), defaults={'c': 3})
|
||||||
|
OtherTuple = namedtuple('TestTuple', ('a', 'b', 'c'), defaults={'c': 4})
|
||||||
|
t1 = TestTuple(a=1, b=2)
|
||||||
|
t2 = OtherTuple(a=3, b=4)
|
||||||
|
self.assertTupleEqual((1, 2, 3), tuple(t1))
|
||||||
|
self.assertTupleEqual((3, 4, 4), tuple(t2))
|
||||||
|
|
||||||
|
def test_defaults_cache(self):
|
||||||
|
# Test that 2 tuple instances don't affect each other's defaults
|
||||||
|
TestTuple = namedtuple('TestTuple', ('a', 'b', 'c'), defaults={'c': 3})
|
||||||
|
self.assertTupleEqual((1, 2, 4), tuple(TestTuple(a=1, b=2, c=4)))
|
||||||
|
self.assertTupleEqual((1, 2, 3), tuple(TestTuple(a=1, b=2)))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user