"""
base restriction class
"""
from functools import partial
from snakeoil import caching, klass
from snakeoil.currying import pretty_docs
[docs]
class base(klass.SlotsPicklingMixin, metaclass=caching.WeakInstMeta):
"""base restriction matching object.
all derivatives *should* be __slots__ based (lot of instances may
wind up in memory).
"""
__inst_caching__ = True
# __weakref__ here is implicit via the metaclass
__slots__ = ()
package_matching = False
klass.inject_immutable_instance(locals())
[docs]
def match(self, *arg, **kwargs):
raise NotImplementedError
[docs]
def force_False(self, *arg, **kwargs):
return not self.match(*arg, **kwargs)
[docs]
def force_True(self, *arg, **kwargs):
return self.match(*arg, **kwargs)
def __len__(self):
return 1
[docs]
class AlwaysBool(base):
"""restriction that always yields a specific boolean"""
__slots__ = ("type", "negate")
__inst_caching__ = True
def __init__(self, node_type=None, negate=False):
"""
:param node_type: the restriction type the instance should be,
typically :obj:`pkgcore.restrictions.restriction.package_type` or
:obj:`pkgcore.restrictions.restriction.value_type`
:param negate: boolean to return for the match
"""
object.__setattr__(self, "negate", negate)
object.__setattr__(self, "type", node_type)
[docs]
def match(self, *a, **kw):
return self.negate
[docs]
def force_True(self, *a, **kw):
return self.negate
[docs]
def force_False(self, *a, **kw):
return not self.negate
def __iter__(self):
return iter(())
def __str__(self):
return f"always '{self.negate}'"
def __repr__(self):
return "<%s always %r @%#8x>" % (self.__class__.__name__, self.negate, id(self))
def __getstate__(self):
return self.negate, self.type
def __setstate__(self, state):
negate, node_type = state
object.__setattr__(self, "negate", negate)
object.__setattr__(self, "type", node_type)
[docs]
class Negate(base):
"""wrap and negate a restriction instance"""
__slots__ = ("type", "_restrict")
__inst_caching__ = False
def __init__(self, restrict):
"""
:param restrict: :obj:`pkgcore.restrictions.restriction.base` instance
to negate
"""
sf = object.__setattr__
sf(self, "type", restrict.type)
sf(self, "_restrict", restrict)
[docs]
def match(self, *a, **kw):
return not self._restrict.match(*a, **kw)
def __str__(self):
return "not (%s)" % self._restrict
[docs]
class FakeType(base):
"""wrapper to wrap and fake a node_type"""
__slots__ = ("type", "_restrict")
__inst_caching__ = False
def __init__(self, restrict, new_type):
"""
:param restrict: :obj:`pkgcore.restrictions.restriction.base` instance
to wrap
:param new_type: new node_type
"""
sf = object.__setattr__
sf(self, "type", new_type)
sf(self, "_restrict", restrict)
[docs]
def match(self, *a, **kw):
return self._restrict.match(*a, **kw)
def __str__(self):
return f"Faked type({self.type}): {self._restrict}"
[docs]
class AnyMatch(base):
"""Apply a nested restriction to every item in a sequence."""
__slots__ = ("restriction", "type", "negate")
def __init__(self, childrestriction, node_type, negate=False):
"""Initialize.
:type childrestriction: restriction
:param childrestriction: child restriction applied to every value.
:type node_type: string
:param node_type: type of this restriction.
"""
sf = object.__setattr__
sf(self, "negate", negate)
sf(self, "restriction", childrestriction)
sf(self, "type", node_type)
[docs]
def match(self, val):
for x in val:
if self.restriction.match(x):
return not self.negate
return self.negate
def __str__(self):
return f"any: {self.restriction} match"
def __repr__(self):
return f"<{self.__class__.__name__} restriction={self.restriction!r} @{id(self):#8x}>"
[docs]
def curry_node_type(cls, node_type, extradoc=None):
"""Helper function for creating restrictions of a certain type.
This uses :obj:`partial` to pass a node_type to the wrapped class,
and extends the docstring.
:param cls: callable (usually a class) that is wrapped.
:param node_type: value passed as node_type.
:param extradoc: addition to the docstring. Defaults to
"Automatically set to %s type." % node_type
:return: a wrapped callable.
"""
if extradoc is None:
extradoc = f"Automatically set to {node_type} type."
doc = cls.__doc__
result = partial(cls, node_type=node_type)
if doc is None:
doc = ""
else:
# do this so indentation on pydoc __doc__ is sane
doc = "\n".join(line.lstrip() for line in doc.split("\n")) + "\n"
doc += extradoc
return pretty_docs(result, doc)
value_type = "values"
package_type = "package"
valid_types = (value_type, package_type)