Recipe 2.15. Adapting a File-like Object to a True File Object
Credit: Michael Kent
Problem
You need to pass a file-like object (e.g.,
the results of a call such as urllib.urlopen) to a
function or method that insists on receiving a true file object
(e.g., a function such as marshal.load).
Solution
To cooperate with such type-checking, we need to write all data from
the file-like object into a temporary file on disk. Then, we can use
the (true) file object for that temporary disk file.
Here's a function that implements this idea:
import types, tempfile
CHUNK_SIZE = 16 * 1024
def adapt_file(fileObj):
if isinstance(fileObj, file): return fileObj
tmpFileObj = tempfile.TemporaryFile
while True:
data = fileObj.read(CHUNK_SIZE)
if not data: break
tmpFileObj.write(data)
fileObj.close( )
tmpFileObj.seek(0)
return tmpFileObj
Discussion
This
recipe demonstrates an unusual Pythonic application of the Adapter
Design Pattern (i.e., what to do when you have an X and you need a Y
instead). While design patterns are most normally thought of in an
object-oriented way, and therefore implemented by writing classes,
nothing is intrinsically necessary about that. In this case, for
example, we don't really need to introduce any new
class, since the adapt_file function is obviously
sufficient. Therefore, we respect Occam's Razor and
do not introduce entities without necessity.One way or another, you should think in terms of adaptation, in
preference to type testing, even when you need to rely on some
lower-level utility that insists on precise types. Instead of raising
an exception when you get passed an object that's
perfectly adequate save for the technicality of type membership,
think of the possibility of adapting what you
get passed to what you need. In this way, your code will be more
flexible and more suitable for reuse.
See Also
Documentation on built-in file objects, and
modules tempfile and marshal,
in the Library Reference and Python
in a Nutshell.