multitool 0.6.0__tar.gz → 0.7.0__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 (30) hide show
  1. multitool-0.7.0/PKG-INFO +278 -0
  2. multitool-0.7.0/README.rst +255 -0
  3. multitool-0.7.0/multitool/__init__.py +23 -0
  4. {multitool-0.6.0 → multitool-0.7.0}/multitool/__main__.py +23 -12
  5. multitool-0.7.0/multitool/console.py +37 -0
  6. multitool-0.7.0/multitool/plugins/commands.py +425 -0
  7. {multitool-0.6.0 → multitool-0.7.0}/multitool/utils.py +56 -46
  8. multitool-0.7.0/multitool/utils_init.py +17 -0
  9. multitool-0.7.0/multitool.egg-info/PKG-INFO +278 -0
  10. {multitool-0.6.0 → multitool-0.7.0}/multitool.egg-info/SOURCES.txt +1 -0
  11. {multitool-0.6.0 → multitool-0.7.0}/pyproject.toml +2 -2
  12. multitool-0.6.0/PKG-INFO +0 -174
  13. multitool-0.6.0/README.rst +0 -151
  14. multitool-0.6.0/multitool/__init__.py +0 -23
  15. multitool-0.6.0/multitool/console.py +0 -25
  16. multitool-0.6.0/multitool/plugins/commands.py +0 -224
  17. multitool-0.6.0/multitool.egg-info/PKG-INFO +0 -174
  18. {multitool-0.6.0 → multitool-0.7.0}/LICENSE +0 -0
  19. {multitool-0.6.0 → multitool-0.7.0}/multitool/cls.py +0 -0
  20. {multitool-0.6.0 → multitool-0.7.0}/multitool/exceptions.py +0 -0
  21. {multitool-0.6.0 → multitool-0.7.0}/multitool/plugins/__init__.py +0 -0
  22. {multitool-0.6.0 → multitool-0.7.0}/multitool/silent.py +0 -0
  23. {multitool-0.6.0 → multitool-0.7.0}/multitool/verbose.py +0 -0
  24. {multitool-0.6.0 → multitool-0.7.0}/multitool.egg-info/dependency_links.txt +0 -0
  25. {multitool-0.6.0 → multitool-0.7.0}/multitool.egg-info/entry_points.txt +0 -0
  26. {multitool-0.6.0 → multitool-0.7.0}/multitool.egg-info/requires.txt +0 -0
  27. {multitool-0.6.0 → multitool-0.7.0}/multitool.egg-info/top_level.txt +0 -0
  28. {multitool-0.6.0 → multitool-0.7.0}/setup.cfg +0 -0
  29. {multitool-0.6.0 → multitool-0.7.0}/tests/__init__.py +0 -0
  30. {multitool-0.6.0 → multitool-0.7.0}/tests/test_utils.py +0 -0
@@ -0,0 +1,278 @@
1
+ Metadata-Version: 2.4
2
+ Name: multitool
3
+ Version: 0.7.0
4
+ Summary: Create and run plugin-based command-line tools.
5
+ Author-email: Matthew Delotavo <matthew.t.delotavo@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mdelotavo/multitool
8
+ Project-URL: Repository, https://github.com/mdelotavo/multitool
9
+ Keywords: cli,tooling,automation
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3.7
15
+ Requires-Python: >=3.7
16
+ Description-Content-Type: text/x-rst
17
+ License-File: LICENSE
18
+ Requires-Dist: click>=8.1.3
19
+ Requires-Dist: click-aliases>=1.0.1
20
+ Requires-Dist: click-option-group>=0.5.5
21
+ Requires-Dist: GitPython>=3.1.30
22
+ Dynamic: license-file
23
+
24
+ multitool
25
+ =========
26
+
27
+ Create and run plugin-based command-line tools.
28
+
29
+ -----
30
+ TL;DR
31
+ -----
32
+
33
+ This section shows how to start prototyping plugins on your local machine.
34
+ See the sections below to learn how to distribute your commands as remote
35
+ plugins that can be installed and updated from Git repositories.
36
+
37
+ Create a new local plugin::
38
+
39
+ multitool plugins new test
40
+
41
+ Show the generated command help::
42
+
43
+ multitool run test hello --help
44
+
45
+ Run the example command::
46
+
47
+ multitool run test hello \
48
+ "Hello, World!" \
49
+ --count 3 \
50
+ --format json \
51
+ -vv \
52
+ --enabled \
53
+ --tag alpha \
54
+ --tag beta \
55
+ --output result.json
56
+
57
+ Edit the generated source code::
58
+
59
+ vim ~/.multitool/plugins/test/plugin_*.py
60
+
61
+ -----
62
+ Usage
63
+ -----
64
+
65
+ .. code-block:: text
66
+
67
+ Usage: multitool [OPTIONS] COMMAND [ARGS]...
68
+
69
+ Create and run plugin-based command-line tools.
70
+
71
+ Options:
72
+ -V, --version Show the version and exit.
73
+ -h, --help Show this message and exit.
74
+
75
+ Commands:
76
+ plugins Manage plugin repositories.
77
+ run Run installed plugin commands.
78
+
79
+ ----------------
80
+ Managing plugins
81
+ ----------------
82
+
83
+ Plugins are distributed as Git repositories containing Click commands.
84
+
85
+ The ``plugins`` command manages plugin repositories, while installed plugin
86
+ commands are available under ``multitool run``.
87
+
88
+ Git is required to install or update plugins. If Git is unavailable, the
89
+ ``plugins`` command cannot install repositories, although plugins can still be
90
+ installed manually by copying them into::
91
+
92
+ ~/.multitool/plugins/PLUGIN_NAME/
93
+
94
+ ^^^^^^^^^^^^^^^^
95
+ Creating plugins
96
+ ^^^^^^^^^^^^^^^^
97
+
98
+ Create a new plugin scaffold with::
99
+
100
+ multitool plugins new PLUGIN_NAME
101
+
102
+ This creates a local plugin repository under::
103
+
104
+ ~/.multitool/plugins/PLUGIN_NAME/
105
+
106
+ The generated structure includes::
107
+
108
+ PLUGIN_NAME/
109
+ ├── __init__.py
110
+ ├── plugin_<unique-id>.py
111
+ ├── multitool-info.json
112
+ ├── README.md
113
+ └── LICENSE
114
+
115
+ The generated plugin contains a Click command group named
116
+ ``PLUGIN_NAME`` and an example ``hello`` command. Add additional commands
117
+ to the generated ``plugin_<unique-id>.py`` module.
118
+
119
+ Test the plugin locally with::
120
+
121
+ multitool run PLUGIN_NAME -h
122
+
123
+ To distribute the plugin:
124
+
125
+ 1. Initialize the plugin directory as a Git repository::
126
+
127
+ cd ~/.multitool/plugins/PLUGIN_NAME
128
+ git init
129
+
130
+ 2. Commit and push it to a remote Git repository such as GitHub or GitLab.
131
+
132
+ 3. Add the repository URL to the Multitool configuration::
133
+
134
+ [sources]
135
+ PLUGIN_NAME = https://github.com/<user>/PLUGIN_NAME.git
136
+
137
+ 4. Install or update plugins::
138
+
139
+ multitool plugins update
140
+
141
+ Alternatively, copy the plugin directory directly into another Multitool
142
+ plugins directory to use it locally.
143
+
144
+ ^^^^^^^^^^^
145
+ Configuring
146
+ ^^^^^^^^^^^
147
+
148
+ Configure plugin repositories with::
149
+
150
+ multitool plugins configure -a
151
+
152
+ This opens your editor to modify the plugin configuration. Omit ``-a`` if you
153
+ don't want changes applied automatically.
154
+
155
+ Example configuration::
156
+
157
+ [sources]
158
+ public = https://github.com/mdelotavo/multitool-plugins.git
159
+
160
+ After saving, Multitool clones each configured repository into::
161
+
162
+ ~/.multitool/plugins/
163
+
164
+ You can configure multiple repositories as long as each key is unique.
165
+
166
+ ^^^^^^^^^^
167
+ Quickstart
168
+ ^^^^^^^^^^
169
+
170
+ Install the example plugins::
171
+
172
+ echo -e '[sources]\npublic = https://github.com/mdelotavo/multitool-plugins.git' >> ~/.multitool/plugins/config
173
+
174
+ multitool plugins update
175
+ multitool plugins show
176
+ multitool plugins show -n public
177
+ multitool plugins show -n public --show-commit-only
178
+ multitool plugins show -n public --show-dependencies-only
179
+ pip3 install $(multitool plugins show -n public --show-dependencies-only)
180
+
181
+ multitool run examples -h
182
+
183
+ ^^^^^^^^
184
+ Updating
185
+ ^^^^^^^^
186
+
187
+ Install new plugins and update existing ones::
188
+
189
+ multitool plugins update
190
+
191
+ ^^^^^^^
192
+ Pruning
193
+ ^^^^^^^
194
+
195
+ Remove repositories no longer listed in the configuration::
196
+
197
+ multitool plugins prune
198
+
199
+ ^^^^^^^
200
+ Showing
201
+ ^^^^^^^
202
+
203
+ Show configured repositories::
204
+
205
+ multitool plugins show
206
+
207
+ Or inspect a specific repository::
208
+
209
+ multitool plugins show -n PLUGIN_NAME --show-commit-only
210
+ multitool plugins show -n PLUGIN_NAME --show-dependencies-only
211
+
212
+ If a plugin declares Python dependencies in ``multitool-info.json``, install
213
+ them with::
214
+
215
+ pip3 install $(multitool plugins show -n PLUGIN_NAME --show-dependencies-only)
216
+
217
+ ---------------
218
+ Troubleshooting
219
+ ---------------
220
+
221
+ If a plugin fails to install or load, check the log file::
222
+
223
+ ~/.multitool/multitool.log
224
+
225
+ It contains installation, dependency, and Git-related errors.
226
+
227
+ -----------
228
+ Limitations
229
+ -----------
230
+
231
+ Plugin command names must be unique across all installed repositories.
232
+
233
+ To avoid naming conflicts, plugin modules should follow the convention of
234
+ including the repository owner and repository name in the command name.
235
+
236
+ For example, a repository configured as::
237
+
238
+ [sources]
239
+ public = https://github.com/mdelotavo/multitool-plugins.git
240
+
241
+ should expose commands using a unique name such as::
242
+
243
+ mdelotavo-multitool-plugins
244
+
245
+ This prevents duplicate command names when multiple repositories provide
246
+ plugins with the same module name. If two plugins expose commands with the
247
+ same name, only one can be loaded and the duplicate plugin will be skipped.
248
+
249
+ If two repositories expose the same command name, Multitool will fail to load
250
+ the duplicate plugin and raise an error similar to::
251
+
252
+ Duplicate plugin command "examples" found in
253
+ /home/user/.multitool/plugins/PLUGIN_NAME/__init__.py.
254
+ Already loaded from plugins_modules.examples
255
+
256
+ To resolve the issue, remove the conflicting plugin repository from your local
257
+ plugins directory::
258
+
259
+ ~/.multitool/plugins/PLUGIN_NAME/
260
+
261
+ and remove the repository from the configured sources.
262
+
263
+ You can edit the configuration file manually::
264
+
265
+ ~/.multitool/plugins/config
266
+
267
+ or open it using::
268
+
269
+ multitool plugins configure
270
+
271
+ After removing the conflicting repository, update the installed plugins::
272
+
273
+ multitool plugins update
274
+
275
+
276
+ .. _`click`: https://click.palletsprojects.com/
277
+ .. _`multitool-plugins`: https://github.com/mdelotavo/multitool-plugins
278
+ .. _`public plugins repository`: https://github.com/mdelotavo/multitool-plugins
@@ -0,0 +1,255 @@
1
+ multitool
2
+ =========
3
+
4
+ Create and run plugin-based command-line tools.
5
+
6
+ -----
7
+ TL;DR
8
+ -----
9
+
10
+ This section shows how to start prototyping plugins on your local machine.
11
+ See the sections below to learn how to distribute your commands as remote
12
+ plugins that can be installed and updated from Git repositories.
13
+
14
+ Create a new local plugin::
15
+
16
+ multitool plugins new test
17
+
18
+ Show the generated command help::
19
+
20
+ multitool run test hello --help
21
+
22
+ Run the example command::
23
+
24
+ multitool run test hello \
25
+ "Hello, World!" \
26
+ --count 3 \
27
+ --format json \
28
+ -vv \
29
+ --enabled \
30
+ --tag alpha \
31
+ --tag beta \
32
+ --output result.json
33
+
34
+ Edit the generated source code::
35
+
36
+ vim ~/.multitool/plugins/test/plugin_*.py
37
+
38
+ -----
39
+ Usage
40
+ -----
41
+
42
+ .. code-block:: text
43
+
44
+ Usage: multitool [OPTIONS] COMMAND [ARGS]...
45
+
46
+ Create and run plugin-based command-line tools.
47
+
48
+ Options:
49
+ -V, --version Show the version and exit.
50
+ -h, --help Show this message and exit.
51
+
52
+ Commands:
53
+ plugins Manage plugin repositories.
54
+ run Run installed plugin commands.
55
+
56
+ ----------------
57
+ Managing plugins
58
+ ----------------
59
+
60
+ Plugins are distributed as Git repositories containing Click commands.
61
+
62
+ The ``plugins`` command manages plugin repositories, while installed plugin
63
+ commands are available under ``multitool run``.
64
+
65
+ Git is required to install or update plugins. If Git is unavailable, the
66
+ ``plugins`` command cannot install repositories, although plugins can still be
67
+ installed manually by copying them into::
68
+
69
+ ~/.multitool/plugins/PLUGIN_NAME/
70
+
71
+ ^^^^^^^^^^^^^^^^
72
+ Creating plugins
73
+ ^^^^^^^^^^^^^^^^
74
+
75
+ Create a new plugin scaffold with::
76
+
77
+ multitool plugins new PLUGIN_NAME
78
+
79
+ This creates a local plugin repository under::
80
+
81
+ ~/.multitool/plugins/PLUGIN_NAME/
82
+
83
+ The generated structure includes::
84
+
85
+ PLUGIN_NAME/
86
+ ├── __init__.py
87
+ ├── plugin_<unique-id>.py
88
+ ├── multitool-info.json
89
+ ├── README.md
90
+ └── LICENSE
91
+
92
+ The generated plugin contains a Click command group named
93
+ ``PLUGIN_NAME`` and an example ``hello`` command. Add additional commands
94
+ to the generated ``plugin_<unique-id>.py`` module.
95
+
96
+ Test the plugin locally with::
97
+
98
+ multitool run PLUGIN_NAME -h
99
+
100
+ To distribute the plugin:
101
+
102
+ 1. Initialize the plugin directory as a Git repository::
103
+
104
+ cd ~/.multitool/plugins/PLUGIN_NAME
105
+ git init
106
+
107
+ 2. Commit and push it to a remote Git repository such as GitHub or GitLab.
108
+
109
+ 3. Add the repository URL to the Multitool configuration::
110
+
111
+ [sources]
112
+ PLUGIN_NAME = https://github.com/<user>/PLUGIN_NAME.git
113
+
114
+ 4. Install or update plugins::
115
+
116
+ multitool plugins update
117
+
118
+ Alternatively, copy the plugin directory directly into another Multitool
119
+ plugins directory to use it locally.
120
+
121
+ ^^^^^^^^^^^
122
+ Configuring
123
+ ^^^^^^^^^^^
124
+
125
+ Configure plugin repositories with::
126
+
127
+ multitool plugins configure -a
128
+
129
+ This opens your editor to modify the plugin configuration. Omit ``-a`` if you
130
+ don't want changes applied automatically.
131
+
132
+ Example configuration::
133
+
134
+ [sources]
135
+ public = https://github.com/mdelotavo/multitool-plugins.git
136
+
137
+ After saving, Multitool clones each configured repository into::
138
+
139
+ ~/.multitool/plugins/
140
+
141
+ You can configure multiple repositories as long as each key is unique.
142
+
143
+ ^^^^^^^^^^
144
+ Quickstart
145
+ ^^^^^^^^^^
146
+
147
+ Install the example plugins::
148
+
149
+ echo -e '[sources]\npublic = https://github.com/mdelotavo/multitool-plugins.git' >> ~/.multitool/plugins/config
150
+
151
+ multitool plugins update
152
+ multitool plugins show
153
+ multitool plugins show -n public
154
+ multitool plugins show -n public --show-commit-only
155
+ multitool plugins show -n public --show-dependencies-only
156
+ pip3 install $(multitool plugins show -n public --show-dependencies-only)
157
+
158
+ multitool run examples -h
159
+
160
+ ^^^^^^^^
161
+ Updating
162
+ ^^^^^^^^
163
+
164
+ Install new plugins and update existing ones::
165
+
166
+ multitool plugins update
167
+
168
+ ^^^^^^^
169
+ Pruning
170
+ ^^^^^^^
171
+
172
+ Remove repositories no longer listed in the configuration::
173
+
174
+ multitool plugins prune
175
+
176
+ ^^^^^^^
177
+ Showing
178
+ ^^^^^^^
179
+
180
+ Show configured repositories::
181
+
182
+ multitool plugins show
183
+
184
+ Or inspect a specific repository::
185
+
186
+ multitool plugins show -n PLUGIN_NAME --show-commit-only
187
+ multitool plugins show -n PLUGIN_NAME --show-dependencies-only
188
+
189
+ If a plugin declares Python dependencies in ``multitool-info.json``, install
190
+ them with::
191
+
192
+ pip3 install $(multitool plugins show -n PLUGIN_NAME --show-dependencies-only)
193
+
194
+ ---------------
195
+ Troubleshooting
196
+ ---------------
197
+
198
+ If a plugin fails to install or load, check the log file::
199
+
200
+ ~/.multitool/multitool.log
201
+
202
+ It contains installation, dependency, and Git-related errors.
203
+
204
+ -----------
205
+ Limitations
206
+ -----------
207
+
208
+ Plugin command names must be unique across all installed repositories.
209
+
210
+ To avoid naming conflicts, plugin modules should follow the convention of
211
+ including the repository owner and repository name in the command name.
212
+
213
+ For example, a repository configured as::
214
+
215
+ [sources]
216
+ public = https://github.com/mdelotavo/multitool-plugins.git
217
+
218
+ should expose commands using a unique name such as::
219
+
220
+ mdelotavo-multitool-plugins
221
+
222
+ This prevents duplicate command names when multiple repositories provide
223
+ plugins with the same module name. If two plugins expose commands with the
224
+ same name, only one can be loaded and the duplicate plugin will be skipped.
225
+
226
+ If two repositories expose the same command name, Multitool will fail to load
227
+ the duplicate plugin and raise an error similar to::
228
+
229
+ Duplicate plugin command "examples" found in
230
+ /home/user/.multitool/plugins/PLUGIN_NAME/__init__.py.
231
+ Already loaded from plugins_modules.examples
232
+
233
+ To resolve the issue, remove the conflicting plugin repository from your local
234
+ plugins directory::
235
+
236
+ ~/.multitool/plugins/PLUGIN_NAME/
237
+
238
+ and remove the repository from the configured sources.
239
+
240
+ You can edit the configuration file manually::
241
+
242
+ ~/.multitool/plugins/config
243
+
244
+ or open it using::
245
+
246
+ multitool plugins configure
247
+
248
+ After removing the conflicting repository, update the installed plugins::
249
+
250
+ multitool plugins update
251
+
252
+
253
+ .. _`click`: https://click.palletsprojects.com/
254
+ .. _`multitool-plugins`: https://github.com/mdelotavo/multitool-plugins
255
+ .. _`public plugins repository`: https://github.com/mdelotavo/multitool-plugins
@@ -0,0 +1,23 @@
1
+ APP = "multitool"
2
+ __version__ = "0.7.0"
3
+ description = "Create and run plugin-based command-line tools."
4
+ long_description = ("Create and run plugin-based command-line tools by dynamically loading Click commands from local or Git-managed plugin repositories.")
5
+
6
+ import builtins
7
+ from pathlib import Path
8
+
9
+ from multitool.utils_init import join_path
10
+
11
+ MULTITOOL_TOGGLE_SILENT = False
12
+ MULTITOOL_TOGGLE_VERBOSE = 0
13
+
14
+ MULTITOOL_DIRECTORY = join_path(Path.home(), ".multitool")
15
+
16
+ MULTITOOL_LOG_FILE = join_path(MULTITOOL_DIRECTORY, f"{APP}.log")
17
+
18
+ MULTITOOL_PLUGINS_DIRECTORY = join_path(MULTITOOL_DIRECTORY, "plugins")
19
+ MULTITOOL_PLUGINS_CONFIG_FILE = join_path(MULTITOOL_PLUGINS_DIRECTORY, "config")
20
+ MULTITOOL_PLUGINS_PATH = join_path(MULTITOOL_PLUGINS_DIRECTORY, "__init__.py")
21
+
22
+ builtins.MULTITOOL_TOGGLE_SILENT = MULTITOOL_TOGGLE_SILENT
23
+ builtins.MULTITOOL_TOGGLE_VERBOSE = MULTITOOL_TOGGLE_VERBOSE
@@ -8,37 +8,48 @@ from multitool import __version__ as version
8
8
  from multitool.cls import AliasedGroup
9
9
  from multitool.exceptions import wrap_with_exception_handling
10
10
  from multitool.plugins.commands import plugins
11
- from multitool.utils import (configure_global_logger, execute_function_on_directory_files, import_plugins_from_directory)
11
+ from multitool.utils import (configure_logger, for_each_file, load_plugins)
12
12
 
13
- CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
13
+ CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
14
14
 
15
15
 
16
16
  @click.group(context_settings=CONTEXT_SETTINGS, cls=AliasedGroup, invoke_without_command=False, chain=False)
17
- @click.version_option(version, '-V', '--version')
17
+ @click.version_option(version, "-V", "--version")
18
18
  @click.pass_context
19
19
  def cli(ctx):
20
- """Quickly create and distribute command-line tools."""
20
+ """Create and run plugin-based command-line tools."""
21
21
  ctx.ensure_object(dict)
22
22
 
23
23
 
24
+ @click.group(cls=AliasedGroup)
25
+ def run():
26
+ """Run installed plugin commands."""
27
+ pass
28
+
29
+
24
30
  @wrap_with_exception_handling
25
31
  def main():
26
- configure_global_logger(MULTITOOL_LOG_FILE)
32
+ configure_logger(MULTITOOL_LOG_FILE)
27
33
 
28
- cli_commands = {plugins}
34
+ cli_commands = {plugins, run}
29
35
 
30
- execute_function_on_directory_files(
36
+ run_commands = {}
37
+
38
+ for_each_file(
31
39
  MULTITOOL_PLUGINS_DIRECTORY,
32
- import_plugins_from_directory,
33
- args=(cli_commands, ),
34
- glob='[!.][!__]*/__init__.py',
40
+ load_plugins,
41
+ args=(run_commands, ),
42
+ glob="[!.][!__]*/__init__.py",
35
43
  )
36
44
 
45
+ for command in run_commands.values():
46
+ run.add_command(command)
47
+
37
48
  for command in cli_commands:
38
49
  cli.add_command(command)
39
50
 
40
51
  cli(prog_name=APP, obj={})
41
52
 
42
53
 
43
- if __name__ == '__main__':
44
- main() # pragma: no cover
54
+ if __name__ == "__main__":
55
+ main()
@@ -0,0 +1,37 @@
1
+ from dataclasses import dataclass, fields, replace
2
+ import builtins
3
+ import sys
4
+
5
+
6
+ @dataclass(slots=True)
7
+ class EchoOptions:
8
+ exit_status: int | None = None
9
+ silent: bool = False
10
+ verbosity: int = 0
11
+ level: int = 0
12
+ end: str = "\n"
13
+ flush: bool = False
14
+
15
+
16
+ def echo(*msg, options: EchoOptions | None = None, **kwargs):
17
+ options = options or EchoOptions()
18
+
19
+ valid_fields = {f.name for f in fields(EchoOptions)}
20
+
21
+ for key, value in kwargs.items():
22
+ if key not in valid_fields:
23
+ raise TypeError(f"echo() got an unexpected keyword argument '{key}'")
24
+ options = replace(options, **{key: value})
25
+
26
+ if options.silent or builtins.MULTITOOL_TOGGLE_SILENT:
27
+ if options.exit_status is not None:
28
+ sys.exit(options.exit_status)
29
+ return
30
+
31
+ verbosity = max(options.verbosity, builtins.MULTITOOL_TOGGLE_VERBOSE)
32
+
33
+ if verbosity >= options.level:
34
+ print(*msg, end=options.end, flush=options.flush)
35
+
36
+ if options.exit_status is not None:
37
+ sys.exit(options.exit_status)