support filename, path, and buffer input

Viewed 110

A common requirement in my Python packages is to allow for a file input either as a string filename, a pathlib.Path, or an already opened buffer. I usually separate out the buffer like

def foo_from_file(filename, *args, **kwargs):
    with open(filename) as f:
        foo_from_buffer(f, *args, **kwargs)


def foo_from_buffer(f):
    f.readline()
    # do something
    return

but a cleaner approach from a user's perspective would probably be

def foo(file_or_buffer):
    if hasattr(file_or_buffer, "readline"):  # ???
        f = file_or_buffer
    else:
        f = open(file_or_buffer)

    f.readline()

(This particular implementation isn't very nice because it doesn't close() on failure.)

Is file_or_buffer a common argument in Python methods or do you separate between the two? How to best implement it?

1 Answers

Adapting from a similar type of question I had regarding an already-open database connection that could have been passed in as a parameter or not, you can use a nullcontext to "do nothing" when whatever you pass in has readline:

from contextlib import nullcontext
from pathlib import Path
from io import StringIO

def foo(file_or_buffer):
    if hasattr(file_or_buffer, "readline"):
        cm = nullcontext(file_or_buffer)
    else:
        cm = open(file_or_buffer)

    with cm as f:
        line = f.readline()
        print(line)
    
pa = Path(__file__)

foo(pa)
foo(f"{pa}")

with pa.open() as fi:
    foo(fi)
with pa.open() as fi:
    buffer = StringIO(fi.read())
foo(buffer)

output:

from contextlib import nullcontext

from contextlib import nullcontext

from contextlib import nullcontext

from contextlib import nullcontext
Related