"""Pluggable product fetch / stock-sync scripts for the admin CMS. Each fetcher module must expose: NAME: str DESCRIPTION: str fetch_products(config: dict) -> list[dict] sync_stocks(config: dict, products: list[dict]) -> list[dict] Product dict keys (camelCase or snake_case accepted by importer): id, slug, name, nameFa/name_fa, price, compareAtPrice/compare_at_price, stock, heroImage/hero_image, category, categoryFa/category_fa, description, sku, externalId/external_id, colorVariants/color_variants, source Each colorVariants item may include: id, slug, name, shade, colorHex, price, compareAtPrice, stock, heroImage, sku, externalId, images[] """ from __future__ import annotations import importlib import pkgutil from pathlib import Path _PACKAGE = Path(__file__).resolve().parent def list_fetchers(): items = [] for mod in pkgutil.iter_modules([str(_PACKAGE)]): if mod.name.startswith("_"): continue module = importlib.import_module(f"fetchers.{mod.name}") if not hasattr(module, "NAME"): continue items.append( { "id": mod.name, "name": getattr(module, "NAME"), "description": getattr(module, "DESCRIPTION", ""), "supports_fetch": callable(getattr(module, "fetch_products", None)), "supports_sync": callable(getattr(module, "sync_stocks", None)), } ) return sorted(items, key=lambda x: x["id"]) def get_fetcher(fetcher_id: str): if not fetcher_id or "/" in fetcher_id or ".." in fetcher_id: raise ValueError("invalid fetcher id") return importlib.import_module(f"fetchers.{fetcher_id}")