Re: Loading Resources From a Python Module/Package
Donald Stufft <[email protected]> Sat, 31 Jan 2015 13:07:29 -0500
| Newsgroups | gmane.comp.python.import |
|---|---|
| Message-ID | <[email protected]> |
> On Jan 31, 2015, at 12:44 PM, Barry Warsaw <[email protected]> wrote: > > On Jan 30, 2015, at 07:52 PM, Donald Stufft wrote: > >>> On Jan 30, 2015, at 7:18 PM, Paul Moore <[email protected]> wrote: >>> Related question - how would the temp files be cleaned up? At exit? >> >> My patch registers an atexit handler that cleans up the temporary files yea. > > Why not implement it as a context manager? > > I'm not a big fan of overloading the atexit handler because there are > situations where it might not get called (e.g. the program crashes or is kill > -9'd), but a context manager allows the resource to be cleaned up asap. > > Reviewing my own uses of pkg_resources.resource_filename() I think it would > work just fine because I rarely need the path much longer than the immediate > operation. If I did need to cache it more permanently, I could easily do: > > with resource_filename('my.package.path', 'foo.dat') as path: > shutil.copy(path, some_more_permanent_location) > > Easy peasy. The reasons for not wanting to use a context manager are sort of intertwined with each other. The competitor to this function is something like: import os.path import time LOGO_PATH = os.path.join(os.path.dirname(__file__), "logo.gif") def print_logo_path(): print(LOGO_PATH) while True: print_logo_path() time.sleep(1) So when looking at an alternative that we want people to use we have to consider the cost of porting to that code from the old way. Using an atexit handler means that the above code can be switched to the new mechanism just by chaning a single line: LOGO_PATH = importlib.resources.get_filename(__name__, "logo.gif") Using a context manager would require something like: LOGO_MAKER = lambda: importlib.resources.get_filename(__name__, "logo.gif") def print_logo_path(): with LOGO_MAKER as filename: print(filename) Or: _LOGO_TMP = importlib.resources.get_filename(__name__, "logo.gif") atexit.register(_LOGO_TMP.cleanup) LOGO_PATH = _LOGO_TMP.name It makes it more akward to use anytime you need to use the file in multiple locations or multiple times and since each context manager instance (in the worst case) is going to need to get bytes, create a temp file, and write bytes for each use of the context manager. The other thing is that for the "common" case, where the resource is available on the file system already because we're just using a FileLoader, there is no need for an atexit handler or a temporary file at all. The context manager would only really exist for the uncommon case where we need to write the data to a temporary file. Using the atexit handler allows us to provide the best API for the common case, without too much problem for the uncommon case. Yes it does mean that in certain cases the temporary files may be left behind, particularly with kill -9 or segfaults or what have you. However that case already exists, the only thing the context manager does is narrow the window of case where a kill -9 or a segfault can leave temporary files behind. --- Donald Stufft PGP: 7C6B 7C5D 5E2B 6356 A926 F04F 6E3C BCE9 3372 DCFA