Claude Code for zipimport: Python Import from Zip Archives — Claude Skills 360 Blog
Blog / AI / Claude Code for zipimport: Python Import from Zip Archives
AI

Claude Code for zipimport: Python Import from Zip Archives

Published: December 12, 2028
Read time: 5 min read
By: Claude Skills 360

Python’s zipimport module lets you import packages directly from .zip files listed on sys.path. import zipimport. Create: zi = zipimport.zipimporter(path)path is either a zip file path ("app.zip") or a path inside a zip ("app.zip/subdir"). Attributes: zi.archive — the zip file path; zi.prefix — the path within the archive. Find (modern): zi.find_spec(fullname, target=None)ModuleSpec | None. Find (legacy): zi.find_module(fullname, path=None)self | None. Load (legacy): zi.load_module(fullname) → module (deprecated, use importlib machinery). Data: zi.get_data(pathname)bytes — reads any file from the zip by archive-relative path. Source: zi.get_source(fullname)str | None — decompressed .py source for the module or None if only .pyc present. Code: zi.get_code(fullname)code | None — compiled code object. Filename: zi.get_filename(fullname) → archive-rooted path. Is-package: zi.is_package(fullname)bool. Use: add a zip file to sys.path and Python’s import machinery picks it up automatically via zipimport.zipimporter; inspect with zipimport.zipimporter(path) directly when you need data access without importing. Claude Code generates single-file app bundles, resource readers, zip-based plugin loaders, and distribution validators.

CLAUDE.md for zipimport

## zipimport Stack
- Stdlib: import zipimport, sys, zipfile, importlib
- Install: sys.path.insert(0, "bundle.zip")  # auto-activates zipimporter
- Inspect: zi = zipimport.zipimporter("bundle.zip")
- Find:    spec = zi.find_spec("mymodule")   # ModuleSpec or None
- Data:    data = zi.get_data("bundle.zip/assets/config.json")
- Source:  src = zi.get_source("mymodule")  # .py source string or None
- Note:    zip entries on sys.path are auto-handled by Python's import system

zipimport Zip Import Pipeline

# app/zipimportutil.py — bundle builder, reader, loader, validator
from __future__ import annotations

import importlib
import importlib.util
import io
import sys
import types
import zipfile
import zipimport
from dataclasses import dataclass, field
from pathlib import Path


# ─────────────────────────────────────────────────────────────────────────────
# 1. Inspection helpers
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class ZipModuleInfo:
    """
    Metadata about one module found inside a zip archive.

    Example:
        for info in list_zip_modules("bundle.zip"):
            print(info)
    """
    name:       str
    is_package: bool
    has_source: bool
    filename:   str

    def __str__(self) -> str:
        flags = []
        if self.is_package:
            flags.append("pkg")
        if self.has_source:
            flags.append("src")
        flag_str = f"  [{', '.join(flags)}]" if flags else ""
        return f"{self.name:30s}  {self.filename}{flag_str}"


def list_zip_modules(zip_path: "str | Path") -> list[ZipModuleInfo]:
    """
    Return metadata for all importable modules inside a zip archive.

    Example:
        for info in list_zip_modules("bundle.zip"):
            print(info)
    """
    zi = zipimport.zipimporter(str(zip_path))
    results = []

    with zipfile.ZipFile(str(zip_path)) as zf:
        names_in_zip = set(zf.namelist())

    # Collect candidate module names from .py and .pyc files
    seen: set[str] = set()
    with zipfile.ZipFile(str(zip_path)) as zf:
        for entry in zf.namelist():
            # Strip prefix inside the zip
            p = entry.replace("\\", "/")
            if p.endswith(".py") or p.endswith(".pyc"):
                # Convert path to dotted module name
                if p.endswith("/__init__.py") or p.endswith("/__init__.pyc"):
                    mod = p.rsplit("/", 1)[0].replace("/", ".")
                elif "/" in p:
                    mod = p.rsplit("/", 1)[1]
                    mod = mod[:-3] if mod.endswith(".py") else mod[:-4]
                    parent = p.rsplit("/", 1)[0].replace("/", ".")
                    mod = f"{parent}.{mod}" if parent else mod
                else:
                    mod = p[:-3] if p.endswith(".py") else p[:-4]
                if not mod or mod in seen:
                    continue
                seen.add(mod)
                try:
                    spec = zi.find_spec(mod)
                    if spec is None:
                        continue
                    src = zi.get_source(mod)
                    fname = zi.get_filename(mod)
                    results.append(ZipModuleInfo(
                        name=mod,
                        is_package=zi.is_package(mod),
                        has_source=src is not None,
                        filename=fname,
                    ))
                except (ImportError, AttributeError):
                    continue

    return sorted(results, key=lambda x: x.name)


def zip_has_module(zip_path: "str | Path", module_name: str) -> bool:
    """
    Return True if module_name is importable from zip_path.

    Example:
        if zip_has_module("bundle.zip", "myapp.config"):
            print("config module found")
    """
    zi = zipimport.zipimporter(str(zip_path))
    return zi.find_spec(module_name) is not None


def read_zip_data(zip_path: "str | Path", inner_path: str) -> bytes:
    """
    Read raw bytes from a file inside a zip using zipimporter.get_data.
    inner_path is relative to the zip root (e.g. "assets/config.json").

    Example:
        cfg = read_zip_data("bundle.zip", "config/settings.toml")
    """
    zi = zipimport.zipimporter(str(zip_path))
    # get_data expects the full archive-rooted path
    full_path = str(Path(zip_path) / inner_path)
    return zi.get_data(full_path)


def get_zip_source(zip_path: "str | Path", module_name: str) -> "str | None":
    """
    Return the source code of module_name inside zip_path, or None.

    Example:
        src = get_zip_source("bundle.zip", "myapp.utils")
        if src:
            print(src[:100])
    """
    zi = zipimport.zipimporter(str(zip_path))
    return zi.get_source(module_name)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Zip bundle builder
# ─────────────────────────────────────────────────────────────────────────────

def build_zip_bundle(
    source_root: "str | Path",
    output: "str | Path",
    include_pyc: bool = False,
    exclude_patterns: "list[str] | None" = None,
) -> Path:
    """
    Package all .py files under source_root into a zip suitable for sys.path.

    Example:
        build_zip_bundle("src/myapp", "dist/myapp.zip")
        sys.path.insert(0, "dist/myapp.zip")
        import myapp
    """
    import fnmatch
    root = Path(source_root)
    dest = Path(output)
    exclude = exclude_patterns or []

    with zipfile.ZipFile(dest, "w", compression=zipfile.ZIP_DEFLATED) as zf:
        for py_file in sorted(root.rglob("*.py")):
            rel = py_file.relative_to(root)
            skip = any(fnmatch.fnmatch(str(rel), pat) for pat in exclude)
            if skip:
                continue
            zf.write(py_file, arcname=str(rel))
        if include_pyc:
            for pyc_file in sorted(root.rglob("**/__pycache__/*.pyc")):
                rel = pyc_file.relative_to(root)
                zf.write(pyc_file, arcname=str(rel))

    return dest


# ─────────────────────────────────────────────────────────────────────────────
# 3. Dynamic zip importer context
# ─────────────────────────────────────────────────────────────────────────────

class ZipImportContext:
    """
    Context manager that temporarily adds a zip archive to sys.path,
    then removes it on exit.

    Example:
        with ZipImportContext("plugins/extra.zip"):
            import extra_plugin
            extra_plugin.run()
        # extra.zip removed from sys.path after the block
    """

    def __init__(self, zip_path: "str | Path") -> None:
        self._path = str(Path(zip_path).resolve())
        self._added: list[str] = []

    def __enter__(self) -> "ZipImportContext":
        if self._path not in sys.path:
            sys.path.insert(0, self._path)
            self._added.append(self._path)
        return self

    def __exit__(self, *_: object) -> None:
        for p in self._added:
            if p in sys.path:
                sys.path.remove(p)
        self._added.clear()


# ─────────────────────────────────────────────────────────────────────────────
# 4. Zip bundle validator
# ─────────────────────────────────────────────────────────────────────────────

@dataclass
class BundleValidation:
    """
    Validate that a zip bundle contains the expected modules and data files.

    Example:
        v = BundleValidation("dist/app.zip")
        v.require_module("myapp")
        v.require_module("myapp.config")
        v.require_data("assets/schema.json")
        v.run()
        if v.errors:
            for e in v.errors:
                print(f"  FAIL: {e}")
    """
    zip_path:       Path
    required_modules: list[str] = field(default_factory=list)
    required_data:    list[str] = field(default_factory=list)
    errors:           list[str] = field(default_factory=list)

    def __post_init__(self) -> None:
        self.zip_path = Path(self.zip_path)

    def require_module(self, name: str) -> "BundleValidation":
        self.required_modules.append(name)
        return self

    def require_data(self, inner_path: str) -> "BundleValidation":
        self.required_data.append(inner_path)
        return self

    def run(self) -> "BundleValidation":
        self.errors.clear()
        zi = zipimport.zipimporter(str(self.zip_path))

        for mod in self.required_modules:
            try:
                spec = zi.find_spec(mod)
                if spec is None:
                    self.errors.append(f"module not found: {mod}")
            except Exception as e:
                self.errors.append(f"module error ({mod}): {e}")

        with zipfile.ZipFile(self.zip_path) as zf:
            names = set(zf.namelist())
        for data_path in self.required_data:
            normalized = data_path.replace("\\", "/")
            if normalized not in names:
                self.errors.append(f"data file not found: {data_path}")

        return self

    @property
    def ok(self) -> bool:
        return len(self.errors) == 0


# ─────────────────────────────────────────────────────────────────────────────
# 5. zipimport path hook inspection
# ─────────────────────────────────────────────────────────────────────────────

def active_zip_importers() -> list[zipimport.zipimporter]:
    """
    Return all zipimporter instances currently active in sys.path_importer_cache.

    Example:
        for zi in active_zip_importers():
            print(f"  archive: {zi.archive}")
    """
    return [
        imp for imp in sys.path_importer_cache.values()
        if isinstance(imp, zipimport.zipimporter)
    ]


def zip_module_origin(module_name: str) -> "str | None":
    """
    If module_name was imported from a zip, return the zip archive path.
    Returns None if it was not loaded via zipimport.

    Example:
        with ZipImportContext("bundle.zip"):
            import mymodule
        print(zip_module_origin("mymodule"))  # "bundle.zip" or None
    """
    mod = sys.modules.get(module_name)
    if mod is None:
        return None
    loader = getattr(mod, "__loader__", None)
    if isinstance(loader, zipimport.zipimporter):
        return loader.archive
    return None


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    import os
    import tempfile

    print("=== zipimport demo ===")

    with tempfile.TemporaryDirectory() as td:
        td_path = Path(td)

        # ── build a small bundle ───────────────────────────────────────────────
        pkg = td_path / "greetpkg"
        pkg.mkdir()
        (pkg / "__init__.py").write_text(
            'def hello(name): return f"Hello, {name}!"\n'
        )
        (pkg / "utils.py").write_text(
            'def shout(s): return s.upper() + "!"\n'
        )
        (td_path / "standalone.py").write_text(
            'ANSWER = 42\n'
        )
        # Add a data file
        assets = td_path / "assets"
        assets.mkdir()
        (assets / "info.txt").write_text("bundle version 1.0")

        zip_path = td_path / "bundle.zip"
        with zipfile.ZipFile(zip_path, "w") as zf:
            zf.write(pkg / "__init__.py",   "greetpkg/__init__.py")
            zf.write(pkg / "utils.py",      "greetpkg/utils.py")
            zf.write(td_path / "standalone.py", "standalone.py")
            zf.write(assets / "info.txt",   "assets/info.txt")

        # ── list_zip_modules ──────────────────────────────────────────────────
        print("\n--- list_zip_modules ---")
        for info in list_zip_modules(zip_path):
            print(f"  {info}")

        # ── zip_has_module ────────────────────────────────────────────────────
        print("\n--- zip_has_module ---")
        for mod in ["greetpkg", "greetpkg.utils", "standalone", "missing"]:
            print(f"  {mod}: {zip_has_module(zip_path, mod)}")

        # ── get_zip_source ────────────────────────────────────────────────────
        print("\n--- get_zip_source ---")
        src = get_zip_source(zip_path, "standalone")
        print(f"  standalone.py source: {src!r}")

        # ── read_zip_data ─────────────────────────────────────────────────────
        print("\n--- read_zip_data ---")
        data = read_zip_data(zip_path, "assets/info.txt")
        print(f"  assets/info.txt: {data.decode()!r}")

        # ── ZipImportContext + import ──────────────────────────────────────────
        print("\n--- ZipImportContext ---")
        with ZipImportContext(zip_path):
            import standalone  # type: ignore[import]
            print(f"  standalone.ANSWER = {standalone.ANSWER}")
            origin = zip_module_origin("standalone")
            print(f"  loaded from zip: {origin!r}")
        # clean up sys.modules
        sys.modules.pop("standalone", None)

        # ── BundleValidation ──────────────────────────────────────────────────
        print("\n--- BundleValidation ---")
        v = (
            BundleValidation(zip_path)
            .require_module("greetpkg")
            .require_module("greetpkg.utils")
            .require_module("standalone")
            .require_module("nonexistent")
            .require_data("assets/info.txt")
            .require_data("readme.txt")
            .run()
        )
        print(f"  ok: {v.ok}  errors: {v.errors}")

        # ── active_zip_importers ──────────────────────────────────────────────
        print("\n--- active_zip_importers ---")
        with ZipImportContext(zip_path):
            importers = active_zip_importers()
            print(f"  found {len(importers)} active zip importer(s)")
            for zi in importers:
                print(f"    archive={zi.archive}  prefix={zi.prefix!r}")

    print("\n=== done ===")

For the sys.path / zipfile alternative — you can open a zip with zipfile.ZipFile and read file contents directly, bypassing the import system entirely — use zipfile when you need to access data files or non-Python content inside a zip; use zipimport when you want to import Python modules from the zip as first-class objects and need find_spec/get_source/get_code for tooling or plugin systems. For the importlib.util.spec_from_file_location alternative — spec_from_file_location(name, path) creates a module spec from an explicit filesystem path, supporting .so/.pyd extension modules that raw zip loading cannot handle — use importlib.util when you need to load extension modules or control the full loader lifecycle (e.g., injecting into sys.modules under a custom name); use zipimport for pure-Python single-file distribution bundles. The Claude Skills 360 bundle includes zipimport skill sets covering ZipModuleInfo + list_zip_modules() archive introspection, zip_has_module()/read_zip_data()/get_zip_source() data access helpers, build_zip_bundle() source packager, ZipImportContext temporary sys.path injector, BundleValidation with require_module()/require_data()/run() pre-deploy validator, and active_zip_importers()/zip_module_origin() runtime inspection. Start with the free tier to try zip import patterns and zipimport pipeline code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free