Skip to main content

Resource discovery and resolution library for Python

Project description

JustMyResource

A precise, lightweight, and extensible resource discovery library for Python. JustMyResource provides a robust "Resource Atlas" for the Python ecosystem—a definitive map of every resource available to an application, whether bundled in a Python package or provided by third-party resource packs.

Features

  • Generic Framework: Unified interface for multiple resource types (SVG icons, raster images, future: audio/video)
  • Extensible: Resource packs can be added via standard Python EntryPoints mechanism
  • Efficient: Lazy discovery with in-memory caching
  • Prefix-Based Resolution: Namespace disambiguation via pack:name format
  • Type-Safe: Returns ResourceContent objects with MIME types and metadata
  • Zero Dependencies: Core library has no required dependencies

Installation

pip install justmyresource

Available Resource Packs

JustMyResource supports icon packs from the justmyresource-icons monorepo. Install individual packs or all packs at once using optional dependency groups:

# Install a specific icon pack
pip install justmyresource[lucide]

# Install all icon packs
pip install justmyresource[icons]
Pack PyPI Package Prefixes Variants License
Lucide justmyresource-lucide lucide, luc ISC
Material Official justmyresource-material-icons material-icons, mi filled, outlined, rounded, sharp, two-tone Apache-2.0
Material Community justmyresource-mdi mdi Apache-2.0
Phosphor justmyresource-phosphor phosphor, ph thin, light, regular, bold, fill, duotone MIT
Font Awesome Free justmyresource-font-awesome font-awesome, fa solid, regular, brands CC-BY-4.0
Heroicons justmyresource-heroicons heroicons, hero 24/outline, 24/solid, 20/solid, 16/solid MIT

For detailed information about each pack, including icon counts, variant details, and usage examples, see the justmyresource-icons repository.

Quick Start

from justmyresource import ResourceRegistry, get_default_registry

# Get default registry
registry = get_default_registry()

# Get resource with fully qualified name (always unique)
content = registry.get_resource("acme-icons/lucide:lightbulb")

# Get resource with short pack name (works if unique)
content = registry.get_resource("lucide:lightbulb")

# Get resource with alias
content = registry.get_resource("luc:lightbulb")

# Check content type and use accordingly
if content.content_type == "image/svg+xml":
    svg_text = content.text  # Decode as UTF-8
    # Use SVG text...

# Get resource without prefix (requires default_prefix to be set)
registry = ResourceRegistry(default_prefix="lucide")
content = registry.get_resource("lightbulb")  # Resolves as "lucide:lightbulb"

Basic Usage

Getting Resources

from justmyresource import ResourceRegistry

registry = ResourceRegistry()

# Get resource with fully qualified name (always unique)
content = registry.get_resource("acme-icons/lucide:lightbulb")

# Get resource with short pack name (works if unique)
content = registry.get_resource("lucide:lightbulb")

# Get resource without prefix (requires default_prefix to be set)
registry = ResourceRegistry(default_prefix="lucide")
content = registry.get_resource("lightbulb")  # Resolves as "lucide:lightbulb"

# Access resource data
if content.content_type == "image/svg+xml":
    svg_text = content.text  # UTF-8 decoded string
elif content.content_type == "image/png":
    png_bytes = content.data  # Raw bytes

Listing Resources

# List all resources from all packs
for resource_info in registry.list_resources():
    print(f"{resource_info.pack}:{resource_info.name} ({resource_info.content_type})")
    # pack is qualified name (e.g., "acme-icons/lucide")

# List resources from a specific pack (qualified or short name)
for resource_info in registry.list_resources(pack="acme-icons/lucide"):
    print(resource_info.name)
for resource_info in registry.list_resources(pack="lucide"):
    print(resource_info.name)

# List registered packs (returns qualified names)
for qualified_name in registry.list_packs():
    print(qualified_name)  # e.g., "acme-icons/lucide"

Command-Line Interface

JustMyResource includes a CLI tool for discovering, inspecting, and extracting resources from the command line.

Installation

The CLI is automatically available after installing justmyresource:

pip install justmyresource
justmyresource --help

Commands

List Resources

List all available resources with optional filtering:

# List all resources from all packs
justmyresource list

# List resources from a specific pack
justmyresource list --pack lucide

# Filter by glob pattern
justmyresource list --filter "arrow-*"

# Show pack and content type information
justmyresource list --verbose

# JSON output
justmyresource list --json

Get Resource (Metadata-First)

Inspect resource metadata or extract to file/stdout:

# Show metadata only (default - safe for binary resources)
justmyresource get lucide:lightbulb

# Output to stdout (for piping)
justmyresource get lucide:lightbulb -o -

# Save to file
justmyresource get lucide:lightbulb -o icon.svg

# JSON output
justmyresource get lucide:lightbulb --json

The default behavior shows metadata (size, content-type, pack info) without outputting binary data to the terminal, preventing accidental terminal corruption.

List Resource Packs

List all registered resource packs:

# Simple list
justmyresource packs

# Detailed information (prefixes, aliases, collisions)
justmyresource packs --verbose

# JSON output
justmyresource packs --json

Resource Information

Show detailed information about a specific resource:

# Detailed resource information
justmyresource info lucide:lightbulb

# JSON output
justmyresource info lucide:lightbulb --json

Global Options

All commands support these global options:

  • --json: Output in JSON format
  • --blocklist <packs>: Comma-separated list of pack names to block
  • --prefix-map <mappings>: Override prefix mappings (format: "alias1=dist1/pack1,alias2=dist2/pack2")
  • --default-prefix <prefix>: Set default prefix for bare-name lookups

Examples

# Discover available icon packs
justmyresource packs

# Find all arrow icons
justmyresource list --filter "*arrow*"

# Inspect a specific icon
justmyresource get lucide:arrow-right

# Extract icon to file
justmyresource get lucide:arrow-right -o arrow.svg

# Use with default prefix
justmyresource --default-prefix lucide get lightbulb

# Pipe SVG to another tool
justmyresource get lucide:lightbulb -o - | grep "path"

Blocking Resource Packs

# Block specific packs (accepts short or qualified names)
registry = ResourceRegistry(blocklist={"broken-pack", "acme-icons/lucide"})

# Block via environment variable
# RESOURCE_DISCOVERY_BLOCKLIST="broken-pack,acme-icons/lucide" python app.py

Handling Prefix Collisions

When multiple packs claim the same prefix, the registry emits warnings and marks the prefix as ambiguous. No winner is picked—you must use qualified names or prefix_map to resolve:

import warnings

# Filter collision warnings if desired
warnings.filterwarnings("ignore", category=PrefixCollisionWarning)

registry = ResourceRegistry()

# Both packs remain accessible via qualified names
content1 = registry.get_resource("acme-icons/lucide:lightbulb")
content2 = registry.get_resource("cool-icons/lucide:lightbulb")

# Short name raises error (ambiguous):
# registry.get_resource("lucide:lightbulb")  # ValueError: ambiguous

# Inspect collisions
collisions = registry.get_prefix_collisions()
# {"lucide": ["acme-icons/lucide", "cool-icons/lucide"]}

Custom Prefix Mapping

Override prefix mappings to resolve collisions or add custom aliases:

# Via constructor
registry = ResourceRegistry(
    prefix_map={
        "icons": "acme-icons/lucide",  # Map "icons" to specific pack
        "mi": "material-icons/core",   # Custom alias
    }
)

# Via environment variable
# RESOURCE_PREFIX_MAP="icons=acme-icons/lucide,mi=material-icons/core" python app.py

# Inspect current mappings
prefix_map = registry.get_prefix_map()
# {"icons": "acme-icons/lucide", "mi": "material-icons/core", ...}

Creating Resource Packs

Resource packs can be registered via Python EntryPoints. This allows applications to bundle resources or third-party packages to provide resources.

First-Party Resource Pack (Application's Own Resources)

# myapp/resources.py
from collections.abc import Iterator
from pathlib import Path
from importlib.resources import files
from justmyresource.types import ResourceContent, ResourcePack

class MyAppResourcePack:
    """Resource pack for application's bundled resources."""
    
    def __init__(self):
        package = files("myapp.resources")
        self._base_path = Path(str(package))
    
    def get_resource(self, name: str) -> ResourceContent:
        """Get resource from bundled files."""
        # Try SVG first
        svg_path = self._base_path / f"{name}.svg"
        if svg_path.exists():
            with open(svg_path, "rb") as f:
                return ResourceContent(
                    data=f.read(),
                    content_type="image/svg+xml",
                    encoding="utf-8",
                )
        
        # Try PNG
        png_path = self._base_path / f"{name}.png"
        if png_path.exists():
            with open(png_path, "rb") as f:
                return ResourceContent(
                    data=f.read(),
                    content_type="image/png",
                )
        
        raise ValueError(f"Resource not found: {name}")
    
    def list_resources(self) -> Iterator[str]:
        """List all resources."""
        for path in self._base_path.iterdir():
            if path.suffix in (".svg", ".png"):
                yield path.stem
    
    def get_prefixes(self) -> list[str]:
        return ["myapp"]  # Optional aliases (pack name is auto-registered)

# myapp/__init__.py
def get_resource_provider():
    """Entry point factory for application's bundled resources."""
    from myapp.resources import MyAppResourcePack
    return MyAppResourcePack()
# pyproject.toml
[project.entry-points."justmyresource.packs"]
"myapp-resources" = "myapp:get_resource_provider"

Third-Party Resource Pack

# my_resource_pack/__init__.py
from my_resource_pack.provider import MyResourcePack

def get_resource_provider():
    """Entry point factory for resource pack."""
    return MyResourcePack()

Using ZippedResourcePack Helper

For packs that bundle resources in a zip file, use the provided helper class:

# my_icon_pack/__init__.py
from justmyresource.pack_utils import ZippedResourcePack

class MyIconPack(ZippedResourcePack):
    def __init__(self):
        super().__init__(
            package_name="my_icon_pack",
            archive_name="icons.zip",
            default_content_type="image/svg+xml",
            prefixes=["myicons"]
        )

def get_resource_provider():
    return MyIconPack()

This provides zip reading, caching, manifest support, and error handling out of the box.

Architecture

JustMyResource follows a unified "Resource Pack" architecture where all resource sources implement the same ResourcePack protocol. This ensures:

  • Consistency: All resources are discovered and resolved the same way
  • Extensibility: New resource sources can be added via EntryPoints
  • Flat Pack Model: All packs are equal; uniquely identified by their FQN (dist/pack)

See docs/architecture.md for detailed architecture documentation.

ResourceContent Type

Resources are returned as ResourceContent objects:

@dataclass(frozen=True, slots=True)
class ResourceContent:
    data: bytes                    # Raw resource bytes
    content_type: str              # MIME type: "image/svg+xml", "image/png", etc.
    encoding: str | None = None    # Encoding for text resources (e.g., "utf-8")
    metadata: dict[str, Any] | None = None  # Optional pack-specific metadata
    
    @property
    def text(self) -> str:
        """Decode data as text (raises if encoding is None)."""
        ...

This wrapper allows consumers to:

  • Branch on content_type to handle different resource types
  • Access text content via .text property for text-based resources
  • Access pack-specific metadata via .metadata dict
  • Handle mixed-format packs (e.g., a samples pack with both SVG and PNG)

Development

Setup

# Clone the repository
git clone https://github.com/kws/justmyresource.git
cd justmyresource

# Install with development dependencies
pip install -e ".[dev]"

Running Tests

# Run tests with coverage
pytest

# Run with coverage report
pytest --cov=justmyresource --cov-report=html

Code Quality

# Format code
ruff format .

# Lint code
ruff check .

# Type checking
mypy src/

Requirements

  • Python 3.10+
  • No required dependencies (core library)
  • Resource packs may have their own dependencies

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please read the architecture documentation in docs/architecture.md and follow the project philosophy outlined in AGENTS.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

justmyresource-1.1.0.tar.gz (50.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

justmyresource-1.1.0-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

File details

Details for the file justmyresource-1.1.0.tar.gz.

File metadata

  • Download URL: justmyresource-1.1.0.tar.gz
  • Upload date:
  • Size: 50.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for justmyresource-1.1.0.tar.gz
Algorithm Hash digest
SHA256 8d1cb142ea0291934f86935012024b36b28484f78c5e4bb38f9dc3e981b31844
MD5 001581e544bdfcdd60192e3975c1b401
BLAKE2b-256 574c181c92cb110e6ba712a815bad8b0652323a09a394b664255d623427c2962

See more details on using hashes here.

File details

Details for the file justmyresource-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: justmyresource-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for justmyresource-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 607da4a588ba7d6bbe03d9c9546f99f9c2253ec77b005c158500fe2d62d1ff1e
MD5 185403a571f9a2c45321e1d559a1bd02
BLAKE2b-256 95a98ebf6785ac0f899ecd11ee378f0a12796edd36d31a7739741f2e35de4e50

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page