I've been getting the following when trying to use resource_filename when my package is in a zipped egg (since somewhere around 0.3a2 or so):

Traceback (most recent call last):
  File "/usr/local/bin/somescript.py", line 4, in ?
    pkg_resources.run_main('somepackage==1.0', 'somescript.py')
  File "setuptools/pkg_resources.py", line 110, in run_main
require(dist_spec)[0].metadata.run_script(script_name, __main__.__dict__)
  File "setuptools/pkg_resources.py", line 599, in run_script
    exec script_code in namespace, namespace
  File "/usr/local/bin/somescript.py", line 7, in ?

  File "egg/somepackage/somemodule.py", line 3, in ?
  File "setuptools/pkg_resources.py", line 388, in resource_filename
    return get_provider(package_name).get_resource_filename(
File "setuptools/pkg_resources.py", line 728, in get_resource_filename
    return self._extract_resource(manager, resource_name)
  File "setuptools/pkg_resources.py", line 745, in _extract_resource
    zip_stat = self.zipinfo[os.path.join(*self.prefix+parts)]
KeyError: 'somepackage/someresource.txt'

Here's a test egg that illustrates the problem:

<http://lesscode.org/eggs/somepackage-1.0-py2.4.egg>

Install that and then run the included "somescript.py". You should see the traceback above.

The problem seems to be with loading resources relative to a module in a package. The egg contains a package "somepackage" and a module within that package "somemodule". somemodule calls ``resource_filename (__name__, 'someresource.txt')``. The ZipProvider seems to handle this case badly.

Attached is a patch against 0.4a3 that fixes this particular case but I'm not confident it won't cause problems elsewhere. I'll comment each hunk here with an explanation of what I think may be happening.

@@ -573,7 +573,7 @@
     def resource_listdir(self,resource_name):
-        return self._listdir(self._fn(self.egg_info,resource_name))
+        return self._listdir(self._fn(self.module_path, resource_name))
     def metadata_listdir(self,name):
         if self.egg_info:

Here we see NullProvider.resource_listdir using [path]/EGG_INFO instead of [path]. This seems like a simple typo to me as there are resource_XXX and metadata_XXX methods, the first based on the module path and the second set based on the EGG_INFO dir.

@@ -703,7 +703,7 @@
     def __init__(self, module):
         DefaultProvider.__init__(self,module)
self.zipinfo = zipimport._zip_directory_cache [self.loader.archive]
-        self.zip_pre = self.loader.archive+os.sep
+        self.zip_pre = self.module_path + os.sep
     def _short_name(self, path):
         if path.startswith(self.zip_pre):

This is where I was a bit shaky:

    loader_archive = /path/to/the-egg.egg
    module_path = /path/to/the-egg.egg/package

Shouldn't resource loading be relative to the package directory and not the root of the egg?

@@ -738,7 +738,7 @@
     def _extract_resource(self, manager, resource_name):
         if self.resource_isdir(resource_name):
-            return self._extract_dir(resource_name)
+            return self._extract_directory(manager, resource_name)
         parts = resource_name.split('/')
         zip_path = os.path.join(self.module_path, *parts)

I believe this was a simple typo / refactoring-miss. This isn't breaking the test case but is causing problems in another package that is using resource_filename to get a directory.

Thanks,

Ryan Tomayko
                                 [EMAIL PROTECTED]
                                 http://naeblis.cx/rtomayko/

Attachment: zip-resource-loading.patch
Description: Binary data


_______________________________________________
Distutils-SIG maillist  -  [email protected]
http://mail.python.org/mailman/listinfo/distutils-sig

Reply via email to