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 made (well, retroactively rationalized) the decision to lookup `val`
first because I thought it would be cheaper. But yeah, I wasn't aware of
tp_descr_set or any of those things, and that definitely changes some of
these performance questions, so you're probably right that we should check
the type of descriptor first.

> I would imagine that it's relatively rare that a data descriptor actually
ends up hiding an instance attribute

I'm not sure, but I kind of thought that the reason for this rule was so
that the data descriptor could store data in the instance attribute. If
people actually do that, then it wouldn't be that rare?


On Tue, Aug 12, 2014 at 4:54 PM, Kevin Modzelewski <notifications@github.com
> wrote:

> In src/runtime/objmodel.cpp:
>
> >          Box* val = NULL;
> > +
> >          if (rewrite_args) {
>
> Oh, ok that's true, and I would imagine that it's relatively rare that a
> data descriptor actually ends up hiding an instance attribute. On the other
> hand, I think we can assume that we'll make looking up __set__fast, such as
> by having the equivalent of CPython's tp_descr_set, but I guess the
> original lookup will be fast too. I'm slightly biased to doing it the same
> way that CPython does (look up the descriptor first, and check if its a
> data descriptor, before looking at the instance attributes), but other than
> that I guess you're right that it could make sense to do it this way.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/dropbox/pyston/pull/123/files#r16150844>.
>