Example #1
0
class SetConstraint(OpenerConstraint):
    """The object must be a Set of some sort, with a given maximum size. To
    accept sets of any size, use maxLength=None. All member objects must obey
    the given constraint. By default this will accept both mutable and
    immutable sets, if you want to require a particular type, set mutable= to
    either True or False.
    """

    # TODO: if mutable!=None, we won't throw out the wrong set type soon
    # enough. We need to override checkOpenType to accomplish this.
    opentypes = [("set", ), ("immutable-set", )]
    name = "SetConstraint"

    def __init__(self, constraint, maxLength=None, mutable=None):
        self.constraint = IConstraint(constraint)
        self.maxLength = maxLength
        self.mutable = mutable

    def checkObject(self, obj, inbound):
        if not isinstance(obj, (set, frozenset)):
            raise Violation("not a set")
        if (self.mutable == True and not isinstance(obj, set)):
            raise Violation("obj is a set, but not a mutable one")
        if (self.mutable == False and not isinstance(obj, frozenset)):
            raise Violation("obj is a set, but not an immutable one")
        if self.maxLength is not None and len(obj) > self.maxLength:
            raise Violation("set is too large")
        if self.constraint:
            for o in obj:
                self.constraint.checkObject(o, inbound)
Example #2
0
class ListConstraint(OpenerConstraint):
    """The object must be a list of objects, with a given maximum length. To
    accept lists of any length, use maxLength=None. All member objects must
    obey the given constraint."""

    opentypes = [(b'list', )]
    name = 'ListConstraint'

    def __init__(self, constraint, maxLength=None, minLength=0):
        self.constraint = IConstraint(constraint)
        self.maxLength = maxLength
        self.minLength = minLength

    def checkObject(self, obj, inbound):
        if not isinstance(obj, list):
            raise Violation("not a list")

        if self.maxLength is not None and len(obj) > self.maxLength:
            raise Violation("list too long")

        if len(obj) < self.minLength:
            raise Violation("list too short")

        for o in obj:
            self.constraint.checkObject(o, inbound)
Example #3
0
class SetConstraint(OpenerConstraint):
    """The object must be a Set of some sort, with a given maximum size. To
    accept sets of any size, use maxLength=None. All member objects must obey
    the given constraint. By default this will accept both mutable and
    immutable sets, if you want to require a particular type, set mutable= to
    either True or False.
    """

    # TODO: if mutable!=None, we won't throw out the wrong set type soon
    # enough. We need to override checkOpenType to accomplish this.
    opentypes = [("set",), ("immutable-set",)]
    name = "SetConstraint"

    def __init__(self, constraint, maxLength=None, mutable=None):
        self.constraint = IConstraint(constraint)
        self.maxLength = maxLength
        self.mutable = mutable

    def checkObject(self, obj, inbound):
        if not isinstance(obj, (set, frozenset)):
            raise Violation("not a set")
        if (self.mutable == True and
            not isinstance(obj, set)):
            raise Violation("obj is a set, but not a mutable one")
        if (self.mutable == False and
            not isinstance(obj, frozenset)):
            raise Violation("obj is a set, but not an immutable one")
        if self.maxLength is not None and len(obj) > self.maxLength:
            raise Violation("set is too large")
        if self.constraint:
            for o in obj:
                self.constraint.checkObject(o, inbound)
Example #4
0
class DictConstraint(OpenerConstraint):
    opentypes = [("dict",)]
    name = "DictConstraint"

    def __init__(self, keyConstraint, valueConstraint, maxKeys=None):
        self.keyConstraint = IConstraint(keyConstraint)
        self.valueConstraint = IConstraint(valueConstraint)
        self.maxKeys = maxKeys
    def checkObject(self, obj, inbound):
        if not isinstance(obj, dict):
            raise Violation("'%s' (%s) is not a Dictionary" % (obj, type(obj)))
        if self.maxKeys != None and len(obj) > self.maxKeys:
            raise Violation("Dict keys=%d > maxKeys=%d" % (len(obj), self.maxKeys))
        for key, value in obj.items():
            self.keyConstraint.checkObject(key, inbound)
            self.valueConstraint.checkObject(value, inbound)
Example #5
0
class DictConstraint(OpenerConstraint):
    opentypes = [("dict",)]
    name = "DictConstraint"

    def __init__(self, keyConstraint, valueConstraint, maxKeys=None):
        self.keyConstraint = IConstraint(keyConstraint)
        self.valueConstraint = IConstraint(valueConstraint)
        self.maxKeys = maxKeys
    def checkObject(self, obj, inbound):
        if not isinstance(obj, dict):
            raise Violation, "'%s' (%s) is not a Dictionary" % (obj,
                                                                type(obj))
        if self.maxKeys != None and len(obj) > self.maxKeys:
            raise Violation, "Dict keys=%d > maxKeys=%d" % (len(obj),
                                                            self.maxKeys)
        for key, value in obj.iteritems():
            self.keyConstraint.checkObject(key, inbound)
            self.valueConstraint.checkObject(value, inbound)
Example #6
0
class RemoteMethodSchema(object):
    """
    This is a constraint for a single remotely-invokable method. It gets to
    require, deny, or impose further constraints upon a set of named
    arguments.

    This constraint is created by using keyword arguments with the same
    names as the target method's arguments. Two special names are used:

    __ignoreUnknown__: if True, unexpected argument names are silently
    dropped. (note that this makes the schema unbounded)

    __acceptUnknown__: if True, unexpected argument names are always
    accepted without a constraint (which also makes this schema unbounded)

    The remotely-accesible object's .getMethodSchema() method may return one
    of these objects.
    """

    taster = {}  # this should not be used as a top-level constraint
    opentypes = []  # overkill
    ignoreUnknown = False
    acceptUnknown = False

    name = None  # method name, set when the RemoteInterface is parsed
    interface = None  # points to the RemoteInterface which defines the method

    # under development
    def __init__(self, method=None, _response=None, __options=[], **kwargs):
        if method:
            self.initFromMethod(method)
            return
        self.argumentNames = []
        self.argConstraints = {}
        self.required = []
        self.responseConstraint = None
        # __response in the argslist gets treated specially, I think it is
        # mangled into _RemoteMethodSchema__response or something. When I
        # change it to use _response instead, it works.
        if _response:
            self.responseConstraint = IConstraint(_response)
        self.options = {}  # return, wait, reliable, etc

        if "__ignoreUnknown__" in kwargs:
            self.ignoreUnknown = kwargs["__ignoreUnknown__"]
            del kwargs["__ignoreUnknown__"]
        if "__acceptUnknown__" in kwargs:
            self.acceptUnknown = kwargs["__acceptUnknown__"]
            del kwargs["__acceptUnknown__"]

        for argname, constraint in list(kwargs.items()):
            self.argumentNames.append(argname)
            constraint = IConstraint(constraint)
            self.argConstraints[argname] = constraint
            if not isinstance(constraint, Optional):
                self.required.append(argname)

    def initFromMethod(self, method):
        # call this with the Interface's prototype method: the one that has
        # argument constraints expressed as default arguments, and which
        # does nothing but returns the appropriate return type

        names, _, _, typeList = inspect.getargspec(method)
        if names and names[0] == 'self':
            why = "RemoteInterface methods should not have 'self' in their argument list"
            raise InvalidRemoteInterface(why)
        if not names:
            typeList = []
        # 'def foo(oops)' results in typeList==None
        if typeList is None or len(names) != len(typeList):
            # TODO: relax this, use schema=Any for the args that don't have
            # default values. This would make:
            #  def foo(a, b=int): return None
            # equivalent to:
            #  def foo(a=Any, b=int): return None
            why = "RemoteInterface methods must have default values for all their arguments"
            raise InvalidRemoteInterface(why)
        self.argumentNames = names
        self.argConstraints = {}
        self.required = []
        for i in range(len(names)):
            argname = names[i]
            constraint = typeList[i]
            if not isinstance(constraint, Optional):
                self.required.append(argname)
            self.argConstraints[argname] = IConstraint(constraint)

        # call the method, its 'return' value is the return constraint
        self.responseConstraint = IConstraint(method())
        self.options = {}  # return, wait, reliable, etc

    def getPositionalArgConstraint(self, argnum):
        if argnum >= len(self.argumentNames):
            raise Violation("too many positional arguments: %d >= %d" %
                            (argnum, len(self.argumentNames)))
        argname = self.argumentNames[argnum]
        c = self.argConstraints.get(argname)
        assert c
        if isinstance(c, Optional):
            c = c.constraint
        return (True, c)

    def getKeywordArgConstraint(self,
                                argname,
                                num_posargs=0,
                                previous_kwargs=[]):
        previous_args = self.argumentNames[:num_posargs]
        for pkw in previous_kwargs:
            assert pkw not in previous_args
            previous_args.append(pkw)
        if argname in previous_args:
            raise Violation("got multiple values for keyword argument '%s'" %
                            (argname, ))
        c = self.argConstraints.get(argname)
        if c:
            if isinstance(c, Optional):
                c = c.constraint
            return (True, c)
        # what do we do with unknown arguments?
        if self.ignoreUnknown:
            return (False, None)
        if self.acceptUnknown:
            return (True, None)
        raise Violation("unknown argument '%s'" % argname)

    def getResponseConstraint(self):
        return self.responseConstraint

    def checkAllArgs(self, args, kwargs, inbound):
        # first we map the positional arguments
        allargs = {}
        if len(args) > len(self.argumentNames):
            raise Violation("method takes %d positional arguments (%d given)" %
                            (len(self.argumentNames), len(args)))
        for i, argvalue in enumerate(args):
            allargs[self.argumentNames[i]] = argvalue
        for argname, argvalue in list(kwargs.items()):
            if argname in allargs:
                raise Violation(
                    "got multiple values for keyword argument '%s'" %
                    (argname, ))
            allargs[argname] = argvalue

        for argname, argvalue in list(allargs.items()):
            accept, constraint = self.getKeywordArgConstraint(argname)
            if not accept:
                # this argument will be ignored by the far end. TODO: emit a
                # warning
                pass
            try:
                constraint.checkObject(argvalue, inbound)
            except Violation as v:
                v.setLocation("%s=" % argname)
                raise

        for argname in self.required:
            if argname not in allargs:
                raise Violation("missing required argument '%s'" % argname)

    def checkResults(self, results, inbound):
        if self.responseConstraint:
            # this might raise a Violation. The caller will annotate its
            # location appropriately: they have more information than we do.
            self.responseConstraint.checkObject(results, inbound)