multitool 0.7.2__tar.gz → 0.8.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 (24) hide show
  1. {multitool-0.7.2 → multitool-0.8.0}/PKG-INFO +63 -13
  2. {multitool-0.7.2 → multitool-0.8.0}/README.rst +61 -12
  3. {multitool-0.7.2 → multitool-0.8.0}/multitool/__init__.py +1 -1
  4. {multitool-0.7.2 → multitool-0.8.0}/multitool/plugins/commands.py +195 -20
  5. {multitool-0.7.2 → multitool-0.8.0}/multitool.egg-info/PKG-INFO +63 -13
  6. {multitool-0.7.2 → multitool-0.8.0}/multitool.egg-info/requires.txt +1 -0
  7. {multitool-0.7.2 → multitool-0.8.0}/pyproject.toml +3 -2
  8. {multitool-0.7.2 → multitool-0.8.0}/LICENSE +0 -0
  9. {multitool-0.7.2 → multitool-0.8.0}/multitool/__main__.py +0 -0
  10. {multitool-0.7.2 → multitool-0.8.0}/multitool/cls.py +0 -0
  11. {multitool-0.7.2 → multitool-0.8.0}/multitool/console.py +0 -0
  12. {multitool-0.7.2 → multitool-0.8.0}/multitool/exceptions.py +0 -0
  13. {multitool-0.7.2 → multitool-0.8.0}/multitool/plugins/__init__.py +0 -0
  14. {multitool-0.7.2 → multitool-0.8.0}/multitool/silent.py +0 -0
  15. {multitool-0.7.2 → multitool-0.8.0}/multitool/utils.py +0 -0
  16. {multitool-0.7.2 → multitool-0.8.0}/multitool/utils_init.py +0 -0
  17. {multitool-0.7.2 → multitool-0.8.0}/multitool/verbose.py +0 -0
  18. {multitool-0.7.2 → multitool-0.8.0}/multitool.egg-info/SOURCES.txt +0 -0
  19. {multitool-0.7.2 → multitool-0.8.0}/multitool.egg-info/dependency_links.txt +0 -0
  20. {multitool-0.7.2 → multitool-0.8.0}/multitool.egg-info/entry_points.txt +0 -0
  21. {multitool-0.7.2 → multitool-0.8.0}/multitool.egg-info/top_level.txt +0 -0
  22. {multitool-0.7.2 → multitool-0.8.0}/setup.cfg +0 -0
  23. {multitool-0.7.2 → multitool-0.8.0}/tests/__init__.py +0 -0
  24. {multitool-0.7.2 → multitool-0.8.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multitool
3
- Version: 0.7.2
3
+ Version: 0.8.0
4
4
  Summary: Create and run plugin-based command-line tools.
5
5
  Author-email: Matthew Delotavo <matthew.t.delotavo@gmail.com>
6
6
  License-Expression: MIT
@@ -19,6 +19,7 @@ Requires-Dist: click>=8.1.3
19
19
  Requires-Dist: click-aliases>=1.0.1
20
20
  Requires-Dist: click-option-group>=0.5.5
21
21
  Requires-Dist: GitPython>=3.1.30
22
+ Requires-Dist: packaging>=26.2
22
23
  Dynamic: license-file
23
24
 
24
25
  multitool
@@ -51,8 +52,7 @@ Run the example command::
51
52
  -vv \
52
53
  --enabled \
53
54
  --tag alpha \
54
- --tag beta \
55
- --output result.json
55
+ --tag beta
56
56
 
57
57
  Edit the generated source code::
58
58
 
@@ -147,10 +147,18 @@ Configuring
147
147
 
148
148
  Configure plugin repositories with::
149
149
 
150
- multitool plugins configure -a
150
+ multitool plugins configure
151
+
152
+ This opens your editor to modify the plugin configuration. After saving your
153
+ changes, apply them with::
154
+
155
+ multitool plugins update
156
+
157
+ To automatically apply the configuration changes after saving and closing the
158
+ file (install new plugins, update existing plugins, and prune removed plugins),
159
+ use::
151
160
 
152
- This opens your editor to modify the plugin configuration. Omit ``-a`` if you
153
- don't want changes applied automatically.
161
+ multitool plugins configure -a
154
162
 
155
163
  Example configuration::
156
164
 
@@ -172,11 +180,11 @@ Install the example plugins::
172
180
  echo -e '[sources]\nmdelotavo-multitool-plugins = https://github.com/mdelotavo/multitool-plugins.git' >> ~/.multitool/plugins/config
173
181
 
174
182
  multitool plugins update
183
+
175
184
  multitool plugins show
176
185
  multitool plugins show -n mdelotavo-multitool-plugins
177
186
  multitool plugins show -n mdelotavo-multitool-plugins --show-commit-only
178
187
  multitool plugins show -n mdelotavo-multitool-plugins --show-dependencies-only
179
- pip3 install $(multitool plugins show -n mdelotavo-multitool-plugins --show-dependencies-only)
180
188
 
181
189
  multitool run examples -h
182
190
 
@@ -184,10 +192,57 @@ Install the example plugins::
184
192
  Updating
185
193
  ^^^^^^^^
186
194
 
187
- Install new plugins and update existing ones::
195
+ Install new plugins, update existing ones, and automatically install any Python
196
+ dependencies declared in the Requires field of the plugin's
197
+ multitool-info.json file::
188
198
 
189
199
  multitool plugins update
190
200
 
201
+ Update a specific plugin only::
202
+
203
+ multitool plugins update -n PLUGIN_NAME
204
+
205
+ The Requires field is where a plugin declares the Python packages it
206
+ depends on. Any packages listed will be installed automatically when
207
+ multitool plugins update is run. For example::
208
+
209
+ {
210
+ "Homepage": "",
211
+ "Requires": "click>=8.1.3 click-aliases>=1.0.1 click-option-group>=0.5.5 GitPython>=3.1.30",
212
+ "Maintainer": "",
213
+ "Description-en": ""
214
+ }
215
+
216
+ If one or more dependencies could not be installed automatically, multitool
217
+ will report the failures and display the appropriate pip command to install
218
+ the remaining packages manually. For example::
219
+
220
+ Updating plugins
221
+ ----------------
222
+ Updating mdelotavo-multitool-plugins... Done
223
+ Updating test... Skipped Git pull (not a Git repository)
224
+
225
+ Checking Python package dependencies
226
+ ------------------------------------
227
+ Checked GitPython (GitPython>=3.1.30)... 3.1.50 installed
228
+ Installing asdfasdfasdf... Failed
229
+ Checked click-aliases (click-aliases>=1.0.1)... 1.0.1 installed
230
+ Checked click-option-group (click-option-group>=0.5.5)... 0.5.5 installed
231
+ Checked click (click>=8.1.3)... 8.1.8 installed
232
+ Installing qwerqwerqwer>=8.1.3... Failed
233
+ Checked tabulate (tabulate)... 0.9.0 installed
234
+
235
+ The following dependencies could not be installed automatically:
236
+ asdfasdfasdf
237
+ qwerqwerqwer>=8.1.3
238
+
239
+ Try installing them manually:
240
+ /usr/bin/python3 -m pip install asdfasdfasdf qwerqwerqwer>=8.1.3
241
+
242
+ You can also display a plugin's declared dependencies at any time::
243
+
244
+ multitool plugins show --name PLUGIN_NAME --show-dependencies-only
245
+
191
246
  ^^^^^^^
192
247
  Pruning
193
248
  ^^^^^^^
@@ -209,11 +264,6 @@ Or inspect a specific repository::
209
264
  multitool plugins show -n PLUGIN_NAME --show-commit-only
210
265
  multitool plugins show -n PLUGIN_NAME --show-dependencies-only
211
266
 
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
267
  ---------------
218
268
  Troubleshooting
219
269
  ---------------
@@ -28,8 +28,7 @@ Run the example command::
28
28
  -vv \
29
29
  --enabled \
30
30
  --tag alpha \
31
- --tag beta \
32
- --output result.json
31
+ --tag beta
33
32
 
34
33
  Edit the generated source code::
35
34
 
@@ -124,10 +123,18 @@ Configuring
124
123
 
125
124
  Configure plugin repositories with::
126
125
 
127
- multitool plugins configure -a
126
+ multitool plugins configure
127
+
128
+ This opens your editor to modify the plugin configuration. After saving your
129
+ changes, apply them with::
130
+
131
+ multitool plugins update
132
+
133
+ To automatically apply the configuration changes after saving and closing the
134
+ file (install new plugins, update existing plugins, and prune removed plugins),
135
+ use::
128
136
 
129
- This opens your editor to modify the plugin configuration. Omit ``-a`` if you
130
- don't want changes applied automatically.
137
+ multitool plugins configure -a
131
138
 
132
139
  Example configuration::
133
140
 
@@ -149,11 +156,11 @@ Install the example plugins::
149
156
  echo -e '[sources]\nmdelotavo-multitool-plugins = https://github.com/mdelotavo/multitool-plugins.git' >> ~/.multitool/plugins/config
150
157
 
151
158
  multitool plugins update
159
+
152
160
  multitool plugins show
153
161
  multitool plugins show -n mdelotavo-multitool-plugins
154
162
  multitool plugins show -n mdelotavo-multitool-plugins --show-commit-only
155
163
  multitool plugins show -n mdelotavo-multitool-plugins --show-dependencies-only
156
- pip3 install $(multitool plugins show -n mdelotavo-multitool-plugins --show-dependencies-only)
157
164
 
158
165
  multitool run examples -h
159
166
 
@@ -161,10 +168,57 @@ Install the example plugins::
161
168
  Updating
162
169
  ^^^^^^^^
163
170
 
164
- Install new plugins and update existing ones::
171
+ Install new plugins, update existing ones, and automatically install any Python
172
+ dependencies declared in the Requires field of the plugin's
173
+ multitool-info.json file::
165
174
 
166
175
  multitool plugins update
167
176
 
177
+ Update a specific plugin only::
178
+
179
+ multitool plugins update -n PLUGIN_NAME
180
+
181
+ The Requires field is where a plugin declares the Python packages it
182
+ depends on. Any packages listed will be installed automatically when
183
+ multitool plugins update is run. For example::
184
+
185
+ {
186
+ "Homepage": "",
187
+ "Requires": "click>=8.1.3 click-aliases>=1.0.1 click-option-group>=0.5.5 GitPython>=3.1.30",
188
+ "Maintainer": "",
189
+ "Description-en": ""
190
+ }
191
+
192
+ If one or more dependencies could not be installed automatically, multitool
193
+ will report the failures and display the appropriate pip command to install
194
+ the remaining packages manually. For example::
195
+
196
+ Updating plugins
197
+ ----------------
198
+ Updating mdelotavo-multitool-plugins... Done
199
+ Updating test... Skipped Git pull (not a Git repository)
200
+
201
+ Checking Python package dependencies
202
+ ------------------------------------
203
+ Checked GitPython (GitPython>=3.1.30)... 3.1.50 installed
204
+ Installing asdfasdfasdf... Failed
205
+ Checked click-aliases (click-aliases>=1.0.1)... 1.0.1 installed
206
+ Checked click-option-group (click-option-group>=0.5.5)... 0.5.5 installed
207
+ Checked click (click>=8.1.3)... 8.1.8 installed
208
+ Installing qwerqwerqwer>=8.1.3... Failed
209
+ Checked tabulate (tabulate)... 0.9.0 installed
210
+
211
+ The following dependencies could not be installed automatically:
212
+ asdfasdfasdf
213
+ qwerqwerqwer>=8.1.3
214
+
215
+ Try installing them manually:
216
+ /usr/bin/python3 -m pip install asdfasdfasdf qwerqwerqwer>=8.1.3
217
+
218
+ You can also display a plugin's declared dependencies at any time::
219
+
220
+ multitool plugins show --name PLUGIN_NAME --show-dependencies-only
221
+
168
222
  ^^^^^^^
169
223
  Pruning
170
224
  ^^^^^^^
@@ -186,11 +240,6 @@ Or inspect a specific repository::
186
240
  multitool plugins show -n PLUGIN_NAME --show-commit-only
187
241
  multitool plugins show -n PLUGIN_NAME --show-dependencies-only
188
242
 
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
243
  ---------------
195
244
  Troubleshooting
196
245
  ---------------
@@ -1,5 +1,5 @@
1
1
  APP = "multitool"
2
- __version__ = "0.7.2"
2
+ __version__ = "0.8.0"
3
3
  description = "Create and run plugin-based command-line tools."
4
4
  long_description = ("Create and run plugin-based command-line tools by dynamically loading Click commands from local or Git-managed plugin repositories.")
5
5
 
@@ -4,6 +4,10 @@ import shutil
4
4
  import stat
5
5
  import sys
6
6
  import uuid
7
+ import shutil
8
+ import subprocess
9
+ from importlib.metadata import PackageNotFoundError, version
10
+ from packaging.requirements import Requirement
7
11
  from pathlib import Path
8
12
 
9
13
  import click
@@ -69,32 +73,173 @@ def clone():
69
73
  console.echo(e)
70
74
 
71
75
 
72
- def update_repos():
76
+ def pip_command():
77
+ if sys.executable:
78
+ return [sys.executable, "-m", "pip"]
73
79
 
74
- def fn(p):
75
- if not is_dir(p):
76
- return
80
+ if shutil.which("pip"):
81
+ return ["pip"]
77
82
 
78
- name = Path(p).stem
83
+ if shutil.which("pip3"):
84
+ return ["pip3"]
85
+
86
+ return None
87
+
88
+
89
+ def package_installed(package):
90
+ try:
91
+ version(package)
92
+ return True
93
+ except PackageNotFoundError:
94
+ return False
95
+
96
+
97
+ def parse_requires(info):
98
+ requires = info.get("Requires")
99
+
100
+ if not requires:
101
+ return []
102
+
103
+ if isinstance(requires, str):
104
+ return requires.split()
105
+
106
+ if isinstance(requires, (list, tuple, set)):
107
+ return [str(r).strip() for r in requires if str(r).strip()]
108
+
109
+ return []
110
+
111
+
112
+ def package_status(requirement):
113
+ req = Requirement(requirement)
114
+
115
+ try:
116
+ installed = version(req.name)
117
+ except PackageNotFoundError:
118
+ return req, None, False
119
+
120
+ return req, installed, req.specifier.contains(installed, prereleases=True)
121
+
122
+
123
+ def install_plugin_dependencies(name=None):
124
+ cmd = pip_command()
125
+
126
+ if cmd is None:
127
+ console.echo("Unable to locate pip.")
128
+ return
129
+
130
+ required = set()
131
+
132
+ def add_dependencies(plugin_name):
133
+ info = plugin_info(plugin_name)
134
+
135
+ if info:
136
+ required.update(parse_requires(info))
137
+
138
+ if name:
139
+ path = Path(MULTITOOL_PLUGINS_DIRECTORY) / name
140
+
141
+ if not is_dir(path):
142
+ console.echo(f'Plugin "{name}" not found.')
143
+ sys.exit(1)
144
+
145
+ add_dependencies(name)
146
+
147
+ else:
148
+
149
+ def fn(path):
150
+ if is_dir(path):
151
+ add_dependencies(Path(path).stem)
152
+
153
+ for_each_file(
154
+ MULTITOOL_PLUGINS_DIRECTORY,
155
+ fn,
156
+ glob="[!.][!__]*",
157
+ )
79
158
 
80
- console.echo(f"Updating {name}... ", end="", flush=True)
159
+ failed = []
160
+
161
+ for requirement in sorted(required):
162
+ req, installed, satisfied = package_status(requirement)
163
+
164
+ if satisfied:
165
+ console.echo(f"Checked {req.name} ({requirement})... "
166
+ f"{installed} installed")
167
+ continue
168
+
169
+ if installed is None:
170
+ console.echo(
171
+ f"Installing {requirement}... ",
172
+ end="",
173
+ flush=True,
174
+ )
175
+ else:
176
+ console.echo(
177
+ f"Updating {req.name} ({installed} -> {requirement})... ",
178
+ end="",
179
+ flush=True,
180
+ )
181
+
182
+ result = subprocess.run(
183
+ cmd + ["install", requirement],
184
+ stdout=subprocess.DEVNULL,
185
+ stderr=subprocess.DEVNULL,
186
+ )
187
+
188
+ if result.returncode == 0:
189
+ console.echo("Done")
190
+ else:
191
+ console.echo("Failed")
192
+ failed.append(requirement)
193
+
194
+ if failed:
195
+ console.echo()
196
+ console.echo("The following dependencies could not be installed automatically:")
197
+
198
+ for requirement in failed:
199
+ console.echo(f" {requirement}")
200
+
201
+ console.echo()
202
+ console.echo("Try installing them manually:")
203
+ console.echo(f" {' '.join(cmd)} install {' '.join(failed)}")
204
+
205
+
206
+ def update_repos(name=None):
207
+
208
+ def update_repo(path):
209
+ plugin_name = Path(path).stem
210
+
211
+ console.echo(f"Updating {plugin_name}... ", end="", flush=True)
81
212
 
82
213
  try:
83
- repo = Repo(p)
214
+ repo = Repo(path)
84
215
 
85
216
  if repo.bare:
86
- console.echo("Skipped (bare repository)")
217
+ console.echo("Skipped Git pull (bare repository)")
87
218
  return
88
219
 
89
220
  if not repo.remotes:
90
- console.echo("Skipped (Git repository has no remote)")
221
+ console.echo("Skipped Git pull (Git repository has no remote)")
91
222
  return
92
223
 
93
224
  repo.remotes["origin"].pull()
94
225
  console.echo("Done")
95
226
 
96
227
  except Exception:
97
- console.echo("Skipped (not a Git repository)")
228
+ console.echo("Skipped Git pull (not a Git repository)")
229
+
230
+ if name:
231
+ path = Path(MULTITOOL_PLUGINS_DIRECTORY) / name
232
+
233
+ if not is_dir(path):
234
+ console.echo(f'Plugin "{name}" not found.')
235
+ sys.exit(1)
236
+
237
+ update_repo(path)
238
+ return
239
+
240
+ def fn(path):
241
+ if is_dir(path):
242
+ update_repo(path)
98
243
 
99
244
  for_each_file(
100
245
  MULTITOOL_PLUGINS_DIRECTORY,
@@ -181,10 +326,17 @@ def configure(silent, verbose, apply_changes):
181
326
  @plugins.command()
182
327
  @common_silent_options
183
328
  @common_verbose_options
184
- def update(silent, verbose):
329
+ @click.option("-n", "--name")
330
+ def update(silent, verbose, name):
185
331
  require_git()
186
332
  clone()
187
- update_repos()
333
+ console.echo("Updating plugins")
334
+ console.echo("----------------")
335
+ update_repos(name)
336
+ console.echo()
337
+ console.echo("Checking Python package dependencies")
338
+ console.echo("------------------------------------")
339
+ install_plugin_dependencies(name)
188
340
 
189
341
 
190
342
  @plugins.command()
@@ -260,7 +412,36 @@ def prune(silent, verbose):
260
412
  @common_silent_options
261
413
  @common_verbose_options
262
414
  @click.argument("name")
263
- def new(silent, verbose, name):
415
+ @click.option(
416
+ "--requires-format",
417
+ type=click.Choice(["array", "string"], case_sensitive=False),
418
+ default="string",
419
+ show_default=True,
420
+ help="Format to use for the Requires field.",
421
+ )
422
+ def new(silent, verbose, name, requires_format):
423
+ if requires_format == "array":
424
+ info = """{
425
+ "Homepage": "",
426
+ "Requires": [
427
+ "click>=8.1.3",
428
+ "click-aliases>=1.0.1",
429
+ "click-option-group>=0.5.5",
430
+ "GitPython>=3.1.30"
431
+ ],
432
+ "Maintainer": "",
433
+ "Description-en": ""
434
+ }
435
+ """
436
+ elif requires_format == "string":
437
+ info = """{
438
+ "Homepage": "",
439
+ "Requires": "click>=8.1.3 click-aliases>=1.0.1 click-option-group>=0.5.5 GitPython>=3.1.30",
440
+ "Maintainer": "",
441
+ "Description-en": ""
442
+ }
443
+ """
444
+
264
445
  init()
265
446
 
266
447
  if not name.isidentifier():
@@ -381,13 +562,7 @@ def hello(
381
562
  '''
382
563
  )
383
564
 
384
- (root / f"{APP}-info.json").write_text("""{
385
- "Homepage": "",
386
- "Requires": "click",
387
- "Maintainer": "",
388
- "Description-en": ""
389
- }
390
- """)
565
+ (root / f"{APP}-info.json").write_text(info)
391
566
 
392
567
  touch(root / "README.md")
393
568
  touch(root / "LICENSE")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multitool
3
- Version: 0.7.2
3
+ Version: 0.8.0
4
4
  Summary: Create and run plugin-based command-line tools.
5
5
  Author-email: Matthew Delotavo <matthew.t.delotavo@gmail.com>
6
6
  License-Expression: MIT
@@ -19,6 +19,7 @@ Requires-Dist: click>=8.1.3
19
19
  Requires-Dist: click-aliases>=1.0.1
20
20
  Requires-Dist: click-option-group>=0.5.5
21
21
  Requires-Dist: GitPython>=3.1.30
22
+ Requires-Dist: packaging>=26.2
22
23
  Dynamic: license-file
23
24
 
24
25
  multitool
@@ -51,8 +52,7 @@ Run the example command::
51
52
  -vv \
52
53
  --enabled \
53
54
  --tag alpha \
54
- --tag beta \
55
- --output result.json
55
+ --tag beta
56
56
 
57
57
  Edit the generated source code::
58
58
 
@@ -147,10 +147,18 @@ Configuring
147
147
 
148
148
  Configure plugin repositories with::
149
149
 
150
- multitool plugins configure -a
150
+ multitool plugins configure
151
+
152
+ This opens your editor to modify the plugin configuration. After saving your
153
+ changes, apply them with::
154
+
155
+ multitool plugins update
156
+
157
+ To automatically apply the configuration changes after saving and closing the
158
+ file (install new plugins, update existing plugins, and prune removed plugins),
159
+ use::
151
160
 
152
- This opens your editor to modify the plugin configuration. Omit ``-a`` if you
153
- don't want changes applied automatically.
161
+ multitool plugins configure -a
154
162
 
155
163
  Example configuration::
156
164
 
@@ -172,11 +180,11 @@ Install the example plugins::
172
180
  echo -e '[sources]\nmdelotavo-multitool-plugins = https://github.com/mdelotavo/multitool-plugins.git' >> ~/.multitool/plugins/config
173
181
 
174
182
  multitool plugins update
183
+
175
184
  multitool plugins show
176
185
  multitool plugins show -n mdelotavo-multitool-plugins
177
186
  multitool plugins show -n mdelotavo-multitool-plugins --show-commit-only
178
187
  multitool plugins show -n mdelotavo-multitool-plugins --show-dependencies-only
179
- pip3 install $(multitool plugins show -n mdelotavo-multitool-plugins --show-dependencies-only)
180
188
 
181
189
  multitool run examples -h
182
190
 
@@ -184,10 +192,57 @@ Install the example plugins::
184
192
  Updating
185
193
  ^^^^^^^^
186
194
 
187
- Install new plugins and update existing ones::
195
+ Install new plugins, update existing ones, and automatically install any Python
196
+ dependencies declared in the Requires field of the plugin's
197
+ multitool-info.json file::
188
198
 
189
199
  multitool plugins update
190
200
 
201
+ Update a specific plugin only::
202
+
203
+ multitool plugins update -n PLUGIN_NAME
204
+
205
+ The Requires field is where a plugin declares the Python packages it
206
+ depends on. Any packages listed will be installed automatically when
207
+ multitool plugins update is run. For example::
208
+
209
+ {
210
+ "Homepage": "",
211
+ "Requires": "click>=8.1.3 click-aliases>=1.0.1 click-option-group>=0.5.5 GitPython>=3.1.30",
212
+ "Maintainer": "",
213
+ "Description-en": ""
214
+ }
215
+
216
+ If one or more dependencies could not be installed automatically, multitool
217
+ will report the failures and display the appropriate pip command to install
218
+ the remaining packages manually. For example::
219
+
220
+ Updating plugins
221
+ ----------------
222
+ Updating mdelotavo-multitool-plugins... Done
223
+ Updating test... Skipped Git pull (not a Git repository)
224
+
225
+ Checking Python package dependencies
226
+ ------------------------------------
227
+ Checked GitPython (GitPython>=3.1.30)... 3.1.50 installed
228
+ Installing asdfasdfasdf... Failed
229
+ Checked click-aliases (click-aliases>=1.0.1)... 1.0.1 installed
230
+ Checked click-option-group (click-option-group>=0.5.5)... 0.5.5 installed
231
+ Checked click (click>=8.1.3)... 8.1.8 installed
232
+ Installing qwerqwerqwer>=8.1.3... Failed
233
+ Checked tabulate (tabulate)... 0.9.0 installed
234
+
235
+ The following dependencies could not be installed automatically:
236
+ asdfasdfasdf
237
+ qwerqwerqwer>=8.1.3
238
+
239
+ Try installing them manually:
240
+ /usr/bin/python3 -m pip install asdfasdfasdf qwerqwerqwer>=8.1.3
241
+
242
+ You can also display a plugin's declared dependencies at any time::
243
+
244
+ multitool plugins show --name PLUGIN_NAME --show-dependencies-only
245
+
191
246
  ^^^^^^^
192
247
  Pruning
193
248
  ^^^^^^^
@@ -209,11 +264,6 @@ Or inspect a specific repository::
209
264
  multitool plugins show -n PLUGIN_NAME --show-commit-only
210
265
  multitool plugins show -n PLUGIN_NAME --show-dependencies-only
211
266
 
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
267
  ---------------
218
268
  Troubleshooting
219
269
  ---------------
@@ -2,3 +2,4 @@ click>=8.1.3
2
2
  click-aliases>=1.0.1
3
3
  click-option-group>=0.5.5
4
4
  GitPython>=3.1.30
5
+ packaging>=26.2
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "multitool"
7
- version = "0.7.2"
7
+ version = "0.8.0"
8
8
  description = "Create and run plugin-based command-line tools."
9
9
  readme = { file = "README.rst", content-type = "text/x-rst" }
10
10
  requires-python = ">=3.7"
@@ -28,7 +28,8 @@ dependencies = [
28
28
  "click>=8.1.3",
29
29
  "click-aliases>=1.0.1",
30
30
  "click-option-group>=0.5.5",
31
- "GitPython>=3.1.30"
31
+ "GitPython>=3.1.30",
32
+ "packaging>=26.2"
32
33
  ]
33
34
 
34
35
  [project.urls]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes