[Python-Dev] bug in PEP 318

Alexander Bernauer python at copton.net
Tue May 30 20:56:54 CEST 2006


Hi

I found two bugs in example 4 of the PEP 318 [1]. People on #python
pointed me to this list. So here is my report. Additionally I appended
an afaics correct implementation for this task.

[1] http://www.python.org/dev/peps/pep-0318/

Bug 1)
The decorator "accepts" gets the function which is returned by the
decorator "returns". This is the function "new_f" which is defined
differently from the function "func". Because the first has an
argument count of zero, the assertion on line 3 is wrong.

Bug 2)
The assertion on line 6 does not work correctly for tuples. If the
second argument of "isinstance" is a tuple, the function returns true,
if the first argument is an instance of either type of the tuple.
Unfortunately the example uses tuples.


Here goes my proposal for this decorators. Feel free to do anything you
like with it.

---8<---
def checktype(o, t): 
   """check if the type of the object "o" is "t" """
    # in case of a tuple descend
    if isinstance(t, tuple):
        assert isinstance(o, tuple), str(type(o))
        assert len(o) == len(t), "%d %d" % (len(o), len(t))
        for (o2, t2) in zip(o, t):
            checktype(o2, t2)

    # isinsance(None, None) raises a TypeError. 
    # so we need extra handling for this case
    elif t == None:  
        assert o == None
    else:
        assert isinstance(o, t), "%s %s" % (str(type(o)), str(t))

class accepts(object):
    def __init__(self, *types):
        self.types = types

    def __call__(self, func):
        self.func = func

        def check(*args, **kwds):
            checktype(args, self.types)
            self.func(*args, **kwds)
     
        return check

class returns(object):
    def __init__(self, *types):
        self.types = types

    def __call__(self, func):
        self.func = func

        def check(*args, **kwds):
            value = self.func(*args, **kwds)
            # if the function returns only on object the single object is no tuple
            # this extra case results in an extra handling here
            if isinstance(value, tuple):
                checktype(value, self.types)
            else:
                checktype((value,), self.types)
            return value

        return check

--->8---

To be honest, I didn't understand what the purpose of setting
"func_name" is, so I left it out.  If its neccessary please feel free to
correct me.

In contrast to tuples lists and dictionaries are not inspected. The
reason is that I don't know how to express: "the function accepts a list
of 3 or more integers" or alike. Perhaps somebody has an idea for this.

Here are some examples of what is possible:

---8<---
@accepts(int)
@returns(int)
def foo(a):
    return a

foo(3)


@accepts(int, (float, int))
@returns(int, str)
def foo(a, b):
    return (1, "asdf")

foo(1, (2.0, 1))


@returns(None)
def foo():
    pass

foo()


@accepts(int, int)
def foo(a, *args):
    pass

foo(3,3)


@accepts(tuple)
@returns(list)
def foo(a):
   return list(a)

foo((1,1))

--->8---

I wonder, how it can be, that those imho obvious mistakes go into a PEP
and stay undetected there for almost 3 years. Perhaps I misunderstood
something completly. In this case, please excuse me and ignore my mail.


The welcome message to this lists asked for introducing myself. Well,
there is nothing special to say about me concerning python. Let's say,
I'm just a fan. 

cu

-- 
Alexander Bernauer


More information about the Python-Dev mailing list