parameter-toolkit 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ from functools import partial
2
+ from typing import LiteralString
3
+ from typing import Callable
4
+ from inspect import Parameter
5
+
6
+ from .parameter_tools import ParameterConvertor, ParameterExtractor, ParameterMapper, parameterT,positionalT, kwargT
7
+
8
+
9
+ def dictParameterGenerator(func:Callable, stringify:bool=False) -> parameterT:
10
+ """ Extracts parameter information from given function and returns them as dict of builtins or stringifed.
11
+ Stringifyied is primarly used if sending data via extarnal protocols.
12
+
13
+ <Parameters> internal <_empty> will be converted to None.
14
+ <Parameters> internal <_kind> will be converted to Str for now
15
+
16
+ Parameters:
17
+ func:Callable = The function which parameter info should be extrackted
18
+ stringify:Bool = specifify if types should be returned as builtIns or as strings.
19
+ Returns:
20
+ Generator with dictornarys {'name':...,'type':...'default'...,'kind':...}
21
+ """
22
+ _method = ParameterConvertor.toDefaultDict
23
+ if stringify:
24
+ _method = ParameterConvertor.toStrDict
25
+ return (_method(pm) for pm in ParameterExtractor.extractParameters(func))
26
+
27
+ def dictFuncParameters(func:Callable, stringify:bool=False)->parameterT:
28
+ """ Returns Functions parameter as dict of builtins or strings """
29
+ parameters = [*dictParameterGenerator(func,stringify)]
30
+ return dict(zip((p['name'] for p in parameters),parameters))
31
+
32
+ def listFuncParameters(func:Callable)->list[Parameter]:
33
+ """ Returns function paramaters as a list of inspect.Parameter objects."""
34
+ return list(ParameterExtractor.extractParameters(func))
35
+
36
+ def parameterFromLiteral(parameterString:LiteralString)-> tuple[positionalT, kwargT]:
37
+ return ParameterConvertor.StringToParameters(parameterString)
38
+
39
+ def mapFuncFromLiteral(func, parameterstring) -> partial:
40
+ _postinals, _keywords = parameterFromLiteral(parameterstring)
41
+ return ParameterMapper.mapFunc(func=func,positinals=_postinals,keywords=_keywords)
42
+
@@ -0,0 +1,101 @@
1
+ # Licensed under GPLv3
2
+ """Examine Python Parameter Objects."""# Licensed under GPLv3
3
+
4
+ """Convert ParameterObjects to useable positional and keyword arguments."""
5
+
6
+ from functools import partial
7
+ from typing import Iterable
8
+ from typing import Callable, Generator, Any, TypeVar
9
+ from inspect import Parameter, _empty
10
+ from inspect import signature
11
+
12
+ parameterT = TypeVar("parameterT")
13
+ positionalT=TypeVar("postionalT")
14
+ kwargT=TypeVar("kwargT")
15
+
16
+
17
+ class ParameterAnalyzer:
18
+ """ Provides functions to convert _ParameterTypes to BuiltIns for ParameterConvert """
19
+ @staticmethod
20
+ def defaultOrNone(parameter:Parameter) -> Any|None:
21
+ """Checks if default value for a paramenter is given and returns it's value.
22
+ Returns None if default is empty."""
23
+ if parameter.default is _empty:
24
+ return None
25
+ return parameter.default
26
+
27
+ @staticmethod
28
+ def typeOrNone(parameter:Parameter) -> type[Any]|None:
29
+ """Checks if default value for a paramenter is given and returns it's value.
30
+ Returns None if default is empty."""
31
+ if parameter.annotation is _empty:
32
+ return None
33
+ return parameter.annotation
34
+
35
+ @staticmethod
36
+ def getname(parameter:Parameter) -> str:
37
+ """Returns name of parameter. """
38
+ return parameter.name
39
+
40
+ @staticmethod
41
+ def getkind(parameter:Parameter) -> str:
42
+ """Returns kind of parameter as string."""
43
+ return parameter.kind
44
+
45
+ @staticmethod
46
+ def analyze(parameter:Parameter)-> tuple[str,type[Any]|None,type[Any]|None,str]:
47
+ """ Returns name, type or none, default or none, ParameterKind.name """
48
+ _name = ParameterAnalyzer.getname(parameter)
49
+ _type = ParameterAnalyzer.typeOrNone(parameter)
50
+ _default = ParameterAnalyzer.defaultOrNone(parameter)
51
+ _kind = ParameterAnalyzer.getkind(parameter)
52
+ return _name,_type,_default,_kind
53
+
54
+ class ParameterConvertor:
55
+ """ Provides function to convert signatures to builtIn/stringified dicts."""
56
+
57
+ @staticmethod
58
+ def toDict(parameter:Parameter, stringify:bool=False) -> dict[str,type]:
59
+ """**Transforms a parameter to a dict of buitlins.**
60
+ Instead of the built in `<_empty>` type which clearifies a diffrence betweent python None this library will return **`None`**!
61
+
62
+ Arguments:
63
+ parameter:Parameter = Parameters whichs info is to be extracted.
64
+ stringifgy: Bool = Specify if dict keys are python lowelvel builtins or represented as string.
65
+
66
+ Returns:
67
+ dict: A dictionary containing name,type,default,kind (None of type or default is not set)
68
+ """
69
+ # Self Zipping the dict is redundanted as using `__getstate__()` method.
70
+ # pminfo = (_name,_type,_default,_kind) = ParameterAnalyzer.analyze(parameter)
71
+ #pmdict = dict(zip(("name","type","default","kind"),pminfo))
72
+ pmdict = parameter.__getstate__()[1]
73
+ if stringify:
74
+ [pmdict.update({k,str(v)}) for k,v in pmdict.items()]
75
+ return pmdict
76
+
77
+ class ParameterExtractor:
78
+ """ Provides Generator to get Parameters of a Function or Parameters Literal"""
79
+
80
+ def extractPara(func:Callable) -> Generator[Parameter,None,int]:
81
+ """Iter thorugh signature and yield its parameters.
82
+ **yields:** `<inspect.Parameter>`
83
+ **returns:** `int` Amount of Parameters """
84
+ paraLen=0
85
+ for parameter in signature(func).parameters.values():
86
+ paraLen+=1
87
+ yield parameter
88
+ return paraLen
89
+
90
+ def getPMList(func:function) -> list[Parameter]:
91
+ """Returns a `list` of `Parameter` objects. """
92
+ return [pm for pm in signature(func).parameters.values()]
93
+
94
+ def getPMDict(func:function) -> dict[str,Parameter]:
95
+ return signature(func).parameters
96
+
97
+ class ParameterMapper:
98
+ """Provides Mapping shortcut for partial(postionals, keywordsargs)."""
99
+
100
+ def mapFunc(func, positinals:Iterable, keywords:dict) ->partial:
101
+ return partial(func, *positinals, **keywords)
@@ -0,0 +1,243 @@
1
+ # Licensed under GPLv3
2
+ """Convert StringLiterals to useable positional and keyword arguments."""
3
+
4
+ # TODO __post_init__ / convert for LPSA-Objects
5
+ # TODO TEST with literal Eval to c if and how quotes are needed.
6
+ # TODO Extract extractStringParameters method & Implement __iter__
7
+ # TODO Refactor
8
+ # class LiteralParameterString -> Raw String holder; Needed?
9
+ # class LiteralParameterArgument -> Baseclass with inherit method convert (literaleval) + toDict for Keyword
10
+ # class LiteralPositionalArgument -> Holds only value
11
+ # Class LiteralKeyWordArgument -> Holds key value
12
+
13
+ from typing import Optional
14
+ from typing import LiteralString
15
+ from typing import Any
16
+ from string import ascii_letters
17
+ from dataclasses import dataclass,field
18
+ from logging import getLogger
19
+
20
+ log = getLogger(__name__)
21
+
22
+ ## Parameter Types
23
+ class PMType:...
24
+ class Postional(PMType):...
25
+ class Keyword(PMType):...
26
+
27
+ ## BaseClasses
28
+ class PMSObjectType:...
29
+ class PMSPrimitive(PMSObjectType):...
30
+ class PMSContainer(PMSObjectType):...
31
+ # Container Defined Classes
32
+ class PMSString(PMSContainer):...
33
+ class PMSList(PMSContainer):
34
+ openingchars = "[";
35
+ closingchar = "]"
36
+ class PMSSetOrDict(PMSContainer):
37
+ openingchars = "{";
38
+ closingchar = "}"
39
+ class PMSTuple(PMSContainer):
40
+ openingchars = "(";
41
+ closingchar = ")"
42
+
43
+ CONTAINERINDICATORS = {
44
+ "(":PMSTuple,
45
+ "{":PMSSetOrDict,
46
+ "[":PMSList,
47
+ "'":PMSString,
48
+ '"':PMSString
49
+ }
50
+
51
+ @dataclass
52
+ class LiteralParameterStringArgument:
53
+ """ A single stringified parameter. Reffered to as PMSA"""
54
+ value:str = field(init=False)
55
+ key:Optional[str]= field(init=False)
56
+
57
+ parameterType:PMType= field(init=False)
58
+ realValue:Any = field(init=False)
59
+ realType:type[Any] = field(init=False)
60
+
61
+ def __repr__(self):
62
+ return f"<LiteralParameterArgument: '{self.value}'>"
63
+
64
+ class LiteralParameterExtractor():
65
+
66
+
67
+
68
+ def __init__(self, parameterliteral:str):
69
+ self.EXTRACTIONMETHODS = {
70
+ PMSString:self._extract_pms_string,
71
+ PMSPrimitive: self._extract_primitive
72
+ }
73
+
74
+ self.org_ParameterString = LiteralString(parameterliteral) # Creating Read Only copy
75
+ self.tmp_ParameterString = str(parameterliteral) # Creating copy objcet / different memory
76
+ self.parameterstring_Arguments:list[LiteralParameterStringArgument] = []
77
+ self.index = 0
78
+
79
+ def extractStringParameters(self) -> list[LiteralParameterStringArgument]:
80
+ """ Keep extracting till string is finished """
81
+ log.debug("\n[Start extracting parameters!]")
82
+ while True:
83
+ try:
84
+ _pmsa, _rests = self._process_spm()
85
+ self.parameterstring_Arguments.append(_pmsa)
86
+ if _rests is None: break
87
+ continue
88
+ except Exception as e: # Temporary Solution
89
+ raise RuntimeError(f"Unkown Error occured: {e}")
90
+ log.debug("\n[Finished extracting...]\n")
91
+ return self.parameterstring_Arguments
92
+
93
+ def _process_spm(self)-> tuple[str, str|None]:
94
+ """ Extract upcomming parameter """
95
+ _tmps:str
96
+ _pmType:PMType
97
+ _pms_type:PMSObjectType
98
+ _pmsao=LiteralParameterStringArgument()
99
+
100
+ log.debug("\nProcess Next...\n")
101
+ _tmps = self.tmp_ParameterString.strip() # Remove Leading Whitespaces if any
102
+
103
+ # Refactor to is Keyword
104
+ _pmType = self._get_PM_Type(_tmps) # Check if PM is keyword by checking first char
105
+ log.debug("\tGot ParameterType {}".format(_pmType.__name__))
106
+
107
+ # Keyword Extraction
108
+ if _pmType is Keyword:
109
+ keyword, _tmps = self._extract_keyword(_tmps)
110
+ _pmsao.key(keyword)
111
+ log.debug("\tGot Keyword {}".format(keyword))
112
+
113
+ # Value Extraction
114
+ if _tmps[0] in CONTAINERINDICATORS:
115
+ _containerT = CONTAINERINDICATORS.get(_tmps[0])
116
+ _extract_method = self.EXTRACTIONMETHODS.get(_containerT,default=self._extract_pms_iterable)
117
+ else:
118
+ _extract_method = self.EXTRACTIONMETHODS.get(PMSPrimitive)
119
+ _pms , _tmps = _extract_method(_tmps)
120
+ _pmsao.value=_pms
121
+ log.debug("\nExtracted object: {}".format(_pmsao))
122
+
123
+ self.tmp_ParameterString = _tmps
124
+ return _pms, _tmps
125
+
126
+ def _extract_keyword(self, _tmps:str) -> tuple[str, str]:
127
+ """Returns PMkeyword as string and rest of SPM"""
128
+ _ = _tmps.split("=",maxsplit=1)
129
+ _keyword = _[0]
130
+ _rests = _[1]
131
+ return _keyword,_rests
132
+
133
+ def _extract_container(self, _tmps:str) -> tuple[str, str|None]:
134
+ """First Detimer type of container i.E. list,dict,etc... , then extracts it.
135
+ Returns Primitive StringParameterArgument and RestString """
136
+
137
+ # Maybe Refactor to if starts with...parse string if not refactored
138
+ containerType = self._get_Container_Type_by_indicator(_tmps[0])
139
+ if containerType is PMSString:
140
+ log.debug("\t Parsing String container...")
141
+ _pmsa, rests = self._extract_pms_string(_tmps)
142
+ else:
143
+ log.debug("\tParsing other container...")
144
+ _pmsa, rests = self._extract_pms_iterable(_tmps)
145
+ return _pmsa, rests
146
+
147
+ @staticmethod
148
+ def _extract_pms_string(_temps:str) -> tuple[str, str|None]:
149
+ # Maybe Refactor to 1 function as nature is similiar
150
+ # Then all can go into the extract container func.
151
+
152
+ cc = _temps[0] # Container Indicator
153
+ i:int = 1 # Index
154
+ log.debug("\tParsing string with {} as indicator...".format(cc))
155
+ while True:
156
+ # Check if container if closed and not escaped
157
+ if _temps[i] == cc and _temps[i-1] != "\\":
158
+ break
159
+ i+=1
160
+ continue
161
+ # Check if Container is empty
162
+ if i == 1:
163
+ _pmsa = "" # Otherwise returns empty string as we cant slice 1:1
164
+ try:
165
+ _rests = _temps[3:].split(",",maxsplit=1)[1]
166
+ except IndexError:
167
+ _rests = None
168
+ else:
169
+ _pmsa = _temps[1:i]
170
+ try:
171
+ _rests = _temps[i+1:].split(",",maxsplit=1)[1]
172
+ except IndexError:
173
+ _rests = None
174
+
175
+ return _pmsa, _rests
176
+
177
+ @staticmethod
178
+ def _extract_pms_iterable(_temps:str) -> tuple[str, str|None]:
179
+ _OC = _temps[0]
180
+ _CC = CONTAINERINDICATORS.get(_OC).closingchar
181
+
182
+ oc_count = 0 # Amount of opened subcontainers
183
+
184
+ substringC:str = None # Substring opening indicator
185
+ issubstring = False # Check if inside a string value
186
+
187
+ i = 0 # Index
188
+ while True:
189
+ i += 1
190
+ _ =_temps[i]
191
+ # if Container Closing check if last
192
+ if _ == _CC and not issubstring:
193
+ if oc_count == 0:
194
+ break
195
+ oc_count -= 1
196
+ continue
197
+
198
+ # Subcontainer detected
199
+ if _ == _OC and not issubstring:
200
+ oc_count+=1
201
+ continue
202
+
203
+ # Switch Substring Mode if not escaped
204
+ if _ in ("'", '"'):
205
+ if not issubstring:
206
+ substringC = _
207
+ issubstring = True
208
+ continue
209
+ if _temps[i-1] != "\\":
210
+ issubstring = False
211
+ continue
212
+ continue
213
+
214
+ _res = _temps[0:i+1]
215
+ _rests = _temps[i+2:].strip()
216
+ if len(_rests)<=1:
217
+ _rests = None
218
+ return _res, _rests
219
+
220
+ # BUG Fix None split
221
+ @staticmethod
222
+ def _extract_primitive(_tmps:str) -> tuple[str, str|None]:
223
+ """Extracts & Returns Primitive StringParameterArgument and RestString """
224
+ # TODO ERROR CATCHING
225
+ rests = _tmps.split(",", maxsplit=1)
226
+ _pmsa = rests[0]
227
+ if len(rests) <= 1:
228
+ return _pmsa, None
229
+ return _pmsa, rests[1]
230
+
231
+ @staticmethod
232
+ def _get_PM_Type(temps:str)->PMType:
233
+ """Checks if Parameter is Postional or Keyword."""
234
+ if temps.startswith(ascii_letters) and not temps.startswith(("None","True","False")):
235
+ return Keyword
236
+ return Postional
237
+
238
+ @staticmethod
239
+ def _get_Container_Type_by_indicator(indicator) -> type[PMSContainer]:
240
+ return CONTAINERINDICATORS.get(indicator)
241
+
242
+ if __name__ == '__main__':
243
+ pass
@@ -0,0 +1,77 @@
1
+ Metadata-Version: 2.3
2
+ Name: parameter-toolkit
3
+ Version: 0.0.1
4
+ Summary: A python library to interact with inspects Parameters objects.
5
+ Requires-Python: >=3.14
6
+ Description-Content-Type: text/markdown
7
+
8
+ # Parameter-Toolkit
9
+ ## A small python library to interact with and convert parameters.
10
+
11
+ Working on a different project where I want to receive optional commands in a human readable manner I dug a bit deeper
12
+ into the world of inspect, functools and astlib and discovered there is no low-level way to interact with python-signatures.
13
+
14
+ This library meant to overcome this bt providing Utitlites to access Parameters of Signatures in a more primitive manner.
15
+
16
+ ### Features:
17
+
18
+ * Get all `Parameter` objects from a func with inspect shorthand (Either as list or generator).
19
+ * Convert function `parameters` to a `dict` of python BuiltIns.
20
+ * Convert a parameter `string literal` into usable `positional` and `keyword arguments`. (Known Bugs with Iterable KeywordsArgs!)
21
+ * Map a parameter `string literal` directly to a function and get a `partial` object.
22
+
23
+ * **NO external dependencies**
24
+
25
+ **Get functions parameter info:**
26
+ ```python
27
+ def helloWorld(name_to_greet:str="World!"):
28
+ return f"Hello {name_to_greet}"
29
+
30
+ func_parameters_info = getFuncParameterAsDict(helloWord)
31
+ for p in func_parameters_info:
32
+ print(p)
33
+
34
+ '''
35
+ {"name":"name_to_greet", "type":"str", "default": "World!", "kind"}
36
+ '''
37
+ ```
38
+
39
+ **Convert a single parameter to dict:**
40
+ ```python
41
+ parameter_info = parameterToDict(myExtractedParemeter)
42
+ print(parameter_info)
43
+
44
+ '''
45
+ {"name":"name_to_greet", "type":"str", "default": "World!", "kind"}
46
+ '''
47
+ ```
48
+
49
+ **Convert a stringified parameters to positional and keyword args:**
50
+ ```python
51
+ myParameterString = '"Please write me in Upper", True, repeat=5"'
52
+ args, kwargs = parameterFromLiteral(myParameterString)
53
+ # args ['Please write me in Upper', True]
54
+ # kwargs {"repeat":5}
55
+ ```
56
+
57
+ **Map a literal directly to a func:**
58
+ ```python
59
+ ...
60
+ mypartial = mapFuncFromLiteral(upperprinter, myParameterString)
61
+ mypartial()
62
+ ```
63
+
64
+ ### Known Bugs
65
+ * StrinParameterConversion:
66
+ * Converting a nested string iterable of the same type will raise a syntax error and is not supported for now.
67
+ * Upper behavior will cause an assertion error when passind a dict containing '{' within key or value str. (WIP)
68
+ * Crash if kwarg is a iterable (further testing requiered.)
69
+ * String Converstion will fail if newline char is not escaped.
70
+
71
+ * No more bugs known (yet!)
72
+
73
+ Happy Hacking!
74
+
75
+ ### Licensing
76
+ All of the provided code as well as readme and other releated files to the library are published under:
77
+ **GPLv3**
@@ -0,0 +1,6 @@
1
+ parameter_toolkit/__init__.py,sha256=L1ac2llieWt3b2293YSLNkcySaNMbE6vbEjgH8-4zms,1959
2
+ parameter_toolkit/parameter_tools.py,sha256=932TA7NlSA8IbLuYr4v9MdhZmL-4BCwMdt_fI6PVp7k,4050
3
+ parameter_toolkit/stringparameter_tools.py,sha256=Cy_yFkVJMgw1fTcPKhxXeW2pRTfEaj-t8HbjFZtCrBw,8398
4
+ parameter_toolkit-0.0.1.dist-info/WHEEL,sha256=q5IF0q2xCp3ktUFRCVWsQLjl2ChNlWXBJtnI1LCGdJ8,80
5
+ parameter_toolkit-0.0.1.dist-info/METADATA,sha256=PoLlLSc9RmnJyIuPuOwcyTX1DwgXDv4YvxDg5plifEk,2670
6
+ parameter_toolkit-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.8
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any