3

I'm receiving an error of AttributeError: __exit__ in the code below after finishing the with section. The Element object has is being returned in the with and it has __exit__ defined, so I'm baffled.

class Builder:
    def __init__(self):
        print("Builder init fires")

    def __getattr__(self, name):
        return Element(name, self)

class Element:
    def __init__(self, name, builder):
        self.name = name
        self.builder = builder
        print("Element init fires for name of", self.name)
    def __call__(*args, **kargs):
        print("CALL fires, now with attributes listed:")
        for attr, value in sorted(kargs.items()):
            print(' %s=>%s' % (attr, value))

    def __enter__(self):  
        return self

    def __exit__(self, type, value, traceback): 
        pass

aa = Builder()        
with aa.feed(xmlns='http://www.w3.org/2005/Atom'):
    print("INSIDE THE WITH")
3
  • 4
    When I run the code, I get an AttributeError for __enter__, because your __call_ returns None. Commented Aug 27, 2017 at 2:43
  • 1
    It appears that under python 3.5.2 it receives an AttributeError for __exit__ but under python 3.6.1 its for __enter__. I see you're correct that __call__() determines what is being returned by the with and it needs to have return self. Thanks. Commented Aug 27, 2017 at 2:59
  • Similar to missing parameters, omitting params and () completely will throw the same exception. For example: with aa.feed: Commented May 18, 2018 at 17:19

1 Answer 1

1

The value of expression after with keyword must be a valid context manager. That means the value of the expresion must have attributes __enter__ and __exit__ and these must accept parameters described in docs With Statement Context Managers. You can easily verify that the part aa.feed would be acceptable, but the value of whole expression is None and it has no necessary attribute. A difference between Python 3.5 and 3.6 is that one fails on missing __exit__ and the latter on missing __enter__. Nothing unexpected.

You also forgot self in the line def __call__(self, *args, **kargs): that would be a problem if you will use args and it is a pythonic pattern also with unused args.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.