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:nameformat - Type-Safe: Returns
ResourceContentobjects 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_typeto handle different resource types - Access text content via
.textproperty for text-based resources - Access pack-specific metadata via
.metadatadict - 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file justmyresource-1.1.1.tar.gz.
File metadata
- Download URL: justmyresource-1.1.1.tar.gz
- Upload date:
- Size: 50.6 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0ad236ffbe8b440241da3e6ef2f1e475f6d86dc7a56d7d21a60dff5a697ba41
|
|
| MD5 |
9c5b60d032e1c1ba7465057d6887c837
|
|
| BLAKE2b-256 |
e884672b419ee79f0e06ac14c6fa0fa99361d206f878cd7250d8b25e6ead2393
|
File details
Details for the file justmyresource-1.1.1-py3-none-any.whl.
File metadata
- Download URL: justmyresource-1.1.1-py3-none-any.whl
- Upload date:
- Size: 22.0 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bba482ef103f4ff5e4227defffc20bfd2190c6fe247172896e73aaff4d181bb9
|
|
| MD5 |
4529428cdc465d477f064bddb0eff7ba
|
|
| BLAKE2b-256 |
dc2cc38d9f7075aea9564792ac70a8c02a78d8af24dbc2ca59768b68dd2cbfa9
|