descriptor support for __get__ and __set__

Descriptor support for `__get__` and `__set__` in the `getattr`, `getclsattr`, and `callattr` paths.

- The `getattr` path follows the protocol described at https://docs.python.org/2/howto/descriptor.html. There are two cases: the object is an instance and the instance is a class.
  - For instances, It looks up attribute in the object's dictionary and the attribute from the class using `typeLookup`. In the latter, it looks for the `__get__` method, but if the attribute exists in _both_ it also checks that `__set__` exists.
  - For classes, it's simpler; it just looks if `__get__` exists in the looked-up item.
- The `getclsattr` path is sort of a mix - it looks up the attribute just in the class but will call `__get__` with the object rather than `None`.
- For `callattr`, the code for looking up the value seemed completely wrong (e.g., it was doing custom lookups through `__getattr__` before looking up the class object) so I scrapped that part. I figured this should basically share all logic with `getattr` or `getclsattr` as appropriate - except that it shouldn't wrap functions in instancemethods. So I refactored `getattr_internal` out into `getattr_internal_general` which takes an additional flag which species that it should not wrap functions, and `callattr` just calls that. (So `callattr` still does binding if necessary.) Similarly, I factored `clsattr_internal` into `clsattr_internal_general`.

One thing of note is that it currently doesn't rewrite the path where `callattr` calls `__get__`, since that involves _two_ function calls: one to `__get__` and another to the function that `__get__` returns. Of course, this path is basically just a getattr and a call, each of which could be written by itself, so this clearly isn't a fundamental limitation.

I got rid of `_handleClsAttr` which already had special-case descriptor logic for functions and member descriptors. Those cases are still handled specially though, just in getattr and getclsattr with the other descriptor logic.

Finally, for `setattr`, I check if `__set__` exists and if so call that.
I didn't like it either and tried to not have them at first. But they
seemed sort of necessary, because there is code you want to run in between
(which I feel makes less to go in the method, and differed between
getclsattr and getattr). Namely, in the getattr path, if it's a nondata
descriptor, you want to check that there is no val, and then handle the
special case.

Do you have any specific suggestion for how to design this as to get rid of
these awkward is_...special case methods? I'd like to get rid of them too,
but I don't see a nice way.


On Thu, Aug 14, 2014 at 4:36 PM, Kevin Modzelewski <notifications@github.com
> wrote:

> In src/runtime/objmodel.cpp:
>
> > +            // so just let callattr handle it.)
> > +            if (rewrite_args) {
> > +                if (!rewrite_args->more_guards_after)
> > +                    rewrite_args->rewriter->setDoneGuarding();
> > +                rewrite_args->obj.setDoneUsing();
> > +                rewrite_args->out_rtn = std::move(r_descr);
> > +                rewrite_args->out_success = true;
> > +            }
> > +            return descr;
> > +        }
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +bool is_data_descriptor_instance_special_case(Box* descr) {
>
> Let's make these camelCase and use underscore_case for variables.
>
> Also, I think it'd be nice if we could get rid of the separate "is_...*special_case"
> and "handle*..._special_case" methods, which would have to be kept in
> sync.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/dropbox/pyston/pull/123/files#r16273624>.
>