-1

In python is there a way to make a function/class that behaves like a function and a context manager?

Note: that I need the function/class to return an object that doesn't have a __exit__ method and I cant change that object (that's why I am wrapping it).

so just making a class with __enter__ and __exit__ won't work because I need it to also behave like a function.

I have tried the contextmanager decorator:

@contextmanager
def my_context_man(my_str):
    my_str = 'begging ' + my_str
    yield my_str+' after func'
    print('end')

And it worked perfectly within the context manger, but not as a function:

a = 'middle'
old_a = my_context_man(a)
print('old_a', old_a)

with my_context_man(a) as new_a:
    print('new_a', new_a)

the output:

old_a <contextlib._GeneratorContextManager object at 0x0000000004F832E8>
new_a begging middle after func
end

while the desired output will be:

old_a begging middle after func
new_a begging middle after func
end

edit: The specific problem that i am having is with the psycopg2 module. I want to use a different context manager. and it returns a connection object.

def connect(dsn=None, connection_factory=None, cursor_factory=None, **kwargs):
    *the connection code*    
    return conn

I am trying to change it so that people will be able to use it with my context manager but in a way that will not break code. I cannot change the conn object

moshevi
  • 4,999
  • 5
  • 33
  • 50
  • You have a bit of [XY](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) going on here. It is definitively possible for a Class to be a context manager and a function. But what you are trying to achieve with that is not clear. – Stephen Rauch Feb 04 '18 at 22:11
  • edited the question, I hope its clearer now. @StephenRauch – moshevi Feb 04 '18 at 22:24
  • Constructing the context manager will give you a context manager. You can then call that context manager, or if you simply want it to return a string, that can also be done. But you have not yet described what you are actually trying to do. Please read the XY link from my earlier comment. – Stephen Rauch Feb 04 '18 at 22:51
  • It's not clear you know what a context manager is or what it is for. – chepner Feb 04 '18 at 22:53
  • I edited again. Is that clear enough? @StephenRauch – moshevi Feb 04 '18 at 23:21
  • 1
    `connection` objects have usable as context managers since [version 2.5](http://initd.org/psycopg/articles/2013/04/07/psycopg-25-released/), released nearly 5 years ago. – chepner Feb 04 '18 at 23:37

1 Answers1

0

Your __new__ method isn't returning an instance of my_context_man, but an instance of str, and str doesn't have an __enter__ method. It's the return value of __enter__ that gets bound to the name after as in the with statement. You want

class my_context_man:
    def __init__(self, my_str):
        self.msg = my_str
        print("beginning " + my_str)

    def __enter__(self):
        return self.msg

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('end') 
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    How does this solve the *I want to call it like a function*? – Stephen Rauch Feb 04 '18 at 22:15
  • you (@StephenRauch) are right, but that`s a bit besides the point. – moshevi Feb 04 '18 at 22:26
  • I don't even know what that means. A context manager is just an object that has `__enter__` and `__exit__` methods that obey the context-manager protocol. – chepner Feb 04 '18 at 22:47
  • Yeah, which is why I did not answer the question initially. OP is having some challenges with some more advanced concepts. Definitely some XY going on. – Stephen Rauch Feb 04 '18 at 22:48
  • 1
    Sure, but I figure seeing how a context manager should be defined from scratch, rather than with `contextlib.contextmanager`, would be a good start. – chepner Feb 04 '18 at 22:52
  • I edited again. Is that clear enough? @chepner and i understand what are context managers for and how they are created. – moshevi Feb 04 '18 at 23:23