1#!/usr/bin/env python3
2"""
3
4"""
5# Imports:
6from __future__ import annotations
7
8# ##-- stdlib imports
9import datetime
10import enum
11import functools as ftz
12import itertools as itz
13import logging as logmod
14import pathlib as pl
15import re
16import time
17import collections
18import contextlib
19import hashlib
20from copy import deepcopy
21from uuid import UUID, uuid1
22from weakref import ref
23import atexit # for @atexit.register
24import faulthandler
25# ##-- end stdlib imports
26
27from .param_spec import ParamSpec
28from .core import AssignParam, ToggleParam, KeyParam, LiteralParam
29
30# ##-- types
31# isort: off
32# General
33import abc
34import collections.abc
35import typing
36import types
37from typing import cast, assert_type, assert_never
38from typing import Generic, NewType, Never
39from typing import no_type_check, final, override, overload
40# Protocols and Interfaces:
41from typing import Protocol, runtime_checkable
42if typing.TYPE_CHECKING:
43 from typing import Final, ClassVar, Any, Self
44 from typing import Literal, LiteralString
45 from typing import TypeGuard
46 from collections.abc import Iterable, Iterator, Callable, Generator
47 from collections.abc import Sequence, Mapping, MutableMapping, Hashable
48
49 from jgdv import Maybe
50
51# isort: on
52# ##-- end types
53
54##-- logging
55logging = logmod.getLogger(__name__)
56##-- end logging
57
58# Vars:
59
60# Body:
61
[docs]
62class RepeatToggleParam(ToggleParam):
63 """ TODO A repeatable toggle
64 eg: -verbose -verbose -verbose
65 """
66 desc : str = "A Repeat Toggle"
67 pass
68
[docs]
69class WildcardParam(AssignParam):
70 """ TODO a wildcard param that matches any --{key}={val} """
71
72 desc : str = "A Wildcard"
73
74 def __init__(self, *args:Any, **kwargs:Any) -> None: # noqa: ANN401, ARG002
75 kwargs.setdefault("type", str)
76 kwargs['name'] = "*"
77 super().__init__(**kwargs)
78
[docs]
79 def matches_head(self, val:str) -> bool:
80 assert(isinstance(self.separator, str))
81 match self.prefix:
82 case str() as p:
83 return (val.startswith(p)
84 and self.separator in val)
85 case _:
86 return False
87
[docs]
88 def next_value(self, args:list) -> tuple[str, list, int]:
89 logging.debug("Getting Wildcard Key Assingment: %s", args)
90 assert(self.separator in args[0]), (self.separator, args[0])
91 key,val = self._processor.split_assignment(self, args[0])
92 match self.prefix:
93 case str():
94 return key.removeprefix(self.prefix), [val], 1
95 case int():
96 return key, [val], 1
97
98
[docs]
99class ImplicitParam(ParamSpec):
100 """
101 A Parameter that is implicit, so doesn't give a help description unless
102 forced to
103 """
104 desc : str = "An Implicit Parameter"
105
[docs]
106 def help_str(self, *, force:bool=False) -> str:
107 return ""
108
[docs]
109class RepeatableParam(KeyParam):
110 """ TODO a repeatable key param
111 -key val -key val2 -key val3
112 """
113
114 type_ : type
115 desc : str = "A Repeatable Key"
116
117 def __init__(self, *args, **kwargs) -> None:
118 super().__init__(*args, **kwargs)
119 self.type_ = list
120
[docs]
121 def next_value(self, args:list) -> tuple[str, list, int]:
122 """ Get as many values as match
123 eg: args[-test, 2, -test, 3, -test, 5, -nottest, 6]
124 -> [2,3,5], [-nottest, 6]
125 """
126 logging.debug("Getting until no more matches: %s : %s", self.name, args)
127 assert(self.repeatable)
128 result, consumed, remaining = [], 0, args[:]
129 while bool(remaining):
130 head, val, *rest = remaining
131 if not self.matches_head(head):
132 break
133 else:
134 result.append(val)
135 remaining = rest
136 consumed += 2
137
138 return self.name, result, consumed
139
[docs]
140class ChoiceParam(LiteralParam):
141 """ TODO A param that must be from a choice of literals
142 eg: ChoiceParam([blah, bloo, blee]) : blah | bloo | blee
143
144 """
145
146 desc : str = "A Choice"
147
148 def __init__(self, name, choices:list[str], **kwargs) -> None:
149 super().__init__(name=name, **kwargs)
150 self._choices = choices
151
[docs]
152 def matches_head(self, val) -> bool:
153 """ test to see if a cli argument matches this param
154
155 Matchs {self.prefix}{self.name} if not an assignment
156 Matches {self.prefix}{self.name}{separator} if an assignment
157 """
158 return val in self._choices
159
[docs]
160class EntryParam(LiteralParam):
161 """ TODO a parameter that if it matches,
162 returns list of more params to parse
163 """
164 desc : str = "An expandable Param"
165 pass
166
[docs]
167class ConstrainedParam(ParamSpec):
168 """
169 TODO a type of parameter which is constrained in the values it can take, beyond just type.
170
171 eg: {name:amount, constraints={min=0, max=10}}
172 """
173 constraints : list[Any]
174 desc : str = "A Constrained Param"