{"id":"2c08b72b318a4b01","repo":"pypa/pip","slug":"resource-filename-only-supported-for-egg-not","errorCode":null,"errorMessage":"resource_filename() only supported for .egg, not .zip","messagePattern":"resource_filename\\(\\) only supported for \\.egg, not \\.zip","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/pkg_resources/__init__.py","lineNumber":2011,"sourceCode":"        if fspath.startswith(self.zip_pre):\n            return fspath[len(self.zip_pre) :]\n        raise AssertionError(\"%s is not a subpath of %s\" % (fspath, self.zip_pre))\n\n    def _parts(self, zip_path):\n        # Convert a zipfile subpath into an egg-relative path part list.\n        # pseudo-fs path\n        fspath = self.zip_pre + zip_path\n        if fspath.startswith(self.egg_root + os.sep):\n            return fspath[len(self.egg_root) + 1 :].split(os.sep)\n        raise AssertionError(\"%s is not a subpath of %s\" % (fspath, self.egg_root))\n\n    @property\n    def zipinfo(self):\n        return self._zip_manifests.load(self.loader.archive)\n\n    def get_resource_filename(self, manager: ResourceManager, resource_name: str):\n        if not self.egg_name:\n            raise NotImplementedError(\n                \"resource_filename() only supported for .egg, not .zip\"\n            )\n        # no need to lock for extraction, since we use temp names\n        zip_path = self._resource_to_zip(resource_name)\n        eagers = self._get_eager_resources()\n        if '/'.join(self._parts(zip_path)) in eagers:\n            for name in eagers:\n                self._extract_resource(manager, self._eager_to_zip(name))\n        return self._extract_resource(manager, zip_path)\n\n    @staticmethod\n    def _get_date_and_size(zip_stat):\n        size = zip_stat.file_size\n        # ymdhms+wday, yday, dst\n        date_time = zip_stat.date_time + (0, 0, -1)\n        # 1980 offset already done\n        timestamp = time.mktime(date_time)\n        return timestamp, size","sourceCodeStart":1993,"sourceCodeEnd":2029,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/pkg_resources/__init__.py#L1993-L2029","documentation":"Raised as NotImplementedError by ZipProvider.get_resource_filename() when self.egg_name is falsy. ZipProvider (registered for zipimport.zipimporter) needs an egg name to compute a stable cache directory for extracted files; it derives egg_name from the archive path via EggProvider._setup_prefix, which only sets it when an ancestor path matches _is_egg_path. A plain .zip (not named/structured as an egg) leaves egg_name empty, so resource_filename cannot extract.","triggerScenarios":"Calling resource_filename()/get_resource_filename() on a module imported from a plain .zip archive (or a zipapp) whose name is not recognized as an egg, so egg_name stays None and extraction is refused.","commonSituations":"Running a Python zipapp (.pyz), shipping code in a zip for portability, or zipimporting a non-egg archive, then trying to get a real filesystem path for a bundled resource.","solutions":["Use resource_string()/get_resource_stream() to read bytes directly from the zip without extraction, avoiding the egg_name requirement.","Rename/repackage the archive as an .egg (or ensure an EGG-INFO entry exists) so _is_egg_path recognizes it and sets egg_name.","Migrate to importlib.resources.as_file / files(), which provides a temporary extracted path for zip-backed resources without egg semantics."],"exampleFix":"// before\npath = pkg_resources.resource_filename('mymod', 'data.bin')  # NotImplementedError in .zip\n\n// after\ndata = pkg_resources.resource_string('mymod', 'data.bin')  # read bytes, no extraction\n\n# or, modern API with temp extraction:\nfrom importlib.resources import files, as_file\nwith as_file(files('mymod') / 'data.bin') as p:\n    use(p)","handlingStrategy":"validation","validationCode":"import pkg_resources\n\ndef safe_resource_filename(module, name):\n    provider = pkg_resources.get_provider(module)\n    if not getattr(provider, 'egg_name', None):\n        # zip/egg without egg name cannot extract to a filename\n        return None  # caller reads bytes instead\n    return pkg_resources.resource_filename(module, name)","typeGuard":"def provider_can_extract_filename(provider) -> bool:\n    return bool(getattr(provider, 'egg_name', None))","tryCatchPattern":"try:\n    path = pkg_resources.resource_filename(pkg, name)\nexcept NotImplementedError as e:\n    if 'only supported for .egg' in str(e):\n        data = pkg_resources.resource_string(pkg, name)  # read bytes, no extraction\n    else:\n        raise","preventionTips":["Prefer resource_string/resource_stream over resource_filename for zip-backed modules.","Package zip archives as .egg (with EGG-INFO) if you need extraction paths.","Use importlib.resources as_file(files(pkg)/name) for portable temp extraction."],"tags":["pkg-resources","zip","egg","extraction","resource-filename"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}