uv-upx 0.2.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.
uv_upx-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.3
2
+ Name: uv-upx
3
+ Version: 0.2.0
4
+ Summary: Upgrade versions of dependencies in pyproject.toml files in uv-managed projects.
5
+ Keywords: uv,upgrade,dependencies,pyproject.toml,automation,cli
6
+ Author: Alirex
7
+ Author-email: Alirex <alirex.prime@gmail.com>
8
+ Requires-Dist: pydantic>=2.12.5
9
+ Requires-Dist: tomlkit>=0.13.3
10
+ Requires-Dist: typer>=0.20.0
11
+ Requires-Python: >=3.14
12
+ Project-URL: Homepage, https://github.com/Alirex/uv_upgrade
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Uv upgrade
16
+
17
+ Upgrade dependencies in `pyproject.toml` files with `uv`.
18
+
19
+ ---
20
+
21
+ # Install
22
+
23
+ For end-users.
24
+
25
+ Note: For developers, see the [dev](docs_extra/dev.md) section.
26
+
27
+ ## Uv install or update
28
+
29
+ You need this for both usage and development.
30
+
31
+ https://docs.astral.sh/uv/getting-started/installation/
32
+
33
+ On Windows it can be better to use `Git Bash` terminal.
34
+
35
+ ```bash
36
+ if ! command -v uv &> /dev/null; then
37
+ curl -LsSf https://astral.sh/uv/install.sh | sh;
38
+ else
39
+ uv self update;
40
+ fi
41
+
42
+ uv --version
43
+ ```
44
+
45
+ ## Install or update a project for system-wide usage
46
+
47
+ Run it from any directory, except the project directory.
48
+
49
+ ```shell
50
+ if ! command -v uv-upgrade &> /dev/null; then
51
+ uv tool install git+https://github.com/Alirex/uv_upgrade
52
+ else
53
+ uv tool upgrade uv-upx
54
+ fi
55
+ ```
56
+
57
+ Or run with uvx:
58
+
59
+ ```shell
60
+ uvx uv-upx
61
+ ```
62
+
63
+ Dev installation: [see here](docs_extra/dev.md#install-app-system-wide-in-development-mode)
64
+
65
+ ## Check that project in the list
66
+
67
+ ```shell
68
+ uv tool list --show-python
69
+ ```
70
+
71
+ ## Remove the project with all data
72
+
73
+ If you need to remove the project with all data, run this command from the project directory:
74
+
75
+ ```shell
76
+ uv tool uninstall uv-upx
77
+ ```
78
+
79
+ ---
80
+
81
+ # Usage
82
+
83
+ ## Run the tool in the folder with the pyproject.toml
84
+
85
+ After installation, you can run the tool from any directory.
86
+
87
+ ```shell
88
+ uv-upgrade
89
+ ```
90
+
91
+ Also, you can run it with the `--help` flag with more details.
92
+
93
+ ## Notes
94
+
95
+ ### Uv as a source of truth
96
+
97
+ Run `uv sync --all-groups --all-extras --all-packages --upgrade`.
98
+
99
+ Get dependencies versions from `uv.lock`.
100
+
101
+ Put them in `pyproject.toml` files in related groups.
102
+
103
+ So, the main responsibility for dependencies resolution is on `uv`.
104
+
105
+ ### Workspace support
106
+
107
+ It works with workspaces. At least for some basic cases.
108
+
109
+ ### Updates simple dependencies
110
+
111
+ Updates `>=` dependencies. Like `bla>=2.0.0`.
112
+
113
+ ### Skip pinned versions
114
+
115
+ It doesn't touch pinned versions. Like `bla==2.0.0`.
116
+
117
+ ### Respect simple ranges
118
+
119
+ It respects simple dependency ranges. Like `bla>=1.0.0,<2.0.0`.
120
+
121
+ It moves the lower bound to the new version.
122
+
123
+ ### Respect extras
124
+
125
+ It respects extras. Like `bla[dev]>=1.0.0;python_version>="3.14"`.
126
+
127
+ ### Skip unhandled constraints
128
+
129
+ It skips unhandled and complex constraints. Like:
130
+
131
+ - `bla<=1.0.0`
132
+ - `bla>=2.0.0,!=1.5.0,<3.0.0`
133
+
134
+ ### Style preservation
135
+
136
+ It preserves comments in the `pyproject.toml` file. Like here:
137
+
138
+ ```toml
139
+ dependencies = [
140
+ # Better classes and data validation
141
+ "pydantic>=2.12.5",
142
+ # TOML parser and writer with preserved formatting
143
+ "tomlkit>=0.13.3",
144
+ # CLI app framework
145
+ "typer>=0.20.0",
146
+ ]
147
+ ```
148
+
149
+ ### Rollback
150
+
151
+ If something goes wrong, it rolls back the changes to the `pyproject.toml` and `uv.lock` files.
152
+
153
+ ### Why?
154
+
155
+ I needed this for my own projects.
156
+
157
+ I know these issues:
158
+
159
+ - [Upgrade dependencies in pyproject.toml (uv upgrade)](https://github.com/astral-sh/uv/issues/6692)
160
+ - [What is the intended workflow for updating dependencies with uv?](ttps://github.com/astral-sh/uv/issues/6794)
161
+
162
+ But I didn't see enough progress.
163
+
164
+ So, I implemented this tool for my own usage.
165
+
166
+ Maybe it will be useful for someone else.
167
+
168
+ ### Why not in Rust?
169
+
170
+ Because it's a temporary Proof-of-Concept.
171
+
172
+ Maybe it will be replaced by Rust in the future.
173
+
174
+ It uses type annotations anyway. For simpler migration.
175
+
176
+ ---
177
+
178
+ # Dev
179
+
180
+ [Dev](docs_extra/dev.md)
181
+
182
+ ---
183
+
184
+ # Other similar tools comparison
185
+
186
+ [Other similar tools comparison](docs_extra/other_similar_tools_comparison.md)
uv_upx-0.2.0/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Uv upgrade
2
+
3
+ Upgrade dependencies in `pyproject.toml` files with `uv`.
4
+
5
+ ---
6
+
7
+ # Install
8
+
9
+ For end-users.
10
+
11
+ Note: For developers, see the [dev](docs_extra/dev.md) section.
12
+
13
+ ## Uv install or update
14
+
15
+ You need this for both usage and development.
16
+
17
+ https://docs.astral.sh/uv/getting-started/installation/
18
+
19
+ On Windows it can be better to use `Git Bash` terminal.
20
+
21
+ ```bash
22
+ if ! command -v uv &> /dev/null; then
23
+ curl -LsSf https://astral.sh/uv/install.sh | sh;
24
+ else
25
+ uv self update;
26
+ fi
27
+
28
+ uv --version
29
+ ```
30
+
31
+ ## Install or update a project for system-wide usage
32
+
33
+ Run it from any directory, except the project directory.
34
+
35
+ ```shell
36
+ if ! command -v uv-upgrade &> /dev/null; then
37
+ uv tool install git+https://github.com/Alirex/uv_upgrade
38
+ else
39
+ uv tool upgrade uv-upx
40
+ fi
41
+ ```
42
+
43
+ Or run with uvx:
44
+
45
+ ```shell
46
+ uvx uv-upx
47
+ ```
48
+
49
+ Dev installation: [see here](docs_extra/dev.md#install-app-system-wide-in-development-mode)
50
+
51
+ ## Check that project in the list
52
+
53
+ ```shell
54
+ uv tool list --show-python
55
+ ```
56
+
57
+ ## Remove the project with all data
58
+
59
+ If you need to remove the project with all data, run this command from the project directory:
60
+
61
+ ```shell
62
+ uv tool uninstall uv-upx
63
+ ```
64
+
65
+ ---
66
+
67
+ # Usage
68
+
69
+ ## Run the tool in the folder with the pyproject.toml
70
+
71
+ After installation, you can run the tool from any directory.
72
+
73
+ ```shell
74
+ uv-upgrade
75
+ ```
76
+
77
+ Also, you can run it with the `--help` flag with more details.
78
+
79
+ ## Notes
80
+
81
+ ### Uv as a source of truth
82
+
83
+ Run `uv sync --all-groups --all-extras --all-packages --upgrade`.
84
+
85
+ Get dependencies versions from `uv.lock`.
86
+
87
+ Put them in `pyproject.toml` files in related groups.
88
+
89
+ So, the main responsibility for dependencies resolution is on `uv`.
90
+
91
+ ### Workspace support
92
+
93
+ It works with workspaces. At least for some basic cases.
94
+
95
+ ### Updates simple dependencies
96
+
97
+ Updates `>=` dependencies. Like `bla>=2.0.0`.
98
+
99
+ ### Skip pinned versions
100
+
101
+ It doesn't touch pinned versions. Like `bla==2.0.0`.
102
+
103
+ ### Respect simple ranges
104
+
105
+ It respects simple dependency ranges. Like `bla>=1.0.0,<2.0.0`.
106
+
107
+ It moves the lower bound to the new version.
108
+
109
+ ### Respect extras
110
+
111
+ It respects extras. Like `bla[dev]>=1.0.0;python_version>="3.14"`.
112
+
113
+ ### Skip unhandled constraints
114
+
115
+ It skips unhandled and complex constraints. Like:
116
+
117
+ - `bla<=1.0.0`
118
+ - `bla>=2.0.0,!=1.5.0,<3.0.0`
119
+
120
+ ### Style preservation
121
+
122
+ It preserves comments in the `pyproject.toml` file. Like here:
123
+
124
+ ```toml
125
+ dependencies = [
126
+ # Better classes and data validation
127
+ "pydantic>=2.12.5",
128
+ # TOML parser and writer with preserved formatting
129
+ "tomlkit>=0.13.3",
130
+ # CLI app framework
131
+ "typer>=0.20.0",
132
+ ]
133
+ ```
134
+
135
+ ### Rollback
136
+
137
+ If something goes wrong, it rolls back the changes to the `pyproject.toml` and `uv.lock` files.
138
+
139
+ ### Why?
140
+
141
+ I needed this for my own projects.
142
+
143
+ I know these issues:
144
+
145
+ - [Upgrade dependencies in pyproject.toml (uv upgrade)](https://github.com/astral-sh/uv/issues/6692)
146
+ - [What is the intended workflow for updating dependencies with uv?](ttps://github.com/astral-sh/uv/issues/6794)
147
+
148
+ But I didn't see enough progress.
149
+
150
+ So, I implemented this tool for my own usage.
151
+
152
+ Maybe it will be useful for someone else.
153
+
154
+ ### Why not in Rust?
155
+
156
+ Because it's a temporary Proof-of-Concept.
157
+
158
+ Maybe it will be replaced by Rust in the future.
159
+
160
+ It uses type annotations anyway. For simpler migration.
161
+
162
+ ---
163
+
164
+ # Dev
165
+
166
+ [Dev](docs_extra/dev.md)
167
+
168
+ ---
169
+
170
+ # Other similar tools comparison
171
+
172
+ [Other similar tools comparison](docs_extra/other_similar_tools_comparison.md)
@@ -0,0 +1,171 @@
1
+ [project]
2
+ name = "uv-upx"
3
+ version = "0.2.0"
4
+
5
+ description = "Upgrade versions of dependencies in pyproject.toml files in uv-managed projects."
6
+ readme = "README.md"
7
+ keywords = ["uv", "upgrade", "dependencies", "pyproject.toml", "automation", "cli"]
8
+
9
+ license_files = ["LICENSE.txt"]
10
+
11
+ authors = [
12
+ { name = "Alirex", email = "alirex.prime@gmail.com" }
13
+ ]
14
+
15
+ requires-python = ">=3.14"
16
+ dependencies = [
17
+ # Better classes and data validation
18
+ "pydantic>=2.12.5",
19
+ # TOML parser and writer with preserved formatting
20
+ "tomlkit>=0.13.3",
21
+ # CLI app framework
22
+ "typer>=0.20.0",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/Alirex/uv_upgrade"
27
+
28
+
29
+ [dependency-groups]
30
+ dev = [
31
+ # [typing]-[BEGIN]
32
+ # Static type checker
33
+ "mypy>=1.19.0",
34
+ # [typing]-[END]
35
+ # [testing]-[BEGIN]
36
+ "pytest>=9.0.2",
37
+ "pytest-benchmark>=5.2.3",
38
+ # [testing]-[END]
39
+ # Extra checker. Used by Zed.
40
+ "basedpyright>=1.36.0",
41
+ # Task runner
42
+ "poethepoet>=0.38.0",
43
+ # Dead code checker
44
+ "vulture>=2.14",
45
+ # Code complexity checker
46
+ "radon>=6.0.1",
47
+ ]
48
+
49
+ [project.scripts]
50
+ uv-upgrade = "uv_upx:main"
51
+
52
+ [build-system]
53
+ requires = ["uv_build>=0.9.15,<0.10.0"]
54
+ build-backend = "uv_build"
55
+
56
+ [tool.poe.tasks]
57
+ pca= [
58
+ {cmd = "prek run --all-files"},
59
+ ]
60
+
61
+ # Run with: poe metrics or uv run poe metrics
62
+ # Check code quality: dead code, complexity, and maintainability
63
+ metrics = [
64
+ {cmd = "vulture . --min-confidence 80 --exclude .venv,build,dist,docs,scripts,tests"},
65
+ {cmd = "radon cc . -a -nb"},
66
+ {cmd = "radon mi . -nb"}
67
+ ]
68
+
69
+ # Run with: poe quality or uv run poe quality
70
+ # Full quality check: format, lint, type check, and metrics
71
+ quality = [
72
+ {cmd = "ruff format ."},
73
+ {cmd = "ruff check . --fix"},
74
+ {cmd = "basedpyright --level error"},
75
+ {cmd = "vulture . --min-confidence 80 --exclude .venv,build,dist,docs,scripts,tests"},
76
+ {cmd = "radon cc . -a -nb"}
77
+ ]
78
+
79
+
80
+ [tool.pytest.ini_options]
81
+ addopts = "--strict-markers"
82
+
83
+ [tool.mypy]
84
+ #plugins = [
85
+ # "pydantic.mypy",
86
+ #]
87
+
88
+ strict = true
89
+
90
+ [tool.ruff]
91
+ line-length = 120
92
+ fix = true
93
+ unsafe-fixes = true
94
+ show-fixes = true
95
+
96
+ extend-exclude = [
97
+ ]
98
+
99
+ [tool.ruff.lint]
100
+ extend-select = [
101
+ "ALL",
102
+ ]
103
+ extend-ignore = [
104
+ # Missing docstring.
105
+ "D1",
106
+ # flake8-future-annotations.
107
+ "FA",
108
+ # Missing type annotation for self and cls.
109
+ "ANN1",
110
+ # f-strings to format logging messages.
111
+ "G004",
112
+ # Consider {expression} instead of string join.
113
+ "FLY002",
114
+ # TODOs
115
+ # Author.
116
+ "TD002",
117
+ # Link to issue
118
+ "TD003",
119
+ # Line contains a TODO or related comment
120
+ "FIX",
121
+ # Args and kwargs are not used and not annotated
122
+ "ANN002",
123
+ "ANN003",
124
+ # Commented code
125
+ "ERA001",
126
+ # empty-comment
127
+ "PLR2044",
128
+ #
129
+ #
130
+ # print used.
131
+ "T201",
132
+ ]
133
+
134
+ task-tags = [
135
+ "TODO",
136
+ "FIXME",
137
+ "HACK",
138
+ "XXX",
139
+ "[",
140
+ ]
141
+
142
+ [tool.ruff.lint.pydocstyle]
143
+ convention = "google"
144
+ # https://betterprogramming.pub/3-different-docstring-formats-for-python-d27be81e0d68
145
+
146
+ [tool.ruff.lint.flake8-comprehensions]
147
+ allow-dict-calls-with-keyword-arguments = true
148
+
149
+
150
+ [tool.ruff.lint.extend-per-file-ignores]
151
+ "__init__.py" = [
152
+ # Unused import.
153
+ "F401",
154
+ ]
155
+
156
+ "test_*.py" = [
157
+ # Allow `assert` statements in tests.
158
+ "S101",
159
+ ]
160
+
161
+ [tool.ruff.lint.flake8-type-checking]
162
+ runtime-evaluated-base-classes = [
163
+ "pydantic.BaseModel",
164
+ ]
165
+ runtime-evaluated-decorators = [
166
+ "pydantic.validate_call",
167
+ "app.command",
168
+ ]
169
+
170
+ [tool.basedpyright]
171
+ typeCheckingMode = "strict"
@@ -0,0 +1,5 @@
1
+ from .main import main
2
+
3
+ __all__ = [
4
+ "main",
5
+ ]
@@ -0,0 +1,4 @@
1
+ from uv_upx.main import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
File without changes
@@ -0,0 +1,37 @@
1
+ import pathlib # noqa: TC003
2
+ from typing import Annotated
3
+
4
+ import typer
5
+
6
+ from uv_upx.services.normalize_and_check_path_to_pyproject import normalize_and_check_path_to_project_root
7
+ from uv_upx.services.run_updater import run_updater
8
+
9
+ app = typer.Typer(
10
+ pretty_exceptions_enable=False,
11
+ )
12
+
13
+
14
+ @app.command()
15
+ def run(
16
+ *,
17
+ project_root_path: Annotated[
18
+ pathlib.Path | None,
19
+ typer.Option(
20
+ "--project",
21
+ "-p",
22
+ help="Path to project root directory. Use current working directory if not specified.",
23
+ ),
24
+ ] = None,
25
+ #
26
+ dry_run: Annotated[bool, typer.Option("--dry-run", help="Show changes without writing file")] = False,
27
+ #
28
+ verbose: Annotated[bool, typer.Option("--verbose", help="Show more output")] = False,
29
+ ) -> None:
30
+ """Update pyproject.toml dependencies to latest PyPI releases."""
31
+ path = normalize_and_check_path_to_project_root(project_root_path)
32
+
33
+ run_updater(
34
+ project_root_path=path,
35
+ dry_run=dry_run,
36
+ verbose=verbose,
37
+ )
@@ -0,0 +1,5 @@
1
+ from .init_logging import init_logging
2
+
3
+ __all__ = [
4
+ "init_logging",
5
+ ]
@@ -0,0 +1,8 @@
1
+ import logging
2
+
3
+
4
+ def init_logging() -> None:
5
+ logging.basicConfig(
6
+ level=logging.DEBUG,
7
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
8
+ )
@@ -0,0 +1,7 @@
1
+ from uv_upx.cli.main import app
2
+ from uv_upx.logging_custom import init_logging
3
+
4
+
5
+ def main() -> None:
6
+ init_logging()
7
+ app()
File without changes
@@ -0,0 +1,36 @@
1
+ from pydantic import BaseModel
2
+
3
+ from uv_upx.services.get_deps_from_project import DependencyName
4
+
5
+
6
+ class VersionConstraint(BaseModel):
7
+ operator: str
8
+ version: str
9
+
10
+
11
+ class DependencyParsed(BaseModel):
12
+ # https://peps.python.org/pep-0508/
13
+
14
+ dependency_name: DependencyName
15
+ """Dependency name (e.g., requests)"""
16
+
17
+ extras: list[str] | None
18
+ """Extras (e.g., [dev])"""
19
+
20
+ version_constraints: list[VersionConstraint]
21
+ """Version constraints (e.g., >=1.2.3, ==4.5.6)"""
22
+
23
+ marker: str | None
24
+ """Environment marker (after ;)"""
25
+
26
+ def get_full_spec(self) -> str:
27
+ parts: list[str] = [self.dependency_name]
28
+ if self.extras:
29
+ extras_str = ",".join(self.extras)
30
+ parts.append(f"[{extras_str}]")
31
+ if self.version_constraints:
32
+ vc_strs = [f"{vc.operator}{vc.version}" for vc in self.version_constraints]
33
+ parts.append(",".join(vc_strs))
34
+ if self.marker:
35
+ parts.append(f"; {self.marker}")
36
+ return "".join(parts)
File without changes
File without changes
@@ -0,0 +1,62 @@
1
+ import pathlib
2
+ from typing import Any, cast
3
+
4
+ from pydantic import BaseModel, ConfigDict
5
+ from tomlkit import TOMLDocument
6
+
7
+ from uv_upx.services.get_pyproject_paths_by_globs import get_pyproject_paths_by_globs
8
+ from uv_upx.services.normalize_and_check_path_to_pyproject import get_and_check_path_to_pyproject
9
+ from uv_upx.services.save_load_toml import load_toml
10
+
11
+
12
+ class PyProjectWrapper(BaseModel):
13
+ path: pathlib.Path
14
+ data: TOMLDocument
15
+
16
+ model_config = ConfigDict(
17
+ arbitrary_types_allowed=True,
18
+ )
19
+
20
+
21
+ class PyProjectsRegistry(BaseModel):
22
+ items: list[PyProjectWrapper]
23
+
24
+
25
+ def get_all_pyprojects(
26
+ project_root_path: pathlib.Path,
27
+ ) -> PyProjectsRegistry:
28
+ """Find all pyproject.toml files in the project tree.
29
+
30
+ Use `workspaces` from uv.
31
+ """
32
+ items: list[PyProjectWrapper] = []
33
+
34
+ root_pyproject_path = get_and_check_path_to_pyproject(project_root_path)
35
+ root_pyproject_data = load_toml(root_pyproject_path)
36
+ items.append(PyProjectWrapper(path=root_pyproject_path, data=root_pyproject_data))
37
+
38
+ # Get workspaces_config
39
+ # https://docs.astral.sh/uv/concepts/projects/workspaces/
40
+
41
+ # Example:
42
+ # [tool.uv.workspace]
43
+ # members = ["packages/*"]
44
+ # exclude = ["packages/seeds"]
45
+
46
+ # TODO: (?) Improve this place for efficiency
47
+
48
+ workspaces_config: dict[str, Any] = root_pyproject_data.get("tool", {}).get("uv", {}).get("workspace", {}) # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
49
+ members_i_relative_glob_based: list[str] = cast("list[str]", workspaces_config.get("members", [])) # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
50
+ exclude_i_relative_glob_based: list[str] = cast("list[str]", workspaces_config.get("exclude", [])) # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
51
+
52
+ # Use glob-based pattern matching to find all pyproject.toml files in the workspace
53
+ members_paths_set = get_pyproject_paths_by_globs(project_root_path, members_i_relative_glob_based)
54
+ exclude_paths_set = get_pyproject_paths_by_globs(project_root_path, exclude_i_relative_glob_based)
55
+
56
+ result_paths_set = members_paths_set - exclude_paths_set
57
+
58
+ for path in result_paths_set:
59
+ data = load_toml(path)
60
+ items.append(PyProjectWrapper(path=path, data=data))
61
+
62
+ return PyProjectsRegistry(items=items)