1

I have a function (not mine) which accepts a filename as input, then does things with the file content.

On the other hand, I receive the data which is the "file content" above from somewhere, not as a file.

To illustrate the case, consider the code below.

  • myfunc() is something I do not control
  • the expectation of the authors of myfunc() is that the data to process will be in a file ...
  • ... but I have it as a string
# a function (which I do not control) which uses a filename as input 
def myfunc(filename):
    with open(filename) as f:
        print(f.read())

# case 1: I have a real file
# creation of the file for the sake of the example. 
# The authors of myfunc() expect that the the file is just there.
with open("afile.txt", "w") as f:
    f.write("hello")

# normal usage of myfunc()
myfunc("afile.txt")

# case 2: I have the contents of the file and need to use myfunc()
# data below is received from somewhere, as a string
data = "world"

# I would like to pass it to myfunc 
# ...

How to pass <something> as the argument of the function so that <something> holds the content of data but behaves as a file name?

The obvious solution is to dump data into a file and pass the filename to myfunc(). I would like to avoid this intermediate step to keep the whole thing in memory.

WoJ
  • 27,165
  • 48
  • 180
  • 345
  • What operating system are you on? Are OS-specific solutions acceptable? (E.g. you can pass `/dev/stdin` on some systems) – alexis Nov 09 '18 at 13:54
  • Take a look in [this question](https://stackoverflow.com/questions/18550127/how-to-do-virtual-file-processing) – Hemerson Tacon Nov 09 '18 at 13:54
  • 1
    @HemersonTacon: thanks. That would be almost it, but I do not see how to access the filename of the created file. File-like objects are mentioned, which I read as the file handler (in other words `f` from `f = open('afile.txt')` and not `afile.txt`) – WoJ Nov 09 '18 at 14:03
  • @alexis: ultimately yes, though I would prefer to use a portable solution. I already thought about `/dev/shm` on Linux – WoJ Nov 09 '18 at 14:05
  • How about [NamedTemporaryFile](https://docs.python.org/3/library/tempfile.html#tempfile.NamedTemporaryFile) method of the same module? – Hemerson Tacon Nov 09 '18 at 14:06
  • 1
    @HemersonTacon in the end it still writes a file, which wouldn't be that different for OP to do manually (which they know they can do, just want to avoid). – r.ook Nov 09 '18 at 14:07
  • 1
    @HemersonTacon: but then I would write the file to disk, which I explicitly wanted to avoid (see the last line of my question) – WoJ Nov 09 '18 at 14:07
  • @Idlehands: this is the second time you are a few seconds faster than me with the same comments :) – WoJ Nov 09 '18 at 14:08
  • There is a small chance, that the routine is able to use a file-like handle like StringIO instance already if you pass one instead of a filename; at least worth a try. – guidot Nov 09 '18 at 14:30
  • @guidot: unfortunately not, I checked the code (which I do not control but can of course see) and it requires a filename – WoJ Nov 09 '18 at 14:47

2 Answers2

1

This is a bit convoluted, so bear with me... but if you absolutely want to avoid writing to disk you could shadow the builtin open() function with your own...

from io import StringIO
from contextlib import contextmanager

@contextmanager
def myfunc_open(*args, **kwargs):
    resource = StringIO(args[0])
    try:
        yield resource
    finally:
        resource.close()

We'll use StringIO since it has the IO functions you'd expect within the with open() context. Depending on the context you might want to use BytesIO instead.

Then, before you call upon myfunc simply back up open() and replace with your own:

def myfunc(filepath):
    with open(filepath, 'r') as file:
        print(file.read())
try:
    myfunc('This is doomed to fail')
except:
    print("Normal myfunc won't work")

_backup_open = open  # back up before you shadow it!
open = myfunc_open
myfunc('This will however be read')

open = _backup_open  # Perfectly balanced, as all things should be.

Result:

# Normal myfunc won't work
# This will however be read

Note: If myfunc is imported from another module, it'll require a little bit of work around:

import myfunc_module

myfunc_module.open = myfunc_open
myfunc_module.myfunc('This works too')

myfunc_module.open = _backup_open

Note you do have to import the whole module instead of from module import func so you can shadow the open within its namespace. Sample here.

This may be the evilest stroke of genius I have so far in my hobbyist journey...

r.ook
  • 13,466
  • 2
  • 22
  • 39
-2

if you send data as an argument in myfunc() then it will give FileNotFoundError. if this file doesn't exist then that must mean it is the file content that you are trying to send as the argument. so, if use exception handling then this may do the trick

try :
  myfunc(data)
except FileNotFoundError:
  print(data)
Nehal Birla
  • 142
  • 1
  • 14
  • 1
    I think you misunderstood my question. I know that what is expected is a filename and that what I have is a string (the content of the expected file). – WoJ Nov 09 '18 at 14:31
  • you want to print the file content right? if it is inside the file then you can easily print it using myfunc() but if you already have the file content as a string then this will work – Nehal Birla Nov 09 '18 at 14:39
  • what `myfunc()` does in the code in the question is just an example. The idea is that I have to use a function which accepts a filename (and does things with the contents of the file, does not matter what - in the example it prints them) but what I have is the contents of the file and not the file itself (and I do not want to dump it into a file). – WoJ Nov 09 '18 at 14:49