Source code for jgdv.structs.dkey._interface

  1#!/usr/bin/env python3
  2"""
  3
  4"""
  5# ruff: noqa: N801, ANN001, ANN002, ANN003
  6# Imports:
  7from __future__ import annotations
  8
  9# ##-- stdlib imports
 10import datetime
 11import enum
 12import functools as ftz
 13import itertools as itz
 14import logging as logmod
 15import pathlib as pl
 16import re
 17import time
 18import types
 19import collections
 20import contextlib
 21import hashlib
 22from copy import deepcopy
 23from uuid import UUID, uuid1
 24from weakref import ref
 25import atexit # for @atexit.register
 26import faulthandler
 27# ##-- end stdlib imports
 28
 29from jgdv import identity_fn
 30from jgdv.structs.strang import _interface as StrangAPI # noqa: N812
 31
 32# ##-- types
 33# isort: off
 34import abc
 35import collections.abc
 36from typing import TYPE_CHECKING, cast, assert_type, assert_never
 37from typing import Generic, NewType, Any
 38# Protocols:
 39from typing import Protocol, runtime_checkable
 40# Typing Decorators:
 41from typing import no_type_check, final, override, overload
 42from collections.abc import Mapping
 43
 44if TYPE_CHECKING:
 45    from typing import Final
 46    from typing import ClassVar, LiteralString
 47    from typing import Never, Self, Literal
 48    from typing import TypeGuard
 49    from collections.abc import Iterable, Iterator, Callable, Generator
 50    from collections.abc import Sequence, MutableMapping, Hashable
 51
 52    from jgdv import Maybe, Rx, Ident, RxStr, Ctor, CHECKTYPE, FmtStr
 53    from ._util._interface import Expander_p, ExpInst_d
 54
 55    type LitFalse    = Literal[False]
 56    type KeyMark     = DKeyMarkAbstract_e|LitFalse|str|type|tuple[KeyMark, ...]
 57##--|
 58
 59# isort: on
 60# ##-- end types
 61
 62##-- logging
 63logging = logmod.getLogger(__name__)
 64##-- end logging
 65
 66# Vars:
 67DEFAULT_COUNT            : Final[int]              = 0
 68LIFT_EXPANSION_PATTERN   : Final[str]              = "L"
 69FMT_PATTERN              : Final[Rx]               = re.compile(r"[wdi]+")
 70EXPANSION_LIMIT_PATTERN  : Final[Rx]               = re.compile(r"e(\d+)")
 71INDIRECT_SUFFIX          : Final[Ident]            = "_"
 72KEY_PATTERN              : Final[RxStr]            = "{(.+?)}"
 73OBRACE                   : Final[str]              = "{"
 74MAX_DEPTH                : Final[int]              = 10
 75MAX_KEY_EXPANSIONS       : Final[int]              = 200
 76PAUSE_COUNT              : Final[int]              = 0
 77RECURSION_GUARD          : Final[int]              = 10
 78PARAM_IGNORES            : Final[tuple[str, str]]  = ("_", "_ex")
 79
 80RAWKEY_ID                : Final[str]              = "_rawkeys"
 81FORCE_ID                 : Final[str]              = "force"
 82ARGS_K                   : Final[Ident]            = "args"
 83KWARGS_K                 : Final[Ident]            = "kwargs"
 84
 85DEFAULT_DKEY_KWARGS      : Final[list[str]]        = [
 86    "ctor", "check", "mark", "fallback",
 87    "max_exp", "fmt", "help", "implicit", "conv",
 88    "named",
 89    RAWKEY_ID, FORCE_ID,
 90    ]
 91
 92##--| Error Messages
 93UnknownDKeyCtorType      : Final[str]              = "Unknown type passed to construct dkey"
 94InsistentKeyFailure     : Final[str]  = "An insistent key was not built"
 95KeyBuildFailure         : Final[str]  = "No key was built"
 96NoMark                  : Final[str]  = "Mark has to be a value"
 97MarkConflictsWithMulti  : Final[str]  = "Mark is MULTI but multi=False"
 98MarkLacksACtor          : Final[str]  = "Couldn't find a ctor for mark"
 99MarkConversionConflict  : Final[str]  = "Kwd Mark/Conversion Conflict"
100UnexpectedKwargs        : Final[str]  = "Key got unexpected kwargs"
101RegistryLacksMark       : Final[str]  = "Can't register when the mark is None"
102RegistryConflict        : Final[str]  = "API.Key_p Registry conflict"
103ConvParamTooLong        : Final[str]  = "Conversion Parameters For Dkey's Can't Be More Than A Single Char"
104ConvParamConflict       : Final[str]  = "Conversion Param Conflict"
105# Enums:
106
[docs] 107class DKeyMarkAbstract_e(StrangAPI.StrangMarkAbstract_e): 108
[docs] 109 @classmethod 110 def default(cls) -> Maybe: ...
111
[docs] 112 @classmethod 113 def null(cls) -> Maybe: ...
114
[docs] 115 @classmethod 116 def multi(cls) -> Maybe: ...
117
[docs] 118class DKeyMark_e(DKeyMarkAbstract_e): 119 """ 120 Enums for how to use/build a dkey 121 122 """ 123 ARGS = enum.auto() # -> list 124 KWARGS = enum.auto() # -> dict 125 POSTBOX = enum.auto() # -> list 126
[docs] 127 @classmethod 128 def default(cls) -> Any: # noqa: ANN401 129 return Any
130
[docs] 131 @classmethod 132 def null(cls) -> Maybe: 133 return False
134
[docs] 135 @classmethod 136 def indirect(cls) -> type: 137 return Mapping
138
[docs] 139 @classmethod 140 def multi(cls) -> type: 141 return list
142 143##--| Data 144
[docs] 145class RawKey_d: 146 """ Utility class for parsed {}-format string parameters. 147 148 :: 149 150 see: https://peps.python.org/pep-3101/ 151 and: https://docs.python.org/3/library/string.html#format-string-syntax 152 153 Provides the data from string.Formatter.parse, but in a structure 154 instead of a tuple. 155 """ 156 __slots__ = ("convert", "format", "key", "prefix") 157 prefix : str 158 key : Maybe[str] 159 format : Maybe[str] 160 convert : Maybe[str] 161 162 def __init__(self, **kwargs) -> None: 163 self.prefix = kwargs.pop("prefix") 164 self.key = kwargs.pop("key", None) 165 self.format = kwargs.pop("format", None) 166 self.convert = kwargs.pop("convert", None) 167 assert(not bool(kwargs)), kwargs 168 169 def __getitem__(self, i) -> Maybe[str]: 170 match i: 171 case 0: 172 return self.prefix 173 case 1: 174 return self.key 175 case 2: 176 return self.format 177 case 3: 178 return self.convert 179 case _: 180 msg = "Tried to access a bad element of DKeyParams" 181 raise ValueError(msg, i) 182 183 def __bool__(self) -> bool: 184 return bool(self.key) 185 186 def __repr__(self) -> str: 187 return f"<RawkKey: {self.joined()}>" 188
[docs] 189 def joined(self) -> str: 190 """ Returns the key and params as one string 191 192 eg: blah, fmt=5, conv=p -> blah:5!p 193 """ 194 args : list[str] 195 if not bool(self.key): 196 return "" 197 198 assert(self.key is not None) 199 args = [self.key] 200 if bool(self.format): 201 assert(self.format is not None) 202 args += [":", self.format] 203 if bool(self.convert): 204 assert(self.convert is not None) 205 args += ["!", self.convert] 206 207 return "".join(args)
208
[docs] 209 def wrapped(self) -> str: 210 """ Returns this key in simple wrapped form 211 212 (it ignores format, conv params and prefix) 213 214 eg: blah -> {blah} 215 """ 216 return "{%s}" % self.key # noqa: UP031
217
[docs] 218 def anon(self) -> str: 219 """ Make a format str of this key, with anon variables. 220 221 eg: blah {key:f!p} -> blah {} 222 """ 223 if bool(self.key): 224 return "%s{}" % self.prefix # noqa: UP031 225 226 return self.prefix or ""
227
[docs] 228 def direct(self) -> str: 229 """ Returns this key in direct form 230 231 :: 232 233 eg: blah -> blah 234 blah_ -> blah 235 """ 236 return (self.key or "").removesuffix(INDIRECT_SUFFIX)
237
[docs] 238 def indirect(self) -> str: 239 """ Returns this key in indirect form 240 241 :: 242 243 eg: blah -> blah_ 244 blah_ -> blah_ 245 """ 246 match self.key: 247 case str() as k if k.endswith(INDIRECT_SUFFIX): 248 return k 249 case str() as k: 250 return f"{k}{INDIRECT_SUFFIX}" 251 case _: 252 return ""
253
[docs] 254 def is_indirect(self) -> bool: 255 match self.key: 256 case str() as k if k.endswith(INDIRECT_SUFFIX): 257 return True 258 case _: 259 return False
260
[docs] 261class DKey_d(StrangAPI.Strang_d): 262 """ Data of a DKey """ 263 __slots__ = ("convert", "expansion_type", "fallback", "format", "help", "max_expansions", "multi", "name", "raw", "typecheck") 264 name : Maybe[str] 265 raw : tuple[RawKey_d, ...] 266 expansion_type : Ctor 267 typecheck : CHECKTYPE 268 fallback : Maybe[Any] 269 format : Maybe[FmtStr] 270 convert : Maybe[FmtStr] 271 help : Maybe[str] 272 max_expansions : Maybe[int] 273 multi : bool 274 275 def __init__(self, **kwargs) -> None: 276 super().__init__() 277 self.name = kwargs.pop("name", None) 278 self.raw = tuple(kwargs.pop(RAWKEY_ID, ())) 279 self.expansion_type = kwargs.pop("ctor", identity_fn) 280 self.typecheck = kwargs.pop("check", Any) 281 self.fallback = kwargs.pop("fallback", None) 282 self.format = kwargs.pop("format", None) 283 self.convert = kwargs.pop("convert", None) 284 self.help = kwargs.pop("help", None) 285 self.max_expansions = kwargs.pop("max_exp", None) 286 self.multi = kwargs.pop("multi", False)
287 288##--| Section Specs 289DKEY_SECTIONS : Final[StrangAPI.Sections_d] = StrangAPI.Sections_d( 290 StrangAPI.Sec_d("body", None, None, str, None, True), # noqa: FBT003 291) 292##--| Protocols 293
[docs] 294@runtime_checkable 295class Key_p(StrangAPI.Strang_p, Protocol): 296 """ The protocol for a Key, something that used in a template system""" 297 _extra_kwargs : ClassVar[set[str]] 298 _processor : ClassVar 299 _expander : ClassVar[Expander_p] 300 data : DKey_d 301
[docs] 302 @staticmethod 303 def MarkOf[T:Key_p](cls:type[T]|T) -> KeyMark: ... # noqa: N802, PLW0211
304
[docs] 305 def redirect(self, spec=None) -> Key_p: ...
306
[docs] 307 def expand(self, *sources, rec=False, insist=False, chain:Maybe[list[Key_p]]=None, on_fail=Any, locs:Maybe[Mapping]=None, **kwargs) -> str: ...
308
[docs] 309 def var_name(self) -> str: ...
310
[docs] 311@runtime_checkable 312class MultiKey_p(Protocol): 313
[docs] 314 def keys(self) -> list[Key_p]: ...
315
[docs] 316 def _multi(self) -> Literal[True]: ...
317
[docs] 318@runtime_checkable 319class IndirectKey_p(Protocol): 320
[docs] 321 def _indirect(self) -> Literal[True]: ...
322##--| Combined Interfaces 323
[docs] 324@runtime_checkable 325class NonKey_p(Protocol): 326
[docs] 327 def _nonkey(self) -> Literal[True]: ...