def accumulated(mass, *attrs, **kwargs): '''Becomes True after accumulating a given "mass". `mass` is the maximum allowed to accumulate. This is usually a positive number. Each value produced by the `unbounded generator` is added together. Yield True when this amount to more than the given `mass`. If any `attrs` are provided, they will be considered attributes (or keys) to search inside the yielded data from the bounded function. If no `attrs` are provided the whole data is accumulated, so it must allow addition. The attribute to be summed is extracted with `~xoutil.objects.get_first_of`:func:, so only the first attribute found is added. If the keyword argument `initial` is provided the accumulator is initialized with that value. By default this is 0. ''' from xoutil.objects import get_first_of accum = kwargs.pop('initial', 0) if kwargs: raise TypeError('Invalid keyword arguments %r' % kwargs.keys()) yield False while accum < mass: data = yield False accum += get_first_of(data, *attrs, default=data) yield True
def _get_can_new(self): for record in self: has_group = self.env['res.users'].has_group( 'xopgi_mail_new_thread.group_new_thread') type = get_first_of(record, 'type', 'message_type') record.can_new_thread = (type not in ('notification', ) and (self._uid == SUPERUSER_ID or has_group))
def __getitem__(self, name): from xoutil.objects import get_first_of result = get_first_of(self._probes, name, default=_unset) if result is not _unset: return result else: raise KeyError(name)
def __getitem__(self, name): from xoutil.objects import get_first_of probes = tuple(c.__dict__ for c in self._target_mro) result = get_first_of(probes, name, default=_unset) if result is not _unset: return result else: raise KeyError(name)
def _serialize(self, data): """Serialize a QuerySet or anything into JSON.""" kwargs = {} if not self.request.is_ajax(): kwargs['indent'] = 2 encoder = self.JSONEncoder(**kwargs) encode = get_first_of(encoder, 'iterencode', 'encode') return encode(data)
def dict_merge(*dicts, **others): '''Merges several dicts into a single one. Merging is similar to updating a dict, but if values are non-scalars they are also merged is this way: - Any two :class:`sequences <collection.Sequence>` or :class:`sets <collections.Set>` are joined together. - Any two mappings are recursively merged. - Other types are just replaced like in :func:`update`. If for a single key two values of incompatible types are found, raise a TypeError. If the values for a single key are compatible but different (i.e a list an a tuple) the resultant type will be the type of the first apparition of the key, unless for mappings which are always cast to dicts. No matter the types of `dicts` the result is always a dict. Without arguments, return the empty dict. ''' from collections import Mapping, Sequence, Set from xoutil.eight import iteritems from xoutil.objects import get_first_of from xoutil.types import are_instances, no_instances if others: dicts = dicts + (others, ) dicts = list(dicts) result = {} collections = (Set, Sequence) while dicts: current = dicts.pop(0) for key, val in iteritems(current): if isinstance(val, Mapping): val = {key: val[key] for key in val} value = result.setdefault(key, val) if value is not val: if are_instances(value, val, collections): join = get_first_of((value, ), '__add__', '__or__') if join: constructor = type(value) value = join(constructor(val)) else: raise ValueError("Invalid value for key '%s'" % key) elif are_instances(value, val, Mapping): value = dict_merge(value, val) elif no_instances(value, val, (Set, Sequence, Mapping)): value = val else: raise TypeError("Found incompatible values for key '%s'" % key) result[key] = value return result
def smart_getattr(name, *sources, **kwargs): '''Gets an attr by `name` for the first source that has it. This is roughly that same as:: get_first_of(sources, name, default=Unset, **kwargs) .. warning:: Deprecated since 1.5.1 ''' from xoutil.iterators import dict_update_new dict_update_new(kwargs, {'default': Unset}) return get_first_of(sources, name, **kwargs)
def resolve(self, node: Node, instance, kwargs): resolver = getattr(node, f'resolve_{self.name}', None) if resolver: schema = Schema(resolver.__annotations__) kwargs = dict(kwargs, **schema.validate(kwargs)) value = partial(resolver, instance) else: value = get_first_of(instance, self.name) if callable(value): value = value(**kwargs) return value
def resolve(self, node: Node, instance, kwargs): resolver = getattr(node, f'resolve_{self.name}', None) if resolver: kwargs = self.validate(resolver, kwargs) value = partial(resolver, instance) else: value = get_first_of(instance, self.name) all_values = getattr(value, 'all', None) if all_values: value = all_values() elif callable(value): value = value(**kwargs) if isinstance(value, UUID): value = str(value) return value
def plan(**plan_kwargs): # The algorithm is simple; first we "intertwine" tokens and filters # using a (stable) partial order: a filter comes before a token if and # only if neither of it's terms is bound to the token. # # The we just build several chained generators that either produce # (affect the vm) or filter. # from xoutil.objects import get_first_of from xotl.ql.expressions import _false parts = list(sorted_parts[:]) vm = get_first_of((plan_kwargs, kwargs), 'vm', default={}) # vm = plan_kwargs.get('vm', None) or kwargs.get('vm', None) or {} result = vmtoken(parts.pop(0), vm, query, only=only) while parts: part = parts.pop(0) if isinstance(part, GeneratorToken): result = vmtoken(part, vm, query, only=only).chain(result) else: result = vminstr(part, vm).chain(result) for _ in result: selected = select(query.selection, vm) if selected is not _false: yield selected
def smart_copy(*args, **kwargs): '''Copies the first apparition of attributes (or keys) from `sources` to `target`. :param sources: The objects from which to extract keys or attributes. :param target: The object to fill. :param defaults: Default values for the attributes to be copied as explained below. Defaults to False. :type defaults: Either a bool, a dictionary, an iterable or a callable. Every `sources` and `target` are always positional arguments. There should be at least one source. `target` will always be the last positional argument. If `defaults` is a dictionary or an iterable then only the names provided by itering over `defaults` will be copied. If `defaults` is a dictionary, and one of its key is not found in any of the `sources`, then the value of the key in the dictionary is copied to `target` unless: - It's the value :class:`xoutil.types.Required` or an instance of Required. - An exception object - A sequence with is first value being a subclass of Exception. In which case :class:`xoutil.data.adapt_exception` is used. In these cases a KeyError is raised if the key is not found in the sources. If `default` is an iterable and a key is not found in any of the sources, None is copied to `target`. If `defaults` is a callable then it should receive one positional arguments for the current `attribute name` and several keyword arguments (we pass ``source``) and return either True or False if the attribute should be copied. If `defaults` is False (or None) only the attributes that do not start with a "_" are copied, if it's True all attributes are copied. When `target` is not a mapping only valid Python identifiers will be copied. Each `source` is considered a mapping if it's an instance of `collections.Mapping` or a `MappingProxyType`. The `target` is considered a mapping if it's an instance of `collections.MutableMapping`. :returns: `target`. .. versionchanged:: 1.7.0 `defaults` is now keyword only. ''' from collections import MutableMapping from xoutil.types import is_collection, is_mapping, Required from xoutil.data import adapt_exception from xoutil.validators.identifiers import is_valid_identifier defaults = kwargs.pop('defaults', False) if kwargs: raise TypeError('smart_copy does not accept a "%s" keyword argument' % kwargs.keys()[0]) sources, target = args[:-1], args[-1] if not sources: raise TypeError('smart_copy requires at least one source') if isinstance(target, (bool, type(None), int, float, str_base)): raise TypeError('target should be a mutable object, not %s' % type(target)) if isinstance(target, MutableMapping): def setter(key, val): target[key] = val else: def setter(key, val): if is_valid_identifier(key): setattr(target, key, val) _mapping = is_mapping(defaults) if _mapping or is_collection(defaults): for key, val in ((key, get_first_of(sources, key, default=Unset)) for key in defaults): if val is Unset: if _mapping: val = defaults.get(key, None) else: val = None exc = adapt_exception(val, key=key) if exc or val is Required or isinstance(val, Required): raise KeyError(key) setter(key, val) else: keys = [] for source in sources: get = smart_getter(source) if is_mapping(source): items = (name for name in source) else: items = dir(source) for key in items: private = isinstance(key, str_base) and key.startswith('_') if (defaults is False or defaults is None) and private: copy = False elif callable(defaults): copy = defaults(key, source=source) else: copy = True if key not in keys: keys.append(key) if copy: setter(key, get(key)) return target
def test_get_first_of(): from xoutil.objects import get_first_of somedict = {"foo": "bar", "spam": "eggs"} assert get_first_of(somedict, "no", "foo", "spam") == 'bar' somedict = {"foo": "bar", "spam": "eggs"} assert get_first_of(somedict, "eggs") is None class Someobject(object): pass inst = Someobject() inst.foo = 'bar' inst.eggs = 'spam' assert get_first_of(inst, 'no', 'eggs', 'foo') == 'spam' assert get_first_of(inst, 'invalid') is None somedict = {"foo": "bar", "spam": "eggs"} class Someobject(object): pass inst = Someobject() inst.foo = 'bar2' inst.eggs = 'spam' assert get_first_of((somedict, inst), 'eggs') == 'spam' assert get_first_of((somedict, inst), 'foo') == 'bar' assert get_first_of((inst, somedict), 'foo') == 'bar2' assert get_first_of((inst, somedict), 'foobar') is None none = object() assert get_first_of((inst, somedict), 'foobar', default=none) is none _eggs = get_first_of(somedict, 'foo', 'spam', pred=lambda v: len(v) > 3) assert _eggs == 'eggs' _none = get_first_of(somedict, 'foo', 'spam', pred=lambda v: len(v) > 4) assert _none is None with pytest.raises(TypeError): get_first_of(None, anything=1)