pyselfupdate 0.5.3__tar.gz → 0.5.5__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.
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/PKG-INFO +18 -4
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/README.md +17 -3
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/pyproject.toml +18 -19
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/install.py +65 -10
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/notifier.py +0 -4
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/typercmd.py +39 -12
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/updater.py +9 -4
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/.gitignore +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/LICENSE +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/__init__.py +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/config.py +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/errors.py +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/github.py +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/py.typed +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/source.py +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/state.py +0 -0
- {pyselfupdate-0.5.3 → pyselfupdate-0.5.5}/src/pyselfupdate/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyselfupdate
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Summary: Self-update and update notification for Python CLIs installed with uv tool
|
|
5
5
|
Project-URL: Repository, https://github.com/datapointchris/pyselfupdate
|
|
6
6
|
Project-URL: Issues, https://github.com/datapointchris/pyselfupdate/issues
|
|
@@ -143,8 +143,8 @@ fail in ways that are hard to read.
|
|
|
143
143
|
|
|
144
144
|
So make `update` the last thing the process does, then call `exit_now`. Or call
|
|
145
145
|
`update_and_reexec`, which replaces the process with the release it installed,
|
|
146
|
-
running the same command line.
|
|
147
|
-
|
|
146
|
+
running the same command line. `update_and_reexec` writes `lock_warning` to
|
|
147
|
+
stderr itself before the re-exec, because its caller never sees the `Result`.
|
|
148
148
|
|
|
149
149
|
`check` reports what `update` would install, and installs nothing:
|
|
150
150
|
|
|
@@ -221,12 +221,26 @@ later `uv tool upgrade` stays held to them.
|
|
|
221
221
|
| Carries none | Installs unlocked and sets `lock_missing`. `lock_warning` names the tag, and `run_update` and `update_and_reexec` print it |
|
|
222
222
|
| Will not clone, uv will not export its lock, or git or uv is not on `PATH` | Raises `LockUnreadableError` and installs nothing |
|
|
223
223
|
|
|
224
|
-
An install from an index reads no lock, because a wheel carries none.
|
|
224
|
+
An install from an index reads no lock, because a wheel carries none. It
|
|
225
|
+
takes the release as a floor, `mytool>=2.0.0`, rather than a pin. uv records
|
|
226
|
+
the specifier in the receipt, and `uv tool upgrade` refuses to move an exact
|
|
227
|
+
pin.
|
|
225
228
|
|
|
226
229
|
An update reinstalls the tool's own extras, as in `mytool[fast]`, with their
|
|
227
230
|
dependencies held to the lock. Every `--with` and `--with-editable`
|
|
228
231
|
requirement is passed back to uv as well.
|
|
229
232
|
|
|
233
|
+
`uv tool install --force` reads nothing back from the receipt it replaces. So
|
|
234
|
+
an update passes back the receipt's `--python` and the flags naming where its
|
|
235
|
+
packages come from: `--index`, `--default-index`, `--index-url`,
|
|
236
|
+
`--extra-index-url`, `--find-links`, `--no-index`, `--index-strategy` and
|
|
237
|
+
`--keyring-provider`. Without them, a tool installed with
|
|
238
|
+
`--index https://pypi.example.com/simple/` resolves its update against PyPI
|
|
239
|
+
alone and is not found. An `explicit` index is left out, because only a config
|
|
240
|
+
file can declare one and the reinstall reads that file again. Dropping any
|
|
241
|
+
other recorded option changes how the tool resolves or builds, never whether it
|
|
242
|
+
is found.
|
|
243
|
+
|
|
230
244
|
## What may be updated is read from uv's receipt, not guessed from the version
|
|
231
245
|
|
|
232
246
|
uv writes `uv-receipt.toml` into the tool's directory at install time. It
|
|
@@ -120,8 +120,8 @@ fail in ways that are hard to read.
|
|
|
120
120
|
|
|
121
121
|
So make `update` the last thing the process does, then call `exit_now`. Or call
|
|
122
122
|
`update_and_reexec`, which replaces the process with the release it installed,
|
|
123
|
-
running the same command line.
|
|
124
|
-
|
|
123
|
+
running the same command line. `update_and_reexec` writes `lock_warning` to
|
|
124
|
+
stderr itself before the re-exec, because its caller never sees the `Result`.
|
|
125
125
|
|
|
126
126
|
`check` reports what `update` would install, and installs nothing:
|
|
127
127
|
|
|
@@ -198,12 +198,26 @@ later `uv tool upgrade` stays held to them.
|
|
|
198
198
|
| Carries none | Installs unlocked and sets `lock_missing`. `lock_warning` names the tag, and `run_update` and `update_and_reexec` print it |
|
|
199
199
|
| Will not clone, uv will not export its lock, or git or uv is not on `PATH` | Raises `LockUnreadableError` and installs nothing |
|
|
200
200
|
|
|
201
|
-
An install from an index reads no lock, because a wheel carries none.
|
|
201
|
+
An install from an index reads no lock, because a wheel carries none. It
|
|
202
|
+
takes the release as a floor, `mytool>=2.0.0`, rather than a pin. uv records
|
|
203
|
+
the specifier in the receipt, and `uv tool upgrade` refuses to move an exact
|
|
204
|
+
pin.
|
|
202
205
|
|
|
203
206
|
An update reinstalls the tool's own extras, as in `mytool[fast]`, with their
|
|
204
207
|
dependencies held to the lock. Every `--with` and `--with-editable`
|
|
205
208
|
requirement is passed back to uv as well.
|
|
206
209
|
|
|
210
|
+
`uv tool install --force` reads nothing back from the receipt it replaces. So
|
|
211
|
+
an update passes back the receipt's `--python` and the flags naming where its
|
|
212
|
+
packages come from: `--index`, `--default-index`, `--index-url`,
|
|
213
|
+
`--extra-index-url`, `--find-links`, `--no-index`, `--index-strategy` and
|
|
214
|
+
`--keyring-provider`. Without them, a tool installed with
|
|
215
|
+
`--index https://pypi.example.com/simple/` resolves its update against PyPI
|
|
216
|
+
alone and is not found. An `explicit` index is left out, because only a config
|
|
217
|
+
file can declare one and the reinstall reads that file again. Dropping any
|
|
218
|
+
other recorded option changes how the tool resolves or builds, never whether it
|
|
219
|
+
is found.
|
|
220
|
+
|
|
207
221
|
## What may be updated is read from uv's receipt, not guessed from the version
|
|
208
222
|
|
|
209
223
|
uv writes `uv-receipt.toml` into the tool's directory at install time. It
|
|
@@ -42,7 +42,7 @@ dev = [
|
|
|
42
42
|
"mypy>=1.10.0",
|
|
43
43
|
"pre-commit>=4.3.0",
|
|
44
44
|
"pytest>=8.0.0",
|
|
45
|
-
"ruff==0.
|
|
45
|
+
"ruff==0.17.1",
|
|
46
46
|
"typer>=0.12.0",
|
|
47
47
|
]
|
|
48
48
|
|
|
@@ -95,6 +95,7 @@ exclude = [
|
|
|
95
95
|
"build",
|
|
96
96
|
"dist",
|
|
97
97
|
]
|
|
98
|
+
extend-exclude = ["*.md"]
|
|
98
99
|
|
|
99
100
|
[tool.ruff.format]
|
|
100
101
|
quote-style = "single"
|
|
@@ -158,12 +159,28 @@ major_on_zero = false
|
|
|
158
159
|
typeCheckingMode = "standard"
|
|
159
160
|
analyzeUnannotatedFunctions = true # or pylance stops analyzing in vscode
|
|
160
161
|
|
|
162
|
+
[tool.hatch.version]
|
|
163
|
+
source = "uv-dynamic-versioning"
|
|
164
|
+
|
|
165
|
+
[tool.uv-dynamic-versioning]
|
|
166
|
+
style = "semver"
|
|
167
|
+
|
|
168
|
+
# A new commit or tag changes the version, so it has to invalidate uv's cache.
|
|
169
|
+
[tool.uv]
|
|
170
|
+
cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true, tags = true } }]
|
|
171
|
+
|
|
172
|
+
# The published sdist holds the package and its metadata, and none of the
|
|
173
|
+
# repo's tooling or tests. hatchling's default takes the whole tree.
|
|
174
|
+
[tool.hatch.build.targets.sdist]
|
|
175
|
+
only-include = ["src"]
|
|
176
|
+
|
|
161
177
|
[tool.forge]
|
|
162
178
|
# Keys the shared toolchain standard owns, written by its generator.
|
|
163
179
|
# Dropping one from the template removes it here on the next sync; a key absent
|
|
164
180
|
# from this list belongs to the project and is never touched. Do not hand-edit.
|
|
165
181
|
managed = [
|
|
166
182
|
["ruff", "line-length"],
|
|
183
|
+
["ruff", "extend-exclude"],
|
|
167
184
|
["ruff", "format", "quote-style"],
|
|
168
185
|
["ruff", "format", "indent-style"],
|
|
169
186
|
["ruff", "format", "skip-magic-trailing-comma"],
|
|
@@ -197,24 +214,6 @@ managed = [
|
|
|
197
214
|
# and extra that names them, so a local run and CI cannot disagree.
|
|
198
215
|
pinned = ["ruff"]
|
|
199
216
|
|
|
200
|
-
# The release tag is the version. python-semantic-release finds no file to
|
|
201
|
-
# stamp, so it tags the merge commit itself and writes no release commit.
|
|
202
|
-
# uv_build cannot do this: its metadata has no dynamic version field.
|
|
203
|
-
[tool.hatch.version]
|
|
204
|
-
source = "uv-dynamic-versioning"
|
|
205
|
-
|
|
206
|
-
[tool.uv-dynamic-versioning]
|
|
207
|
-
style = "semver"
|
|
208
|
-
|
|
209
|
-
# A new commit or tag changes the version, so it has to invalidate uv's cache.
|
|
210
|
-
[tool.uv]
|
|
211
|
-
cache-keys = [{ file = "pyproject.toml" }, { git = { commit = true, tags = true } }]
|
|
212
|
-
|
|
213
|
-
# The published sdist holds the package and its metadata, and none of the
|
|
214
|
-
# repo's tooling or tests, as uv_build's did.
|
|
215
|
-
[tool.hatch.build.targets.sdist]
|
|
216
|
-
only-include = ["src"]
|
|
217
|
-
|
|
218
217
|
[build-system]
|
|
219
218
|
requires = ["hatchling", "uv-dynamic-versioning"]
|
|
220
219
|
build-backend = "hatchling.build"
|
|
@@ -77,6 +77,11 @@ class Installation:
|
|
|
77
77
|
# editable back.
|
|
78
78
|
with_requirements: tuple[str, ...] = ()
|
|
79
79
|
|
|
80
|
+
# uv flags reproducing the receipt's Python and package sources, from
|
|
81
|
+
# `_source_flags`. `uv tool install --force` reads neither back from the
|
|
82
|
+
# receipt it replaces, so an update passes them itself.
|
|
83
|
+
source_flags: tuple[str, ...] = ()
|
|
84
|
+
|
|
80
85
|
# Names of the receipt requirements `_requirement_line` returns None for.
|
|
81
86
|
# `read_installation` records them rather than raising: the notify gate
|
|
82
87
|
# treats a failed read as a local install and prints no notice. `update`
|
|
@@ -90,6 +95,7 @@ def tool_dir() -> Path:
|
|
|
90
95
|
`UV_TOOL_DIR` first. Otherwise uv's data directory: `%APPDATA%\\uv` on
|
|
91
96
|
Windows, where uv ignores `XDG_DATA_HOME`, and `$XDG_DATA_HOME/uv` or
|
|
92
97
|
`~/.local/share/uv` everywhere else.
|
|
98
|
+
|
|
93
99
|
Resolved from the environment rather than by running `uv tool dir`, which
|
|
94
100
|
costs a subprocess on a path that runs before every command.
|
|
95
101
|
"""
|
|
@@ -122,7 +128,8 @@ def read_installation(tool: str) -> Installation:
|
|
|
122
128
|
except (OSError, tomllib.TOMLDecodeError) as error:
|
|
123
129
|
raise NotInstalledError(f'cannot read {receipt}: {error}') from error
|
|
124
130
|
|
|
125
|
-
|
|
131
|
+
recorded = payload.get('tool') or {}
|
|
132
|
+
requirements = recorded.get('requirements') or []
|
|
126
133
|
own = next((requirement for requirement in requirements if requirement.get('name') == tool), None)
|
|
127
134
|
if own is None:
|
|
128
135
|
raise NotInstalledError(f'{receipt} lists no requirement named {tool}')
|
|
@@ -140,7 +147,7 @@ def read_installation(tool: str) -> Installation:
|
|
|
140
147
|
|
|
141
148
|
def installed_as(kind: InstallKind, url: str = '', revision: str = '') -> Installation:
|
|
142
149
|
extras = tuple(own.get('extras') or ())
|
|
143
|
-
return Installation(tool, kind, url, revision, extras, tuple(rebuilt), tuple(unrebuildable))
|
|
150
|
+
return Installation(tool, kind, url, revision, extras, tuple(rebuilt), _source_flags(recorded), tuple(unrebuildable))
|
|
144
151
|
|
|
145
152
|
# A local checkout, however uv spelled it. Reinstalling one would
|
|
146
153
|
# discard the working copy the user is developing against.
|
|
@@ -156,6 +163,43 @@ def read_installation(tool: str) -> Installation:
|
|
|
156
163
|
return installed_as(InstallKind.INDEX)
|
|
157
164
|
|
|
158
165
|
|
|
166
|
+
def _source_flags(recorded: dict) -> tuple[str, ...]:
|
|
167
|
+
"""The uv flags that reinstall onto the receipt's Python from the receipt's package sources.
|
|
168
|
+
|
|
169
|
+
Without them a reinstall resolves against PyPI alone and moves to uv's
|
|
170
|
+
default Python. uv records a setting from a flag, the environment or a
|
|
171
|
+
config file alike, and cannot say which. An `explicit` or `flat` index only
|
|
172
|
+
a config file can declare, and that file is read again by the reinstall, so
|
|
173
|
+
it is left out. Dropping any other recorded option changes how the tool
|
|
174
|
+
resolves or builds, never whether its packages can be found.
|
|
175
|
+
"""
|
|
176
|
+
options = recorded.get('options') or {}
|
|
177
|
+
pairs: list[tuple[str, str]] = []
|
|
178
|
+
if recorded.get('python'):
|
|
179
|
+
pairs.append(('--python', str(recorded['python'])))
|
|
180
|
+
for index in options.get('index') or ():
|
|
181
|
+
if index.get('explicit') or index.get('format', 'simple') != 'simple' or not index.get('url'):
|
|
182
|
+
continue
|
|
183
|
+
if index.get('default'):
|
|
184
|
+
pairs.append(('--default-index', str(index['url'])))
|
|
185
|
+
else:
|
|
186
|
+
name = index.get('name')
|
|
187
|
+
pairs.append(('--index', f'{name}={index["url"]}' if name else str(index['url'])))
|
|
188
|
+
if options.get('index-url'):
|
|
189
|
+
pairs.append(('--index-url', str(options['index-url'])))
|
|
190
|
+
pairs += [('--extra-index-url', str(url)) for url in options.get('extra-index-url') or ()]
|
|
191
|
+
pairs += [('--find-links', str(location)) for location in options.get('find-links') or ()]
|
|
192
|
+
pairs += [(f'--{key}', str(options[key])) for key in ('index-strategy', 'keyring-provider') if options.get(key)]
|
|
193
|
+
if options.get('no-index'):
|
|
194
|
+
pairs.append(('--no-index', ''))
|
|
195
|
+
|
|
196
|
+
# A source declared in a config file is recorded again beside the flag that
|
|
197
|
+
# passes it back. Passing each source once holds it at two copies, rather
|
|
198
|
+
# than adding one to the receipt per update.
|
|
199
|
+
distinct = dict.fromkeys(pairs)
|
|
200
|
+
return tuple(part for flag, value in distinct for part in (flag, value) if part)
|
|
201
|
+
|
|
202
|
+
|
|
159
203
|
def _split_git(git: str) -> tuple[str, dict[str, str], str]:
|
|
160
204
|
"""A receipt's git source as its URL, its query and the commit after `#`.
|
|
161
205
|
|
|
@@ -221,11 +265,15 @@ def current_version(package: str) -> str:
|
|
|
221
265
|
|
|
222
266
|
|
|
223
267
|
def requirement_for(installation: Installation, ref: str) -> str:
|
|
224
|
-
"""The requirement string that installs `ref` of an already-installed tool, with the extras it has.
|
|
268
|
+
"""The requirement string that installs `ref` of an already-installed tool, with the extras it has.
|
|
269
|
+
|
|
270
|
+
An index install takes `ref` as a floor. uv records the specifier in the
|
|
271
|
+
receipt, and `uv tool upgrade` refuses to move an exact pin.
|
|
272
|
+
"""
|
|
225
273
|
named = f'{installation.tool}[{",".join(installation.extras)}]' if installation.extras else installation.tool
|
|
226
274
|
if installation.kind is InstallKind.GIT:
|
|
227
275
|
return f'{named} @ git+{installation.url}@{ref}'
|
|
228
|
-
return f'{named}
|
|
276
|
+
return f'{named}>={ref.removeprefix("v")}'
|
|
229
277
|
|
|
230
278
|
|
|
231
279
|
@dataclass(frozen=True)
|
|
@@ -355,7 +403,14 @@ def _pins_from_export(exported: str, extras: dict[str, tuple[str, ...]]) -> Pins
|
|
|
355
403
|
return Pins(tuple(constraints), tuple(overrides))
|
|
356
404
|
|
|
357
405
|
|
|
358
|
-
def run_install(
|
|
406
|
+
def run_install(
|
|
407
|
+
requirement: str,
|
|
408
|
+
*,
|
|
409
|
+
quiet: bool = True,
|
|
410
|
+
pins: Pins | None = None,
|
|
411
|
+
with_requirements: Sequence[str] = (),
|
|
412
|
+
source_flags: Sequence[str] = (),
|
|
413
|
+
) -> None:
|
|
359
414
|
"""Install a requirement over any existing tool of that name, held to `pins` when given.
|
|
360
415
|
|
|
361
416
|
`with_requirements` are requirements-file lines installed beside the tool,
|
|
@@ -374,7 +429,7 @@ def run_install(requirement: str, *, quiet: bool = True, pins: Pins | None = Non
|
|
|
374
429
|
listed = Path(scratch) / 'with.txt'
|
|
375
430
|
listed.write_text(''.join(f'{line}\n' for line in with_requirements), encoding='utf-8')
|
|
376
431
|
beside = ['--with-requirements', str(listed)]
|
|
377
|
-
command = [executable, 'tool', 'install', '--force', *held, *beside, requirement]
|
|
432
|
+
command = [executable, 'tool', 'install', '--force', *source_flags, *held, *beside, requirement]
|
|
378
433
|
if quiet:
|
|
379
434
|
command.insert(1, '--quiet')
|
|
380
435
|
completed = _run(command)
|
|
@@ -388,11 +443,11 @@ def _run(command: list[str], cwd: Path | None = None) -> subprocess.CompletedPro
|
|
|
388
443
|
|
|
389
444
|
|
|
390
445
|
def _reason(completed: subprocess.CompletedProcess[str]) -> str:
|
|
391
|
-
"""Everything a failing command printed
|
|
446
|
+
"""Everything a failing command printed.
|
|
392
447
|
|
|
393
|
-
uv puts a lock's schema error on its
|
|
394
|
-
|
|
395
|
-
sentence.
|
|
448
|
+
No single line carries the reason: uv puts a lock's schema error on its
|
|
449
|
+
first line, git puts a missing ref on its last, and uv wraps a resolution
|
|
450
|
+
failure so its last line is half a sentence.
|
|
396
451
|
"""
|
|
397
452
|
return (completed.stderr or completed.stdout or '').strip() or f'exit status {completed.returncode}'
|
|
398
453
|
|
|
@@ -124,10 +124,6 @@ def _asking_for_help() -> bool:
|
|
|
124
124
|
screen. Nothing a notice is for happens on a help screen, and the check
|
|
125
125
|
costs a releases-API call and a state write every time.
|
|
126
126
|
|
|
127
|
-
It lands hardest on a reader that walks a tool's help. Reading every
|
|
128
|
-
command's help is one invocation per command plus one for the root, and
|
|
129
|
-
each of them reaches this.
|
|
130
|
-
|
|
131
127
|
A `--` ends the scan, because everything after it is the command's own
|
|
132
128
|
argument. Skipping when it should not have costs a missed notice, which is
|
|
133
129
|
the cheap direction to be wrong in.
|
|
@@ -32,10 +32,39 @@ from pyselfupdate.updater import install_release
|
|
|
32
32
|
from pyselfupdate.updater import require_updatable
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def update_help(tool: str, name: str, markup_mode: str | None) -> str:
|
|
36
|
+
"""The update command's help: one line on what it does, then annotated examples.
|
|
37
|
+
|
|
38
|
+
Each example keeps a line of its own under each of typer's markup modes,
|
|
39
|
+
which lay out a help's later paragraphs differently. Markdown joins a
|
|
40
|
+
paragraph's lines, so the examples sit in a fenced block there. Rich markup
|
|
41
|
+
keeps the lines as written and would print a fence's backticks. Without
|
|
42
|
+
rich, click rewraps every paragraph that does not open with its `\\b`
|
|
43
|
+
marker.
|
|
44
|
+
"""
|
|
45
|
+
command = f'{tool} {name}'
|
|
46
|
+
width = len(command) + len(' --no-changelog') + 2
|
|
47
|
+
examples = '\n'.join(
|
|
48
|
+
f'{call:<{width}}{purpose}'
|
|
49
|
+
for call, purpose in (
|
|
50
|
+
(command, 'install the newest and list its commits'),
|
|
51
|
+
(f'{command} --check', 'only report whether a newer one exists'),
|
|
52
|
+
(f'{command} --no-changelog', 'install it without listing its commits'),
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
if markup_mode == 'markdown':
|
|
56
|
+
block = f'```text\n{examples}\n```'
|
|
57
|
+
elif markup_mode == 'rich':
|
|
58
|
+
block = examples
|
|
59
|
+
else:
|
|
60
|
+
block = f'\b\n{examples}'
|
|
61
|
+
return f'Update {tool} to the latest release\n\nExamples:\n\n{block}'
|
|
62
|
+
|
|
63
|
+
|
|
35
64
|
def add_update_command(app: typer.Typer, config: Config, *, name: str = 'update') -> None:
|
|
36
65
|
"""Register `<tool> update` on an existing typer app."""
|
|
37
66
|
|
|
38
|
-
@app.command(name=name, help=
|
|
67
|
+
@app.command(name=name, help=update_help(config.tool, name, app.rich_markup_mode))
|
|
39
68
|
def update_command(
|
|
40
69
|
check_only: bool = typer.Option(False, '--check', help='Report whether an update is available without installing it'),
|
|
41
70
|
skip_changelog: bool = typer.Option(False, '--no-changelog', help='Do not list the commits between versions'),
|
|
@@ -52,17 +81,15 @@ def run_update(config: Config, *, check_only: bool = False, skip_changelog: bool
|
|
|
52
81
|
library report an update in the same words.
|
|
53
82
|
|
|
54
83
|
`check_only` reads the receipt first, as an update does, and refuses the
|
|
55
|
-
same installs.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
transport module. The release it had just installed did not depend on that
|
|
65
|
-
module, so the install had deleted it.
|
|
84
|
+
same installs. `updater.installed_from_release` says why a version alone
|
|
85
|
+
cannot decide.
|
|
86
|
+
|
|
87
|
+
Everything that reaches the network or imports a module happens before the
|
|
88
|
+
install; after it, this process only prints what it already holds and exits
|
|
89
|
+
without unwinding. An import after the install can name a module the new
|
|
90
|
+
release no longer ships, which the install has already deleted. An HTTP
|
|
91
|
+
client's lazy import of its transport is one, and it raises ImportError
|
|
92
|
+
after the update has succeeded.
|
|
66
93
|
"""
|
|
67
94
|
tool = config.tool
|
|
68
95
|
try:
|
|
@@ -97,9 +97,8 @@ def installed_from_release(installation: Installation, tag_prefix: str) -> bool:
|
|
|
97
97
|
|
|
98
98
|
An index install is. A git install is when its revision is a release tag:
|
|
99
99
|
`tag_prefix` followed by a semantic version, the form a release's
|
|
100
|
-
`install_ref` takes.
|
|
101
|
-
|
|
102
|
-
branch from a tag.
|
|
100
|
+
`install_ref` takes. `Installation.revision` holds a branch or a commit in
|
|
101
|
+
the same field, so only the value tells them apart.
|
|
103
102
|
|
|
104
103
|
A build from a branch reports a version that ranks wrongly against a
|
|
105
104
|
release. Versioned from git tags, a branch three commits past v1.2.3 builds
|
|
@@ -131,7 +130,13 @@ def install_release(config: Config, result: Result, installation: Installation,
|
|
|
131
130
|
|
|
132
131
|
ref = result.release.install_ref()
|
|
133
132
|
pins = read_lock(installation.url, ref, extras=installation.extras) if installation.kind is InstallKind.GIT else None
|
|
134
|
-
run_install(
|
|
133
|
+
run_install(
|
|
134
|
+
requirement_for(installation, ref),
|
|
135
|
+
quiet=quiet,
|
|
136
|
+
pins=pins,
|
|
137
|
+
with_requirements=installation.with_requirements,
|
|
138
|
+
source_flags=installation.source_flags,
|
|
139
|
+
)
|
|
135
140
|
|
|
136
141
|
return Result(
|
|
137
142
|
current=result.current,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|