pypa/pip · error · NotImplementedError
resource_filename() only supported for .egg, not .zip
Error message
resource_filename() only supported for .egg, not .zip
What it means
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.
Source
Thrown at src/pip/_vendor/pkg_resources/__init__.py:2011
if fspath.startswith(self.zip_pre):
return fspath[len(self.zip_pre) :]
raise AssertionError("%s is not a subpath of %s" % (fspath, self.zip_pre))
def _parts(self, zip_path):
# Convert a zipfile subpath into an egg-relative path part list.
# pseudo-fs path
fspath = self.zip_pre + zip_path
if fspath.startswith(self.egg_root + os.sep):
return fspath[len(self.egg_root) + 1 :].split(os.sep)
raise AssertionError("%s is not a subpath of %s" % (fspath, self.egg_root))
@property
def zipinfo(self):
return self._zip_manifests.load(self.loader.archive)
def get_resource_filename(self, manager: ResourceManager, resource_name: str):
if not self.egg_name:
raise NotImplementedError(
"resource_filename() only supported for .egg, not .zip"
)
# no need to lock for extraction, since we use temp names
zip_path = self._resource_to_zip(resource_name)
eagers = self._get_eager_resources()
if '/'.join(self._parts(zip_path)) in eagers:
for name in eagers:
self._extract_resource(manager, self._eager_to_zip(name))
return self._extract_resource(manager, zip_path)
@staticmethod
def _get_date_and_size(zip_stat):
size = zip_stat.file_size
# ymdhms+wday, yday, dst
date_time = zip_stat.date_time + (0, 0, -1)
# 1980 offset already done
timestamp = time.mktime(date_time)
return timestamp, sizeView on GitHub (pinned to d7d0d0a394)
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.
Example fix
// before
path = pkg_resources.resource_filename('mymod', 'data.bin') # NotImplementedError in .zip
// after
data = pkg_resources.resource_string('mymod', 'data.bin') # read bytes, no extraction
# or, modern API with temp extraction:
from importlib.resources import files, as_file
with as_file(files('mymod') / 'data.bin') as p:
use(p) Defensive patterns
Strategy: validation
Validate before calling
import pkg_resources
def safe_resource_filename(module, name):
provider = pkg_resources.get_provider(module)
if not getattr(provider, 'egg_name', None):
# zip/egg without egg name cannot extract to a filename
return None # caller reads bytes instead
return pkg_resources.resource_filename(module, name) Type guard
def provider_can_extract_filename(provider) -> bool:
return bool(getattr(provider, 'egg_name', None)) Try / catch
try:
path = pkg_resources.resource_filename(pkg, name)
except NotImplementedError as e:
if 'only supported for .egg' in str(e):
data = pkg_resources.resource_string(pkg, name) # read bytes, no extraction
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- "egg_name" is empty. This likely means no egg could be found
- Can't change extraction path, files already extracted
- "os.rename" and "os.unlink" are not supported on this platfo
- Error decoding metadata for {self._wheel_name}: {e} in {name
- Wheel '{name}' located at {location} is invalid.
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/2c08b72b318a4b01.json.
Report an issue: GitHub.