Traceback: AttributeError:addinfourl instance has no attribute '__exit__'

That error is caused by this line:

with urlopen('https://www.python.org') as story:

You can't use any random object in a with...as statement.

There are two ways to fix this:

Solution 1: Use contextlib.closing:

from contextlib import closing

with closing(urlopen('https://www.python.org')) as story:
    ...

Solution 2: Don't use a with...as statement; instead assign the value to a variable:

story = urlopen('https://www.python.org')
...

Why is this happening?

You can't use any random object in a with ... as statement.

Only those objects will work which have two magic methods: __enter__ and __exit__ implemented on them. Collectively, these methods are called "context manager". An introductory tutorial about this can be found below.

The AttributeError was raised because there isn't any context manager implemented for urlopen (i.e. it doesn't have __enter__ and __exit__ methods defined for it).

This leaves you with two choices:

  1. either don't use with...as statement.
  2. or use contextlib.closing (thanks to @vaultah who provided this solution in a comment below). It automatically implements context manager for any object, thereby allowing you to use with...as statement.

(Note: In Python 3, urlopen does have a context manager, and thus can be used in a with...as statement.)


Tutorial: How to implement the context manager?

To make an object work in a with...as statement, you first need to implement the context manager for that object. In simpler terms, you need to define __enter__ and __exit__ methods for that object/class.

Do read these docs on context managers.

Example:

>>> class Person(object):
        """To implement context manager, just create two methods 
           __enter__ and __exit__.
        """

        def __init__(self, name):
            self.name = name

        def __enter__(self):
            # The value returned by this method is 
            # assigned to the variable after ``as``
            return self

        def __exit__(self, exc_type, exc_value, exc_traceback ):
            # returns either True or False
            # Don't raise any exceptions in this method
            return True


>>> with Person("John Doe") as p:
        print p.name

>>> "John Doe" #success