Python Forum
How does open context manager work?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How does open context manager work?
#1
While answering a question in another thread I wrote this:
import contextlib
 
@contextlib.contextmanager
def CreateFile(FilePath):
    file = open(FilePath, 'w', encoding="utf-8")
    file.write("Court,Location,Citation Number,Case Description,File Date\n")
    yield file
    file.close()
I occasionally create context managers when writing code that requires "cleaning up", but I never did it for a file. To use the code above, I am forced to us a context manager:
with CreateFile(filename) as file:
    # do stuff with file
I cannot do this because CreateFile creates a context object, not a file:
file = CreateFile(filename)
So how does open() let me do this?
file_one = open(filelename_one)
with open(filename_two) as file_two:
    # do file_two stuff
The best I can do to mimic open() is this:
class CreateFile():
    def __init__(self, filename, mode="w", encoding="utf-8"):
        print(f"CreateFile({filename})")
        self.file = open(filename, mode, encoding=encoding)
        self.file.write("Court,Location,Citation Number,Case Description,File Date\n")

    def __enter__(self):
        print("enter")
        return self.file

    def __exit__(self, *_):
        print("exit")
        self.file.close()

    def __getattr__(self, attribute):
        print(f"__getattr__({attribute})")
        return getattr(self.file, attribute)

file = CreateFile("junk.txt")
file.write("This is a test\n")
print(file.tell())
file.close()

print("\n")
with CreateFile("junk2.txt") as file:
    file.write("This is a test\n")
Output:
CreateFile(junk.txt) __getattr__(write) __getattr__(tell) 75 __getattr__(close) CreateFile(junk2.txt) enter exit
Reply
#2
I would imagine you have pretty much mimicked open, __enter__ would return itself on the real open
Maybe something like this
class MyOpen:
    def __init__(self, file):
        print(f"Opened {file}")

    def __enter__(self):
        print("Enter")
        return self

    def __exit__(self, *args):
        print("Exit")
        return self.close()

    def write(self, string):
        print(f"write: {string}")

    def close(self):
        print("File closed")

    def tell(self):
        return 75


print("file = CreateFile()")
file = MyOpen("junk.txt")
file.write("This is a test")
print(file.tell())
file.close()

print("\n\nwith CreateFile() as file")
with MyOpen("junk2.txt") as file:
    file.write("This is a test")
Output:
file = CreateFile() Opened junk.txt write: This is a test 75 File closed with CreateFile() as file Opened junk2.txt Enter write: This is a test Exit File closed
Reply
#3
No sure I understand the issue but you could nest contexts like so
import contextlib
  
@contextlib.contextmanager
def CreateFile(FilePath):
    with open(FilePath, 'w', encoding="utf-8") as file:
        file.write("Court,Location,Citation Number,Case Description,File Date\n")
        yield file
Reply
#4
There is no issue. I am wondering how open() can be used like a function or a conrext. The contexts I make have to be used with "with" and don't work when called like a function. Not without extra effort. I was wondering if I'm missing something.
Reply
#5
The built-in function open returns relying on mode and buffering an instance of TextIOWrapper, BufferedReader or FileIO.
Each object has the __enter__ method, which is called by the contextmanager. The returned object is the instance itself.
After leaving the block, the __exit__ method is called.

Easy example:
class File:
    def __init__(self, path):
        print("Creating instance")
        self.path = path
    def __enter__(self):
        print("Entering Context")
        return self
    def __exit__(self, exc_type, exc_obj, exc_tb):
        print("Leaving context")
    def __str__(self):
        return "<Instance of File>"

def open(path):
    """
    Returns a new Instance of File
    """
    return File(path)


with open("something") as fd:
    print(fd)

  1. When open("something") is called, a new instance of File is returned
  2. The contextmanager calls the __enter__ method
  3. The returned object from __enter__ is assigned to fd
  4. Statements in the with block are executed
  5. after the last statement in the block, tthe __exit__ method of the instance is called
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#6
So the reason why you can use open() in both a context (with open() as f:) and like a function (f = open()) is because open is special?
Reply
#7
open is just a function, which returns an object.
The returned object is used and not the function open itself.

The returned instance has the methods __enter__ and __exit__ which gives the object the ability to used with a conextmanager.
If you're not using the contextmanager, these methods are never called.
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
Reply
#8
So the magic is not in the open, but the underlying objects. open() might return a text io wrapper. Text IO wrapper knows how to open/create/read/write/whatever, but it also has __enter__ and __exit__ methods. This is essentially what Yoriz posted. If I want to use my classes in a context manager I should design context management into the classes instead of tacking it on with something like contextlib.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  How to not open the context menu if a mouse gesture is executed? MicheliBarcello 2 639 Aug-22-2023, 02:47 PM
Last Post: deanhystad
  Open a new window does not work Nietzsche 4 1,012 Jun-14-2023, 08:52 AM
Last Post: Nietzsche
  with open context inside of a recursive function billykid999 1 552 May-23-2023, 02:37 AM
Last Post: deanhystad
  Context-sensitive delimiter ZZTurn 9 1,394 May-16-2023, 07:31 AM
Last Post: Gribouillis
  Decimal context stevendaprano 1 1,012 Apr-11-2022, 09:44 PM
Last Post: deanhystad
  How to create an app manager _ShevaKadu 8 3,722 Nov-01-2020, 12:47 PM
Last Post: _ShevaKadu
  TextIOWrapper.tell() with Python 3.6.9 in context of 0D/0A fschaef 0 2,040 Mar-29-2020, 09:18 AM
Last Post: fschaef
  Is it OK to use a context manager to simplify attribute access? nholtz 0 2,022 Jun-11-2019, 01:19 AM
Last Post: nholtz
  Smtplib: What does context argument means? Pythenx 1 3,051 Mar-27-2019, 06:25 PM
Last Post: nilamo
  Dealing with multiple context managers heras 5 4,603 Nov-16-2018, 09:01 AM
Last Post: DeaD_EyE

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020