-1

In python, I'd like to use a with-statement context manager when using a particular third-party package because their context manager will handle clean-up for me in a best-practice way.

Right now I have (say):

class MyClass(object):

    def __init__(self):
        self.open()

    def open(self):
        # Instantiate something *e.g.* open() a file

    def run(self):
        # Do some work *e.g.* read() from a filehandle

    def cleanup(self):
        # Clean everything up *e.g.* close() filehandle

This would then be called as

my_obj = MyClass()
my_obj.open()
my_obj.run()
my_obj.cleanup()

I'd ideally like to do all this using a context manager referenced within the class (if possible). It's the run() method that needs the context here. I am not sure how to do this - here are some theoretical attempts:

Option 1:

my_obj2=MyClass2()
with my_obj2.third_party_context_manager_method() as mcmm:
    my_obj2.run()

Option 2:

my_obj3=MyClass3()
my_obj3.run_modified_containing_third_party_context_manager()

Bonus: Now, in fact I need a double context manager in this case - how on earth..?

Please note: The file open example is just an example! My case is somewhat more complicated but the idea is that if I can make it work for that I can achieve anything :)

Another note: The use case is a dask cluster/client cleanup scenario - REF: How can I exit Dask cleanly?

jtlz2
  • 7,700
  • 9
  • 64
  • 114
  • 2
    Where does the third-party context manager come in? I see where *your* context manager comes in, but not the third-party context manager. – user2357112 May 26 '23 at 08:13
  • What is the problem with writing such a context manager and returning it when `my_context_manager_method` is called? – Michael Butscher May 26 '23 at 08:16
  • Someone voted to close because of lacking detail and clarity - where am I lacking either..? I thought I had done quite well there :( – jtlz2 May 26 '23 at 08:34
  • @user2357112 That is exactly the point - how do I call my `run()` method inside a `with third_party_context_manager:` statement? – jtlz2 May 26 '23 at 08:35
  • @MichaelButscher I don't follow - if that's the start of an answer I'd love to see one. Thanks – jtlz2 May 26 '23 at 08:36
  • I voted to close (and downvoted) because you needed more than 20 minutes to react on the question of @user2357112 (and mine). – Michael Butscher May 26 '23 at 08:43
  • I don't understand the problem yet. You wrote to user2357112: "how do I call my `run()` method inside a `with third_party_context_manager:`statement?" But what is the problem with writing `with third_party_context_manager: my_obj.run()`? – Michael Butscher May 26 '23 at 08:48
  • I don't see the relationship between reaction and time - is there a rule about that? I was actually just away from my desk I'm afraid. Sorry to say, I think you are being a bit mean :( In terms of your question: I would like the `third_party_context_manager` to be contained within `my_obj`. – jtlz2 May 26 '23 at 09:02
  • The question was contradictory (own context manager, third party context manager) and (is yet) hard to understand. In such cases I write a comment (if not someone else did already) and wait at least 10 minutes for a reply before I vote. Many people here don't wait that long (or don't ask for clarification at all) before voting to close. – Michael Butscher May 26 '23 at 09:11
  • 1
    What exactly means "`third_party_context_manager` to be contained within `my_obj`".? Should `my_obj` hold a reference to the context manager class (or factory function) or should the code of the context manager be contained in the code of `MyClass`? – Michael Butscher May 26 '23 at 09:15
  • The code of the context manager should be contained within the code of MyClass (option 2 that I give), or it should hold a reference to the third-party context manager (option 1 that I give). Is that enough for you to go on? – jtlz2 May 26 '23 at 09:17

1 Answers1

0

I'm not sure if you have any limitations you have but here's a couple of basic ideas.

With a decorator:

def with_context(cm):
    def decorator(fn):
        def wrapper(*args, **kwargs):
            with cm:
                return fn(*args, **kwargs)
        return wrapper
    return decorator

class MyClass(object):
    ...
    @with_context(ThirdPartyCM)
    def run(self):
        # Do some work *e.g.* read() from a filehandle

Then just the simple way:

class MyClass(object):
    ...

    def _run(self):
        # Do some work *e.g.* read() from a filehandle

    def run(self):
        with ThirdPartyCM() as cm:
            self._run()

Alternatively you could inherit the 3rd party class if you needed it to be open for everything, but as you only need it open for run, I don't see why the above wouldn't work.

Peter
  • 3,186
  • 3
  • 26
  • 59