hasattr telling lies? (AttributeError: 'method' object has no attribute '__annotations__')





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







4















The following code



class Foo:
def bar(self) -> None:
pass

foo = Foo()
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__ = 'hi'


crashes with



AttributeError: 'method' object has no attribute '__annotations__'


How can this happen?










share|improve this question























  • hasattr return True if getattr(object, name) don't raise, and when you do getattr(foo.bar, "__annotations__") you get {'return':None}. This is why hasattr(foo.bar, 'annotations') return True.

    – iElden
    Nov 26 '18 at 8:21











  • Seems Python bug to me. AttributeError: readonly attribute would be more sensible error message here.

    – wim
    Nov 26 '18 at 17:49


















4















The following code



class Foo:
def bar(self) -> None:
pass

foo = Foo()
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__ = 'hi'


crashes with



AttributeError: 'method' object has no attribute '__annotations__'


How can this happen?










share|improve this question























  • hasattr return True if getattr(object, name) don't raise, and when you do getattr(foo.bar, "__annotations__") you get {'return':None}. This is why hasattr(foo.bar, 'annotations') return True.

    – iElden
    Nov 26 '18 at 8:21











  • Seems Python bug to me. AttributeError: readonly attribute would be more sensible error message here.

    – wim
    Nov 26 '18 at 17:49














4












4








4


0






The following code



class Foo:
def bar(self) -> None:
pass

foo = Foo()
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__ = 'hi'


crashes with



AttributeError: 'method' object has no attribute '__annotations__'


How can this happen?










share|improve this question














The following code



class Foo:
def bar(self) -> None:
pass

foo = Foo()
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__ = 'hi'


crashes with



AttributeError: 'method' object has no attribute '__annotations__'


How can this happen?







python-3.x






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 23 '18 at 17:07









Tobias HermannTobias Hermann

2,98422356




2,98422356













  • hasattr return True if getattr(object, name) don't raise, and when you do getattr(foo.bar, "__annotations__") you get {'return':None}. This is why hasattr(foo.bar, 'annotations') return True.

    – iElden
    Nov 26 '18 at 8:21











  • Seems Python bug to me. AttributeError: readonly attribute would be more sensible error message here.

    – wim
    Nov 26 '18 at 17:49



















  • hasattr return True if getattr(object, name) don't raise, and when you do getattr(foo.bar, "__annotations__") you get {'return':None}. This is why hasattr(foo.bar, 'annotations') return True.

    – iElden
    Nov 26 '18 at 8:21











  • Seems Python bug to me. AttributeError: readonly attribute would be more sensible error message here.

    – wim
    Nov 26 '18 at 17:49

















hasattr return True if getattr(object, name) don't raise, and when you do getattr(foo.bar, "__annotations__") you get {'return':None}. This is why hasattr(foo.bar, 'annotations') return True.

– iElden
Nov 26 '18 at 8:21





hasattr return True if getattr(object, name) don't raise, and when you do getattr(foo.bar, "__annotations__") you get {'return':None}. This is why hasattr(foo.bar, 'annotations') return True.

– iElden
Nov 26 '18 at 8:21













Seems Python bug to me. AttributeError: readonly attribute would be more sensible error message here.

– wim
Nov 26 '18 at 17:49





Seems Python bug to me. AttributeError: readonly attribute would be more sensible error message here.

– wim
Nov 26 '18 at 17:49












2 Answers
2






active

oldest

votes


















1





+50









The attribute error here is raised because you can't set any attribute on a method object:



>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'


The exception here is perhaps confusing because method objects wrap a function object and proxy attribute read access to that underlying function object. So when attributes on the function exist, then hasattr() on the method will return True:



>>> hasattr(foo.bar, 'baz')
False
>>> foo.bar.__func__.baz = 42
>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz
42


However, you still can't set those attributes via the method, regardless:



>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'


So, just because the attribute can be read doesn't mean you can set it. hasattr() is speaking the truth, you just interpreted it to mean something different.



Now, if you tried to set the __annotations__ attribute directly on the underlying function object you'd get another error message:



>>> foo.bar.__func__.__annotations__ = 'hi'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __annotations__ must be set to a dict object


You would want to use a dictionary object here:



>>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
>>> foo.bar.__annotations__
{'return': 'hi'}


However, because __annotations__ is a mutable dictionary, it is just easier to directly manipulate the keys and values to that object, which is perfectly feasible to do via the method wrapper:



>>> foo.bar.__annotations__['return'] = 'int'
>>> foo.bar.__annotations__
{'return': 'int'}


Now, if you were hoping to set per instance annotations, you can't get away with setting attributes on method objects, because method objects are ephemeral, they are created just for the call, then usually discarded right after.



You would have to use custom method descriptor objects via a metaclass and re-create the __annotations__ attribute for those each time, or you could instead pre-bind methods with a new function object that would be given their own attributes. You then have to pay a larger memory price:



import functools

foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
foo.bar.__annotations__['return'] = 'hi'


Either way you completely kill important speed optimisations made in Python 3.7 this way.



And tools that operate on the most important use case for __annatotions__, type hints, do not actually execute code, they read code statically and would completely miss these runtime alterations.






share|improve this answer


























  • Great answer, as usual. Thank you. :) Don't worry, I don't plan to actually change __annotations__ of methods at runtime. I ran into this more by accident, but was confused by the error, so I wanted to find out only for understanding. Your explanation totally makes sense. However I think the error message could be a bit more obvious instead of has no attribute. ;)

    – Tobias Hermann
    Nov 27 '18 at 6:24






  • 1





    @TobiasHermann: The problem there is that then method would have to implement a __setattr__ hook that was just there to test if the attribute existed on the wrapped object, then adjust the error message of the AttributeError exception it throws. That's... going to lead to surprising semantics the moment its a custom object that's being wrapped.

    – Martijn Pieters
    Nov 27 '18 at 11:29






  • 1





    @TobiasHermann: and that's aside from a discussion of the possible side effects and cost of the hasattr() call.

    – Martijn Pieters
    Nov 27 '18 at 11:30



















2














You're getting an error. because __annotations__ is a dictionary. If you want to change values you'll have to do it like this:



if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__['return'] = 'hi'


This will make the return value of your foo.bar be hi instead of None. The only thing I'm not sure about is how the __annotations__ are protected, not allowing you to change them from a dict to string, but I suppose it's some internal check in the source.



UPDATE



For more control over the signature you can use the inspect module and get the Signature object of your class(or method) and edit it from there. For example



import inspect
sig = inspect.signature(foo.bar)
sig.return_annotation # prints None (before modifying)
sig.replace(return_annotation="anything you want")


More on that here






share|improve this answer


























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53450624%2fhasattr-telling-lies-attributeerror-method-object-has-no-attribute-annot%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1





    +50









    The attribute error here is raised because you can't set any attribute on a method object:



    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    The exception here is perhaps confusing because method objects wrap a function object and proxy attribute read access to that underlying function object. So when attributes on the function exist, then hasattr() on the method will return True:



    >>> hasattr(foo.bar, 'baz')
    False
    >>> foo.bar.__func__.baz = 42
    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz
    42


    However, you still can't set those attributes via the method, regardless:



    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    So, just because the attribute can be read doesn't mean you can set it. hasattr() is speaking the truth, you just interpreted it to mean something different.



    Now, if you tried to set the __annotations__ attribute directly on the underlying function object you'd get another error message:



    >>> foo.bar.__func__.__annotations__ = 'hi'
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: __annotations__ must be set to a dict object


    You would want to use a dictionary object here:



    >>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
    >>> foo.bar.__annotations__
    {'return': 'hi'}


    However, because __annotations__ is a mutable dictionary, it is just easier to directly manipulate the keys and values to that object, which is perfectly feasible to do via the method wrapper:



    >>> foo.bar.__annotations__['return'] = 'int'
    >>> foo.bar.__annotations__
    {'return': 'int'}


    Now, if you were hoping to set per instance annotations, you can't get away with setting attributes on method objects, because method objects are ephemeral, they are created just for the call, then usually discarded right after.



    You would have to use custom method descriptor objects via a metaclass and re-create the __annotations__ attribute for those each time, or you could instead pre-bind methods with a new function object that would be given their own attributes. You then have to pay a larger memory price:



    import functools

    foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
    functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
    foo.bar.__annotations__['return'] = 'hi'


    Either way you completely kill important speed optimisations made in Python 3.7 this way.



    And tools that operate on the most important use case for __annatotions__, type hints, do not actually execute code, they read code statically and would completely miss these runtime alterations.






    share|improve this answer


























    • Great answer, as usual. Thank you. :) Don't worry, I don't plan to actually change __annotations__ of methods at runtime. I ran into this more by accident, but was confused by the error, so I wanted to find out only for understanding. Your explanation totally makes sense. However I think the error message could be a bit more obvious instead of has no attribute. ;)

      – Tobias Hermann
      Nov 27 '18 at 6:24






    • 1





      @TobiasHermann: The problem there is that then method would have to implement a __setattr__ hook that was just there to test if the attribute existed on the wrapped object, then adjust the error message of the AttributeError exception it throws. That's... going to lead to surprising semantics the moment its a custom object that's being wrapped.

      – Martijn Pieters
      Nov 27 '18 at 11:29






    • 1





      @TobiasHermann: and that's aside from a discussion of the possible side effects and cost of the hasattr() call.

      – Martijn Pieters
      Nov 27 '18 at 11:30
















    1





    +50









    The attribute error here is raised because you can't set any attribute on a method object:



    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    The exception here is perhaps confusing because method objects wrap a function object and proxy attribute read access to that underlying function object. So when attributes on the function exist, then hasattr() on the method will return True:



    >>> hasattr(foo.bar, 'baz')
    False
    >>> foo.bar.__func__.baz = 42
    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz
    42


    However, you still can't set those attributes via the method, regardless:



    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    So, just because the attribute can be read doesn't mean you can set it. hasattr() is speaking the truth, you just interpreted it to mean something different.



    Now, if you tried to set the __annotations__ attribute directly on the underlying function object you'd get another error message:



    >>> foo.bar.__func__.__annotations__ = 'hi'
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: __annotations__ must be set to a dict object


    You would want to use a dictionary object here:



    >>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
    >>> foo.bar.__annotations__
    {'return': 'hi'}


    However, because __annotations__ is a mutable dictionary, it is just easier to directly manipulate the keys and values to that object, which is perfectly feasible to do via the method wrapper:



    >>> foo.bar.__annotations__['return'] = 'int'
    >>> foo.bar.__annotations__
    {'return': 'int'}


    Now, if you were hoping to set per instance annotations, you can't get away with setting attributes on method objects, because method objects are ephemeral, they are created just for the call, then usually discarded right after.



    You would have to use custom method descriptor objects via a metaclass and re-create the __annotations__ attribute for those each time, or you could instead pre-bind methods with a new function object that would be given their own attributes. You then have to pay a larger memory price:



    import functools

    foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
    functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
    foo.bar.__annotations__['return'] = 'hi'


    Either way you completely kill important speed optimisations made in Python 3.7 this way.



    And tools that operate on the most important use case for __annatotions__, type hints, do not actually execute code, they read code statically and would completely miss these runtime alterations.






    share|improve this answer


























    • Great answer, as usual. Thank you. :) Don't worry, I don't plan to actually change __annotations__ of methods at runtime. I ran into this more by accident, but was confused by the error, so I wanted to find out only for understanding. Your explanation totally makes sense. However I think the error message could be a bit more obvious instead of has no attribute. ;)

      – Tobias Hermann
      Nov 27 '18 at 6:24






    • 1





      @TobiasHermann: The problem there is that then method would have to implement a __setattr__ hook that was just there to test if the attribute existed on the wrapped object, then adjust the error message of the AttributeError exception it throws. That's... going to lead to surprising semantics the moment its a custom object that's being wrapped.

      – Martijn Pieters
      Nov 27 '18 at 11:29






    • 1





      @TobiasHermann: and that's aside from a discussion of the possible side effects and cost of the hasattr() call.

      – Martijn Pieters
      Nov 27 '18 at 11:30














    1





    +50







    1





    +50



    1




    +50





    The attribute error here is raised because you can't set any attribute on a method object:



    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    The exception here is perhaps confusing because method objects wrap a function object and proxy attribute read access to that underlying function object. So when attributes on the function exist, then hasattr() on the method will return True:



    >>> hasattr(foo.bar, 'baz')
    False
    >>> foo.bar.__func__.baz = 42
    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz
    42


    However, you still can't set those attributes via the method, regardless:



    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    So, just because the attribute can be read doesn't mean you can set it. hasattr() is speaking the truth, you just interpreted it to mean something different.



    Now, if you tried to set the __annotations__ attribute directly on the underlying function object you'd get another error message:



    >>> foo.bar.__func__.__annotations__ = 'hi'
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: __annotations__ must be set to a dict object


    You would want to use a dictionary object here:



    >>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
    >>> foo.bar.__annotations__
    {'return': 'hi'}


    However, because __annotations__ is a mutable dictionary, it is just easier to directly manipulate the keys and values to that object, which is perfectly feasible to do via the method wrapper:



    >>> foo.bar.__annotations__['return'] = 'int'
    >>> foo.bar.__annotations__
    {'return': 'int'}


    Now, if you were hoping to set per instance annotations, you can't get away with setting attributes on method objects, because method objects are ephemeral, they are created just for the call, then usually discarded right after.



    You would have to use custom method descriptor objects via a metaclass and re-create the __annotations__ attribute for those each time, or you could instead pre-bind methods with a new function object that would be given their own attributes. You then have to pay a larger memory price:



    import functools

    foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
    functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
    foo.bar.__annotations__['return'] = 'hi'


    Either way you completely kill important speed optimisations made in Python 3.7 this way.



    And tools that operate on the most important use case for __annatotions__, type hints, do not actually execute code, they read code statically and would completely miss these runtime alterations.






    share|improve this answer















    The attribute error here is raised because you can't set any attribute on a method object:



    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    The exception here is perhaps confusing because method objects wrap a function object and proxy attribute read access to that underlying function object. So when attributes on the function exist, then hasattr() on the method will return True:



    >>> hasattr(foo.bar, 'baz')
    False
    >>> foo.bar.__func__.baz = 42
    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz
    42


    However, you still can't set those attributes via the method, regardless:



    >>> hasattr(foo.bar, 'baz')
    True
    >>> foo.bar.baz = 42
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    AttributeError: 'method' object has no attribute 'baz'


    So, just because the attribute can be read doesn't mean you can set it. hasattr() is speaking the truth, you just interpreted it to mean something different.



    Now, if you tried to set the __annotations__ attribute directly on the underlying function object you'd get another error message:



    >>> foo.bar.__func__.__annotations__ = 'hi'
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: __annotations__ must be set to a dict object


    You would want to use a dictionary object here:



    >>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
    >>> foo.bar.__annotations__
    {'return': 'hi'}


    However, because __annotations__ is a mutable dictionary, it is just easier to directly manipulate the keys and values to that object, which is perfectly feasible to do via the method wrapper:



    >>> foo.bar.__annotations__['return'] = 'int'
    >>> foo.bar.__annotations__
    {'return': 'int'}


    Now, if you were hoping to set per instance annotations, you can't get away with setting attributes on method objects, because method objects are ephemeral, they are created just for the call, then usually discarded right after.



    You would have to use custom method descriptor objects via a metaclass and re-create the __annotations__ attribute for those each time, or you could instead pre-bind methods with a new function object that would be given their own attributes. You then have to pay a larger memory price:



    import functools

    foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
    functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
    foo.bar.__annotations__['return'] = 'hi'


    Either way you completely kill important speed optimisations made in Python 3.7 this way.



    And tools that operate on the most important use case for __annatotions__, type hints, do not actually execute code, they read code statically and would completely miss these runtime alterations.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 26 '18 at 19:39

























    answered Nov 26 '18 at 17:44









    Martijn PietersMartijn Pieters

    727k14525542356




    727k14525542356













    • Great answer, as usual. Thank you. :) Don't worry, I don't plan to actually change __annotations__ of methods at runtime. I ran into this more by accident, but was confused by the error, so I wanted to find out only for understanding. Your explanation totally makes sense. However I think the error message could be a bit more obvious instead of has no attribute. ;)

      – Tobias Hermann
      Nov 27 '18 at 6:24






    • 1





      @TobiasHermann: The problem there is that then method would have to implement a __setattr__ hook that was just there to test if the attribute existed on the wrapped object, then adjust the error message of the AttributeError exception it throws. That's... going to lead to surprising semantics the moment its a custom object that's being wrapped.

      – Martijn Pieters
      Nov 27 '18 at 11:29






    • 1





      @TobiasHermann: and that's aside from a discussion of the possible side effects and cost of the hasattr() call.

      – Martijn Pieters
      Nov 27 '18 at 11:30



















    • Great answer, as usual. Thank you. :) Don't worry, I don't plan to actually change __annotations__ of methods at runtime. I ran into this more by accident, but was confused by the error, so I wanted to find out only for understanding. Your explanation totally makes sense. However I think the error message could be a bit more obvious instead of has no attribute. ;)

      – Tobias Hermann
      Nov 27 '18 at 6:24






    • 1





      @TobiasHermann: The problem there is that then method would have to implement a __setattr__ hook that was just there to test if the attribute existed on the wrapped object, then adjust the error message of the AttributeError exception it throws. That's... going to lead to surprising semantics the moment its a custom object that's being wrapped.

      – Martijn Pieters
      Nov 27 '18 at 11:29






    • 1





      @TobiasHermann: and that's aside from a discussion of the possible side effects and cost of the hasattr() call.

      – Martijn Pieters
      Nov 27 '18 at 11:30

















    Great answer, as usual. Thank you. :) Don't worry, I don't plan to actually change __annotations__ of methods at runtime. I ran into this more by accident, but was confused by the error, so I wanted to find out only for understanding. Your explanation totally makes sense. However I think the error message could be a bit more obvious instead of has no attribute. ;)

    – Tobias Hermann
    Nov 27 '18 at 6:24





    Great answer, as usual. Thank you. :) Don't worry, I don't plan to actually change __annotations__ of methods at runtime. I ran into this more by accident, but was confused by the error, so I wanted to find out only for understanding. Your explanation totally makes sense. However I think the error message could be a bit more obvious instead of has no attribute. ;)

    – Tobias Hermann
    Nov 27 '18 at 6:24




    1




    1





    @TobiasHermann: The problem there is that then method would have to implement a __setattr__ hook that was just there to test if the attribute existed on the wrapped object, then adjust the error message of the AttributeError exception it throws. That's... going to lead to surprising semantics the moment its a custom object that's being wrapped.

    – Martijn Pieters
    Nov 27 '18 at 11:29





    @TobiasHermann: The problem there is that then method would have to implement a __setattr__ hook that was just there to test if the attribute existed on the wrapped object, then adjust the error message of the AttributeError exception it throws. That's... going to lead to surprising semantics the moment its a custom object that's being wrapped.

    – Martijn Pieters
    Nov 27 '18 at 11:29




    1




    1





    @TobiasHermann: and that's aside from a discussion of the possible side effects and cost of the hasattr() call.

    – Martijn Pieters
    Nov 27 '18 at 11:30





    @TobiasHermann: and that's aside from a discussion of the possible side effects and cost of the hasattr() call.

    – Martijn Pieters
    Nov 27 '18 at 11:30













    2














    You're getting an error. because __annotations__ is a dictionary. If you want to change values you'll have to do it like this:



    if hasattr(foo.bar, '__annotations__'):
    foo.bar.__annotations__['return'] = 'hi'


    This will make the return value of your foo.bar be hi instead of None. The only thing I'm not sure about is how the __annotations__ are protected, not allowing you to change them from a dict to string, but I suppose it's some internal check in the source.



    UPDATE



    For more control over the signature you can use the inspect module and get the Signature object of your class(or method) and edit it from there. For example



    import inspect
    sig = inspect.signature(foo.bar)
    sig.return_annotation # prints None (before modifying)
    sig.replace(return_annotation="anything you want")


    More on that here






    share|improve this answer






























      2














      You're getting an error. because __annotations__ is a dictionary. If you want to change values you'll have to do it like this:



      if hasattr(foo.bar, '__annotations__'):
      foo.bar.__annotations__['return'] = 'hi'


      This will make the return value of your foo.bar be hi instead of None. The only thing I'm not sure about is how the __annotations__ are protected, not allowing you to change them from a dict to string, but I suppose it's some internal check in the source.



      UPDATE



      For more control over the signature you can use the inspect module and get the Signature object of your class(or method) and edit it from there. For example



      import inspect
      sig = inspect.signature(foo.bar)
      sig.return_annotation # prints None (before modifying)
      sig.replace(return_annotation="anything you want")


      More on that here






      share|improve this answer




























        2












        2








        2







        You're getting an error. because __annotations__ is a dictionary. If you want to change values you'll have to do it like this:



        if hasattr(foo.bar, '__annotations__'):
        foo.bar.__annotations__['return'] = 'hi'


        This will make the return value of your foo.bar be hi instead of None. The only thing I'm not sure about is how the __annotations__ are protected, not allowing you to change them from a dict to string, but I suppose it's some internal check in the source.



        UPDATE



        For more control over the signature you can use the inspect module and get the Signature object of your class(or method) and edit it from there. For example



        import inspect
        sig = inspect.signature(foo.bar)
        sig.return_annotation # prints None (before modifying)
        sig.replace(return_annotation="anything you want")


        More on that here






        share|improve this answer















        You're getting an error. because __annotations__ is a dictionary. If you want to change values you'll have to do it like this:



        if hasattr(foo.bar, '__annotations__'):
        foo.bar.__annotations__['return'] = 'hi'


        This will make the return value of your foo.bar be hi instead of None. The only thing I'm not sure about is how the __annotations__ are protected, not allowing you to change them from a dict to string, but I suppose it's some internal check in the source.



        UPDATE



        For more control over the signature you can use the inspect module and get the Signature object of your class(or method) and edit it from there. For example



        import inspect
        sig = inspect.signature(foo.bar)
        sig.return_annotation # prints None (before modifying)
        sig.replace(return_annotation="anything you want")


        More on that here







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 26 '18 at 9:54

























        answered Nov 26 '18 at 9:25









        BorisuBorisu

        549412




        549412






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53450624%2fhasattr-telling-lies-attributeerror-method-object-has-no-attribute-annot%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            If I really need a card on my start hand, how many mulligans make sense? [duplicate]

            Alcedinidae

            Can an atomic nucleus contain both particles and antiparticles? [duplicate]