import functools
import sys
def file_reader(func):
@functools.wraps(func)
def helper(file, *args, **kwargs):
return File(file, func, *args, **kwargs)
return helper
class File(object):
def __init__(self, file, func, *args, **kwargs):
self.close_file = kwargs.pop('close', True)
# accept either filename or file-like object
self.file = file if hasattr(file, 'read') else open(file)
try:
# func is responsible for self.file if it doesn't return it
self.file = func(self.file, *args, **kwargs)
except: # clean up on any error
self.__exit__(*sys.exc_info())
raise
# context manager support
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
if not self.close_file:
return # do nothing
# clean up
exit = getattr(self.file, '__exit__', None)
if exit is not None:
return exit(*args, **kwargs)
else:
exit = getattr(self.file, 'close', None)
if exit is not None:
exit()
# iterator support
def __iter__(self):
return self
def __next__(self):
return next(self.file)
next = __next__ # Python 2 support
# delegate everything else to file object
def __getattr__(self, attr):
return getattr(self.file, attr)
@file_reader
def f(file):
print(repr(file.read(10)))
return file
file = f('prog.py') # use as ordinary function
print(repr(file.read(20)))
file.seek(0)
for line in file:
print(repr(line))
break
file.close() # need to close explicitly
with f('prog.py') as file: # use as a context manager
print(repr(file.read(20)))
aW1wb3J0IGZ1bmN0b29scwppbXBvcnQgc3lzCgpkZWYgZmlsZV9yZWFkZXIoZnVuYyk6CiAgICBAZnVuY3Rvb2xzLndyYXBzKGZ1bmMpCiAgICBkZWYgaGVscGVyKGZpbGUsICphcmdzLCAqKmt3YXJncyk6CiAgICAgICAgcmV0dXJuIEZpbGUoZmlsZSwgZnVuYywgKmFyZ3MsICoqa3dhcmdzKQogICAgcmV0dXJuIGhlbHBlcgoKCmNsYXNzIEZpbGUob2JqZWN0KToKCiAgICBkZWYgX19pbml0X18oc2VsZiwgZmlsZSwgZnVuYywgKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICBzZWxmLmNsb3NlX2ZpbGUgPSBrd2FyZ3MucG9wKCdjbG9zZScsIFRydWUpCiAgICAgICAgIyBhY2NlcHQgZWl0aGVyIGZpbGVuYW1lIG9yIGZpbGUtbGlrZSBvYmplY3QKICAgICAgICBzZWxmLmZpbGUgPSBmaWxlIGlmIGhhc2F0dHIoZmlsZSwgJ3JlYWQnKSBlbHNlIG9wZW4oZmlsZSkKCiAgICAgICAgdHJ5OgogICAgICAgICAgICAjIGZ1bmMgaXMgcmVzcG9uc2libGUgZm9yIHNlbGYuZmlsZSBpZiBpdCBkb2Vzbid0IHJldHVybiBpdAogICAgICAgICAgICBzZWxmLmZpbGUgPSBmdW5jKHNlbGYuZmlsZSwgKmFyZ3MsICoqa3dhcmdzKQogICAgICAgIGV4Y2VwdDogICMgY2xlYW4gdXAgb24gYW55IGVycm9yCiAgICAgICAgICAgIHNlbGYuX19leGl0X18oKnN5cy5leGNfaW5mbygpKQogICAgICAgICAgICByYWlzZQoKICAgICMgY29udGV4dCBtYW5hZ2VyIHN1cHBvcnQKICAgIGRlZiBfX2VudGVyX18oc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYKCiAgICBkZWYgX19leGl0X18oc2VsZiwgKmFyZ3MsICoqa3dhcmdzKToKICAgICAgICBpZiBub3Qgc2VsZi5jbG9zZV9maWxlOgogICAgICAgICAgICByZXR1cm4gICMgZG8gbm90aGluZwogICAgICAgICMgY2xlYW4gdXAKICAgICAgICBleGl0ID0gZ2V0YXR0cihzZWxmLmZpbGUsICdfX2V4aXRfXycsIE5vbmUpCiAgICAgICAgaWYgZXhpdCBpcyBub3QgTm9uZToKICAgICAgICAgICAgcmV0dXJuIGV4aXQoKmFyZ3MsICoqa3dhcmdzKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGV4aXQgPSBnZXRhdHRyKHNlbGYuZmlsZSwgJ2Nsb3NlJywgTm9uZSkKICAgICAgICAgICAgaWYgZXhpdCBpcyBub3QgTm9uZToKICAgICAgICAgICAgICAgIGV4aXQoKQoKICAgICMgaXRlcmF0b3Igc3VwcG9ydAogICAgZGVmIF9faXRlcl9fKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmCgogICAgZGVmIF9fbmV4dF9fKHNlbGYpOgogICAgICAgIHJldHVybiBuZXh0KHNlbGYuZmlsZSkKCiAgICBuZXh0ID0gX19uZXh0X18gICMgUHl0aG9uIDIgc3VwcG9ydAoKICAgICMgZGVsZWdhdGUgZXZlcnl0aGluZyBlbHNlIHRvIGZpbGUgb2JqZWN0CiAgICBkZWYgX19nZXRhdHRyX18oc2VsZiwgYXR0cik6CiAgICAgICAgcmV0dXJuIGdldGF0dHIoc2VsZi5maWxlLCBhdHRyKQoKCkBmaWxlX3JlYWRlcgpkZWYgZihmaWxlKToKICAgIHByaW50KHJlcHIoZmlsZS5yZWFkKDEwKSkpCiAgICByZXR1cm4gZmlsZQoKZmlsZSA9IGYoJ3Byb2cucHknKSAgIyB1c2UgYXMgb3JkaW5hcnkgZnVuY3Rpb24KcHJpbnQocmVwcihmaWxlLnJlYWQoMjApKSkKZmlsZS5zZWVrKDApCmZvciBsaW5lIGluIGZpbGU6CiAgICBwcmludChyZXByKGxpbmUpKQogICAgYnJlYWsKZmlsZS5jbG9zZSgpICAjIG5lZWQgdG8gY2xvc2UgZXhwbGljaXRseQoKd2l0aCBmKCdwcm9nLnB5JykgYXMgZmlsZTogICMgdXNlIGFzIGEgY29udGV4dCBtYW5hZ2VyCiAgICBwcmludChyZXByKGZpbGUucmVhZCgyMCkpKQ==