preparse 0.0.2__tar.gz → 0.0.4__tar.gz

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.
Files changed (35) hide show
  1. {preparse-0.0.2/src/preparse.egg-info → preparse-0.0.4}/PKG-INFO +2 -1
  2. {preparse-0.0.2 → preparse-0.0.4}/pyproject.toml +2 -1
  3. {preparse-0.0.2 → preparse-0.0.4}/src/preparse/core/__init__.py +106 -83
  4. preparse-0.0.4/src/preparse/tests/test_extra.py +160213 -0
  5. preparse-0.0.4/src/preparse/tests/test_extra_no_permutation.py +160213 -0
  6. preparse-0.0.4/src/preparse/tests/test_extra_posix.py +159704 -0
  7. preparse-0.0.4/src/preparse/tests/test_long_only.py +48826 -0
  8. preparse-0.0.4/src/preparse/tests/test_long_only_no_permutation.py +48826 -0
  9. preparse-0.0.4/src/preparse/tests/test_long_only_posix.py +48805 -0
  10. preparse-0.0.4/src/preparse/tests/test_parse.py +48715 -0
  11. preparse-0.0.4/src/preparse/tests/test_parse_no_permutation.py +48715 -0
  12. preparse-0.0.4/src/preparse/tests/test_parse_posix.py +48540 -0
  13. preparse-0.0.4/src/preparse/tests/test_warn.py +42 -0
  14. preparse-0.0.4/src/preparse/tests/test_warn_9001.py +80 -0
  15. preparse-0.0.4/src/preparse/tests/test_warn_9002.py +104 -0
  16. preparse-0.0.4/src/preparse/tests/test_warn_9003.py +21 -0
  17. preparse-0.0.4/src/preparse/tests/test_warn_9004.py +68 -0
  18. preparse-0.0.4/src/preparse/tests/test_warn_9005.py +28 -0
  19. preparse-0.0.4/src/preparse/tests/test_warn_9006.py +76 -0
  20. preparse-0.0.4/src/preparse/tests/test_warn_9007.py +122 -0
  21. preparse-0.0.4/src/preparse/tests/test_warn_even_more.py +80 -0
  22. preparse-0.0.4/src/preparse/tests/test_warn_more.py +68 -0
  23. {preparse-0.0.2 → preparse-0.0.4/src/preparse.egg-info}/PKG-INFO +2 -1
  24. preparse-0.0.4/src/preparse.egg-info/SOURCES.txt +32 -0
  25. {preparse-0.0.2 → preparse-0.0.4}/src/preparse.egg-info/requires.txt +1 -0
  26. preparse-0.0.2/src/preparse/tests/test_1984.py +0 -10
  27. preparse-0.0.2/src/preparse.egg-info/SOURCES.txt +0 -14
  28. {preparse-0.0.2 → preparse-0.0.4}/LICENSE.txt +0 -0
  29. {preparse-0.0.2 → preparse-0.0.4}/MANIFEST.in +0 -0
  30. {preparse-0.0.2 → preparse-0.0.4}/README.rst +0 -0
  31. {preparse-0.0.2 → preparse-0.0.4}/setup.cfg +0 -0
  32. {preparse-0.0.2 → preparse-0.0.4}/src/preparse/__init__.py +0 -0
  33. {preparse-0.0.2 → preparse-0.0.4}/src/preparse/tests/__init__.py +0 -0
  34. {preparse-0.0.2 → preparse-0.0.4}/src/preparse.egg-info/dependency_links.txt +0 -0
  35. {preparse-0.0.2 → preparse-0.0.4}/src/preparse.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: preparse
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: preparse arguments
5
5
  Author-email: Johannes <johannes-programming@mailfence.com>
6
6
  License: The MIT License (MIT)
@@ -40,6 +40,7 @@ License-File: LICENSE.txt
40
40
  Requires-Dist: click>=8.1.7
41
41
  Requires-Dist: datarepr>=0.0.4
42
42
  Requires-Dist: makeprop>=0.1.0
43
+ Requires-Dist: tofunc>=1.0
43
44
 
44
45
  ========
45
46
  preparse
@@ -19,13 +19,14 @@ dependencies = [
19
19
  "click>=8.1.7",
20
20
  "datarepr>=0.0.4",
21
21
  "makeprop>=0.1.0",
22
+ "tofunc>=1.0",
22
23
  ]
23
24
  description = "preparse arguments"
24
25
  keywords = []
25
26
  name = "preparse"
26
27
  readme = "README.rst"
27
28
  requires-python = ">=3.8"
28
- version = "0.0.2"
29
+ version = "0.0.4"
29
30
 
30
31
  [project.license]
31
32
  file = "LICENSE.txt"
@@ -1,6 +1,7 @@
1
1
  import dataclasses
2
2
  import enum
3
3
  import functools
4
+ import inspect
4
5
  import operator
5
6
  import os
6
7
  import sys
@@ -11,8 +12,9 @@ from typing import *
11
12
  import click as cl
12
13
  from datarepr import datarepr
13
14
  from makeprop import makeprop
15
+ from tofunc import tofunc
14
16
 
15
- __all__ = ["PreParser"]
17
+ __all__ = ["Abbrev", "Nargs", "PreParser"]
16
18
 
17
19
 
18
20
  class Abbrev(enum.IntEnum):
@@ -36,7 +38,7 @@ class PreParser:
36
38
  abbrev: Any = Abbrev.COMPLETE,
37
39
  permutate: Any = True,
38
40
  posix: Any = "infer",
39
- ):
41
+ ) -> None:
40
42
  self._optdict = dict()
41
43
  self.optdict = optdict
42
44
  self.prog = prog
@@ -45,16 +47,53 @@ class PreParser:
45
47
  self.posix = posix
46
48
 
47
49
  def __repr__(self) -> str:
50
+ """Return repr(self)."""
48
51
  return datarepr(type(self).__name__, **self.todict())
49
52
 
50
53
  @makeprop()
51
- def abbrev(self, value):
54
+ def abbrev(self, value: SupportsInt) -> Abbrev:
55
+ """Property that decides how to handle abbreviations."""
52
56
  return Abbrev(value)
53
57
 
54
- def click(self, *, cmd=True, ctx=True):
55
- return Click(parser=self, cmd=cmd, ctx=ctx)
56
-
57
- def clickCommand(self, cmd: cl.Command):
58
+ @tofunc
59
+ @dataclasses.dataclass
60
+ class click:
61
+ """Return a decorator that infuses the current instance into parse_args."""
62
+
63
+ _self: Any
64
+ cmd: Any = True
65
+ ctx: Any = True
66
+
67
+ @functools.singledispatchmethod
68
+ def __call__(self, target):
69
+ target.parse_args = self(target.parse_args)
70
+ return target
71
+
72
+ @__call__.register
73
+ def _(self, target: types.FunctionType):
74
+ @functools.wraps(target)
75
+ def ans(cmd, ctx, args):
76
+ p = self._self.copy()
77
+ if self.cmd:
78
+ p.clickCommand(cmd)
79
+ if self.ctx:
80
+ p.clickContext(ctx)
81
+ return target(cmd, ctx, p.parse_args(args))
82
+
83
+ return ans
84
+
85
+ @__call__.register
86
+ def _(self, target: types.MethodType):
87
+ func = self(target.__func__)
88
+ ans = types.MethodType(func, target.__self__)
89
+ return ans
90
+
91
+ click.__signature__ = inspect.signature(click).replace(
92
+ return_annotation=click.__wrapped__
93
+ )
94
+
95
+ def clickCommand(self, cmd: cl.Command) -> None:
96
+ """Reflect a click.Command object."""
58
97
  optdict = dict()
59
98
  for p in cmd.params:
60
99
  if not isinstance(p, cl.Option):
@@ -70,14 +109,17 @@ class PreParser:
70
109
  self.optdict.clear()
71
110
  self.optdict.update(optdict)
72
111
 
73
- def clickContext(self, ctx: cl.Context):
112
+ def clickContext(self, ctx: cl.Context) -> None:
113
+ """Reflect a click.Context object."""
74
114
  self.prog = ctx.info_name
75
115
 
76
- def copy(self):
116
+ def copy(self) -> Self:
117
+ """Return a copy."""
77
118
  return type(self)(**self.todict())
78
119
 
79
120
  @makeprop()
80
- def optdict(self, value):
121
+ def optdict(self, value: Any) -> dict:
122
+ """Dictionary of options."""
81
123
  if value is None:
82
124
  self._optdict.clear()
83
125
  return self._optdict
@@ -86,32 +128,42 @@ class PreParser:
86
128
  self._optdict.update(value)
87
129
  return self._optdict
88
130
 
89
- def parse_args(self, args: Optional[Iterable] = None) -> List[str]:
131
+ def parse_args(
132
+ self,
133
+ args: Optional[Iterable] = None,
134
+ ) -> List[str]:
135
+ """Parse args."""
90
136
  if args is None:
91
137
  args = sys.argv[1:]
92
- return _Parsing(
138
+ return Parsing(
93
139
  parser=self.copy(),
94
- args=list(args),
140
+ args=[str(a) for a in args],
95
141
  ).ans
96
142
 
97
143
  @makeprop()
98
- def permutate(self, value):
144
+ def permutate(self, value: Any) -> bool:
145
+ """Property that decides if the arguments will be permutated."""
99
146
  return bool(value)
100
147
 
101
148
  @makeprop()
102
- def posix(self, value):
149
+ def posix(self, value: Any) -> bool:
150
+ """Property that decides if posix parsing is used, \
151
+ i.e. a positional argument causes all the arguments after it \
152
+ to be also interpreted as positional."""
103
153
  if value == "infer":
104
154
  value = os.environ.get("POSIXLY_CORRECT")
105
155
  value = bool(value)
106
156
  return value
107
157
 
108
158
  @makeprop()
109
- def prog(self, value):
159
+ def prog(self, value: Any) -> str:
160
+ """Property that represents the name of the program."""
110
161
  if value is None:
111
162
  value = os.path.basename(sys.argv[0])
112
163
  return str(value)
113
164
 
114
165
  def todict(self) -> dict:
166
+ """Return a dict representing the current instance."""
115
167
  return dict(
116
168
  optdict=self.optdict,
117
169
  prog=self.prog,
@@ -120,76 +172,40 @@ class PreParser:
120
172
  posix=self.posix,
121
173
  )
122
174
 
123
- def warn(self, message):
175
+ def warn(self, message: Any) -> None:
176
+ """Warn about something."""
124
177
  warnings.warn("%s: %s" % (self.prog, message))
125
178
 
126
- def warnAboutUnrecognizedOption(self, option):
179
+ def warnAboutUnrecognizedOption(self, option: Any) -> None:
180
+ """Warn about an unrecognized option."""
127
181
  self.warn("unrecognized option %r" % option)
128
182
 
129
- def warnAboutInvalidOption(self, option):
183
+ def warnAboutInvalidOption(self, option: Any) -> None:
184
+ """Warn about an invalid option."""
130
185
  self.warn("invalid option -- %r" % option)
131
186
 
132
- def warnAboutAmbigousOption(self, option, possibilities):
187
+ def warnAboutAmbiguousOption(self, option: Any, possibilities: Iterable) -> None:
188
+ """Warn about an ambiguous option."""
133
189
  msg = "option %r is ambiguous; possibilities:" % option
134
190
  for x in possibilities:
135
191
  msg += " %r" % x
136
192
  self.warn(msg)
137
193
 
138
- def warnAboutNotAllowedArgument(self, option):
194
+ def warnAboutUnallowedArgument(self, option: Any) -> None:
195
+ """Warn about an unallowed argument."""
139
196
  self.warn("option %r doesn't allow an argument" % option)
140
197
 
141
- def warnAboutRequiredArgument(self, option):
198
+ def warnAboutRequiredArgument(self, option: Any) -> None:
199
+ """Warn about a required argument."""
142
200
  self.warn("option requires an argument -- %r" % option)
143
201
 
144
202
 
145
203
  @dataclasses.dataclass
146
- class Click:
147
- parser: Any
148
- cmd: Any
149
- ctx: Any
150
-
151
- def __call__(self, target: Any):
152
- if isinstance(target, types.FunctionType):
153
- return self._f(target)
154
- elif isinstance(target, types.MethodType):
155
- return self._m(target)
156
- elif isinstance(target, type):
157
- return self._t(target)
158
- else:
159
- return self._o(target)
160
-
161
- def _f(self, target):
162
- @functools.wraps(target)
163
- def ans(cmd, ctx, args):
164
- p = self.parser.copy()
165
- if self.cmd:
166
- p.clickCommand(cmd)
167
- if self.ctx:
168
- p.clickContext(ctx)
169
- return target(cmd, ctx, p.parse_args(args))
170
-
171
- return ans
172
-
173
- def _m(self, target):
174
- func = self._f(target.__func__)
175
- ans = types.MethodType(func, target.__self__)
176
- return ans
177
-
178
- def _t(self, target):
179
- target.parse_args = self._f(target.parse_args)
180
- return target
181
-
182
- def _o(self, target):
183
- target.parse_args = self._m(target.parse_args)
184
- return target
185
-
186
-
187
- @dataclasses.dataclass
188
- class _Parsing:
204
+ class Parsing:
189
205
  parser: PreParser
190
206
  args: list[str]
191
207
 
192
- def __post_init__(self):
208
+ def __post_init__(self) -> None:
193
209
  self.ans = list()
194
210
  self.spec = list()
195
211
  optn = 0
@@ -197,10 +213,14 @@ class _Parsing:
197
213
  optn = self.tick(optn)
198
214
  if optn == 1:
199
215
  self.parser.warnAboutRequiredArgument(self.ans[-1])
216
+ self.dumpspec()
217
+
218
+ def dumpspec(self):
200
219
  self.ans += self.spec
220
+ self.spec.clear()
201
221
 
202
222
  @functools.cached_property
203
- def islongonly(self):
223
+ def islongonly(self) -> bool:
204
224
  for k in self.optdict.keys():
205
225
  if len(k) < 3:
206
226
  continue
@@ -212,7 +232,7 @@ class _Parsing:
212
232
  return False
213
233
 
214
234
  @functools.cached_property
215
- def optdict(self):
235
+ def optdict(self) -> Dict[str, Nargs]:
216
236
  ans = dict()
217
237
  for k, v in self.parser.optdict.items():
218
238
  ans[str(k)] = Nargs(v)
@@ -242,21 +262,13 @@ class _Parsing:
242
262
  return "break"
243
263
  elif arg.startswith("-") and arg != "-":
244
264
  if arg.startswith("--") or self.islongonly:
245
- return self.tick_long(arg)
265
+ return self.tick_opt_long(arg)
246
266
  else:
247
- return self.tick_short(arg)
267
+ return self.tick_opt_short(arg)
248
268
  else:
249
- if self.parser.posix:
250
- self.spec.append(arg)
251
- return "break"
252
- elif self.parser.permutate:
253
- self.spec.append(arg)
254
- return 0
255
- else:
256
- self.ans.append(arg)
257
- return 0
269
+ return self.tick_pos(arg)
258
270
 
259
- def tick_long(self, arg: str):
271
+ def tick_opt_long(self, arg: str):
260
272
  try:
261
273
  i = arg.index("=")
262
274
  except ValueError:
@@ -268,7 +280,7 @@ class _Parsing:
268
280
  self.ans.append(arg)
269
281
  return 0
270
282
  if len(possibilities) > 1:
271
- self.parser.warnAboutAmbigousOption(arg, possibilities)
283
+ self.parser.warnAboutAmbiguousOption(arg, possibilities)
272
284
  self.ans.append(arg)
273
285
  return 0
274
286
  opt = possibilities[0]
@@ -278,12 +290,12 @@ class _Parsing:
278
290
  self.ans.append(arg)
279
291
  if "=" in arg:
280
292
  if self.optdict[opt] == 0:
281
- self.parser.warnAboutNotAllowedArgument(opt)
293
+ self.parser.warnAboutUnallowedArgument(opt)
282
294
  return 0
283
295
  else:
284
296
  return self.optdict[opt]
285
297
 
286
- def tick_short(self, arg: str):
298
+ def tick_opt_short(self, arg: str):
287
299
  self.ans.append(arg)
288
300
  for i in range(1 - len(arg), 0):
289
301
  optn = self.optdict.get("-" + arg[i])
@@ -295,3 +307,14 @@ class _Parsing:
295
307
  if i == -1 and optn == 1:
296
308
  return 1
297
309
  return 0
310
+
311
+ def tick_pos(self, arg: str):
312
+ if self.parser.posix:
313
+ self.spec.append(arg)
314
+ return "break"
315
+ elif self.parser.permutate:
316
+ self.spec.append(arg)
317
+ return 0
318
+ else:
319
+ self.ans.append(arg)
320
+ return 0