This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Added the Jugulis management dictionary

+1538 -54
+1 -4
.gitignore
··· 16 16 .hypothesis 17 17 18 18 # Pytest 19 - .pytest_cache 20 - 21 - # Random Tests 22 - /test*.py 19 + .pytest_cache
+1303
packages/bundle/bundle/__init__.py
··· 1 + import operator 2 + import os 3 + import rich_click as click 4 + 5 + from addict import Dict 6 + from autoslot import SlotsMeta 7 + from beartype import beartype 8 + from beartype.door import is_bearable, die_if_unbearable 9 + from collections import defaultdict 10 + from collections.abc import Callable 11 + from contextlib import suppress 12 + from copy import deepcopy 13 + from docstring_parser import parse 14 + from functools import partial, reduce, wraps 15 + from inspect import signature, Parameter, isclass 16 + from itertools import chain 17 + from more_itertools import partition 18 + from plum import dispatch, Dispatcher 19 + from rich.pretty import pprint 20 + from rich_click.rich_help_formatter import RichHelpFormatter 21 + from string import ascii_lowercase as lower, ascii_uppercase as upper 22 + from types import EllipsisType, MappingProxyType 23 + from typing import ( 24 + Any, 25 + get_args, 26 + get_origin, 27 + Literal, 28 + Optional, 29 + Union, 30 + ) 31 + 32 + from .pipe import pipe, PipeArgs 33 + from .python import ( 34 + can_unpack_variable, 35 + deepfreeze, 36 + dirs, 37 + flatten, 38 + normalize, 39 + normalizeMultiline, 40 + pk_variables, 41 + plumeta, 42 + process, 43 + ) 44 + from .typing import ( 45 + ArgReturn, 46 + Attr, 47 + BString, 48 + Callback, 49 + Coll, 50 + Count, 51 + Eager, 52 + Obj, 53 + Origin, 54 + Pass, 55 + Password, 56 + Split, 57 + subtype, 58 + Switch, 59 + ValueReturn, 60 + ) 61 + from .model import BaseModel 62 + 63 + 64 + class Parent(metaclass=plumeta): 65 + @property 66 + def kwargs(self): 67 + kwargs = self.parent.kwargs | self.cls.kwargs 68 + kwargs.pop("name", None) 69 + return kwargs 70 + 71 + def __init__(self): 72 + self.cls = Dict() 73 + self.func = click 74 + 75 + def __init__(self, cls: "bundle"): 76 + self.cls = cls 77 + 78 + def __init__(self, cls: "Parent"): 79 + self.cls = cls.cls 80 + 81 + def __getattr__(self, attr): 82 + return getattr(self.cls, attr) 83 + 84 + def __bool__(self): 85 + return bool(self.func) 86 + 87 + 88 + @dispatch(precedence=1) 89 + def process_bundle( 90 + annotation: Any, 91 + value: None, 92 + ) -> None: ... 93 + 94 + 95 + @dispatch 96 + def process_bundle( 97 + annotation: Origin[Callback, Eager], 98 + value: Any, 99 + ): 100 + return process_bundle(get_args(annotation)[0], value) 101 + 102 + 103 + @dispatch 104 + def process_bundle( 105 + annotation: Origin[Literal], 106 + value: ValueReturn, 107 + ) -> ArgReturn | ValueReturn: 108 + for arg in get_args(annotation): 109 + if value in (arg, str(arg)): 110 + return arg 111 + die_if_unbearable(value, annotation) 112 + 113 + 114 + @dispatch 115 + def process_bundle( 116 + annotation: Origin[Split], 117 + value: BString, 118 + ) -> Coll: 119 + annotation, delimiter = get_args(annotation) 120 + return process_bundle(annotation, value.split(delimiter)) 121 + 122 + 123 + @dispatch 124 + def process_bundle( 125 + annotation: Origin[Split], 126 + value: Coll, 127 + ) -> Coll: 128 + annotation, delimiter = get_args(annotation) 129 + return process_bundle(annotation, flatten(item.split(delimiter) for item in value)) 130 + 131 + 132 + for method in process._pending + process._resolved: 133 + process_bundle.register(method[0], precedence=method[2]) 134 + 135 + 136 + class Model(BaseModel): 137 + def __init__(self, *args, **kwargs): 138 + self.__processor__ = process_bundle 139 + super().__init__(*args, **kwargs) 140 + 141 + 142 + names = ("__name__", "__qualname__") 143 + 144 + 145 + def deflambda(name, f=None): 146 + f = f or (lambda: None) 147 + for attr in names: 148 + setattr(f, attr, name) 149 + return f 150 + 151 + 152 + class Bundle(SlotsMeta, metaclass=plumeta): 153 + def __new__(cls, name, bases, ns, subclass=False): 154 + if subclass: 155 + cls._dunders = tuple() 156 + ns["__slots__"] = chain( 157 + tuple() if bases else ("__weakref__",), 158 + cls._dunders, 159 + names, 160 + ( 161 + "docstring", 162 + "superparams", 163 + "signature", 164 + "_func", 165 + "print", 166 + ), 167 + ) 168 + cls._specopts = {} 169 + cls._specdefs = Dict(disabled=False) 170 + cls._pathvars = ["PATH", "PATHS"] 171 + cls._defaults = Dict( 172 + option={"metavar": str.__name__, "show_default": True}, 173 + group={ 174 + "invoke_without_command": True, 175 + "no_args_is_help": True, 176 + }, 177 + ) 178 + cls._defaults.group.context_settings.help_option_names = ("-h", "--help") 179 + cls._globals = Dict() 180 + cls._class_roots = Dict() 181 + cls._class_instances = Dict() 182 + cls._roots = Dict() 183 + cls._instances = Dict() 184 + return super().__new__(cls, name, bases, ns) 185 + return subtype(name, bundler, ns) 186 + 187 + class __prepare__(dict): 188 + def __init__(self, name, bases, subclass=False): 189 + self.subclass = subclass 190 + if self.subclass: 191 + self.dispatcher = Dispatcher() 192 + super().__setitem__("_dispatcher", self.dispatcher) 193 + 194 + def __setitem__(self, key, value): 195 + if callable(value) and self.subclass: 196 + args, kwargs = getattr(value, "__plume__", (tuple(), {})) 197 + if not kwargs.get("disabled", False): 198 + value = getattr(self.get(key), "dispatch", self.dispatcher)( 199 + value, *args, **kwargs 200 + ) 201 + super().__setitem__(key, value) 202 + 203 + def drive(cls, func: Callable, *args, **kwargs): 204 + return cls(func).quark(*args, **kwargs) 205 + 206 + def drive(cls, **kwargs): 207 + return cls().quark(**kwargs) 208 + 209 + def up(cls, name: str, *args, **kwargs): 210 + def wrapper(self): 211 + cls._specopts[self][name] = Dict( 212 + {k: kwargs.pop(k, v) for k, v in cls._specdefs.items()} 213 + ) 214 + setattr( 215 + self, 216 + "__bundle__", 217 + getattr(self, "__bundle__", Dict()) 218 + | Dict(params={name: {"args": args, "kwargs": kwargs}}), 219 + ) 220 + return self 221 + 222 + return wrapper 223 + 224 + def up( 225 + cls, 226 + self: Union["Bundler", "bundle"], 227 + parent: Optional[Union["Bundler", "bundle"]] = None, 228 + **kwargs, 229 + ) -> Union["bundle", type["bundler"]]: 230 + self._rewrap(kwargs) 231 + if parent: 232 + parent._adopt(self) 233 + return self 234 + 235 + def up( 236 + cls, 237 + self: Callable, 238 + parent: None = None, 239 + **kwargs, 240 + ): 241 + name = kwargs.get("name", self.__name__) 242 + setattr( 243 + self, 244 + "__bundle__", 245 + getattr(self, "__bundle__", Dict()) | Dict(group=Dict(kwargs)), 246 + ) 247 + for attr in names: 248 + setattr(self, attr, name) 249 + return self 250 + 251 + def up( 252 + cls, 253 + parent: Optional[Union["Bundler", "bundle"]] = None, 254 + **kwargs, 255 + ): 256 + def wrapper(self: Callable): 257 + return cls.up(self, parent, **kwargs) 258 + 259 + return wrapper 260 + 261 + def __getattr__(cls, attr): 262 + return getattr(click, attr) 263 + 264 + 265 + class bundle(metaclass=Bundle, subclass=True): 266 + # TODO: There may be a few problems with the adoption... 267 + def __new__( 268 + cls, 269 + name: str, 270 + bases: tuple["bundle", ...], 271 + ns: dict | MappingProxyType, 272 + **kwargs, 273 + ): 274 + self = subtype(name, bundler, ns) 275 + self._rewrap(bases[0].kwargs) 276 + try: 277 + bases[0]._adopt(self) 278 + except AttributeError: 279 + with suppress(AttributeError, TypeError): 280 + bases[0].parent._adopt(self) 281 + return self 282 + 283 + def __new__( 284 + cls, 285 + self: "Bundler", 286 + parent: Optional[Union["Bundler", "bundle"]] = None, 287 + **kwargs, 288 + ): 289 + self._rewrap(kwargs) 290 + if parent: 291 + parent._adopt(self) 292 + return self 293 + 294 + def __new__( 295 + cls, 296 + self: type, 297 + parent: Optional[Union["Bundler", "bundle"]] = None, 298 + **kwargs, 299 + ): 300 + return cls.__new__(cls, subtype(self, bundler), parent, **kwargs) 301 + 302 + def __new__(cls, *args, **kwargs): 303 + return super().__new__(cls) 304 + 305 + class __prepare__(dict): 306 + def __init__(self, name, bases): 307 + pass 308 + 309 + def __init__( 310 + self, 311 + func: Callable, 312 + parent: Optional[Union[type["bundle"], "bundle", Parent]] = None, 313 + /, 314 + **kwargs, 315 + ): 316 + if isinstance(parent, (self.__class__, Parent)): 317 + self.parent = Parent(parent) 318 + self.root = self.parent.root 319 + else: 320 + self.parent = Parent() 321 + self.root = self 322 + 323 + self.children = Dict() 324 + self.kwargs = Dict(kwargs) 325 + self.func = None 326 + self._wrap(func) 327 + 328 + def __init__( 329 + self, 330 + parent: Union["bundle", Parent], 331 + kwargs: dict, 332 + ): 333 + self.parent = Parent(parent) 334 + self.root = self.parent.root 335 + self.children = Dict() 336 + self.kwargs = Dict(kwargs) 337 + self.func = None 338 + 339 + def __init__(self, **kwargs): 340 + self.parent = Parent() 341 + self.root = self 342 + self.children = Dict() 343 + self.kwargs = Dict(kwargs) 344 + self.func = None 345 + 346 + def __getattr__(self, attr): 347 + try: 348 + object.__getattribute__(self, "func") 349 + except AttributeError as e: 350 + raise AttributeError( 351 + f"'{self.__class__.__name__}' object has no attribute '{attr}'" 352 + ) from e 353 + else: 354 + try: 355 + return getattr(self.func, attr) 356 + except AttributeError as e: 357 + raise AttributeError( 358 + f"'{self.__class__.__name__}' and '{self.func.__class__.__name__}' objects have no attribute '{attr}'" 359 + ) from e 360 + 361 + def _run(self, arg): 362 + return is_bearable( 363 + arg, next(iter(self._func.__signature__.parameters.values())).annotation 364 + ) 365 + 366 + # TODO: There may be a few problems with the adoption... 367 + def __call__(self, cls: "Bundler"): 368 + if self._run(cls): 369 + return beartype(self._func)(cls) 370 + cls._rewrap(self.kwargs) 371 + try: 372 + self._adopt(cls) 373 + except AttributeError: 374 + with suppress(AttributeError, TypeError): 375 + self.parent._adopt(cls) 376 + return cls 377 + 378 + def __call__(self, cls: type): 379 + if self._run(cls): 380 + return beartype(self._func)(cls) 381 + return self(subtype(cls, bundler)) 382 + 383 + def __call__(self, func: Callable): 384 + if self.func is None: 385 + return self._wrap(func) 386 + if self._run(func): 387 + return beartype(self._func)(func) 388 + return self.__class__(func, self) 389 + 390 + def _run(self, args, kwargs): 391 + if args or not (args or kwargs): 392 + return True 393 + run_score = 0 394 + run_params = self._func.__signature__.parameters 395 + click_score = 0 396 + command_params = signature(click.Command).parameters 397 + group_params = signature(click.Group).parameters 398 + for k, v in kwargs.items(): 399 + if k in run_params: 400 + annotation = run_params[k].annotation 401 + if annotation is Parameter.empty or is_bearable(v, annotation): 402 + run_score += 1 403 + if k in command_params: 404 + command_annotation = command_params[k].annotation 405 + if command_annotation is Parameter.empty or is_bearable( 406 + v, command_annotation 407 + ): 408 + click_score += 1 409 + elif k in group_params: 410 + group_annotation = group_params[k].annotation 411 + if group_annotation is Parameter.empty or is_bearable( 412 + v, group_annotation 413 + ): 414 + click_score += 1 415 + return run_score >= click_score 416 + 417 + def __call__(self, *args, **kwargs): 418 + if self._run(args, kwargs): 419 + return beartype(self._func)(*args, **kwargs) 420 + return self.__class__(self, kwargs) 421 + 422 + def __del__(self): 423 + with suppress(AttributeError): 424 + for child in self.children.values(): 425 + del child 426 + 427 + def flip(self, **kwargs): 428 + return self.func(**kwargs) 429 + 430 + def _driver(self, parent: "bundle", func: Callable, name: str = ""): 431 + return self.__class__(func, parent, name=name) 432 + 433 + def _driver(self, parent: "bundle", args: Coll): 434 + for f in args: 435 + self._driver(parent, f) 436 + 437 + def _driver(self, parent: "bundle", kwargs: dict): 438 + for k, v in kwargs.items(): 439 + if isinstance(v, dict): 440 + self._driver(self._driver(parent, v.pop(k, deflambda(k)), k), v) 441 + elif isinstance(v, Coll): 442 + different_names, same_names = partition( 443 + lambda func: func.__name__ == k, v 444 + ) 445 + self._driver( 446 + self._driver(parent, next(same_names, deflambda(k)), k), 447 + different_names, 448 + ) 449 + else: 450 + self._driver(parent, v, k) 451 + 452 + def quark(self, *args, **kwargs): 453 + if self.func is None: 454 + self._wrap(args[0]) 455 + self._driver(self, args[1:]) 456 + else: 457 + self._driver(self, args) 458 + self._driver(self, kwargs) 459 + return self 460 + 461 + def quark(self, **kwargs): 462 + different_names = tuple() 463 + if self.func is None: 464 + if kwargs: 465 + name = self.kwargs.name = next(iter(kwargs.keys())) 466 + 467 + # TODO: If the first kwarg is not a callable, create a new root. 468 + # if isinstance(kwargs[name], dict): 469 + # func = kwargs[name].pop(name, deflambda(name)) 470 + # elif isinstance(kwargs[name], Coll): 471 + # different_names, same_names = partition( 472 + # lambda func: func.__name__ == name, kwargs[name] 473 + # ) 474 + # func = next(same_names, deflambda(name)) 475 + # else: 476 + # func = kwargs.pop(name, deflambda(name)) 477 + if callable(kwargs[name]): 478 + func = kwargs.pop(name, deflambda(name)) 479 + else: 480 + # NOTE: The name is for the dictionary or collection, not this. 481 + func = lambda: None 482 + 483 + else: 484 + func = lambda: None 485 + self._wrap(func) 486 + self._driver(self, different_names) 487 + self._driver(self, kwargs) 488 + return self 489 + 490 + def _adopt( 491 + self, 492 + cls: Union["bundle", "bundler", type["bundler"]], 493 + name: Optional[str] = None, 494 + ): 495 + name = name or cls.func.name 496 + cls = cls.__bundles__[name] if isinstance(cls, bundler) else cls 497 + self.func.commands[name] = cls.func 498 + self.children[name] = cls 499 + cls.parent = self 500 + 501 + def _filter_params( 502 + self, params: list[click.Parameter], param_type: type[click.Parameter] 503 + ): 504 + return list(filter(lambda param: isinstance(param, param_type), params)) 505 + 506 + @property 507 + def _arguments(self): 508 + return self._filter_params(self.params, click.Argument) 509 + 510 + @property 511 + def _options(self): 512 + return self._filter_params(self.params, click.Option) 513 + 514 + def _deepfreeze(self, x, *ignores): 515 + return deepfreeze( 516 + x, 517 + {t: lambda y: None for t in (Callable, RichHelpFormatter)}, 518 + ignores=ignores, 519 + ) 520 + 521 + def _equal( 522 + self, 523 + obj: click.Command | click.Parameter, 524 + other: click.Command | click.Parameter, 525 + *ignores, 526 + obj_name="", 527 + other_name="", 528 + ): 529 + ignores = (*ignores, "__dict__") 530 + dct = defaultdict(list) 531 + for k in dir(obj): 532 + if k not in ignores: 533 + dct[k].append( 534 + (obj_name or obj.name, self._deepfreeze(getattr(obj, k), *ignores)) 535 + ) 536 + dct[k].append( 537 + ( 538 + other_name or other.name, 539 + self._deepfreeze(getattr(other, k), *ignores), 540 + ) 541 + ) 542 + errors = {k: v for k, v in dct.items() if len({t[1] for t in v}) != 1} 543 + if errors: 544 + if "PYTEST_CURRENT_TEST" in os.environ: 545 + pprint(errors) 546 + return False 547 + return True 548 + 549 + def _equals(self, other: Union["Bundler", "bundle", click.Group], *ignores) -> bool: 550 + options = self._filter_params(other.params, click.Option) 551 + arguments = self._filter_params(other.params, click.Argument) 552 + return ( 553 + all( 554 + self._equal( 555 + param, 556 + arguments[index], 557 + obj_name=f"{self.name}:{param.name}", 558 + other_name=f"{other.name}:{param.name}", 559 + ) 560 + for index, param in enumerate(self._arguments) 561 + ) 562 + and all( 563 + self._equal( 564 + param, 565 + options[index], 566 + obj_name=f"{self.name}:{param.name}", 567 + other_name=f"{other.name}:{param.name}", 568 + ) 569 + for index, param in enumerate(self._options) 570 + ) 571 + and self._equal( 572 + self.func, 573 + other if isinstance(other, click.Group) else other.func, 574 + *ignores, 575 + "params", 576 + obj_name=f"self: {self.name}", 577 + other_name=f"other: {other.name}", 578 + ) 579 + and all( 580 + v._equals(other.commands[k], *ignores) if k in other.commands else False 581 + for k, v in self.children.items() 582 + ) 583 + ) 584 + 585 + def equals(self, other: Union["Bundler", "bundle", click.Group], *ignores) -> bool: 586 + return self._equals(other, *ignores, "name") 587 + 588 + def __eq__(self, other: Union["Bundler", "bundle", click.Group]) -> bool: 589 + return ( 590 + self.equals(other) 591 + and self._func == other.callback 592 + and all( 593 + (v._func == other.commands[k].callback) 594 + if k in other.commands 595 + else False 596 + for k, v in self.children.items() 597 + ) 598 + ) 599 + 600 + def _hash(self, *ignores): 601 + ignores = (*ignores, "params") 602 + return ( 603 + *[self._deepfreeze(dirs(arg)) for arg in self._arguments], 604 + *[self._deepfreeze(dirs(opt)) for opt in self._options], 605 + *[self._deepfreeze(dirs(self.func, ignores=ignores), *ignores)], 606 + *[child._hash(*ignores) for child in self.children.values()], 607 + ) 608 + 609 + def hash(self, *ignores): 610 + return hash(self._hash(*ignores, "name")) 611 + 612 + def __hash__(self): 613 + return hash( 614 + ( 615 + ( 616 + self.hash(), 617 + self._func, 618 + *(child._func for child in self.children.values()), 619 + ) 620 + ) 621 + ) 622 + 623 + def __eq__(self, other) -> bool: 624 + return False 625 + 626 + def _recursive_difference(self, before, after): 627 + difference = {} 628 + for k, v in after.items(): 629 + if k not in before: 630 + difference[k] = v 631 + elif before[k] != v: 632 + if isinstance(before[k], dict) and isinstance(v, dict): 633 + difference[k] = self._recursive_difference(before[k], v) 634 + elif not callable(before[k]) and not callable(v): 635 + difference[k] = v 636 + return difference 637 + 638 + # def _rewrap(self, **kwargs) -> "bundle": 639 + # subcommands = dict(self.func.commands) 640 + # self.kwargs |= kwargs 641 + # self._wrap() 642 + # kwargs.pop("name", None) 643 + # for child in self.children.values(): 644 + # child._rewrap(**kwargs) 645 + # self.func.commands = subcommands 646 + # return self 647 + 648 + def _rewrap(self, *args, **kwargs) -> "bundle": 649 + kwargs = reduce(operator.or_, [Dict(d) for d in (*args, kwargs)]) 650 + self.kwargs |= kwargs 651 + for k, v in self._make_group_kwargs().items(): 652 + setattr(self.func, k, v) 653 + kwargs.pop("name", None) 654 + for child in self.children.values(): 655 + child._rewrap(kwargs) 656 + return self 657 + 658 + def _make_group_kwargs(self, base_kwargs: Optional[dict] = None, **kwargs): 659 + return reduce( 660 + operator.or_, 661 + ( 662 + self.__class__._defaults.group, 663 + base_kwargs or {}, 664 + self.__class__._globals.group, 665 + self.__class__._roots[self.root._func].group, 666 + self.__class__._instances[self._func].group, 667 + self.parent.kwargs, 668 + getattr(self._func, "__bundle__", Dict()).group, 669 + self.kwargs, 670 + kwargs, 671 + ), 672 + ) 673 + 674 + # Adapted From: 675 + # Answer 1: https://stackoverflow.com/a/12627202 676 + # User 1: https://stackoverflow.com/users/748858/mgilson 677 + # Answer 2: https://stackoverflow.com/a/50177498 678 + # User 2: https://stackoverflow.com/users/2867928/mazdak 679 + def _wrap(self, func: Optional[Callable] = None, **kwargs) -> "bundle": 680 + if func: 681 + self.docstring = parse(func.__doc__) if func.__doc__ else Dict() 682 + self.signature = func.__signature__ = signature(func) 683 + self._func = func 684 + for attr in self.__class__._dunders: 685 + setattr(self, attr, getattr(self._func, attr)) 686 + self.superparams = Dict( 687 + disabled=[ 688 + k for k, v in self.__class__._specopts[func].items() if v.disabled 689 + ], 690 + pre={ 691 + param.name: param 692 + for param in getattr(func, "__click_params__", tuple()) 693 + }, 694 + # TODO: Test this. 695 + help={param.arg_name: param for param in self.docstring.params}, 696 + ) 697 + else: 698 + func = self._func 699 + 700 + ignore_and_accept = False 701 + for k, v in self.signature.parameters.items(): 702 + if v.kind is Parameter.VAR_KEYWORD: 703 + self.superparams.disabled.append(k) 704 + ignore_and_accept = True 705 + if k not in self.superparams.disabled: 706 + self.superparams.working[k] = v 707 + if k in self.superparams.pre: 708 + if v.annotation is not Parameter.empty: 709 + self.superparams.pre[k].callback = self._make_callback( 710 + k, v.annotation, func, self.superparams.pre[k].callback 711 + ) 712 + if k in self.superparams.help: 713 + self.superparams.pre[k].help = self.superparams.help[k] 714 + elif k not in self.superparams.disabled: 715 + self.superparams.processing[k] = v 716 + 717 + envvarg = False 718 + # Adapted From: 719 + # Answer: https://stackoverflow.com/a/47017849 720 + # User: https://stackoverflow.com/users/1583052/dipu 721 + for i, (k, v) in enumerate( 722 + dict(reversed(self.superparams.processing.items())).items() 723 + ): 724 + func = self._get_funky( 725 + len(self.superparams.processing) - 1 - i, 726 + k, 727 + v, 728 + func, 729 + )(func) 730 + self.superparams.processed[func.__click_params__[-1]] = v 731 + if k.isupper() and v.kind in ( 732 + Parameter.POSITIONAL_ONLY, 733 + Parameter.VAR_POSITIONAL, 734 + ): 735 + envvarg = True 736 + 737 + for param, value in dict(reversed(self.superparams.processed.items())).items(): 738 + if ( 739 + isinstance(param, click.Option) 740 + and not param.name.startswith("__") 741 + and not any( 742 + filter( 743 + self._shortform_predicate, 744 + flatten(param.opts, param.secondary_opts), 745 + ) 746 + ) 747 + ): 748 + forms = self._get_name(param.name, value, func, True) 749 + param.opts.append(forms[0]) 750 + if len(forms) > 1: 751 + param.secondary_opts.append(forms[1]) 752 + 753 + wrapped = normalize( 754 + func, 755 + exclude=self.superparams.disabled, 756 + ) 757 + 758 + # Adapted From: 759 + # Answer: https://stackoverflow.com/a/24734171 760 + # User: https://stackoverflow.com/users/681785/famousgarkin 761 + if envvarg: 762 + current_locals = locals().copy() 763 + current_locals["Dict"] = Dict 764 + lower_params = [str.lower(param) for param in self.superparams.working] 765 + exec( 766 + normalizeMultiline( 767 + f""" 768 + def wrapper({",".join(lower_params)}): 769 + return wrapped({",".join(lower_params)}) 770 + """ 771 + ), 772 + current_locals, 773 + ) 774 + wrapped = current_locals["wrapper"] 775 + 776 + base_kwargs = Dict() 777 + if ignore_and_accept: 778 + base_kwargs.context_settings = Dict( 779 + ignore_unknown_options=True, 780 + allow_extra_args=True, 781 + ) 782 + 783 + self.func = self.parent.func.group( 784 + **self._make_group_kwargs(base_kwargs, **kwargs), 785 + )(wraps(func)(wrapped)) 786 + self.parent.children[self.func.name] = self 787 + for attr in names: 788 + setattr(self, attr, self.func.name) 789 + return self 790 + 791 + def _longform_predicate(self, arg): 792 + return arg.startswith("--") 793 + 794 + def _get_longform(self, name: str, *args): 795 + return f"--{name.strip('_').lower().replace('_', '-')}" 796 + 797 + def _shortform_predicate(self, arg): 798 + return arg.startswith("-") and not arg.startswith("--") 799 + 800 + def _get_shortform(self, name: str, func: Callable): 801 + first_letter = name.strip("_")[0] 802 + 803 + if hasattr(func, "__click_params__"): 804 + popts = { 805 + opt[1] 806 + for opt in flatten( 807 + (param.opts, param.secondary_opts) 808 + for param in func.__click_params__ 809 + ) 810 + if self._shortform_predicate(opt) 811 + } 812 + if first_letter in popts: 813 + try: 814 + first_index = lower.index(first_letter) 815 + except ValueError: 816 + first_index = upper.index(first_letter) 817 + i = first_index 818 + while first_letter in popts: 819 + first_letter = first_letter.swapcase() 820 + if first_letter in popts: 821 + i = (i + 1) % 26 822 + if i == first_index: 823 + raise click.UsageError( 824 + "no more lowercase or uppercase letters left for shortform names" 825 + ) 826 + first_letter = lower[i] 827 + else: 828 + break 829 + 830 + return f"-{first_letter}" 831 + 832 + def _get_name( 833 + self, name: str, value: Parameter, func: Callable, shortform: bool = False 834 + ): 835 + if shortform: 836 + processor = self._get_shortform 837 + predicate = self._shortform_predicate 838 + else: 839 + processor = self._get_longform 840 + predicate = self._longform_predicate 841 + 842 + default = processor(name, func) 843 + if get_origin(value.annotation) is Switch: 844 + default += f"/{processor(get_args(value.annotation)[0], func)}" 845 + 846 + # Adapted From: 847 + # Answer: https://stackoverflow.com/a/9542768 848 + # User: https://stackoverflow.com/users/916657/niklas-b 849 + form = next( 850 + filter(predicate, getattr(func, "__bundle__", Dict()).params[name].args), 851 + default, 852 + ) 853 + if "/" in form: 854 + return form.split("/") 855 + return [form] 856 + 857 + def _get_allforms(self, func): 858 + return flatten( 859 + (param.name, param.human_readable_name, param.opts, param.secondary_opts) 860 + for param in getattr(func, "__click_params__", tuple()) 861 + ) 862 + 863 + def _get_shortforms(self, func): 864 + return list(filter(self._shortform_predicate, self._get_allforms(func))) 865 + 866 + def _get_longforms(self, func): 867 + return list(filter(self._longform_predicate, self._get_allforms(func))) 868 + 869 + def _get_forms(self, func): 870 + return [ 871 + param 872 + for param in self._get_allforms(func) 873 + if param.startswith("--") or not param.startswith("-") 874 + ] 875 + 876 + def _trysubclass(self, annotation: Any, *types): 877 + try: 878 + return issubclass(annotation, types) 879 + except TypeError: 880 + return is_bearable(annotation, types) 881 + 882 + def _callback(self, annotation, ctx, param, value, *, debug=False): 883 + if debug: 884 + print(annotation, ctx, param, value) 885 + return ctx, param, process_bundle(annotation, value) 886 + 887 + def _make_callback( 888 + self, 889 + name: str, 890 + annotation: Any, 891 + func: Callable, 892 + precallback: Optional[Callable] = None, 893 + ): 894 + return pipe( 895 + partial(self._callback, annotation, debug=False), 896 + precallback, 897 + *( 898 + get_args(annotation)[1:] 899 + if get_origin(annotation) is Callback 900 + else tuple() 901 + ), 902 + getattr(func, "__bundle__", Dict()).params[name].kwargs.callback, 903 + lambda ctx, param, value: value, 904 + cls=PipeArgs, 905 + ) 906 + 907 + def _make_kwargs( 908 + self, 909 + name: str, 910 + annotation: Any, 911 + default: Any, 912 + func: Callable, 913 + kwargs: dict, 914 + ): 915 + kwargs = Dict(kwargs) 916 + if default is Parameter.empty: 917 + if name.isupper(): 918 + kwargs.envvar = name 919 + else: 920 + kwargs.default = default 921 + return ( 922 + kwargs 923 + | getattr(func, "__bundle__", Dict()).params[name].kwargs 924 + | {"callback": self._make_callback(name, annotation, func)} 925 + ) 926 + 927 + def _default_annotation(self, value: Parameter): 928 + default = value.default 929 + annotation = value.annotation 930 + if annotation is Parameter.empty and default is not Parameter.empty: 931 + annotation = type(default) 932 + elif get_origin(annotation) is Switch: 933 + annotation = bool 934 + default = False 935 + return default, annotation 936 + 937 + # TODO: Implement and test 938 + def _get_funky( 939 + self, 940 + index: int, 941 + name: Literal["password"], 942 + value: Attr[Parameter.empty, "annotation"], 943 + func: Callable, 944 + ): 945 + return click.password_option 946 + 947 + def _get_funky( 948 + self, 949 + index: int, 950 + name: str, 951 + value: Attr[type[Password], "annotation"], 952 + func: Callable, 953 + ): 954 + return click.option( 955 + *self._get_name(name, value, func), 956 + prompt=True, 957 + hide_input=True, 958 + confirmation_prompt=True, 959 + ) 960 + 961 + def _get_funky(self, index: Literal[0], name: Literal["ctx"], *args): 962 + return click.pass_context 963 + 964 + def _get_funky( 965 + self, 966 + index: Literal[0], 967 + name: str, 968 + value: Attr[Origin[Pass], "annotation"], 969 + func: Callable, 970 + ): 971 + return click.make_pass_decorator(get_args(value.annotation)[0], ensure=True) 972 + 973 + def _get_funky( 974 + self, 975 + index: Literal[0], 976 + name: str, 977 + value: Attr[type[Obj], "annotation"], 978 + func: Callable, 979 + ): 980 + return click.pass_obj 981 + 982 + def _get_funky( 983 + self, 984 + index: int, 985 + name: str, 986 + value: Attr[ 987 + Literal[ 988 + Parameter.POSITIONAL_ONLY, 989 + Parameter.VAR_POSITIONAL, 990 + ], 991 + "kind", 992 + ], 993 + func: Callable, 994 + ): 995 + default, annotation = self._default_annotation(value) 996 + 997 + # TODO: Should arguments be required by default? 998 + # opts = Dict(required=default is Parameter.empty and value.kind is Parameter.POSITIONAL_ONLY) 999 + opts = Dict() 1000 + 1001 + args = get_args(annotation) 1002 + if self._trysubclass(annotation, Model): 1003 + opts.nargs = len(annotation.__annotations__) 1004 + elif args and self._trysubclass(get_origin(annotation), tuple): 1005 + opts.nargs = -1 if ... in args or EllipsisType in args else len(args) 1006 + else: 1007 + variable = value.kind is Parameter.VAR_POSITIONAL or self._trysubclass( 1008 + annotation, Coll 1009 + ) 1010 + opts = Dict(nargs=-1 if variable else 1) 1011 + with suppress(ValueError): 1012 + sig = signature(annotation) 1013 + opts.nargs = ( 1014 + -1 1015 + if variable or can_unpack_variable(sig) 1016 + else (len(pk_variables(sig)) or 1) 1017 + ) 1018 + 1019 + kwargs = self._make_kwargs( 1020 + name, 1021 + annotation, 1022 + default, 1023 + func, 1024 + reduce( 1025 + operator.or_, 1026 + ( 1027 + self.__class__._defaults.argument, 1028 + opts, 1029 + self.__class__._globals.argument, 1030 + self.__class__._roots[self.root._func].argument, 1031 + self.__class__._instances[self._func].argument, 1032 + ), 1033 + ), 1034 + ) 1035 + if kwargs.nargs != 1 and kwargs.envvar in self.__class__._pathvars: 1036 + kwargs.type = kwargs.type or click.Path() 1037 + return click.argument( 1038 + name, 1039 + **kwargs, 1040 + ) 1041 + 1042 + def _get_funky( 1043 + self, 1044 + index: int, 1045 + name: str, 1046 + value: Attr[ 1047 + Literal[ 1048 + Parameter.KEYWORD_ONLY, 1049 + Parameter.POSITIONAL_OR_KEYWORD, 1050 + ], 1051 + "kind", 1052 + ], 1053 + func: Callable, 1054 + ): 1055 + default, annotation = self._default_annotation(value) 1056 + origin = get_origin(annotation) or annotation 1057 + opts = Dict( 1058 + required=default is Parameter.empty 1059 + and value.kind is Parameter.KEYWORD_ONLY, 1060 + count=annotation is Count, 1061 + is_eager=origin is Eager, 1062 + help=self.superparams.help[name].description or None, 1063 + ) 1064 + 1065 + args = get_args(annotation) 1066 + if self._trysubclass(annotation, Model): 1067 + opts.nargs = len(annotation.__annotations__) 1068 + elif args and self._trysubclass(origin, tuple): 1069 + if ... in args or EllipsisType in args: 1070 + opts.multiple = True 1071 + else: 1072 + opts.nargs = len(args) 1073 + else: 1074 + opts.multiple = self._trysubclass(annotation, Coll) 1075 + 1076 + if ( 1077 + not self._trysubclass(annotation, bool) 1078 + and annotation is not Parameter.empty 1079 + ): 1080 + opts.metavar = annotation.__name__ 1081 + with suppress(ValueError): 1082 + sig = signature(annotation) 1083 + opts.multiple = opts.multiple or can_unpack_variable(sig) 1084 + if not opts.multiple: 1085 + opts.nargs = len(pk_variables(sig)) or 1 1086 + with suppress(TypeError): 1087 + if issubclass(annotation, bool): 1088 + opts.is_flag = True 1089 + kwargs = self._make_kwargs( 1090 + name, 1091 + annotation, 1092 + default, 1093 + func, 1094 + reduce( 1095 + operator.or_, 1096 + ( 1097 + self.__class__._defaults.option, 1098 + opts, 1099 + self.__class__._globals.option, 1100 + self.__class__._roots[self.root._func].option, 1101 + self.__class__._instances[self._func].option, 1102 + ), 1103 + ), 1104 + ) 1105 + if kwargs.multiple and kwargs.envvar in self.__class__._pathvars: 1106 + kwargs.type = kwargs.type or click.Path() 1107 + return click.option( 1108 + name, 1109 + "/".join(self._get_name(name, value, func)), 1110 + **kwargs, 1111 + ) 1112 + 1113 + 1114 + class Bundler(type, metaclass=plumeta): 1115 + def __new__( 1116 + cls, name: str, bases: tuple[Callable, ...], ns: dict | MappingProxyType 1117 + ): 1118 + result = super().__new__(cls, name, bases, ns) 1119 + for key, value in result.__classes__.items(): 1120 + if isinstance(value, Bundler): 1121 + result._adopt(value, key) 1122 + else: 1123 + result.__bundles__[key] = subtype(key, result, value.__dict__) 1124 + 1125 + # NOTE: This returns a `bundle`, not a `bundler`. 1126 + result._rewrap(getattr(result, "__bundle__", Dict()).group) 1127 + return result 1128 + 1129 + def __getattr__(cls, attr): 1130 + return getattr(cls.__cls__, attr) 1131 + 1132 + def __eq__(cls, other: Union["Bundler", "bundle", click.Group]) -> bool: 1133 + return cls.__cls__.__eq__(other) 1134 + 1135 + def __eq__(self, other) -> bool: 1136 + return False 1137 + 1138 + def __hash__(cls): 1139 + return cls.__cls__.__hash__() 1140 + 1141 + class __prepare__(dict): 1142 + @property 1143 + def cls(self): 1144 + return self.get("__cls__") 1145 + 1146 + @cls.setter 1147 + def cls(self, value): 1148 + super().__setitem__("__cls__", value) 1149 + 1150 + def __init__(self, name, bases): 1151 + self.name = name 1152 + self.bases = bases 1153 + self.names = ("__cls__", self.name) 1154 + self.root = not bases or bases[0] is bundler 1155 + self.parent = bundle if self.root else bases[0].__cls__ 1156 + self.cls = bundle(deflambda(self.name), self.parent, name=self.name) 1157 + self.bundles = Dict({self.name: self.cls}) 1158 + super().__setitem__("__bundles__", self.bundles) 1159 + self.classes = Dict() 1160 + super().__setitem__("__classes__", self.classes) 1161 + self.dispatcher = Dispatcher() 1162 + super().__setitem__("__dispatcher__", self.dispatcher) 1163 + self.original_dict = Dict() 1164 + super().__setitem__("__original_dict__", self.original_dict) 1165 + 1166 + def update(self, other: dict): 1167 + for k, v in other.items(): 1168 + self[k] = v 1169 + return self 1170 + 1171 + def __ior__(self, other: dict): 1172 + return self.update(other) 1173 + 1174 + def __or__(self, other: dict): 1175 + return deepcopy(self).update(other) 1176 + 1177 + def __ror__(self, other: dict): 1178 + return ( 1179 + self.__class__(self.name, self.bases) 1180 + .update(other) 1181 + .update(self.original_dict) 1182 + ) 1183 + 1184 + def __setitem__(self, key, value): 1185 + self.original_dict[key] = value 1186 + if callable(value): 1187 + if key.startswith("__") and key.endswith("__"): 1188 + args, kwargs = getattr(value, "__plume__", (tuple(), {})) 1189 + if not kwargs.get("disabled", False): 1190 + value = getattr(self.get(key), "dispatch", self.dispatcher)( 1191 + value, *args, **kwargs 1192 + ) 1193 + elif isclass(value): 1194 + return self.classes.__setitem__(key, value) 1195 + else: 1196 + kwargs = Dict(getattr(value, "__bundle__", {})).group 1197 + if key == self.name: 1198 + if isinstance(value, (bundle, bundler)): 1199 + if not self.root: 1200 + self.parent._adopt(value, key) 1201 + else: 1202 + name = kwargs.get("name", self.name) 1203 + 1204 + # Adapted From: 1205 + # Answer: https://stackoverflow.com/a/3655857 1206 + # User: https://stackoverflow.com/users/95810 1207 + if value.__name__ == (lambda: None).__name__: 1208 + for attr in names: 1209 + setattr(value, attr, name) 1210 + 1211 + value = bundle(value, self.parent, name=name) 1212 + for k, v in self.bundles.items(): 1213 + if not (isclass(v) or k in self.names): 1214 + value._adopt(v, k) 1215 + self.cls = value 1216 + elif isinstance(value, (bundle, bundler)): 1217 + self.cls._adopt(value, key) 1218 + else: 1219 + name = kwargs.get("name", key) 1220 + 1221 + # Adapted From: 1222 + # Answer: https://stackoverflow.com/a/3655857 1223 + # User: https://stackoverflow.com/users/95810 1224 + if value.__name__ == (lambda: None).__name__: 1225 + for attr in names: 1226 + setattr(value, attr, name) 1227 + 1228 + value = bundle(value, self.cls, name=name) 1229 + return self.bundles.__setitem__(key, value) 1230 + return super().__setitem__(key, value) 1231 + 1232 + 1233 + class bundler(metaclass=Bundler): 1234 + # TODO: There may be a few problems with the adoption... 1235 + def __new__( 1236 + cls, 1237 + name: str, 1238 + bases: tuple["bundler", ...], 1239 + ns: dict | MappingProxyType, 1240 + **kwargs, 1241 + ): 1242 + self = subtype(name, cls, ns) 1243 + self._rewrap(bases[0].__kwargs__) 1244 + try: 1245 + bases[0]._adopt(self) 1246 + except AttributeError: 1247 + with suppress(AttributeError, TypeError): 1248 + bases[0].parent._adopt(self) 1249 + return self 1250 + 1251 + def __new__( 1252 + cls, 1253 + self: "Bundler", 1254 + parent: Optional[Union["Bundler", "bundle"]] = None, 1255 + **kwargs, 1256 + ): 1257 + self._rewrap(kwargs) 1258 + if parent: 1259 + parent._adopt(self) 1260 + return self 1261 + 1262 + def __new__( 1263 + cls, 1264 + self: type, 1265 + parent: Optional[Union["Bundler", "bundle"]] = None, 1266 + **kwargs, 1267 + ): 1268 + return cls.__new__(cls, subtype(self, cls), parent, **kwargs) 1269 + 1270 + def __new__( 1271 + cls, 1272 + self: Callable, 1273 + parent: Optional[Union["Bundler", "bundle"]] = None, 1274 + **kwargs, 1275 + ): 1276 + return cls.__new__( 1277 + cls, subtype(self.__name__, cls, {self.__name__: self}), parent, **kwargs 1278 + ) 1279 + 1280 + def __new__(cls, *args, **kwargs): 1281 + if cls._run(args, kwargs) and cls is not bundler: 1282 + return beartype(cls._func)(*args, **kwargs) 1283 + return super().__new__(cls) 1284 + 1285 + class __prepare__(dict): 1286 + def __init__(self, name, bases): 1287 + pass 1288 + 1289 + def __init__(self, **kwargs): 1290 + self.__kwargs__ = Dict(kwargs) 1291 + 1292 + def __getattr__(self, attr): 1293 + return getattr(self.__cls__, attr) 1294 + 1295 + def __call__(self, cls: "Bundler"): 1296 + cls._rewrap(self.__kwargs__) 1297 + return cls 1298 + 1299 + def __call__(self, cls: type): 1300 + return self(subtype(cls, self.__class__)) 1301 + 1302 + def __call__(self, func: Callable): 1303 + return self(subtype(func.__name__, self.__class__, {func.__name__: func}))
+5 -2
packages/hydrox/hydrox/helpers/helpers.py
··· 7 7 import types 8 8 9 9 from contextlib import suppress, contextmanager 10 + from cytoolz.itertoolz import unique 10 11 from functools import partial 11 12 from inspect import ( 12 13 currentframe, ··· 14 15 getmodule, 15 16 ) 16 17 from itertools import permutations 17 - from more_itertools import unique_everseen, powerset 18 + from more_itertools import powerset, collapse 18 19 19 20 BString: typing.TypeAlias = str | bytes | bytearray 20 21 ··· 103 104 104 105 class FlattenMeta(type): 105 106 def __call__(self, *iterables, use=builtins.tuple(), levels=None, current_level=0): 107 + if levels is None: 108 + yield from collapse(use or iterables) 106 109 for iterable in use or iterables: 107 110 if isinstance(iterable, Collection) and ( 108 111 (levels is None) or (current_level < levels) ··· 172 175 173 176 174 177 def superpowerset(*iterables): 175 - return unique_everseen( 178 + return unique( 176 179 flatten((powerset(p) for p in permutations(flatten(iterables))), levels=2) 177 180 ) 178 181
+7 -4
packages/hydrox/hydrox/typing/generic.py
··· 21 21 from abc import ABCMeta 22 22 from contextlib import suppress 23 23 from collections import defaultdict 24 + from cytoolz.itertoolz import unique, partition 25 + from cytoolz.dicttoolz import keymap, valmap 24 26 from functools import partial, reduce, cache as defcache 25 27 from inspect import Parameter 26 28 from inspect import getmro, stack, FrameInfo 27 - from more_itertools import partition, unique_everseen 28 29 from random import randint, sample 29 30 from rich import print 30 31 from rich.traceback import Traceback ··· 1053 1054 format_args( 1054 1055 dict, 1055 1056 [ 1056 - unionize(types.UnionType, [inner(item) for item in obj.keys()]), 1057 - unionize(types.UnionType, [inner(item) for item in obj.values()]), 1057 + unionize(types.UnionType, keymap(inner, obj)), 1058 + unionize(types.UnionType, valmap(inner, obj)), 1058 1059 ], 1059 1060 ) 1060 1061 ) ··· 1241 1242 return flatten( 1242 1243 partition( 1243 1244 annotation_is_not_generic, 1244 - unique_everseen( 1245 + unique( 1245 1246 flatten( 1246 1247 annotation, 1247 1248 map( ··· 1253 1254 ) 1254 1255 ) 1255 1256 1257 + 1256 1258 def unwrap_check(cls): 1257 1259 if has_custom_init(cls): 1258 1260 cls = type(cls) ··· 1260 1262 unwrap(GenericMeta.__subclasscheck__), 1261 1263 unwrap(GenericAlias.__subclasscheck__), 1262 1264 ) 1265 + 1263 1266 1264 1267 # TODO: This is going to trip up with types that have the same name as the official generics, 1265 1268 # but don't aren't connected to them, such as `hh.Collection` and `abc.Collection`.
+2 -4
packages/hydrox/hydrox/typing/types.py
··· 31 31 ) 32 32 from ..variables import conversion_table 33 33 from contextlib import suppress 34 - from more_itertools import unique_everseen, partition 34 + from cytoolz.itertoolz import unique, partition 35 35 36 36 37 37 @Generalize ··· 246 246 class UnhashableLiteral(Literal, generic=True, force=True): 247 247 def __aliasargsfullprocessor__(origin, args): 248 248 new_args = [] 249 - for arg in unique_everseen( 250 - args if args and isinstance(args, tuple) else (args,) 251 - ): 249 + for arg in unique(args if args and isinstance(args, tuple) else (args,)): 252 250 if typing.get_origin(arg) == Generics.UnhashableLiteral: 253 251 new_args.extend(typing.get_args(arg)) 254 252 else:
+2 -1
packages/hydrox/pyproject.toml
··· 14 14 urls."Source code" = "https://tangled.sh/@syvl.org/hydrox" 15 15 dependencies = [ 16 16 "autoslot>=2024.12.1", 17 + "cytoolz>=1.0.1", 17 18 "more-itertools>=10.7.0", 18 19 "rich==13.8.1", 19 20 ] ··· 23 24 build-backend = "hatchling.build" 24 25 25 26 [tool.hatch.build.targets.wheel] 26 - packages = [ "hydrox" ] 27 + packages = [ "hydrox" ]
+2 -1
packages/hydrox/test1.py
··· 22 22 from contextlib import contextmanager 23 23 from rich.rule import Rule 24 24 25 + 25 26 # NOTE: Can't cache this; booleans, floats, strings, and integers sometimes have the same hash values. 26 27 def scoreinstance(left, right): 27 28 if anything(right): ··· 32 33 if has_custom_init(right): 33 34 return type(right).__instancescore__(right, left) 34 35 return right.__instancescore__(left) 35 - return scoresubclass(get_type(left), right) 36 + return scoresubclass(get_type(left), right)
+1 -1
packages/hydrox/test2.py
··· 40 40 # Aleph0 Aleph0 41 41 # Aleph0 Aleph1 42 42 # Aleph1 Aleph0 43 - # Aleph1 Aleph1 43 + # Aleph1 Aleph1
+1
packages/jugulis/jugulis/__init__.py
··· 1 1 from .deino import Deino 2 2 from .hydreigon import Hydreigon, AmbiguityError 3 + from .jugulis import Jugulis, jugulis, IronJugulis, IronJugudict, nydra, hydread 3 4 from .functions import * 4 5 from rich.traceback import install 5 6
+5
packages/jugulis/jugulis/functions.py
··· 14 14 15 15 16 16 @defcache 17 + def process_key(key): 18 + return key if isinstance(key, str) else key.__name__ 19 + 20 + 21 + @defcache 17 22 def normalize_typevars( 18 23 annotation: list | ht.Annotation, 19 24 typevar: TypeVar,
-36
packages/jugulis/jugulis/hydreigon.py
··· 1 - from autoslot import SlotsMeta, SlotsPlusDictMeta 2 1 from hydrox.helpers import as_decorator, query, cache_get 3 2 from .deino import Deino, format_message 4 3 from collections import defaultdict, namedtuple ··· 107 106 self.__initialized__ = True 108 107 self.__append_func__(args[0]) 109 108 return self 110 - 111 - 112 - def nydra(func): 113 - func.__hydread__ = (tuple(), {"disabled": True}) 114 - return func 115 - 116 - 117 - def hydread(*args, **kwargs): 118 - def wrapper(func): 119 - func.__hydread__ = (args, kwargs) 120 - return func 121 - 122 - return wrapper 123 - 124 - 125 - class prepare(dict): 126 - def __init__(self, name, bases): 127 - pass 128 - 129 - def __setitem__(self, key, value): 130 - if callable(value): 131 - args, kwargs = getattr(value, "__hydread__", (tuple(), {})) 132 - if not kwargs.get("disabled", False): 133 - value = getattr(self.get(key), "roar", Hydreigon)( 134 - value, *args, **kwargs 135 - ) 136 - super().__setitem__(key, value) 137 - 138 - 139 - class MetaHydra(SlotsMeta): 140 - __prepare__ = prepare 141 - 142 - 143 - class MetaHydrict(SlotsPlusDictMeta): 144 - __prepare__ = prepare
+68
packages/jugulis/jugulis/jugulis.py
··· 1 + from autoslot import SlotsMeta, SlotsPlusDictMeta 2 + from hydrox.helpers import as_decorator, query 3 + from .functions import process_key 4 + from .hydreigon import Hydreigon 5 + from rich.pretty import pprint 6 + from collections import defaultdict 7 + 8 + 9 + # TODO: Implement all dict methods here. 10 + class Jugulis(defaultdict): 11 + def __init__(self): 12 + super().__init__(Hydreigon) 13 + 14 + def __call__(self, *args, **kwargs): 15 + if as_decorator(query=query): 16 + 17 + def wrapper(func): 18 + return self[func].roar(func, *args, **kwargs) 19 + 20 + return wrapper 21 + func = args[0] 22 + return self[func].roar(func, *args[1:], **kwargs) 23 + 24 + def __contains__(self, key): 25 + return super().__contains__(process_key(key)) 26 + 27 + def __getitem__(self, key): 28 + return super().__getitem__(process_key(key)) 29 + 30 + def __setitem__(self, key, value): 31 + super().__setitem__(key if isinstance(key, str) else key.__name__, value) 32 + 33 + 34 + jugulis = Jugulis() 35 + 36 + 37 + def nydra(func): 38 + func.__hydread__ = (tuple(), {"disabled": True}) 39 + return func 40 + 41 + 42 + def hydread(*args, **kwargs): 43 + def wrapper(func): 44 + func.__hydread__ = (args, kwargs) 45 + return func 46 + 47 + return wrapper 48 + 49 + 50 + class prepare(dict): 51 + def __init__(self, name, bases): 52 + self.__jugulis__ = __jugulis__ = Jugulis() 53 + super().__setitem__("__jugulis__", __jugulis__) 54 + 55 + def __setitem__(self, key, value): 56 + if callable(value): 57 + args, kwargs = getattr(value, "__hydread__", (tuple(), {})) 58 + if not kwargs.get("disabled", False): 59 + value = self.__jugulis__(value, *args, **kwargs) 60 + super().__setitem__(key, value) 61 + 62 + 63 + class IronJugulis(SlotsMeta): 64 + __prepare__ = prepare 65 + 66 + 67 + class IronJugudict(SlotsPlusDictMeta): 68 + __prepare__ = prepare
+7 -1
packages/jugulis/test1.py
··· 1 1 from jugulis import Hydreigon 2 2 3 + 3 4 @Hydreigon 4 5 def f(a: int, /) -> int: 5 6 return 0 7 + 6 8 7 9 @f.roar 8 10 def f(a: int, /, b: int) -> int: 9 11 return 1 10 12 13 + 11 14 @f.roar 12 15 def f(a: int, /, b: int, *c) -> int: 13 16 return 2 17 + 14 18 15 19 @f.roar 16 20 def f(a: int, /, b: int, *c, d: int) -> int: 17 21 return 3 18 22 23 + 19 24 @f.roar 20 25 def f(a: int, /, b: int, *c, d: int, **e) -> int: 21 26 return 4 22 27 23 - print(f("")) 28 + 29 + print(f(""))
+90
packages/jugulis/test2.py
··· 1 + from jugulis import jugulis, IronJugulis 2 + from rich.pretty import pprint 3 + 4 + 5 + @jugulis 6 + def f(a: int, /) -> int: 7 + return 0 8 + 9 + 10 + @jugulis 11 + def f(a: int, /, b: int) -> int: 12 + return 1 13 + 14 + 15 + @jugulis 16 + def f(a: int, /, b: int, *c) -> int: 17 + return 2 18 + 19 + 20 + @jugulis 21 + def f(a: int, /, b: int, *c, d: int) -> int: 22 + return 3 23 + 24 + 25 + @jugulis 26 + def f(a: int, /, b: int, *c, d: int, **e) -> int: 27 + return 4 28 + 29 + 30 + @jugulis 31 + def g(a: int, /) -> int: 32 + return 0 33 + 34 + 35 + @jugulis 36 + def g(a: int, /, b: int) -> int: 37 + return 1 38 + 39 + 40 + @jugulis 41 + def g(a: int, /, b: int, *c) -> int: 42 + return 2 43 + 44 + 45 + @jugulis 46 + def g(a: int, /, b: int, *c, d: int) -> int: 47 + return 3 48 + 49 + 50 + @jugulis 51 + def g(a: int, /, b: int, *c, d: int, **e) -> int: 52 + return 4 53 + 54 + 55 + pprint(jugulis) 56 + 57 + 58 + class Test(metaclass=IronJugulis): 59 + def f(a: int, /) -> int: 60 + return 0 61 + 62 + def f(a: int, /, b: int) -> int: 63 + return 1 64 + 65 + def f(a: int, /, b: int, *c) -> int: 66 + return 2 67 + 68 + def f(a: int, /, b: int, *c, d: int) -> int: 69 + return 3 70 + 71 + def f(a: int, /, b: int, *c, d: int, **e) -> int: 72 + return 4 73 + 74 + def g(a: int, /) -> int: 75 + return 0 76 + 77 + def g(a: int, /, b: int) -> int: 78 + return 1 79 + 80 + def g(a: int, /, b: int, *c) -> int: 81 + return 2 82 + 83 + def g(a: int, /, b: int, *c, d: int) -> int: 84 + return 3 85 + 86 + def g(a: int, /, b: int, *c, d: int, **e) -> int: 87 + return 4 88 + 89 + 90 + pprint(Test.__jugulis__)
+24
test1.py
··· 1 + from rich.traceback import install 2 + 3 + install(show_locals=True) 4 + 5 + from functools import wraps 6 + 7 + 8 + def wrap(func): 9 + a = 0 10 + 11 + @wraps(func) 12 + def wrapper(*args, **kwargs): 13 + return func() 14 + 15 + return wrapper 16 + 17 + 18 + @wrap 19 + def test(): 20 + b = 1 21 + raise Exception(b) 22 + 23 + 24 + test()
+20
uv.lock
··· 49 49 ] 50 50 51 51 [[package]] 52 + name = "cytoolz" 53 + version = "1.0.1" 54 + source = { registry = "https://pypi.org/simple" } 55 + dependencies = [ 56 + { name = "toolz" }, 57 + ] 58 + sdist = { url = "https://files.pythonhosted.org/packages/a7/f9/3243eed3a6545c2a33a21f74f655e3fcb5d2192613cd3db81a93369eb339/cytoolz-1.0.1.tar.gz", hash = "sha256:89cc3161b89e1bb3ed7636f74ed2e55984fd35516904fc878cae216e42b2c7d6", size = 626652, upload-time = "2024-12-13T05:47:36.672Z" } 59 + 60 + [[package]] 52 61 name = "execnet" 53 62 version = "2.1.1" 54 63 source = { registry = "https://pypi.org/simple" } ··· 63 72 source = { editable = "packages/hydrox" } 64 73 dependencies = [ 65 74 { name = "autoslot" }, 75 + { name = "cytoolz" }, 66 76 { name = "more-itertools" }, 67 77 { name = "rich" }, 68 78 ] ··· 70 80 [package.metadata] 71 81 requires-dist = [ 72 82 { name = "autoslot", specifier = ">=2024.12.1" }, 83 + { name = "cytoolz", specifier = ">=1.0.1" }, 73 84 { name = "more-itertools", specifier = ">=10.7.0" }, 74 85 { name = "rich", specifier = "==13.8.1" }, 75 86 ] ··· 363 374 sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/3d75c196ac07ac8749600b60b03f4f6094d54e132c4d94ebac6ee0e0add0/termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970", size = 14324, upload-time = "2025-04-30T11:37:53.791Z" } 364 375 wheels = [ 365 376 { url = "https://files.pythonhosted.org/packages/4f/bd/de8d508070629b6d84a30d01d57e4a65c69aa7f5abe7560b8fad3b50ea59/termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa", size = 7684, upload-time = "2025-04-30T11:37:52.382Z" }, 377 + ] 378 + 379 + [[package]] 380 + name = "toolz" 381 + version = "1.0.0" 382 + source = { registry = "https://pypi.org/simple" } 383 + sdist = { url = "https://files.pythonhosted.org/packages/8a/0b/d80dfa675bf592f636d1ea0b835eab4ec8df6e9415d8cfd766df54456123/toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02", size = 66790, upload-time = "2024-10-04T16:17:04.001Z" } 384 + wheels = [ 385 + { url = "https://files.pythonhosted.org/packages/03/98/eb27cc78ad3af8e302c9d8ff4977f5026676e130d28dd7578132a457170c/toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236", size = 56383, upload-time = "2024-10-04T16:17:01.533Z" }, 366 386 ] 367 387 368 388 [[package]]