Added namedtuple with defaults compatible.

This commit is contained in:
M1ha 2019-02-02 23:02:22 +05:00
parent 29a251b892
commit 27c9ca4dfc
2 changed files with 83 additions and 0 deletions

View 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)

View 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)))