The details are a little technical, so let's start with a simple version:
Some types know how to use them in a with statement. An example of this type are file objects, for example, what you return from open . As it turned out, the objects returned from urllib.request.urlopen are also an example of this type, so your second example can be written in the same way as the first.
But some types do not know how to use them in a with statement. The closing function is intended to wrap such types, if they have a close method, it is called by their close method when exiting the with statement.
Of course, some types do not know how to use the with statement, nor can they be used with closing because their cleaning method is not called close (or because it clears them more difficult than just closing them). In this case, you need to write your own context manager. But even this is usually not that difficult.
In technical terms:
Operator
A with requires a context manager , an object with __enter__ and __exit__ methods. It will call the __enter__ method and give you the value returned by this method in the as clause, and then call the __exit__ method at the end of the with statement.
File objects inherit from io.IOBase , which is the context manager, the __enter__ method returns itself and whose __exit__ calls self.close() .
The object returned by urlopen (assuming an http or https URL) is an HTTPResponse , which, as the docs say, "can be used with the with statement."
closing function:
Return the context manager that closes the object after the block completes. This is basically equivalent to:
@contextmanager def closing(thing): try: yield thing finally: thing.close()
This is not always 100% clear in documents whose types are context managers and which are not. Moreover, since the creation of the main drive with 3.1, everything has been done that could be a context manager in one (and for that matter, do everything that is basically file-like in the actual IOBase , if that makes sense), but this is still not 100% compared to 3.4.
You can always just try and see. If you get AttributeError: __exit__ , then the object cannot be used as a context manager. If you think it should be, report an error indicating a change. If you do not receive this error, but the documents do not mention that it is legal, indicate the error that suggests updating the documents.