optfunc2 0.2.0__tar.gz → 0.2.2__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.
@@ -0,0 +1,201 @@
1
+ Metadata-Version: 2.3
2
+ Name: optfunc2
3
+ Version: 0.2.2
4
+ Summary: Generate command line options and help/tips from function automatically.
5
+ License: PyPA
6
+ Author: bajeer
7
+ Author-email: z-bajeer@yeah.net
8
+ Requires-Python: >=3.8,<4.0
9
+ Classifier: License :: Other/Proprietary License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Dist: docstring-parser (>=0.16,<0.17)
18
+ Requires-Dist: prettytable (==3.11.0)
19
+ Project-URL: Bug Reports, https://gitee.com/z-bajer/autocall/issues
20
+ Project-URL: Homepage, https://gitee.com/z-bajer/autocall
21
+ Project-URL: Source, https://gitee.com/z-bajer/autocall
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Call function directly in cmd line
25
+
26
+ ### Features
27
+ 1. Allow user call functions directly in command line.
28
+ 2. Generate help tips automatically.
29
+ 3. Add default called functions if not function was specific.
30
+
31
+ ### Notice
32
+ 1. It's better to add argument type for each autocall functions.
33
+ 2. Function with @optfunc_default has @optfunc implicitly.
34
+ 3. Not support two type of variadic arguments.
35
+
36
+ ### ChangeLog
37
+ ### 0.2.2 (2025-2-16)
38
+ 1. Support single argument in bool type.
39
+ 2. Don't need user to pass globals() in cmdline_start().
40
+ 3. Support pytest to run test.
41
+ 4. Support omitting called function name who is default.
42
+ 5. pyproject.toml format change for poetry version 2.1.0.
43
+
44
+ #### 0.2.1 (2025-2-14)
45
+ 1. Fix installing dependencies automatically.
46
+ 2. Add function 'called_directly' used to check if the function is called as entry point.
47
+ This function can be used in function development.
48
+
49
+ #### 0.1.2 (2023-05-06)
50
+ 1. Add support for default called functions.
51
+ 2. Fix README.md.
52
+ 3. Add ChangeLog in README.md.
53
+
54
+ ### Code example1 -- calculator
55
+ ``` python
56
+ from optfunc2 import cmdline, cmdline_default, cmdline_start
57
+
58
+ @cmdline_default
59
+ def add(a: float, b: float):
60
+ """add two numbers
61
+
62
+ Args:
63
+ a (float): The First number
64
+ b (float): The Second number
65
+ """
66
+ print(f"{a} + {b} = {a + b}")
67
+
68
+ @cmdline
69
+ def multiply(x: int, y: int = 5):
70
+ """multiply two numbers. The second number is optional.
71
+
72
+ Args:
73
+ x (int): The First number
74
+ y (int, optional): The Second number. Defaults to 5.
75
+ """
76
+ print(f"{x} × {y} = {x * y}")
77
+
78
+ @cmdline
79
+ def stats(numbers: list):
80
+ """statistics of numbers in list
81
+
82
+ Args:
83
+ numbers (list): Target List.
84
+ """
85
+ print(f"sum: {sum(numbers)}")
86
+ print(f"average: {sum(numbers)/len(numbers):.2f}")
87
+
88
+ if __name__ == "__main__":
89
+ cmdline_start(header_doc="✨ calc CLI", has_abbrev=True)
90
+ ```
91
+
92
+ ### Generate help tips automatically
93
+ ``` bash
94
+ ~/optfunc2$ python src/example_calc.py help
95
+ Usage: src/example_calc.py [command] [<args>|--help]
96
+
97
+ ✨ calc CLI
98
+
99
+ commands:
100
+ add [default] add two numbers
101
+ multiply multiply two numbers. The second number is optional.
102
+ stats statistics of numbers in list
103
+
104
+ ~/optfunc2$ python src/example_calc.py add -h
105
+ Usage: src/example_calc.py add [OPTIONS]
106
+
107
+ add two numbers
108
+
109
+
110
+ Arguments:
111
+ +-----+--------+-------+---------+-------------------+
112
+ | Opt | Abbrev | Type | Default | Desc |
113
+ +-----+--------+-------+---------+-------------------+
114
+ | --a | -a | float | | The First number |
115
+ | --b | -b | float | | The Second number |
116
+ +-----+--------+-------+---------+-------------------+
117
+
118
+
119
+ ~/optfunc2$ python src/example_calc.py stats -h
120
+ Usage: src/example_calc.py stats [OPTIONS]
121
+
122
+ statistics of numbers in list
123
+
124
+
125
+ Arguments:
126
+ +-----------+--------+------+---------+--------------+
127
+ | Opt | Abbrev | Type | Default | Desc |
128
+ +-----------+--------+------+---------+--------------+
129
+ | --numbers | -n | list | | Target List. |
130
+ +-----------+--------+------+---------+--------------+
131
+ ```
132
+
133
+ ### Usage
134
+ ``` bash
135
+ ~/optfunc2$ python src/example_calc.py add -a 2.3 -b 3
136
+ 2.3 + 3.0 = 5.3
137
+ ~/optfunc2$ python src/example_calc.py -a 2.3 -b 3
138
+ 2.3 + 3.0 = 5.3
139
+ ~/optfunc2$ python src/example_calc.py multiply -x 3
140
+ 3 × 5 = 15
141
+ ~/optfunc2$ python src/example_calc.py stats --numbers '[1, 2, 3, 4, 5]'
142
+ sum: 15
143
+ average: 3.00
144
+ ```
145
+
146
+ ### Code example2 -- list files
147
+ ``` python
148
+ from optfunc2 import cmdline, cmdline_default, cmdline_start
149
+ import os
150
+
151
+ @cmdline_default
152
+ def list_files(directory: str = ".", show_size: bool = False):
153
+ """List files in a directory.
154
+
155
+ Args:
156
+ directory (str, optional): Target directory. Defaults to ".".
157
+ show_size (bool, optional): Whether to show size of file. Defaults to False.
158
+ """
159
+ for f in os.listdir(directory):
160
+ path = os.path.join(directory, f)
161
+ if show_size and os.path.isfile(path):
162
+ print(f"{f} ({os.path.getsize(path)} bytes)")
163
+ else:
164
+ print(f)
165
+
166
+ if __name__ == "__main__":
167
+ cmdline_start(header_doc="📁 file manager", has_abbrev=True)
168
+ ```
169
+
170
+ ### Usage
171
+ ``` bash
172
+ $ python src/example_file_operator.py -h
173
+ Usage: src/example_file_operator.py [command] [<args>|--help]
174
+
175
+ 📁 file manager
176
+
177
+ commands:
178
+ list_files [default] List files in a directory.
179
+
180
+ $ python src/example_file_operator.py list_files -h
181
+ Usage: src/example_file_operator.py list_files [OPTIONS]
182
+
183
+ List files in a directory.
184
+
185
+
186
+ Arguments:
187
+ +-------------+--------+------+---------+--------------------------------------------------+
188
+ | Opt | Abbrev | Type | Default | Desc |
189
+ +-------------+--------+------+---------+--------------------------------------------------+
190
+ | --directory | -d | str | '.' | Target directory. Defaults to ".". |
191
+ | --show_size | -s | bool | False | Whether to show size of file. Defaults to False. |
192
+ +-------------+--------+------+---------+--------------------------------------------------+
193
+ $ python src/example_file_operator.py
194
+ LICENSE.txt
195
+ .gitignore
196
+ pyproject.toml
197
+ $ python src/example_file_operator.py -s
198
+ LICENSE.txt (1081 bytes)
199
+ .gitignore (132 bytes)
200
+ pyproject.toml (719 bytes)
201
+ ```
@@ -0,0 +1,178 @@
1
+ # Call function directly in cmd line
2
+
3
+ ### Features
4
+ 1. Allow user call functions directly in command line.
5
+ 2. Generate help tips automatically.
6
+ 3. Add default called functions if not function was specific.
7
+
8
+ ### Notice
9
+ 1. It's better to add argument type for each autocall functions.
10
+ 2. Function with @optfunc_default has @optfunc implicitly.
11
+ 3. Not support two type of variadic arguments.
12
+
13
+ ### ChangeLog
14
+ ### 0.2.2 (2025-2-16)
15
+ 1. Support single argument in bool type.
16
+ 2. Don't need user to pass globals() in cmdline_start().
17
+ 3. Support pytest to run test.
18
+ 4. Support omitting called function name who is default.
19
+ 5. pyproject.toml format change for poetry version 2.1.0.
20
+
21
+ #### 0.2.1 (2025-2-14)
22
+ 1. Fix installing dependencies automatically.
23
+ 2. Add function 'called_directly' used to check if the function is called as entry point.
24
+ This function can be used in function development.
25
+
26
+ #### 0.1.2 (2023-05-06)
27
+ 1. Add support for default called functions.
28
+ 2. Fix README.md.
29
+ 3. Add ChangeLog in README.md.
30
+
31
+ ### Code example1 -- calculator
32
+ ``` python
33
+ from optfunc2 import cmdline, cmdline_default, cmdline_start
34
+
35
+ @cmdline_default
36
+ def add(a: float, b: float):
37
+ """add two numbers
38
+
39
+ Args:
40
+ a (float): The First number
41
+ b (float): The Second number
42
+ """
43
+ print(f"{a} + {b} = {a + b}")
44
+
45
+ @cmdline
46
+ def multiply(x: int, y: int = 5):
47
+ """multiply two numbers. The second number is optional.
48
+
49
+ Args:
50
+ x (int): The First number
51
+ y (int, optional): The Second number. Defaults to 5.
52
+ """
53
+ print(f"{x} × {y} = {x * y}")
54
+
55
+ @cmdline
56
+ def stats(numbers: list):
57
+ """statistics of numbers in list
58
+
59
+ Args:
60
+ numbers (list): Target List.
61
+ """
62
+ print(f"sum: {sum(numbers)}")
63
+ print(f"average: {sum(numbers)/len(numbers):.2f}")
64
+
65
+ if __name__ == "__main__":
66
+ cmdline_start(header_doc="✨ calc CLI", has_abbrev=True)
67
+ ```
68
+
69
+ ### Generate help tips automatically
70
+ ``` bash
71
+ ~/optfunc2$ python src/example_calc.py help
72
+ Usage: src/example_calc.py [command] [<args>|--help]
73
+
74
+ ✨ calc CLI
75
+
76
+ commands:
77
+ add [default] add two numbers
78
+ multiply multiply two numbers. The second number is optional.
79
+ stats statistics of numbers in list
80
+
81
+ ~/optfunc2$ python src/example_calc.py add -h
82
+ Usage: src/example_calc.py add [OPTIONS]
83
+
84
+ add two numbers
85
+
86
+
87
+ Arguments:
88
+ +-----+--------+-------+---------+-------------------+
89
+ | Opt | Abbrev | Type | Default | Desc |
90
+ +-----+--------+-------+---------+-------------------+
91
+ | --a | -a | float | | The First number |
92
+ | --b | -b | float | | The Second number |
93
+ +-----+--------+-------+---------+-------------------+
94
+
95
+
96
+ ~/optfunc2$ python src/example_calc.py stats -h
97
+ Usage: src/example_calc.py stats [OPTIONS]
98
+
99
+ statistics of numbers in list
100
+
101
+
102
+ Arguments:
103
+ +-----------+--------+------+---------+--------------+
104
+ | Opt | Abbrev | Type | Default | Desc |
105
+ +-----------+--------+------+---------+--------------+
106
+ | --numbers | -n | list | | Target List. |
107
+ +-----------+--------+------+---------+--------------+
108
+ ```
109
+
110
+ ### Usage
111
+ ``` bash
112
+ ~/optfunc2$ python src/example_calc.py add -a 2.3 -b 3
113
+ 2.3 + 3.0 = 5.3
114
+ ~/optfunc2$ python src/example_calc.py -a 2.3 -b 3
115
+ 2.3 + 3.0 = 5.3
116
+ ~/optfunc2$ python src/example_calc.py multiply -x 3
117
+ 3 × 5 = 15
118
+ ~/optfunc2$ python src/example_calc.py stats --numbers '[1, 2, 3, 4, 5]'
119
+ sum: 15
120
+ average: 3.00
121
+ ```
122
+
123
+ ### Code example2 -- list files
124
+ ``` python
125
+ from optfunc2 import cmdline, cmdline_default, cmdline_start
126
+ import os
127
+
128
+ @cmdline_default
129
+ def list_files(directory: str = ".", show_size: bool = False):
130
+ """List files in a directory.
131
+
132
+ Args:
133
+ directory (str, optional): Target directory. Defaults to ".".
134
+ show_size (bool, optional): Whether to show size of file. Defaults to False.
135
+ """
136
+ for f in os.listdir(directory):
137
+ path = os.path.join(directory, f)
138
+ if show_size and os.path.isfile(path):
139
+ print(f"{f} ({os.path.getsize(path)} bytes)")
140
+ else:
141
+ print(f)
142
+
143
+ if __name__ == "__main__":
144
+ cmdline_start(header_doc="📁 file manager", has_abbrev=True)
145
+ ```
146
+
147
+ ### Usage
148
+ ``` bash
149
+ $ python src/example_file_operator.py -h
150
+ Usage: src/example_file_operator.py [command] [<args>|--help]
151
+
152
+ 📁 file manager
153
+
154
+ commands:
155
+ list_files [default] List files in a directory.
156
+
157
+ $ python src/example_file_operator.py list_files -h
158
+ Usage: src/example_file_operator.py list_files [OPTIONS]
159
+
160
+ List files in a directory.
161
+
162
+
163
+ Arguments:
164
+ +-------------+--------+------+---------+--------------------------------------------------+
165
+ | Opt | Abbrev | Type | Default | Desc |
166
+ +-------------+--------+------+---------+--------------------------------------------------+
167
+ | --directory | -d | str | '.' | Target directory. Defaults to ".". |
168
+ | --show_size | -s | bool | False | Whether to show size of file. Defaults to False. |
169
+ +-------------+--------+------+---------+--------------------------------------------------+
170
+ $ python src/example_file_operator.py
171
+ LICENSE.txt
172
+ .gitignore
173
+ pyproject.toml
174
+ $ python src/example_file_operator.py -s
175
+ LICENSE.txt (1081 bytes)
176
+ .gitignore (132 bytes)
177
+ pyproject.toml (719 bytes)
178
+ ```
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "optfunc2"
3
+ version = "0.2.2"
4
+ description = "Generate command line options and help/tips from function automatically."
5
+ authors = [
6
+ {name = "bajeer",email = "z-bajeer@yeah.net"}
7
+ ]
8
+ license = {text = "PyPA"}
9
+ readme = "README.md"
10
+ requires-python = ">=3.8,<4.0"
11
+ dependencies = [
12
+ "docstring-parser (>=0.16,<0.17)",
13
+ "prettytable (==3.11.0)"
14
+ ]
15
+
16
+
17
+ [build-system]
18
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
19
+ build-backend = "poetry.core.masonry.api"
20
+
21
+ [project.urls]
22
+ "Homepage" = "https://gitee.com/z-bajer/autocall"
23
+ "Bug Reports" = "https://gitee.com/z-bajer/autocall/issues"
24
+ "Source" = "https://gitee.com/z-bajer/autocall"
25
+
26
+ [tool.poetry.group.dev.dependencies]
27
+ pytest = "^8.3.4"
28
+ pytest-xdist = "^3.6.1"
29
+
@@ -1,8 +1,9 @@
1
1
  #!/bin/python3
2
2
 
3
3
  __all__ = ['cmdline', 'cmdline_default', 'cmdline_start',
4
- 'optfunc', 'optfunc_default', 'optfunc_start']
4
+ 'optfunc', 'optfunc_default', 'optfunc_start', 'called_directly']
5
5
 
6
+ import atexit
6
7
  from optfunc2.parser import cmdline as cmdline
7
8
  from optfunc2.parser import cmdline_default as cmdline_default
8
9
  from optfunc2.parser import cmdline_start as cmdline_start
@@ -10,4 +11,4 @@ from optfunc2.parser import cmdline_start as cmdline_start
10
11
  from optfunc2.parser import cmdline as optfunc
11
12
  from optfunc2.parser import cmdline_default as optfunc_default
12
13
  from optfunc2.parser import cmdline_start as optfunc_start
13
-
14
+ from .parser import called_directly
@@ -8,6 +8,9 @@ import ast
8
8
  registered_funcs = []
9
9
  registered_func_default = None
10
10
 
11
+ # Main eval function in this module must be called only once.
12
+ _called_func = None
13
+
11
14
  colors = {
12
15
  "red": 91,
13
16
  "blue": 94,
@@ -39,9 +42,6 @@ def decode_func_args(func: callable):
39
42
  abbrev_list.append('h')
40
43
 
41
44
  for name, param in inspect.signature(func).parameters.items():
42
- if len(name) == 1:
43
- abbrev_list.append(name)
44
-
45
45
  abbrev = name[0]
46
46
 
47
47
  desc = inspect.Signature.empty
@@ -61,23 +61,23 @@ def decode_func_args(func: callable):
61
61
  return args_list
62
62
 
63
63
  def get_and_check_opt(args_list: list, opt_name):
64
- opt_name2 = opt_name
64
+ opt_name = opt_name
65
+ opt_name_full = ''
66
+ opt_name_abbrev = ''
65
67
  if opt_name.startswith('--'):
66
- opt_name = opt_name[2:]
67
- if len(opt_name) == 1:
68
- raise ValueError(f'{opt_name} is not a valid option name.')
68
+ opt_name_full = opt_name[2:]
69
69
  elif opt_name.startswith('-'):
70
- opt_name = opt_name[1:]
71
- if len(opt_name) != 1:
72
- raise ValueError(f'{opt_name} is not a valid option name.')
70
+ opt_name_abbrev = opt_name[1:]
71
+ if len(opt_name_abbrev) != 1:
72
+ raise ValueError(f'{opt_name} should be a single character abbreviation or full name beginning with --.')
73
73
  else:
74
74
  raise ValueError(f'{opt_name} is not a valid option name.')
75
75
 
76
76
  for idx, (_, name, abbrev, _, _, _) in enumerate(args_list):
77
- if name == opt_name or abbrev == opt_name:
77
+ if name == opt_name_full or abbrev == opt_name_abbrev:
78
78
  return args_list.pop(idx)
79
79
 
80
- raise ValueError(f'{opt_name2} not found.')
80
+ raise ValueError(f'{opt_name} not found.')
81
81
 
82
82
  def decode_opts(arg_pairs, func: callable):
83
83
  """
@@ -88,8 +88,12 @@ def decode_opts(arg_pairs, func: callable):
88
88
 
89
89
  for (opt, val) in arg_pairs:
90
90
  typ, name, _, anno, _, _ = get_and_check_opt(arg_list, opt)
91
+ # special case for assuming bool argument
92
+ if type(val) == bool and anno != bool:
93
+ raise ValueError(f'--{name} need more arguments.')
94
+
91
95
  try:
92
- if anno != inspect.Signature.empty:
96
+ if anno != inspect.Signature.empty and anno not in [dict, list]:
93
97
  value = anno(val)
94
98
  else:
95
99
  try:
@@ -97,7 +101,13 @@ def decode_opts(arg_pairs, func: callable):
97
101
  except Exception:
98
102
  value = str(val)
99
103
 
100
- anno = type(value)
104
+ anno_new = type(value)
105
+
106
+ if anno != inspect.Signature.empty:
107
+ if anno_new != anno:
108
+ raise ValueError(f'Value type of {opt} should be {anno.__name__}.')
109
+
110
+ anno = anno_new
101
111
 
102
112
  opt_list.append((typ, name, anno, value))
103
113
 
@@ -126,12 +136,6 @@ def cmdline_default(func):
126
136
  global registered_funcs
127
137
 
128
138
  parameters = inspect.getfullargspec(func)
129
-
130
- if len(parameters.args) != 0 and len(parameters.args) != len(parameters.defaults):
131
- color_begin('red')
132
- print(f'[Autocall Erroring]: Function annotated by @cmdline_default should have zero arguments or all arguments have default value.')
133
- color_end()
134
- exit(1)
135
139
 
136
140
  if registered_func_default:
137
141
  color_begin('yellow')
@@ -187,10 +191,7 @@ def cmd_help(func: callable, has_abbrev = True):
187
191
 
188
192
  for (type, name, abbrev, anno, default, desc) in args_list:
189
193
  # abbrev, anno, default, desc all may be empty
190
- if len(name) == 1:
191
- name = '-' + name
192
- else:
193
- name = '--' + name
194
+ name = '--' + name
194
195
 
195
196
  if abbrev == inspect.Signature.empty:
196
197
  abbrev = empty_tag
@@ -243,6 +244,10 @@ def help(header_doc):
243
244
  color_begin('green')
244
245
  print(f' {f.__name__:<{max_name_len}s}', end='')
245
246
  color_end()
247
+ if f == registered_func_default:
248
+ color_begin('yellow')
249
+ print('[default] ', end='')
250
+ color_end()
246
251
  if doc:
247
252
  doc_list = [d.strip() for d in doc.split('\n') if d.strip()]
248
253
  doc = doc_list[0]
@@ -257,7 +262,8 @@ def match_and_check(name):
257
262
  Match the name with the function name.
258
263
  """
259
264
  global registered_funcs
260
- for f in registered_funcs:
265
+ # [::-1] ensure the order of functions with same name.
266
+ for f in registered_funcs[::-1]:
261
267
  if f.__name__ == name:
262
268
  return f
263
269
 
@@ -267,8 +273,10 @@ def pair_argv(argv, has_abbrev = True):
267
273
  """ Support these styles:
268
274
  --arg val
269
275
  --arg=val
276
+ --arg
270
277
  -a val
271
278
  -aval
279
+ -a
272
280
  """
273
281
  if len(argv) == 0:
274
282
  return []
@@ -283,9 +291,14 @@ def pair_argv(argv, has_abbrev = True):
283
291
  arg_pairs.append((arg, val))
284
292
  else:
285
293
  if len(argv) == idx + 1:
286
- raise ValueError(f'No value given for argument {arg}.')
287
- arg_pairs.append((arg, argv[idx+1]))
288
- idx += 1
294
+ arg_pairs.append((arg, True))
295
+ else:
296
+ if argv[idx+1].startswith('-'):
297
+ arg_pairs.append((arg, True))
298
+ else:
299
+ arg_pairs.append((arg, argv[idx+1]))
300
+ idx += 1
301
+
289
302
  elif arg.startswith('-'):
290
303
  if not has_abbrev:
291
304
  raise ValueError(f'Unknown argument {arg}. Abbreviations are not supported.')
@@ -293,14 +306,33 @@ def pair_argv(argv, has_abbrev = True):
293
306
  if len(arg) != 2:
294
307
  arg_pairs.append((arg[0:2], arg[2:]))
295
308
  else:
296
- arg_pairs.append((arg, argv[idx+1]))
297
- idx += 1
309
+ # bool type can be used in single character.
310
+ if len(argv) == idx + 1: # Cmdline ended with a abbreviation, we assume it is a bool type argument.
311
+ arg_pairs.append((arg, True))
312
+ else: # Next argument is belong to the next pair. We assume it is a bool type argument.
313
+ if argv[idx+1].startswith('-'):
314
+ arg_pairs.append((arg, True))
315
+ else: # Normal.
316
+ arg_pairs.append((arg, argv[idx+1]))
317
+ idx += 1
318
+
298
319
  idx += 1
299
320
 
300
321
  return arg_pairs
301
322
 
323
+ def called_directly():
324
+ """Check if the function is called by optfunc2 directly.
325
+
326
+ Returns:
327
+ bool: True means called by optfunc2 directly.
328
+ """
329
+ global _called_func
330
+ return _called_func.__name__ == inspect.currentframe().f_back.f_code.co_name
331
+
332
+
333
+
302
334
  # def parse_run(argv = sys.argv[1:]):
303
- def cmdline_start(globals, locals = None, *, argv = sys.argv, header_doc: str = 'Help Tips Provided by Autocall.', has_abbrev = True):
335
+ def cmdline_start(globals = None, locals = None, *, argv = sys.argv, header_doc: str = 'Help Tips Provided by Autocall.', has_abbrev = False):
304
336
  """Begin to handle argv and execute the corresponding function.
305
337
 
306
338
  Args:
@@ -308,18 +340,31 @@ def cmdline_start(globals, locals = None, *, argv = sys.argv, header_doc: str =
308
340
  locals (dict, optional): Used to be the execution environment.
309
341
  argv (list, optional): Advanced usage. Defaults to sys.argv.
310
342
  header_doc (str, optional): Will be printed as the header of help information.
311
- has_abbrev (bool, optional): Whether to support abbreviations. Defaults to True.
343
+ has_abbrev (bool, optional): Whether to support abbreviations. Defaults to False. Be careful when using this option. The abbreviation may be dynamically changed.
312
344
  """
313
-
345
+ global _called_func
346
+
347
+ # ensure only one call
348
+ if _called_func:
349
+ #return
350
+ pass # This function has to be called in main program explicitly.
351
+
352
+ if not globals:
353
+ globals = inspect.currentframe().f_back.f_globals
354
+
314
355
  if len(argv) == 1:
315
356
  if not registered_func_default:
316
- return
357
+ return
317
358
  argv.append(registered_func_default.__name__)
318
359
 
319
360
  if argv[1] == 'help' or argv[1] == '--help' or argv[1] == '-h':
320
361
  help(header_doc)
321
362
  return
322
363
 
364
+ if argv[1][0] == '-':
365
+ # use the default function
366
+ argv.insert(1, registered_func_default.__name__)
367
+
323
368
  func = match_and_check(argv[1])
324
369
 
325
370
  if not func:
@@ -334,6 +379,8 @@ def cmdline_start(globals, locals = None, *, argv = sys.argv, header_doc: str =
334
379
  cmd_help(func, has_abbrev)
335
380
  return
336
381
 
382
+ _called_func = func
383
+
337
384
  # parse argv
338
385
  arg_pairs = pair_argv(argv, has_abbrev)
339
386
 
@@ -352,4 +399,5 @@ def cmdline_start(globals, locals = None, *, argv = sys.argv, header_doc: str =
352
399
  func_call_str += ', '.join(final_args)
353
400
  func_call_str += ')'
354
401
 
355
- eval(func_call_str, globals, locals)
402
+ # Return the called function's return value.
403
+ return eval(func_call_str, globals, locals)
optfunc2-0.2.0/PKG-INFO DELETED
@@ -1,99 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: optfunc2
3
- Version: 0.2.0
4
- Summary: Generate options from function.
5
- License: PyPA
6
- Author: bajeer
7
- Author-email: z_bajer@yeah.net
8
- Requires-Python: >=3.9,<4.0
9
- Classifier: License :: Other/Proprietary License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.9
13
- Requires-Dist: docstring_parser (>=0.16,<0.17)
14
- Requires-Dist: prettytable (>=3.14.0,<4.0.0)
15
- Description-Content-Type: text/markdown
16
-
17
- # Call function directly in cmd line
18
-
19
- ### Features
20
- 1. Allow user call functions directly in command line.
21
- 2. Generate help tips automatically.
22
- 3. Add default called functions if not function was specific.
23
-
24
- ### Notice
25
- 1. It's better to add argument type for each autocall functions.
26
- 2. Function with @optfunc_default has @optfunc implicitly.
27
- 3. Arguments of function with @optfunc_default should be optional or no argument.
28
- 4. Not support two type of variadic arguments.
29
-
30
- ### ChangeLog
31
- #### 0.1.2 (2023-05-06)
32
- 1. Add support for default called functions.
33
- 2. Fix README.md.
34
- 3. Add ChangeLog in README.md.
35
-
36
- ### Code example
37
- ``` python
38
- from optfunc2 import *
39
-
40
- @optfunc
41
- def arg_test_positional_only(pos_only0, pos_only1: int, pos_only2 = 5, pos_only3: int = 6):
42
- """summary for the function
43
-
44
- Args:
45
- pos_only0 (_type_): This is the first positional-only argument.
46
- pos_only1 (int): This is the second positional-only argument.
47
- pos_only2 (int, optional): This is the third positional-only argument. Defaults to 5.
48
- pos_only3 (int, optional): This is the fourth positional-only argument. Defaults to 6.
49
- """
50
- " Argument test for positional-only arguments. "
51
- print(f'pos_only0: {pos_only0}, pos_only1: {pos_only1}, pos_only2: {pos_only2}, pos_only3: {pos_only3}')
52
- pass
53
-
54
- @optfunc
55
- def arg_test_positional_or_keyword(pos_or_kw, pos_or_kw1: int, pos_or_kw2 = 3, pos_or_kw3: int = 4):
56
- " Argument test for positional-or-keyword arguments. "
57
- print(f'pos_or_kw: {pos_or_kw}, pos_or_kw1: {pos_or_kw1}, pos_or_kw2: {pos_or_kw2}, pos_or_kw3: {pos_or_kw3}')
58
- pass
59
-
60
- @optfunc
61
- def arg_test_kw_only(*, kw_only0, kw_only1: int, kw_only2 = 9, kw_only3: int = 10):
62
- " Argument test for keyword-only arguments. "
63
- print(f'kw_only0: {kw_only0}, kw_only1: {kw_only1}, kw_only2: {kw_only2}, kw_only3: {kw_only3}')
64
- pass
65
-
66
- if __name__ == '__main__':
67
- optfunc_start(globals=globals(), has_abbrev=False, header_doc='This is a test file for the module "autocall".')
68
- ```
69
- ### Run the code
70
- ``` bash
71
- ~/:$ python3 test.py -h
72
- Usage: test.py [command] [<args>|--help]
73
-
74
- This is a test file for the module "autocall".
75
-
76
- commands:
77
- arg_test_positional_only summary for the function
78
- arg_test_positional_or_keyword Argument test for positional-or-keyword arguments.
79
- arg_test_kw_only Argument test for keyword-only arguments.
80
-
81
- ~/:$ python3 test.py arg_test_positional_only -h
82
- Usage: test.py arg_test_positional_only [OPTIONS]
83
-
84
- summary for the function
85
-
86
-
87
- Arguments:
88
- +-------------+------+---------+-------------------------------------------------------------+
89
- | Opt | Type | Default | Desc |
90
- +-------------+------+---------+-------------------------------------------------------------+
91
- | --pos_only0 | any | | This is the first positional-only argument. |
92
- | --pos_only1 | int | | This is the second positional-only argument. |
93
- | --pos_only2 | any | 5 | This is the third positional-only argument. Defaults to 5. |
94
- | --pos_only3 | int | 6 | This is the fourth positional-only argument. Defaults to 6. |
95
- +-------------+------+---------+-------------------------------------------------------------+
96
- ~/:$ python3 test.py arg_test_positional_only --pos_only0 "good day" --pos_only1 2
97
- pos_only0: good day, pos_only1: 2, pos_only2: 5, pos_only3: 6
98
- ```
99
-
optfunc2-0.2.0/README.md DELETED
@@ -1,82 +0,0 @@
1
- # Call function directly in cmd line
2
-
3
- ### Features
4
- 1. Allow user call functions directly in command line.
5
- 2. Generate help tips automatically.
6
- 3. Add default called functions if not function was specific.
7
-
8
- ### Notice
9
- 1. It's better to add argument type for each autocall functions.
10
- 2. Function with @optfunc_default has @optfunc implicitly.
11
- 3. Arguments of function with @optfunc_default should be optional or no argument.
12
- 4. Not support two type of variadic arguments.
13
-
14
- ### ChangeLog
15
- #### 0.1.2 (2023-05-06)
16
- 1. Add support for default called functions.
17
- 2. Fix README.md.
18
- 3. Add ChangeLog in README.md.
19
-
20
- ### Code example
21
- ``` python
22
- from optfunc2 import *
23
-
24
- @optfunc
25
- def arg_test_positional_only(pos_only0, pos_only1: int, pos_only2 = 5, pos_only3: int = 6):
26
- """summary for the function
27
-
28
- Args:
29
- pos_only0 (_type_): This is the first positional-only argument.
30
- pos_only1 (int): This is the second positional-only argument.
31
- pos_only2 (int, optional): This is the third positional-only argument. Defaults to 5.
32
- pos_only3 (int, optional): This is the fourth positional-only argument. Defaults to 6.
33
- """
34
- " Argument test for positional-only arguments. "
35
- print(f'pos_only0: {pos_only0}, pos_only1: {pos_only1}, pos_only2: {pos_only2}, pos_only3: {pos_only3}')
36
- pass
37
-
38
- @optfunc
39
- def arg_test_positional_or_keyword(pos_or_kw, pos_or_kw1: int, pos_or_kw2 = 3, pos_or_kw3: int = 4):
40
- " Argument test for positional-or-keyword arguments. "
41
- print(f'pos_or_kw: {pos_or_kw}, pos_or_kw1: {pos_or_kw1}, pos_or_kw2: {pos_or_kw2}, pos_or_kw3: {pos_or_kw3}')
42
- pass
43
-
44
- @optfunc
45
- def arg_test_kw_only(*, kw_only0, kw_only1: int, kw_only2 = 9, kw_only3: int = 10):
46
- " Argument test for keyword-only arguments. "
47
- print(f'kw_only0: {kw_only0}, kw_only1: {kw_only1}, kw_only2: {kw_only2}, kw_only3: {kw_only3}')
48
- pass
49
-
50
- if __name__ == '__main__':
51
- optfunc_start(globals=globals(), has_abbrev=False, header_doc='This is a test file for the module "autocall".')
52
- ```
53
- ### Run the code
54
- ``` bash
55
- ~/:$ python3 test.py -h
56
- Usage: test.py [command] [<args>|--help]
57
-
58
- This is a test file for the module "autocall".
59
-
60
- commands:
61
- arg_test_positional_only summary for the function
62
- arg_test_positional_or_keyword Argument test for positional-or-keyword arguments.
63
- arg_test_kw_only Argument test for keyword-only arguments.
64
-
65
- ~/:$ python3 test.py arg_test_positional_only -h
66
- Usage: test.py arg_test_positional_only [OPTIONS]
67
-
68
- summary for the function
69
-
70
-
71
- Arguments:
72
- +-------------+------+---------+-------------------------------------------------------------+
73
- | Opt | Type | Default | Desc |
74
- +-------------+------+---------+-------------------------------------------------------------+
75
- | --pos_only0 | any | | This is the first positional-only argument. |
76
- | --pos_only1 | int | | This is the second positional-only argument. |
77
- | --pos_only2 | any | 5 | This is the third positional-only argument. Defaults to 5. |
78
- | --pos_only3 | int | 6 | This is the fourth positional-only argument. Defaults to 6. |
79
- +-------------+------+---------+-------------------------------------------------------------+
80
- ~/:$ python3 test.py arg_test_positional_only --pos_only0 "good day" --pos_only1 2
81
- pos_only0: good day, pos_only1: 2, pos_only2: 5, pos_only3: 6
82
- ```
@@ -1,23 +0,0 @@
1
- [tool.poetry]
2
- name = "optfunc2"
3
- version = "0.2.0"
4
- description = "Generate options from function."
5
- authors = ["bajeer <z_bajer@yeah.net>"]
6
- readme = "README.md"
7
- license = "PyPA"
8
-
9
- [tool.poetry.dependencies]
10
- python = ">=3.9,<4.0"
11
- prettytable = "^3.14.0"
12
- docstring_parser = "^0.16"
13
-
14
- [tool.poetry.dev-dependencies]
15
-
16
- [build-system]
17
- requires = ["poetry-core>=1.0.0"]
18
- build-backend = "poetry.core.masonry.api"
19
-
20
- [project.urls]
21
- "Homepage" = "https://gitee.com/z-bajer/autocall"
22
- "Bug Reports" = "https://gitee.com/z-bajer/autocall/issues"
23
- "Source" = "https://gitee.com/z-bajer/autocall"
optfunc2-0.2.0/setup.py DELETED
@@ -1,34 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- package_dir = \
5
- {'': 'src'}
6
-
7
- packages = \
8
- ['optfunc2']
9
-
10
- package_data = \
11
- {'': ['*']}
12
-
13
- install_requires = \
14
- ['docstring_parser>=0.16,<0.17', 'prettytable>=3.14.0,<4.0.0']
15
-
16
- setup_kwargs = {
17
- 'name': 'optfunc2',
18
- 'version': '0.2.0',
19
- 'description': 'Generate options from function.',
20
- 'long_description': '# Call function directly in cmd line\n\n### Features\n1. Allow user call functions directly in command line.\n2. Generate help tips automatically.\n3. Add default called functions if not function was specific.\n\n### Notice\n1. It\'s better to add argument type for each autocall functions.\n2. Function with @optfunc_default has @optfunc implicitly.\n3. Arguments of function with @optfunc_default should be optional or no argument.\n4. Not support two type of variadic arguments.\n\n### ChangeLog\n#### 0.1.2 (2023-05-06)\n1. Add support for default called functions.\n2. Fix README.md.\n3. Add ChangeLog in README.md.\n\n### Code example\n``` python\nfrom optfunc2 import *\n\n@optfunc\ndef arg_test_positional_only(pos_only0, pos_only1: int, pos_only2 = 5, pos_only3: int = 6):\n """summary for the function\n\n Args:\n pos_only0 (_type_): This is the first positional-only argument.\n pos_only1 (int): This is the second positional-only argument.\n pos_only2 (int, optional): This is the third positional-only argument. Defaults to 5.\n pos_only3 (int, optional): This is the fourth positional-only argument. Defaults to 6.\n """\n " Argument test for positional-only arguments. "\n print(f\'pos_only0: {pos_only0}, pos_only1: {pos_only1}, pos_only2: {pos_only2}, pos_only3: {pos_only3}\')\n pass\n\n@optfunc\ndef arg_test_positional_or_keyword(pos_or_kw, pos_or_kw1: int, pos_or_kw2 = 3, pos_or_kw3: int = 4):\n " Argument test for positional-or-keyword arguments. "\n print(f\'pos_or_kw: {pos_or_kw}, pos_or_kw1: {pos_or_kw1}, pos_or_kw2: {pos_or_kw2}, pos_or_kw3: {pos_or_kw3}\')\n pass\n\n@optfunc\ndef arg_test_kw_only(*, kw_only0, kw_only1: int, kw_only2 = 9, kw_only3: int = 10):\n " Argument test for keyword-only arguments. "\n print(f\'kw_only0: {kw_only0}, kw_only1: {kw_only1}, kw_only2: {kw_only2}, kw_only3: {kw_only3}\')\n pass\n\nif __name__ == \'__main__\':\n optfunc_start(globals=globals(), has_abbrev=False, header_doc=\'This is a test file for the module "autocall".\')\n```\n### Run the code\n``` bash\n~/:$ python3 test.py -h\nUsage: test.py [command] [<args>|--help]\n\nThis is a test file for the module "autocall".\n\ncommands:\n arg_test_positional_only summary for the function\n arg_test_positional_or_keyword Argument test for positional-or-keyword arguments.\n arg_test_kw_only Argument test for keyword-only arguments.\n\n~/:$ python3 test.py arg_test_positional_only -h\nUsage: test.py arg_test_positional_only [OPTIONS]\n\nsummary for the function\n\n\nArguments:\n+-------------+------+---------+-------------------------------------------------------------+\n| Opt | Type | Default | Desc |\n+-------------+------+---------+-------------------------------------------------------------+\n| --pos_only0 | any | | This is the first positional-only argument. |\n| --pos_only1 | int | | This is the second positional-only argument. |\n| --pos_only2 | any | 5 | This is the third positional-only argument. Defaults to 5. |\n| --pos_only3 | int | 6 | This is the fourth positional-only argument. Defaults to 6. |\n+-------------+------+---------+-------------------------------------------------------------+\n~/:$ python3 test.py arg_test_positional_only --pos_only0 "good day" --pos_only1 2\npos_only0: good day, pos_only1: 2, pos_only2: 5, pos_only3: 6\n```\n',
21
- 'author': 'bajeer',
22
- 'author_email': 'z_bajer@yeah.net',
23
- 'maintainer': None,
24
- 'maintainer_email': None,
25
- 'url': None,
26
- 'package_dir': package_dir,
27
- 'packages': packages,
28
- 'package_data': package_data,
29
- 'install_requires': install_requires,
30
- 'python_requires': '>=3.9,<4.0',
31
- }
32
-
33
-
34
- setup(**setup_kwargs)
File without changes