pyselfupdate 0.5.0__tar.gz → 0.5.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.
- pyselfupdate-0.5.2/.gitignore +13 -0
- pyselfupdate-0.5.0/README.md → pyselfupdate-0.5.2/PKG-INFO +44 -2
- pyselfupdate-0.5.0/PKG-INFO → pyselfupdate-0.5.2/README.md +21 -26
- pyselfupdate-0.5.0/pyproject.toml.orig → pyselfupdate-0.5.2/pyproject.toml +38 -14
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/config.py +2 -1
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/errors.py +6 -4
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/github.py +3 -4
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/install.py +14 -11
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/notifier.py +24 -23
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/typercmd.py +9 -7
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/updater.py +39 -11
- pyselfupdate-0.5.0/pyproject.toml +0 -300
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/LICENSE +0 -0
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/__init__.py +0 -0
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/py.typed +0 -0
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/source.py +0 -0
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/state.py +0 -0
- {pyselfupdate-0.5.0 → pyselfupdate-0.5.2}/src/pyselfupdate/version.py +0 -0
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pyselfupdate
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: Self-update and update notification for Python CLIs installed with uv tool
|
|
5
|
+
Project-URL: Repository, https://github.com/datapointchris/pyselfupdate
|
|
6
|
+
Project-URL: Issues, https://github.com/datapointchris/pyselfupdate/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/datapointchris/pyselfupdate/releases
|
|
8
|
+
Author-email: Chris Birch <datapointchris@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,release,self-update,update-notifier,uv
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Classifier: Topic :: System :: Software Distribution
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Provides-Extra: typer
|
|
21
|
+
Requires-Dist: typer>=0.12.0; extra == 'typer'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
1
24
|
# pyselfupdate
|
|
2
25
|
|
|
3
26
|
Self-update and update notification for Python CLIs installed with `uv tool`.
|
|
@@ -71,7 +94,7 @@ Nothing is printed when any of these hold:
|
|
|
71
94
|
| `NO_AUTO_UPDATE` or `MYTOOL_NO_AUTO_UPDATE` is set | Opted out |
|
|
72
95
|
| `CI`, `BUILD_NUMBER`, `RUN_ID`, `GITHUB_ACTIONS`, `CODESPACES` | Not a human |
|
|
73
96
|
| stdout or stderr is not a terminal | `mytool list > out 2>&1` must stay clean |
|
|
74
|
-
| Installed from a local path, an editable checkout, or a
|
|
97
|
+
| Installed from a local path, an editable checkout, a branch or a commit | Nothing to compare against |
|
|
75
98
|
| Checked within the interval | One request per day, not per invocation |
|
|
76
99
|
|
|
77
100
|
Presence-only, any value: `NO_AUTO_UPDATE=0` disables it, the same way
|
|
@@ -90,6 +113,13 @@ if result.update_available:
|
|
|
90
113
|
result = update(config) # installs, raises on failure
|
|
91
114
|
```
|
|
92
115
|
|
|
116
|
+
`check` compares versions and reads nothing else. A branch build's version can
|
|
117
|
+
rank below the release it is ahead of. Three commits past `v1.2.3`, a tool
|
|
118
|
+
versioned from git tags builds as `1.2.3-post.3`, which semver ranks below
|
|
119
|
+
`v1.2.3`. Call `require_updatable` before reporting what `check` found. It
|
|
120
|
+
reads uv's receipt and refuses every install `update` refuses, as
|
|
121
|
+
[What will not be updated](#what-will-not-be-updated) lists.
|
|
122
|
+
|
|
93
123
|
Or take the whole command:
|
|
94
124
|
|
|
95
125
|
```python
|
|
@@ -98,6 +128,9 @@ from pyselfupdate.typercmd import add_update_command
|
|
|
98
128
|
add_update_command(app, CONFIG) # gives you `mytool update [--check]`
|
|
99
129
|
```
|
|
100
130
|
|
|
131
|
+
`mytool update --check` reads the receipt first, as `update` does. On an
|
|
132
|
+
install `update` refuses, it prints the same refusal and exits 1.
|
|
133
|
+
|
|
101
134
|
`update` runs `uv tool install --force`, which **rebuilds the virtual
|
|
102
135
|
environment the running interpreter lives in**. Unlike replacing a Unix binary
|
|
103
136
|
— where the process holds an inode and is untouched — this pulls modules out
|
|
@@ -153,12 +186,21 @@ runtime:
|
|
|
153
186
|
| --- | --- |
|
|
154
187
|
| `git = "...git?rev=v1.2.3"` | Updatable |
|
|
155
188
|
| `name = "mytool"` (from an index) | Updatable |
|
|
156
|
-
| `git = "...git"` with no `rev` | Refused — tracks
|
|
189
|
+
| `git = "...git"` with no `rev` | Refused — tracks the default branch, so its version says nothing about how far behind it is |
|
|
190
|
+
| `git = "...git?rev=main"`, or a `rev` naming a commit | Refused — uv writes `rev=` for whatever followed the `@`, so a `rev` counts as a release only when it is `tag_prefix` followed by a semantic version |
|
|
157
191
|
| `directory` / `path` / `editable` | Refused — reinstalling would discard a working copy |
|
|
158
192
|
| A `--with` requirement no requirements-file line reproduces, such as a relative path | Refused with `NotInstalledError` — the update would drop it |
|
|
159
193
|
|
|
160
194
|
A tool that cannot be identified at all is treated as local and left alone.
|
|
161
195
|
|
|
196
|
+
`update` and `mytool update --check` refuse every row marked Refused. The
|
|
197
|
+
notice stays silent on each of them except the `--with` row, because a newer
|
|
198
|
+
release is still worth knowing about there.
|
|
199
|
+
|
|
200
|
+
A caller with a custom `source` whose refs carry a prefix sets `tag_prefix` to
|
|
201
|
+
match. Without it, a tag install from that source is refused as a branch
|
|
202
|
+
install.
|
|
203
|
+
|
|
162
204
|
## Configuration
|
|
163
205
|
|
|
164
206
|
```python
|
|
@@ -1,27 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: pyselfupdate
|
|
3
|
-
Version: 0.5.0
|
|
4
|
-
Summary: Self-update and update notification for Python CLIs installed with uv tool
|
|
5
|
-
Keywords: uv,cli,self-update,update-notifier,release
|
|
6
|
-
Author: Chris Birch
|
|
7
|
-
Author-email: Chris Birch <datapointchris@gmail.com>
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Classifier: Development Status :: 4 - Beta
|
|
11
|
-
Classifier: Environment :: Console
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
-
Classifier: Topic :: System :: Software Distribution
|
|
16
|
-
Classifier: Typing :: Typed
|
|
17
|
-
Requires-Dist: typer>=0.12.0 ; extra == 'typer'
|
|
18
|
-
Requires-Python: >=3.11
|
|
19
|
-
Project-URL: Repository, https://github.com/datapointchris/pyselfupdate
|
|
20
|
-
Project-URL: Issues, https://github.com/datapointchris/pyselfupdate/issues
|
|
21
|
-
Project-URL: Changelog, https://github.com/datapointchris/pyselfupdate/releases
|
|
22
|
-
Provides-Extra: typer
|
|
23
|
-
Description-Content-Type: text/markdown
|
|
24
|
-
|
|
25
1
|
# pyselfupdate
|
|
26
2
|
|
|
27
3
|
Self-update and update notification for Python CLIs installed with `uv tool`.
|
|
@@ -95,7 +71,7 @@ Nothing is printed when any of these hold:
|
|
|
95
71
|
| `NO_AUTO_UPDATE` or `MYTOOL_NO_AUTO_UPDATE` is set | Opted out |
|
|
96
72
|
| `CI`, `BUILD_NUMBER`, `RUN_ID`, `GITHUB_ACTIONS`, `CODESPACES` | Not a human |
|
|
97
73
|
| stdout or stderr is not a terminal | `mytool list > out 2>&1` must stay clean |
|
|
98
|
-
| Installed from a local path, an editable checkout, or a
|
|
74
|
+
| Installed from a local path, an editable checkout, a branch or a commit | Nothing to compare against |
|
|
99
75
|
| Checked within the interval | One request per day, not per invocation |
|
|
100
76
|
|
|
101
77
|
Presence-only, any value: `NO_AUTO_UPDATE=0` disables it, the same way
|
|
@@ -114,6 +90,13 @@ if result.update_available:
|
|
|
114
90
|
result = update(config) # installs, raises on failure
|
|
115
91
|
```
|
|
116
92
|
|
|
93
|
+
`check` compares versions and reads nothing else. A branch build's version can
|
|
94
|
+
rank below the release it is ahead of. Three commits past `v1.2.3`, a tool
|
|
95
|
+
versioned from git tags builds as `1.2.3-post.3`, which semver ranks below
|
|
96
|
+
`v1.2.3`. Call `require_updatable` before reporting what `check` found. It
|
|
97
|
+
reads uv's receipt and refuses every install `update` refuses, as
|
|
98
|
+
[What will not be updated](#what-will-not-be-updated) lists.
|
|
99
|
+
|
|
117
100
|
Or take the whole command:
|
|
118
101
|
|
|
119
102
|
```python
|
|
@@ -122,6 +105,9 @@ from pyselfupdate.typercmd import add_update_command
|
|
|
122
105
|
add_update_command(app, CONFIG) # gives you `mytool update [--check]`
|
|
123
106
|
```
|
|
124
107
|
|
|
108
|
+
`mytool update --check` reads the receipt first, as `update` does. On an
|
|
109
|
+
install `update` refuses, it prints the same refusal and exits 1.
|
|
110
|
+
|
|
125
111
|
`update` runs `uv tool install --force`, which **rebuilds the virtual
|
|
126
112
|
environment the running interpreter lives in**. Unlike replacing a Unix binary
|
|
127
113
|
— where the process holds an inode and is untouched — this pulls modules out
|
|
@@ -177,12 +163,21 @@ runtime:
|
|
|
177
163
|
| --- | --- |
|
|
178
164
|
| `git = "...git?rev=v1.2.3"` | Updatable |
|
|
179
165
|
| `name = "mytool"` (from an index) | Updatable |
|
|
180
|
-
| `git = "...git"` with no `rev` | Refused — tracks
|
|
166
|
+
| `git = "...git"` with no `rev` | Refused — tracks the default branch, so its version says nothing about how far behind it is |
|
|
167
|
+
| `git = "...git?rev=main"`, or a `rev` naming a commit | Refused — uv writes `rev=` for whatever followed the `@`, so a `rev` counts as a release only when it is `tag_prefix` followed by a semantic version |
|
|
181
168
|
| `directory` / `path` / `editable` | Refused — reinstalling would discard a working copy |
|
|
182
169
|
| A `--with` requirement no requirements-file line reproduces, such as a relative path | Refused with `NotInstalledError` — the update would drop it |
|
|
183
170
|
|
|
184
171
|
A tool that cannot be identified at all is treated as local and left alone.
|
|
185
172
|
|
|
173
|
+
`update` and `mytool update --check` refuse every row marked Refused. The
|
|
174
|
+
notice stays silent on each of them except the `--with` row, because a newer
|
|
175
|
+
release is still worth knowing about there.
|
|
176
|
+
|
|
177
|
+
A caller with a custom `source` whose refs carry a prefix sets `tag_prefix` to
|
|
178
|
+
match. Without it, a tag install from that source is refused as a branch
|
|
179
|
+
install.
|
|
180
|
+
|
|
186
181
|
## Configuration
|
|
187
182
|
|
|
188
183
|
```python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyselfupdate"
|
|
3
|
-
|
|
3
|
+
dynamic = ["version"]
|
|
4
4
|
description = "Self-update and update notification for Python CLIs installed with uv tool"
|
|
5
5
|
authors = [{ name = "Chris Birch", email = "datapointchris@gmail.com" }]
|
|
6
6
|
license = "MIT"
|
|
@@ -112,6 +112,15 @@ select = [
|
|
|
112
112
|
"SIM", # flake8-simplify
|
|
113
113
|
"I", # isort
|
|
114
114
|
"ICN", # flake8-import-conventions
|
|
115
|
+
# flake8-bandit by rule, never as the family. Most of what the family
|
|
116
|
+
# reports is a subprocess call made on purpose, and silencing that takes an
|
|
117
|
+
# ignore list that grows with every project.
|
|
118
|
+
"S102", # exec
|
|
119
|
+
"S113", # an HTTP request with no timeout, which can hang forever
|
|
120
|
+
"S301", # pickle, which runs code from the bytes it loads
|
|
121
|
+
"S307", # eval
|
|
122
|
+
"S324", # md5 or sha1 used for security
|
|
123
|
+
"S501", # TLS certificate verification turned off
|
|
115
124
|
]
|
|
116
125
|
ignore = [
|
|
117
126
|
"SIM108", # ternary operator — often less readable
|
|
@@ -131,15 +140,15 @@ datetime = "dt"
|
|
|
131
140
|
sqlalchemy = "sa"
|
|
132
141
|
"pyspark.sql.functions" = "sf"
|
|
133
142
|
"pyspark.sql.types" = "st"
|
|
143
|
+
dataclasses = "dc"
|
|
134
144
|
|
|
135
145
|
[tool.semantic_release]
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"""
|
|
146
|
+
# A feature release on 0.x stays on 0.x. python-semantic-release v10 defaults
|
|
147
|
+
# this to false, which makes the next release of a 0.x project 1.0.0.
|
|
148
|
+
allow_zero_version = true
|
|
149
|
+
# 1.0 is a compatibility promise, so a breaking change on 0.x is a minor bump.
|
|
150
|
+
# Removing this key is how the project reaches 1.0.
|
|
151
|
+
major_on_zero = false
|
|
143
152
|
|
|
144
153
|
|
|
145
154
|
# ---------- Build System ---------- #
|
|
@@ -165,6 +174,7 @@ managed = [
|
|
|
165
174
|
["ruff", "lint", "isort", "force-single-line"],
|
|
166
175
|
["ruff", "lint", "flake8-import-conventions", "banned-from"],
|
|
167
176
|
["ruff", "lint", "flake8-import-conventions", "extend-aliases", "datetime"],
|
|
177
|
+
["ruff", "lint", "flake8-import-conventions", "extend-aliases", "dataclasses"],
|
|
168
178
|
["ruff", "lint", "flake8-import-conventions", "extend-aliases", "sqlalchemy"],
|
|
169
179
|
["ruff", "lint", "flake8-import-conventions", "extend-aliases", "pyspark.sql.functions"],
|
|
170
180
|
["ruff", "lint", "flake8-import-conventions", "extend-aliases", "pyspark.sql.types"],
|
|
@@ -187,10 +197,24 @@ managed = [
|
|
|
187
197
|
# and extra that names them, so a local run and CI cannot disagree.
|
|
188
198
|
pinned = ["ruff"]
|
|
189
199
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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 } }]
|
|
193
212
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
+
[build-system]
|
|
219
|
+
requires = ["hatchling", "uv-dynamic-versioning"]
|
|
220
|
+
build-backend = "hatchling.build"
|
|
@@ -51,7 +51,8 @@ class Config:
|
|
|
51
51
|
|
|
52
52
|
# Selects one release stream in a repository publishing several, as in
|
|
53
53
|
# "cli/" for tags of the form cli/v1.2.3. Configures the default GitHub
|
|
54
|
-
# source
|
|
54
|
+
# source. Whatever the source, a git install whose receipt `rev` is not
|
|
55
|
+
# this prefix followed by a semantic version is refused as a branch install.
|
|
55
56
|
tag_prefix: str = ''
|
|
56
57
|
|
|
57
58
|
# Locates releases. Defaults to a GitHubSource built from the fields above.
|
|
@@ -15,11 +15,13 @@ class InvalidConfigError(SelfUpdateError):
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class LocalInstallError(SelfUpdateError):
|
|
18
|
-
"""The
|
|
18
|
+
"""The running build is not a release, so no release can be compared against it.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
Raised for an install from a local path or an editable checkout, for a git
|
|
21
|
+
install from a branch or a commit rather than a release tag, and for a
|
|
22
|
+
running version that is not a semantic version. Reinstalling would replace
|
|
23
|
+
the build with a release that may be older, with no way to tell which is
|
|
24
|
+
newer. This is the analog of goselfupdate's ErrDevBuild.
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
27
|
|
|
@@ -88,10 +88,9 @@ DEFAULT_TOKEN_COMMAND = 'gh auth token' # nosec B105
|
|
|
88
88
|
|
|
89
89
|
Authenticating is the default because the alternative is not "no credential" but
|
|
90
90
|
"sixty requests an hour, charged per IP address and shared by every host behind
|
|
91
|
-
one egress".
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
nobody sets, and eleven of fourteen tools here had not.
|
|
91
|
+
one egress". Two machines checking on a timer can hold that pool at zero for
|
|
92
|
+
whole hours, and every tool behind the same address that asks anonymously is
|
|
93
|
+
then refused. A default that has to be opted into is a default nobody sets.
|
|
95
94
|
"""
|
|
96
95
|
|
|
97
96
|
|
|
@@ -27,6 +27,7 @@ from dataclasses import field
|
|
|
27
27
|
from enum import Enum
|
|
28
28
|
from importlib.metadata import PackageNotFoundError
|
|
29
29
|
from importlib.metadata import version as installed_version
|
|
30
|
+
from itertools import chain
|
|
30
31
|
from pathlib import Path
|
|
31
32
|
|
|
32
33
|
from pyselfupdate.errors import InstallFailedError
|
|
@@ -63,9 +64,10 @@ class Installation:
|
|
|
63
64
|
kind: InstallKind
|
|
64
65
|
url: str = ''
|
|
65
66
|
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
67
|
+
# Whatever followed the `@` at install time: a tag, a branch or a commit,
|
|
68
|
+
# which uv records alike as `rev=`. Empty when the install follows the
|
|
69
|
+
# default branch. `updater.installed_from_release` reads it as a release
|
|
70
|
+
# tag or refuses it.
|
|
69
71
|
revision: str = ''
|
|
70
72
|
|
|
71
73
|
extras: tuple[str, ...] = ()
|
|
@@ -81,9 +83,6 @@ class Installation:
|
|
|
81
83
|
# raises NotInstalledError on them instead of dropping them.
|
|
82
84
|
_unrebuildable: tuple[str, ...] = field(default=(), repr=False)
|
|
83
85
|
|
|
84
|
-
def is_updatable(self) -> bool:
|
|
85
|
-
return self.kind is not InstallKind.LOCAL
|
|
86
|
-
|
|
87
86
|
|
|
88
87
|
def tool_dir() -> Path:
|
|
89
88
|
"""uv's tool directory.
|
|
@@ -153,8 +152,9 @@ def read_installation(tool: str) -> Installation:
|
|
|
153
152
|
def _split_git(git: str) -> tuple[str, dict[str, str], str]:
|
|
154
153
|
"""A receipt's git source as its URL, its query and the commit after `#`.
|
|
155
154
|
|
|
156
|
-
uv writes `...git?rev=v1.2.3` for
|
|
157
|
-
entirely for
|
|
155
|
+
uv writes `...git?rev=v1.2.3` for `@v1.2.3`, `...git?rev=main` for
|
|
156
|
+
`@main`, and omits the query entirely for an install that follows the
|
|
157
|
+
default branch.
|
|
158
158
|
"""
|
|
159
159
|
rest, _, commit = git.partition('#')
|
|
160
160
|
url, _, query = rest.partition('?')
|
|
@@ -281,7 +281,7 @@ def read_lock(url: str, ref: str, *, extras: Sequence[str] = ()) -> Pins | None:
|
|
|
281
281
|
lock = checkout / LOCK_NAME
|
|
282
282
|
if not lock.is_file():
|
|
283
283
|
return None
|
|
284
|
-
exported = _run([uv, *EXPORT, *(
|
|
284
|
+
exported = _run([uv, *EXPORT, *chain.from_iterable(('--extra', extra) for extra in extras)], cwd=checkout)
|
|
285
285
|
if exported.returncode != 0:
|
|
286
286
|
raise LockUnreadableError(f'uv export would not read the uv.lock at {ref}: {_reason(exported)}')
|
|
287
287
|
try:
|
|
@@ -308,7 +308,9 @@ def _override_extras(lock: dict, extras: Sequence[str]) -> dict[str, tuple[str,
|
|
|
308
308
|
for package in lock.get('package') or []:
|
|
309
309
|
packages.setdefault(package.get('name', ''), []).append(package)
|
|
310
310
|
project = ({'editable': '.'}, {'virtual': '.'})
|
|
311
|
-
roots = [
|
|
311
|
+
roots: list[str] = []
|
|
312
|
+
for name, entries in packages.items():
|
|
313
|
+
roots.extend(name for package in entries if package.get('source') in project)
|
|
312
314
|
|
|
313
315
|
active: dict[str, set[str]] = {root: set(extras) for root in roots}
|
|
314
316
|
pending = list(roots)
|
|
@@ -316,7 +318,8 @@ def _override_extras(lock: dict, extras: Sequence[str]) -> dict[str, tuple[str,
|
|
|
316
318
|
name = pending.pop()
|
|
317
319
|
for package in packages.get(name, ()):
|
|
318
320
|
groups = package.get('optional-dependencies') or {}
|
|
319
|
-
|
|
321
|
+
optional = chain.from_iterable(groups.get(extra, ()) for extra in sorted(active[name]))
|
|
322
|
+
edges = [*(package.get('dependencies') or ()), *optional]
|
|
320
323
|
for edge in edges:
|
|
321
324
|
target, wanted = edge.get('name', ''), set(edge.get('extra') or ())
|
|
322
325
|
if target not in active or not wanted <= active[target]:
|
|
@@ -22,17 +22,18 @@ from typing import TextIO
|
|
|
22
22
|
from pyselfupdate import state as state_module
|
|
23
23
|
from pyselfupdate.config import Config
|
|
24
24
|
from pyselfupdate.errors import SelfUpdateError
|
|
25
|
-
from pyselfupdate.install import InstallKind
|
|
26
25
|
from pyselfupdate.install import read_installation
|
|
27
26
|
from pyselfupdate.updater import check
|
|
27
|
+
from pyselfupdate.updater import installed_from_release
|
|
28
28
|
|
|
29
29
|
DEFAULT_INTERVAL = dt.timedelta(hours=24)
|
|
30
30
|
|
|
31
|
-
#
|
|
31
|
+
# The environment variables every tool honors, beside its own `<TOOL>_` forms.
|
|
32
|
+
# The disable switch is presence-only, any value including empty -- the
|
|
32
33
|
# NO_COLOR convention -- so that NO_AUTO_UPDATE=0 cannot mean "on". The interval
|
|
33
34
|
# gets its own name precisely so presence and value never share one variable.
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
_SHARED_DISABLE_VARIABLE = 'NO_AUTO_UPDATE'
|
|
36
|
+
_SHARED_INTERVAL_VARIABLE = 'AUTO_UPDATE_INTERVAL'
|
|
36
37
|
|
|
37
38
|
CI_VARIABLES = ('CI', 'BUILD_NUMBER', 'RUN_ID', 'GITHUB_ACTIONS', 'CODESPACES')
|
|
38
39
|
|
|
@@ -97,19 +98,20 @@ def notify(
|
|
|
97
98
|
def enabled(config: Config, *, interactive: bool | None = None) -> tuple[bool, Skip | None]:
|
|
98
99
|
"""Whether a check would run, without touching the network or the clock.
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
For a caller that reports why a tool does or does not check, such as a
|
|
102
|
+
dashboard listing installed tools or a `<tool> update --why`. The interval
|
|
103
|
+
is deliberately not consulted: this answers "is this tool opted in", not "is
|
|
104
|
+
it due".
|
|
103
105
|
"""
|
|
104
106
|
if _disabled_by_env(config.tool):
|
|
105
107
|
return False, Skip.DISABLED
|
|
106
108
|
if _asking_for_help():
|
|
107
109
|
return False, Skip.HELP
|
|
108
|
-
if not (
|
|
110
|
+
if not (is_interactive() if interactive is None else interactive):
|
|
109
111
|
return False, Skip.NOT_A_TTY
|
|
110
112
|
if _in_ci():
|
|
111
113
|
return False, Skip.CI
|
|
112
|
-
if _is_local_install(config
|
|
114
|
+
if _is_local_install(config):
|
|
113
115
|
return False, Skip.LOCAL_INSTALL
|
|
114
116
|
return True, None
|
|
115
117
|
|
|
@@ -122,9 +124,9 @@ def _asking_for_help() -> bool:
|
|
|
122
124
|
screen. Nothing a notice is for happens on a help screen, and the check
|
|
123
125
|
costs a releases-API call and a state write every time.
|
|
124
126
|
|
|
125
|
-
It lands hardest on a reader that walks a tool's help.
|
|
126
|
-
|
|
127
|
-
|
|
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.
|
|
128
130
|
|
|
129
131
|
A `--` ends the scan, because everything after it is the command's own
|
|
130
132
|
argument. Skipping when it should not have costs a missed notice, which is
|
|
@@ -201,14 +203,14 @@ def _emit(message: str, *, out: TextIO | None, defer: bool) -> None:
|
|
|
201
203
|
|
|
202
204
|
|
|
203
205
|
def _disabled_by_env(tool: str) -> bool:
|
|
204
|
-
return
|
|
206
|
+
return _SHARED_DISABLE_VARIABLE in os.environ or _tool_variable(tool, 'NO_AUTO_UPDATE') in os.environ
|
|
205
207
|
|
|
206
208
|
|
|
207
209
|
def _in_ci() -> bool:
|
|
208
210
|
return any(name in os.environ for name in CI_VARIABLES)
|
|
209
211
|
|
|
210
212
|
|
|
211
|
-
def
|
|
213
|
+
def is_interactive(streams: Iterable[Any] | None = None) -> bool:
|
|
212
214
|
"""Both streams must be terminals.
|
|
213
215
|
|
|
214
216
|
Checking both matters: `tool list | jq` should still be allowed to notify on
|
|
@@ -232,35 +234,34 @@ def _is_interactive(streams: Iterable[Any] | None = None) -> bool:
|
|
|
232
234
|
return True
|
|
233
235
|
|
|
234
236
|
|
|
235
|
-
def _is_local_install(
|
|
237
|
+
def _is_local_install(config: Config) -> bool:
|
|
236
238
|
"""A local, editable, or branch-tracking install must never be nagged.
|
|
237
239
|
|
|
238
240
|
Failing closed: anything that cannot be positively identified as a pinned
|
|
239
241
|
release install is treated as local, because a false negative here prints a
|
|
240
|
-
wrong notice on every command in a dev checkout.
|
|
242
|
+
wrong notice on every command in a dev checkout. `update` refuses the same
|
|
243
|
+
installs, through the same `installed_from_release`.
|
|
241
244
|
"""
|
|
242
245
|
try:
|
|
243
|
-
installation = read_installation(tool)
|
|
246
|
+
installation = read_installation(config.tool)
|
|
244
247
|
except SelfUpdateError:
|
|
245
248
|
return True
|
|
246
|
-
|
|
247
|
-
return True
|
|
248
|
-
return installation.kind is InstallKind.GIT and not installation.revision
|
|
249
|
+
return not installed_from_release(installation, config.tag_prefix)
|
|
249
250
|
|
|
250
251
|
|
|
251
252
|
def _interval(tool: str, override: dt.timedelta | None) -> dt.timedelta:
|
|
252
253
|
if override is not None:
|
|
253
254
|
return override
|
|
254
|
-
for name in (_tool_variable(tool, 'AUTO_UPDATE_INTERVAL'),
|
|
255
|
+
for name in (_tool_variable(tool, 'AUTO_UPDATE_INTERVAL'), _SHARED_INTERVAL_VARIABLE):
|
|
255
256
|
raw = os.environ.get(name)
|
|
256
257
|
if raw:
|
|
257
|
-
parsed =
|
|
258
|
+
parsed = parse_interval(raw)
|
|
258
259
|
if parsed is not None:
|
|
259
260
|
return parsed
|
|
260
261
|
return DEFAULT_INTERVAL
|
|
261
262
|
|
|
262
263
|
|
|
263
|
-
def
|
|
264
|
+
def parse_interval(raw: str) -> dt.timedelta | None:
|
|
264
265
|
"""Parse `30m`, `24h`, `7d`, or a bare number of seconds."""
|
|
265
266
|
raw = raw.strip().lower()
|
|
266
267
|
if not raw:
|
|
@@ -25,7 +25,6 @@ import typer
|
|
|
25
25
|
|
|
26
26
|
from pyselfupdate.config import Config
|
|
27
27
|
from pyselfupdate.errors import SelfUpdateError
|
|
28
|
-
from pyselfupdate.install import Installation
|
|
29
28
|
from pyselfupdate.install import exit_now
|
|
30
29
|
from pyselfupdate.updater import changelog
|
|
31
30
|
from pyselfupdate.updater import check
|
|
@@ -49,8 +48,13 @@ def run_update(config: Config, *, check_only: bool = False, skip_changelog: bool
|
|
|
49
48
|
|
|
50
49
|
Split out from the command so a CLI that wants its own flags, or that nests
|
|
51
50
|
the command under a group, does not have to reimplement the reporting. The
|
|
52
|
-
output format is identical to goselfupdate's,
|
|
53
|
-
|
|
51
|
+
output format is identical to goselfupdate's, so tools built on either
|
|
52
|
+
library report an update in the same words.
|
|
53
|
+
|
|
54
|
+
`check_only` reads the receipt first, as an update does, and refuses the
|
|
55
|
+
same installs. A branch build's version can rank below the release it is
|
|
56
|
+
ahead of, so a check that skipped the receipt would offer that older
|
|
57
|
+
release as an update.
|
|
54
58
|
|
|
55
59
|
The step order is the load-bearing part. Everything that reaches the network
|
|
56
60
|
or imports a module happens before the install; after it, this process only
|
|
@@ -61,10 +65,8 @@ def run_update(config: Config, *, check_only: bool = False, skip_changelog: bool
|
|
|
61
65
|
on, and so had just deleted.
|
|
62
66
|
"""
|
|
63
67
|
tool = config.tool
|
|
64
|
-
installation: Installation | None = None
|
|
65
68
|
try:
|
|
66
|
-
|
|
67
|
-
installation = require_updatable(config)
|
|
69
|
+
installation = require_updatable(config)
|
|
68
70
|
result = check(config)
|
|
69
71
|
except SelfUpdateError as error:
|
|
70
72
|
typer.echo(f'✗ {tool} update failed: {error}', err=True)
|
|
@@ -76,7 +78,7 @@ def run_update(config: Config, *, check_only: bool = False, skip_changelog: bool
|
|
|
76
78
|
|
|
77
79
|
subjects = [] if skip_changelog else changelog(config, result.current, result.latest)
|
|
78
80
|
|
|
79
|
-
if
|
|
81
|
+
if check_only:
|
|
80
82
|
typer.echo(f'✓ {tool} update available: {result.current} → {result.latest}')
|
|
81
83
|
_echo_changes(subjects)
|
|
82
84
|
return
|
|
@@ -58,7 +58,12 @@ class Result:
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
def check(config: Config) -> Result:
|
|
61
|
-
"""Report whether a newer release exists, without installing anything.
|
|
61
|
+
"""Report whether a newer release exists, without installing anything.
|
|
62
|
+
|
|
63
|
+
Compares versions and reads nothing else. A branch build's version can rank
|
|
64
|
+
below the release it is ahead of, so a caller reporting the result runs
|
|
65
|
+
`require_updatable` first, as `update` and `typercmd.run_update` do.
|
|
66
|
+
"""
|
|
62
67
|
resolved = config.resolved()
|
|
63
68
|
current = _current_version(resolved)
|
|
64
69
|
|
|
@@ -81,11 +86,34 @@ def require_updatable(config: Config) -> Installation:
|
|
|
81
86
|
`typercmd.run_update` does, to fetch a changelog while the environment is
|
|
82
87
|
still intact -- keeps the refusal ahead of the network by starting here.
|
|
83
88
|
"""
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
resolved = config.resolved()
|
|
90
|
+
installation = read_installation(resolved.tool)
|
|
91
|
+
_require_updatable(installation, resolved.tag_prefix)
|
|
86
92
|
return installation
|
|
87
93
|
|
|
88
94
|
|
|
95
|
+
def installed_from_release(installation: Installation, tag_prefix: str) -> bool:
|
|
96
|
+
"""Whether the running build is a release, so its version can be compared with the latest one.
|
|
97
|
+
|
|
98
|
+
An index install is. A git install is when its revision is a release tag:
|
|
99
|
+
`tag_prefix` followed by a semantic version, the form a release's
|
|
100
|
+
`install_ref` takes. uv writes `?rev=` for whatever followed the `@`.
|
|
101
|
+
`@main` and `@v1.2.3` land in the same field, so only the value tells a
|
|
102
|
+
branch from a tag.
|
|
103
|
+
|
|
104
|
+
A build from a branch reports a version that ranks wrongly against a
|
|
105
|
+
release. Versioned from git tags, a branch three commits past v1.2.3 builds
|
|
106
|
+
as `1.2.3-post.3+g<sha>`, a semver pre-release that ranks below the v1.2.3
|
|
107
|
+
it is ahead of.
|
|
108
|
+
"""
|
|
109
|
+
if installation.kind is InstallKind.INDEX:
|
|
110
|
+
return True
|
|
111
|
+
if installation.kind is not InstallKind.GIT:
|
|
112
|
+
return False
|
|
113
|
+
revision = installation.revision
|
|
114
|
+
return revision.startswith(tag_prefix) and semver.is_valid(revision[len(tag_prefix) :])
|
|
115
|
+
|
|
116
|
+
|
|
89
117
|
def install_release(config: Config, result: Result, installation: Installation, *, quiet: bool = True) -> Result:
|
|
90
118
|
"""Install the release `result` names, over the running one.
|
|
91
119
|
|
|
@@ -156,12 +184,12 @@ def changelog(config: Config, from_version: str, to_version: str) -> list[str]:
|
|
|
156
184
|
|
|
157
185
|
|
|
158
186
|
def _current_version(config: Config) -> str:
|
|
159
|
-
"""The running version, canonicalized, rejecting one that is not a
|
|
187
|
+
"""The running version, canonicalized, rejecting one that is not a semantic version.
|
|
160
188
|
|
|
161
|
-
An
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
189
|
+
An empty version, from a distribution that is not installed, fails here, and
|
|
190
|
+
so does a PEP 440 form such as `1.2.3.post3.dev0`. A branch build's version
|
|
191
|
+
usually parses, so `installed_from_release` refuses a branch install from
|
|
192
|
+
the receipt instead.
|
|
165
193
|
"""
|
|
166
194
|
canonical = semver.canonical(config.version)
|
|
167
195
|
if not semver.is_valid(canonical):
|
|
@@ -169,14 +197,14 @@ def _current_version(config: Config) -> str:
|
|
|
169
197
|
return canonical
|
|
170
198
|
|
|
171
199
|
|
|
172
|
-
def _require_updatable(installation: Installation) -> None:
|
|
200
|
+
def _require_updatable(installation: Installation, tag_prefix: str) -> None:
|
|
173
201
|
if installation.kind is InstallKind.LOCAL:
|
|
174
202
|
raise LocalInstallError(
|
|
175
203
|
f'{installation.tool} is installed from {installation.url}; update the checkout instead of reinstalling over it'
|
|
176
204
|
)
|
|
177
|
-
if
|
|
205
|
+
if not installed_from_release(installation, tag_prefix):
|
|
178
206
|
raise LocalInstallError(
|
|
179
|
-
f'{installation.tool} is installed from
|
|
207
|
+
f'{installation.tool} is installed from {installation.revision or "its default branch"} rather than a release tag, '
|
|
180
208
|
f'so a release version cannot be compared against it; '
|
|
181
209
|
f'reinstall from a tagged release to enable updates'
|
|
182
210
|
)
|
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "pyselfupdate"
|
|
3
|
-
version = "0.5.0"
|
|
4
|
-
description = "Self-update and update notification for Python CLIs installed with uv tool"
|
|
5
|
-
license = "MIT"
|
|
6
|
-
license-files = ["LICENSE"]
|
|
7
|
-
readme = "README.md"
|
|
8
|
-
keywords = [
|
|
9
|
-
"uv",
|
|
10
|
-
"cli",
|
|
11
|
-
"self-update",
|
|
12
|
-
"update-notifier",
|
|
13
|
-
"release",
|
|
14
|
-
]
|
|
15
|
-
classifiers = [
|
|
16
|
-
"Development Status :: 4 - Beta",
|
|
17
|
-
"Environment :: Console",
|
|
18
|
-
"Intended Audience :: Developers",
|
|
19
|
-
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
-
"Topic :: Software Development :: Libraries",
|
|
21
|
-
"Topic :: System :: Software Distribution",
|
|
22
|
-
"Typing :: Typed",
|
|
23
|
-
]
|
|
24
|
-
requires-python = ">=3.11"
|
|
25
|
-
dependencies = []
|
|
26
|
-
|
|
27
|
-
[[project.authors]]
|
|
28
|
-
name = "Chris Birch"
|
|
29
|
-
email = "datapointchris@gmail.com"
|
|
30
|
-
|
|
31
|
-
[project.optional-dependencies]
|
|
32
|
-
typer = ["typer>=0.12.0"]
|
|
33
|
-
|
|
34
|
-
[project.urls]
|
|
35
|
-
Repository = "https://github.com/datapointchris/pyselfupdate"
|
|
36
|
-
Issues = "https://github.com/datapointchris/pyselfupdate/issues"
|
|
37
|
-
Changelog = "https://github.com/datapointchris/pyselfupdate/releases"
|
|
38
|
-
|
|
39
|
-
[dependency-groups]
|
|
40
|
-
dev = [
|
|
41
|
-
"bandit>=1.7.8",
|
|
42
|
-
"mypy>=1.10.0",
|
|
43
|
-
"pre-commit>=4.3.0",
|
|
44
|
-
"pytest>=8.0.0",
|
|
45
|
-
"ruff==0.12.5",
|
|
46
|
-
"typer>=0.12.0",
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
[tool.bandit]
|
|
50
|
-
exclude_dirs = [
|
|
51
|
-
".git",
|
|
52
|
-
".mypy_cache",
|
|
53
|
-
".pytest_cache",
|
|
54
|
-
".ruff_cache",
|
|
55
|
-
".venv",
|
|
56
|
-
"tests",
|
|
57
|
-
]
|
|
58
|
-
skips = [
|
|
59
|
-
"B311",
|
|
60
|
-
"B404",
|
|
61
|
-
"B603",
|
|
62
|
-
]
|
|
63
|
-
|
|
64
|
-
[tool.codespell]
|
|
65
|
-
skip = "*.lock"
|
|
66
|
-
check-filenames = true
|
|
67
|
-
check-hidden = true
|
|
68
|
-
builtin = "clear,rare"
|
|
69
|
-
uri-ignore-words-list = "*"
|
|
70
|
-
|
|
71
|
-
[tool.mypy]
|
|
72
|
-
pretty = true
|
|
73
|
-
ignore_missing_imports = true
|
|
74
|
-
check_untyped_defs = false
|
|
75
|
-
warn_return_any = false
|
|
76
|
-
|
|
77
|
-
[tool.pytest.ini_options]
|
|
78
|
-
minversion = "8.0"
|
|
79
|
-
testpaths = ["tests"]
|
|
80
|
-
verbosity_assertions = 2
|
|
81
|
-
|
|
82
|
-
[tool.refurb]
|
|
83
|
-
enable_all = true
|
|
84
|
-
|
|
85
|
-
[tool.ruff]
|
|
86
|
-
line-length = 140
|
|
87
|
-
exclude = [
|
|
88
|
-
".git",
|
|
89
|
-
"__pycache__",
|
|
90
|
-
".mypy_cache",
|
|
91
|
-
".ruff_cache",
|
|
92
|
-
".vscode",
|
|
93
|
-
".venv",
|
|
94
|
-
"build",
|
|
95
|
-
"dist",
|
|
96
|
-
]
|
|
97
|
-
|
|
98
|
-
[tool.ruff.format]
|
|
99
|
-
quote-style = "single"
|
|
100
|
-
indent-style = "space"
|
|
101
|
-
skip-magic-trailing-comma = false
|
|
102
|
-
line-ending = "auto"
|
|
103
|
-
docstring-code-format = true
|
|
104
|
-
|
|
105
|
-
[tool.ruff.lint]
|
|
106
|
-
select = [
|
|
107
|
-
"E",
|
|
108
|
-
"F",
|
|
109
|
-
"UP",
|
|
110
|
-
"B",
|
|
111
|
-
"SIM",
|
|
112
|
-
"I",
|
|
113
|
-
"ICN",
|
|
114
|
-
]
|
|
115
|
-
ignore = ["SIM108"]
|
|
116
|
-
|
|
117
|
-
[tool.ruff.lint.isort]
|
|
118
|
-
force-single-line = true
|
|
119
|
-
|
|
120
|
-
[tool.ruff.lint.per-file-ignores]
|
|
121
|
-
"__init__.py" = ["F401"]
|
|
122
|
-
|
|
123
|
-
[tool.ruff.lint.flake8-import-conventions]
|
|
124
|
-
banned-from = [
|
|
125
|
-
"datetime",
|
|
126
|
-
"pyspark.sql.functions",
|
|
127
|
-
"pyspark.sql.types",
|
|
128
|
-
]
|
|
129
|
-
|
|
130
|
-
[tool.ruff.lint.flake8-import-conventions.extend-aliases]
|
|
131
|
-
datetime = "dt"
|
|
132
|
-
sqlalchemy = "sa"
|
|
133
|
-
"pyspark.sql.functions" = "sf"
|
|
134
|
-
"pyspark.sql.types" = "st"
|
|
135
|
-
|
|
136
|
-
[tool.semantic_release]
|
|
137
|
-
version_toml = ["pyproject.toml:project.version"]
|
|
138
|
-
commit_message = "build(release): {version}"
|
|
139
|
-
build_command = """
|
|
140
|
-
pip install uv
|
|
141
|
-
uv lock --upgrade-package pyselfupdate
|
|
142
|
-
git add uv.lock
|
|
143
|
-
"""
|
|
144
|
-
|
|
145
|
-
[tool.pyright]
|
|
146
|
-
typeCheckingMode = "standard"
|
|
147
|
-
analyzeUnannotatedFunctions = true
|
|
148
|
-
|
|
149
|
-
[tool.forge]
|
|
150
|
-
managed = [
|
|
151
|
-
[
|
|
152
|
-
"ruff",
|
|
153
|
-
"line-length",
|
|
154
|
-
],
|
|
155
|
-
[
|
|
156
|
-
"ruff",
|
|
157
|
-
"format",
|
|
158
|
-
"quote-style",
|
|
159
|
-
],
|
|
160
|
-
[
|
|
161
|
-
"ruff",
|
|
162
|
-
"format",
|
|
163
|
-
"indent-style",
|
|
164
|
-
],
|
|
165
|
-
[
|
|
166
|
-
"ruff",
|
|
167
|
-
"format",
|
|
168
|
-
"skip-magic-trailing-comma",
|
|
169
|
-
],
|
|
170
|
-
[
|
|
171
|
-
"ruff",
|
|
172
|
-
"format",
|
|
173
|
-
"line-ending",
|
|
174
|
-
],
|
|
175
|
-
[
|
|
176
|
-
"ruff",
|
|
177
|
-
"format",
|
|
178
|
-
"docstring-code-format",
|
|
179
|
-
],
|
|
180
|
-
[
|
|
181
|
-
"ruff",
|
|
182
|
-
"lint",
|
|
183
|
-
"select",
|
|
184
|
-
],
|
|
185
|
-
[
|
|
186
|
-
"ruff",
|
|
187
|
-
"lint",
|
|
188
|
-
"ignore",
|
|
189
|
-
],
|
|
190
|
-
[
|
|
191
|
-
"ruff",
|
|
192
|
-
"lint",
|
|
193
|
-
"isort",
|
|
194
|
-
"force-single-line",
|
|
195
|
-
],
|
|
196
|
-
[
|
|
197
|
-
"ruff",
|
|
198
|
-
"lint",
|
|
199
|
-
"flake8-import-conventions",
|
|
200
|
-
"banned-from",
|
|
201
|
-
],
|
|
202
|
-
[
|
|
203
|
-
"ruff",
|
|
204
|
-
"lint",
|
|
205
|
-
"flake8-import-conventions",
|
|
206
|
-
"extend-aliases",
|
|
207
|
-
"datetime",
|
|
208
|
-
],
|
|
209
|
-
[
|
|
210
|
-
"ruff",
|
|
211
|
-
"lint",
|
|
212
|
-
"flake8-import-conventions",
|
|
213
|
-
"extend-aliases",
|
|
214
|
-
"sqlalchemy",
|
|
215
|
-
],
|
|
216
|
-
[
|
|
217
|
-
"ruff",
|
|
218
|
-
"lint",
|
|
219
|
-
"flake8-import-conventions",
|
|
220
|
-
"extend-aliases",
|
|
221
|
-
"pyspark.sql.functions",
|
|
222
|
-
],
|
|
223
|
-
[
|
|
224
|
-
"ruff",
|
|
225
|
-
"lint",
|
|
226
|
-
"flake8-import-conventions",
|
|
227
|
-
"extend-aliases",
|
|
228
|
-
"pyspark.sql.types",
|
|
229
|
-
],
|
|
230
|
-
[
|
|
231
|
-
"ruff",
|
|
232
|
-
"lint",
|
|
233
|
-
"per-file-ignores",
|
|
234
|
-
"__init__.py",
|
|
235
|
-
],
|
|
236
|
-
[
|
|
237
|
-
"mypy",
|
|
238
|
-
"pretty",
|
|
239
|
-
],
|
|
240
|
-
[
|
|
241
|
-
"mypy",
|
|
242
|
-
"ignore_missing_imports",
|
|
243
|
-
],
|
|
244
|
-
[
|
|
245
|
-
"mypy",
|
|
246
|
-
"check_untyped_defs",
|
|
247
|
-
],
|
|
248
|
-
[
|
|
249
|
-
"mypy",
|
|
250
|
-
"warn_return_any",
|
|
251
|
-
],
|
|
252
|
-
[
|
|
253
|
-
"pyright",
|
|
254
|
-
"typeCheckingMode",
|
|
255
|
-
],
|
|
256
|
-
[
|
|
257
|
-
"pyright",
|
|
258
|
-
"analyzeUnannotatedFunctions",
|
|
259
|
-
],
|
|
260
|
-
[
|
|
261
|
-
"codespell",
|
|
262
|
-
"check-filenames",
|
|
263
|
-
],
|
|
264
|
-
[
|
|
265
|
-
"codespell",
|
|
266
|
-
"check-hidden",
|
|
267
|
-
],
|
|
268
|
-
[
|
|
269
|
-
"codespell",
|
|
270
|
-
"builtin",
|
|
271
|
-
],
|
|
272
|
-
[
|
|
273
|
-
"codespell",
|
|
274
|
-
"uri-ignore-words-list",
|
|
275
|
-
],
|
|
276
|
-
[
|
|
277
|
-
"pytest",
|
|
278
|
-
"ini_options",
|
|
279
|
-
"verbosity_assertions",
|
|
280
|
-
],
|
|
281
|
-
[
|
|
282
|
-
"pytest",
|
|
283
|
-
"ini_options",
|
|
284
|
-
"minversion",
|
|
285
|
-
],
|
|
286
|
-
[
|
|
287
|
-
"pytest",
|
|
288
|
-
"ini_options",
|
|
289
|
-
"testpaths",
|
|
290
|
-
],
|
|
291
|
-
]
|
|
292
|
-
pinned = ["ruff"]
|
|
293
|
-
|
|
294
|
-
[tool.uv.build-backend]
|
|
295
|
-
module-name = "pyselfupdate"
|
|
296
|
-
module-root = "src"
|
|
297
|
-
|
|
298
|
-
[build-system]
|
|
299
|
-
requires = ["uv_build>=0.12.2,<0.13.0"]
|
|
300
|
-
build-backend = "uv_build"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|