
with open("demo.txt", "r", encoding="utf-8") as f:
    pass

# écrire un manager
class File(object):
    def __init__(self, file_name, method):
        self.file_obj = open(file_name, method)
    def __enter__(self):
        return self.file_obj
    def __exit__(self, type, value, traceback):
        self.file_obj.close()

# Just by defining __enter__ and __exit__ methods we can use our new class in a with statement. Let’s try:
#
# with File('demo.txt', 'w') as opened_file:
#     opened_file.write('Hola!')
#
# Our __exit__ method accepts three arguments. They are required by every __exit__ method which is a part of a Context Manager class. Let’s talk about what happens under-the-hood.
#
#     The with statement stores the __exit__ method of the File class.
#     It calls the __enter__ method of the File class.
#     The __enter__ method opens the file and returns it.
#     The opened file handle is passed to opened_file.
#     We write to the file using .write().
#     The with statement calls the stored __exit__ method.
#     The __exit__ method closes the file.


# We did not talk about the type, value and traceback arguments of the __exit__ method. Between the 4th and 6th step, if an exception occurs, Python passes the type, value and traceback of the exception to the __exit__ method. It allows the __exit__ method to decide how to close the file and if any further steps are required. In our case we are not paying any attention to them.
#
# What if our file object raises an exception? We might be trying to access a method on the file object which it does not supports. For instance:
#
# with File('demo.txt', 'w') as opened_file:
#     opened_file.undefined_function('Hola!')
#
# Let’s list the steps which are taken by the with statement when an error is encountered:
#
#     It passes the type, value and traceback of the error to the __exit__ method.
#     It allows the __exit__ method to handle the exception.
#     If __exit__ returns True then the exception was gracefully handled.
#     If anything other than True is returned by the __exit__ method then the exception is raised by the with statement.

