-2

I have the following scheme, with different conditional with statements:

if not remote:
    _open = open
    os.chdir(localpath)
else:
    sftp = pysftp.Connection(host, username=user, password=sftppwd)
    with sftp:
        sftp.chdir(remotepath)
        _open = sftp.open

with _open('myfile', 'rb') as f:        # and then lots of other files are opened too
    x = f.read(4)
    ...

The last line fails in the remote case because the sftp object / context manager has expired.

I have read Conditional with statement in Python, but here it's not exactly the same: I could create a dummy context manager for the non-remote case, but I'm not sure it would be enough.

I have thought about using ExitStack but then I'm afraid it would look complex when opening further files: each simple with _open(...) as f: would need to be rewritten into a less readable stack.enter_context(_open(...)) when some more code is arriving.

What's the simplest solution in this case? (avoiding creating new functions if possible and keep a simple if ... else flow)

Basj
  • 41,386
  • 99
  • 383
  • 673
  • I don't understand why you wouldn't just do `with pysftp.Connection(...) as sftp:`. Otherwise, have you considered creating a wrapper object that would do all necessary setup procedures and return the object that needs to be used as the context manager ? – ApplePie Nov 22 '20 at 23:08
  • The context manager is doing its job: closing the connection once you get out scope. The one way out is not to use `with` but then you'll have to remember to `close` the connection by yourself in the end. Which might not be bad either. – sal Nov 22 '20 at 23:11
  • Why no new functions? I believe this can be easily solved by adding just one function – Dani Mesejo Nov 22 '20 at 23:11
  • @sal if I remember a with ... also does things when there are exceptions in order to close things well anyway. So I wanted to keep it and avoid doing close myself. – Basj Nov 22 '20 at 23:18
  • How @DaniMesejo? If you mean the last two lines in a new function, no (because it's more complex than this in reality). – Basj Nov 22 '20 at 23:19
  • It still remains being a duplicate of what you have found. Setting `_open` and changing the folder can remain in `if`-s (inside the `with pysftp.Connection() if remote else dummyStuff as sftp:`, `if remote: _open=sftp.open; sftp.chdir()`, `else: _open=open; chdir()`). Where is the difference? – tevemadar Nov 22 '20 at 23:39

1 Answers1

2

The main feature of your code is that you want your with _open(): ... context manager to be inside another context manager for the remote branch and to be freely roaming in the other case. I think the easiest is to use a dummy context manager, which is discussed in more depth here: How do I write a null (no-op) contextmanager in Python?

Briefly, you would set up a dummy context manager in the non-remote case, e.g.:

import os
# from contextlib import suppress as nullcontext  # Python 3.4+
from contextlib import nullcontext  # Python 3.7+
import pysftp


remote = False
localpath = '.'
remotepath = '.'

if not remote:
    _open_cm = nullcontext()
    _open_cm.chdir = os.chdir
    _open_cm.open = open
    _path = localpath
else:
    _open_cm = pysftp.Connection(host, username=user, password=sftppwd)
    _path = remotepath

with _open_cm:
    _open_cm.chdir(_path)
    with _open_cm.open('myfile', 'rb') as f:
        x = f.read(4)
        ...

(WARNING! I have not tested the above code, and it uses monkey-patching, but just to get you the idea)

norok2
  • 25,683
  • 4
  • 73
  • 99