dwt / fluent

Python wrapper for stdlib (and other) objects to give them a fluent interface.
ISC License
60 stars 2 forks source link

Automatic fallback to .self on AttributeError #11

Closed laundmo closed 3 years ago

laundmo commented 3 years ago

What is the reasoning for not falling back to self automatically if a wrapped function returns None?

for example,

_([1, 5, 2]).append(6).append(3).sort().call(print)._

would be way nicer than

_([1, 5, 2]).append(6).self.append(3).self.sort().self.call(print)._

Could there be a option to enable such a fallback?

dwt commented 3 years ago

Well, it's quite some time ago that I really thought about this and decided that this is the best way to go. If I remember correctly, I deduced that this behaviour is less surprising.

Ah, I've documented it in the code. :-)

    @property
    def self(self):
        """Returns the previous wrapped object. This is especially usefull for APIs that return None.

        For example ``_([1,3,2]).sort().self.print()`` will print the sorted list, even though
        ``sort()`` did return ``None``.

        This is simpler than using .previous as there are often multiple wrappers involved where you might expect only one.
        E.g. ``_([2,1]).sort().self._ == [1,2]`` but ``_([2,1]).sort().previous._`` will return the function ``list.sort()``
        as the attrget and call are two steps of the call chain.

        This eases chaining using APIs that where not designed with chaining in mind. 
        (Inspired by SmallTalk's default behaviour)
        """
        # This behavior is always triggered, not just in the `None` return case to avoid
        # code that behaves differently for methods that _sometimes_ return `None`.

        # Depending on wether the previous method was a transplanted method
        # we need to go back one level or two
        if isinstance(self.previous, CallableWrapper):
            return self.previous.previous
        else:
            return self.previous

I think that is still quite a valid reason - code that sometimes returns None and sometimes doesn't would trigger really strange bugs otherwise. Or am I missing something?

laundmo commented 3 years ago

You are right, i had not considered methods that sometimes return none. It might be possible to use AST to look up whether there is any explicit returns in the function, and only fall back if the only possible return is the implicit None, but that seems like a lot of effort.

dwt commented 3 years ago

Yes. And probably brittle as hell, given that things like decorators will wreak havoc with this. I'll close this for now, if there's anything we missed, feel freer to reopen / add to the conversation.