Commits

Anonymous committed aefa224

Add types.self notation, for signatures on methods

Comments (0)

Files changed (4)

pypy/annotation/signature.py

                                              s_input))
         inputcells[:] = args_s
 
-def apply_bookkeeper(paramtype, bookkeeper):
+def finish_type(paramtype, bookkeeper, func):
+    from pypy.annotation.types import SelfTypeMarker
     if isinstance(paramtype, SomeObject):
         return paramtype
+    elif isinstance(paramtype, SelfTypeMarker):
+        raise Exception("%r argument declared as annotation.types.self(); class needs decorator rlib.signature.finishsigs()" % (func,))
     else:
         return paramtype(bookkeeper)
 
 def enforce_signature_args(funcdesc, paramtypes, actualtypes):
     assert len(paramtypes) == len(actualtypes)
-    params_s = [apply_bookkeeper(paramtype, funcdesc.bookkeeper) for paramtype in paramtypes]
+    params_s = [finish_type(paramtype, funcdesc.bookkeeper, funcdesc.pyobj) for paramtype in paramtypes]
     for i, (s_param, s_actual) in enumerate(zip(params_s, actualtypes)):
         if not s_param.contains(s_actual):
             raise Exception("%r argument %d:\n"
     actualtypes[:] = params_s
 
 def enforce_signature_return(funcdesc, sigtype, inferredtype):
-    return apply_bookkeeper(sigtype, funcdesc.bookkeeper)
+    return finish_type(sigtype, funcdesc.bookkeeper, funcdesc.pyobj)

pypy/annotation/types.py

 
 def instance(class_):
     return lambda bookkeeper: model.SomeInstance(bookkeeper.getuniqueclassdef(class_))
+
+class SelfTypeMarker(object):
+    pass
+
+def self():
+    return SelfTypeMarker()

pypy/rlib/signature.py

+from pypy.annotation import types
 
 def signature(*paramtypes, **kwargs):
     """Decorate a function to specify its type signature.
         f._signature_ = (paramtypes, returntype)
         return f
     return decorator
+
+
+def finishsigs(cls):
+    """Decorate a class to finish any method signatures involving types.self().
+
+    This is required if any method has a signature with types.self() in it.
+    """
+    # A bit annoying to have to use this, but it avoids performing any
+    # terrible hack in the implementation.  Eventually we'll offer signatures
+    # on classes, and then that decorator can do this on the side.
+    def fix(sigtype):
+        if isinstance(sigtype, types.SelfTypeMarker):
+            return types.instance(cls)
+        return sigtype
+    for attr in cls.__dict__.values():
+        if hasattr(attr, '_signature_'):
+            paramtypes, returntype = attr._signature_
+            attr._signature_ = (tuple(fix(t) for t in paramtypes), fix(returntype))
+    return cls

pypy/rlib/test/test_signature.py

 import py
-from pypy.rlib.signature import signature
+from pypy.rlib.signature import signature, finishsigs
 from pypy.annotation import types, model
 from pypy.translator.translator import TranslationContext, graphof
 
     @check_annotator_fails
     def bad_for_body():
         f(C1())
+
+def test_signature_self():
+    @finishsigs
+    class C(object):
+        @signature(types.self(), types.self(), returns=types.none())
+        def f(self, other):
+            pass
+    class D1(C):
+        pass
+    class D2(C):
+        pass
+
+    def g():
+        D1().f(D2())
+    a = annotate_at(g)
+
+    argtype = sigof(a, C.__dict__['f'])[0]
+    assert isinstance(argtype, model.SomeInstance)
+    assert argtype.classdef.classdesc.pyobj == C
+
+def test_signature_self_error():
+    class C(object):
+        @signature(types.self(), returns=types.none())
+        def incomplete_sig_meth(self):
+            pass
+
+    exc = py.test.raises(Exception, annotate_at, C.incomplete_sig_meth).value
+    assert 'incomplete_sig_meth' in repr(exc.args)
+    assert 'finishsigs' in repr(exc.args)