Source code for jgdv.util.plugins.selector

  1#!/usr/bin/env python3
  2"""
  3A function to select an appropriate plugin by passed in name or names
  4
  5"""
  6# Imports:
  7from __future__ import annotations
  8
  9# ##-- stdlib imports
 10import datetime
 11import enum
 12import functools as ftz
 13import importlib
 14import itertools as itz
 15import logging as logmod
 16import pathlib as pl
 17import re
 18import time
 19import types
 20import weakref
 21from importlib.metadata import EntryPoint
 22from uuid import UUID, uuid1
 23
 24# ##-- end stdlib imports
 25
 26from jgdv.structs.strang import CodeReference
 27
 28# ##-- types
 29# isort: off
 30import abc
 31import collections.abc
 32from typing import TYPE_CHECKING, Generic, cast, assert_type, assert_never
 33# Protocols:
 34from typing import Protocol, runtime_checkable
 35# Typing Decorators:
 36from typing import no_type_check, final, override, overload
 37
 38if TYPE_CHECKING:
 39   from jgdv import Maybe
 40   from typing import Final
 41   from typing import ClassVar, Any, LiteralString
 42   from typing import Never, Self, Literal
 43   from typing import TypeGuard
 44   from collections.abc import Iterable, Iterator, Callable, Generator
 45   from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 46   from jgdv.structs.chainguard import ChainGuard
 47
 48# isort: on
 49# ##-- end types
 50
 51##-- logging
 52logging = logmod.getLogger(__name__)
 53##-- end logging
 54
[docs] 55def plugin_selector(plugins:ChainGuard, *, # noqa: PLR0911, PLR0912 56 target:str="default", 57 fallback:Maybe[type|str|CodeReference]=None) -> Maybe[type]: 58 """ Selects and loads a plugin from a chainguard, 59 based on a target, 60 with an available fallback constructor 61 62 if the target is a suitable coderef, it is coerced and loaded instead, 63 bypassing the plugins 64 """ 65 logging.debug("Selecting plugin for target: %s", target) 66 67 match target: 68 case "default": 69 pass 70 case str() as x: 71 try: 72 name = CodeReference(target) 73 return name() 74 except ImportError as _: 75 pass 76 except (AttributeError, KeyError) as _: 77 pass 78 79 match plugins: 80 case []: 81 pass 82 case [EntryPoint() as l]: # if theres only one, use that 83 return l.load() 84 case [EntryPoint() as l, *_] if target == "default": # If the preference is the default, use the first 85 return l.load() 86 case [*_] as loaders: # Otherwise, use the loader that matches the preferred's name 87 matching = [x for x in loaders if x.name == target] 88 if bool(matching): 89 return matching[0].load() 90 else: 91 msg = "No matching plugin for target" 92 raise ValueError(msg, target) 93 case type() as x: 94 return x 95 case _: 96 msg = "Unknown type passed to plugin selector" 97 raise TypeError(msg, plugins) 98 99 match fallback: 100 case str(): 101 coderef = CodeReference(fallback) 102 return coderef() 103 case CodeReference(): 104 return fallback() 105 case type(): 106 return fallback 107 case _: 108 msg = "No Available Plugin, and no fallback constructor" 109 raise ValueError(msg, target, fallback, plugins)