def attr_func(*attrs, **kwargs): """Creates an "attribute function" for given attribute name(s). Resulting function will retrieve attributes with given names, in order, from the object that has been passed to it. For example, ``attr_func('a', 'b')(foo)`` yields the same as ``foo.a.b`` :param attrs: Attribute names :param default: Optional keyword argument specifying default value that will be returned when some attribute is not present :return: Unary attribute function """ ensure_argcount(attrs, min_=1) ensure_keyword_args(kwargs, optional=('default',)) # preprocess argument list: # * allow dots in arguments, interpreting them as multiple attributes, # e.g. ``attr_func('a.b')`` as ``attr_func('a', 'b')`` # * make sure the attribute names are valid Python identifiers attrs = map(ensure_string, attrs) attrs = flatten(attr.split('.') if '.' in attr else [attr] for attr in attrs) for attr in attrs: if not is_identifier(attr): raise ValueError("'%s' is not a valid attribute name", attr) if 'default' in kwargs: default = kwargs['default'] if len(attrs) == 1: getattrs = lambda obj: getattr(obj, attrs[0], default) else: def getattrs(obj): for attr in attrs: try: obj = getattr(obj, attr) except AttributeError: return default return obj else: if len(attrs) == 1: getattrs = operator.attrgetter(attrs[0]) else: def getattrs(obj): for attr in attrs: obj = getattr(obj, attr) return obj return getattrs
def key_func(*keys, **kwargs): """Creates a "key function" based on given keys. Resulting function will perform lookup using specified keys, in order, on the object passed to it as an argument. For example, ``key_func('a', 'b')(foo)`` is equivalent to ``foo['a']['b']``. :param keys: Lookup keys :param default: Optional keyword argument specifying default value that will be returned when some lookup key is not present :return: Unary key function """ ensure_argcount(keys, min_=1) ensure_keyword_args(kwargs, optional=('default',)) keys = list(map(ensure_string, keys)) if 'default' in kwargs: default = kwargs['default'] def getitems(obj): for key in keys: try: obj = obj[key] except KeyError: return default return obj else: if len(keys) == 1: getitems = operator.itemgetter(keys[0]) else: def getitems(obj): for key in keys: obj = obj[key] return obj return getitems
def test_args__none(self): with self.assertRaises(TypeError): __unit__.ensure_argcount(None, min_=1, max_=1)
def test_invalid_limits(self): with self.assertRaises(ValueError) as r: __unit__.ensure_argcount(self.FEW_ARGS, min_=2, max_=1) self.assertIn("greater", str(r.exception))
def test_no_limits(self): with self.assertRaises(ValueError): __unit__.ensure_argcount(self.FEW_ARGS)
def test_args__exact(self): __unit__.ensure_argcount(self.FEW_ARGS, min_=self.FEW, max_=self.FEW) __unit__.ensure_argcount(self.MANY_ARGS, min_=self.MANY, max_=self.MANY)
def test_args__exactly_max(self): __unit__.ensure_argcount(self.MANY_ARGS, min_=self.LESS_THAN_MANY, max_=self.MANY)
def test_args__exactly_min(self): __unit__.ensure_argcount(self.FEW_ARGS, min_=self.FEW, max_=self.MORE_THAN_FEW)
def test_args__more_than_max(self): with self.assertRaises(TypeError) as r: __unit__.ensure_argcount(self.MANY_ARGS, max_=self.LESS_THAN_MANY) self.assertIn("expected at most", str(r.exception))
def test_args__less_than_min(self): with self.assertRaises(TypeError) as r: __unit__.ensure_argcount(self.FEW_ARGS, min_=self.MORE_THAN_FEW) self.assertIn("expected at least", str(r.exception))
def test_args__empty(self): __unit__.ensure_argcount([], min_=0, max_=0) __unit__.ensure_argcount([], min_=0, max_=self.MANY) with self.assertRaises(TypeError): __unit__.ensure_argcount([], min_=self.FEW)
def test_args__some_object(self): with self.assertRaises(TypeError): __unit__.ensure_argcount(object(), min_=1, max_=1)