python-update-checker 0.8.0__tar.gz → 0.10.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 (25) hide show
  1. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/PKG-INFO +4 -3
  2. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/README.md +3 -2
  3. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/pyproject.toml +3 -3
  4. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/pyproject.toml.orig +3 -3
  5. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/src/puc/__init__.py +2 -0
  6. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/src/puc/cli.py +28 -1
  7. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/src/puc/pyprojecttoml.py +9 -0
  8. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/src/puc/requirementstxt.py +7 -0
  9. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/src/puc/uvlock.py +11 -2
  10. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/data/pyproject.toml +2 -0
  11. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/data/requirements.txt +2 -0
  12. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/data/uv.lock +14 -0
  13. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/test_pyproject_toml.py +20 -2
  14. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/test_uv_lock.py +22 -3
  15. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/.gitignore +0 -0
  16. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/.ruff.toml +0 -0
  17. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/Makefile +0 -0
  18. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/scripts/install_dev.sh +0 -0
  19. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/src/puc/dependencies.py +0 -0
  20. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/src/puc/logging.py +0 -0
  21. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/__init__.py +0 -0
  22. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/data/other-requirements.txt +0 -0
  23. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/data/pyproject_compatible.toml +0 -0
  24. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/test_latest.py +0 -0
  25. {python_update_checker-0.8.0 → python_update_checker-0.10.0}/tests/test_requirements_txt.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-update-checker
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  License-Expression: GPL-3.0-only
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Environment :: Console
@@ -46,8 +46,9 @@ about different pinning strategies.
46
46
  ## Quickstart
47
47
 
48
48
  ```bash
49
-
50
- uvx --from python-update-checker puc --exclude-newer "1 day" update pyproject.toml
49
+ # update dependencies in pyproject.toml to the latest version
50
+ uvx --from python-update-checker puc --exclude-newer="1 day" update pyproject.toml
51
+ # install updated dependencies in virtual environment
51
52
  uv sync
52
53
  ```
53
54
 
@@ -28,8 +28,9 @@ about different pinning strategies.
28
28
  ## Quickstart
29
29
 
30
30
  ```bash
31
-
32
- uvx --from python-update-checker puc --exclude-newer "1 day" update pyproject.toml
31
+ # update dependencies in pyproject.toml to the latest version
32
+ uvx --from python-update-checker puc --exclude-newer="1 day" update pyproject.toml
33
+ # install updated dependencies in virtual environment
33
34
  uv sync
34
35
  ```
35
36
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-update-checker"
3
- version = "0.8.0"
3
+ version = "0.10.0"
4
4
  readme = "README.md"
5
5
  requires-python = ">=3.11"
6
6
  license = "GPL-3.0-only"
@@ -26,8 +26,8 @@ puc = "puc:cli.main"
26
26
  dev = [
27
27
  "bump2version==1.0.1",
28
28
  "pytest==9.1.1",
29
- "ruff==0.16.4",
30
- "rumdl==0.2.61",
29
+ "ruff==0.16.5",
30
+ "rumdl==0.2.62",
31
31
  "tombi==1.4.1",
32
32
  "ty==0.0.75",
33
33
  "zizmor==1.29.0",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-update-checker"
3
- version = "0.8.0"
3
+ version = "0.10.0"
4
4
  readme = "README.md"
5
5
  requires-python = ">=3.11"
6
6
  license = "GPL-3.0-only"
@@ -36,9 +36,9 @@ dev = [
36
36
  # for testing
37
37
  "pytest==9.1.1",
38
38
  # for python code linting
39
- "ruff==0.16.4",
39
+ "ruff==0.16.5",
40
40
  # Markdown style checking and formatting
41
- "rumdl==0.2.61",
41
+ "rumdl==0.2.62",
42
42
  # reformat toml files
43
43
  "tombi==1.4.1",
44
44
  # for python type checking
@@ -1,3 +1,5 @@
1
1
  # Author: Bastian Kleineidam
2
2
  # Copyright: GPL-v3
3
3
  """module file"""
4
+
5
+ __version__ = "0.10.0"
@@ -21,6 +21,7 @@ from .logging import logger
21
21
  from .pyprojecttoml import handle_pyproject_toml
22
22
  from .requirementstxt import handle_requirements_txt
23
23
  from .uvlock import handle_uv_lock
24
+ from . import __version__
24
25
 
25
26
 
26
27
  def usage(msg: str | None = None) -> None:
@@ -69,6 +70,12 @@ def get_option_parser() -> argparse.ArgumentParser:
69
70
  dest="exclude_newer_package",
70
71
  help="Limit candidate packages for specific packages to those that were uploaded prior to the given date. Accepts package-date pairs in the format PACKAGE=DATE.",
71
72
  )
73
+ parser.add_argument(
74
+ "--exclude-package",
75
+ dest="exclude_packages",
76
+ action="append",
77
+ help="Ignore given package. Can be used multiple times.",
78
+ )
72
79
  parser.add_argument(
73
80
  "--constraints",
74
81
  dest="constraints",
@@ -94,6 +101,13 @@ def get_option_parser() -> argparse.ArgumentParser:
94
101
  default=False,
95
102
  help="Print debug messages.",
96
103
  )
104
+ parser.add_argument(
105
+ "--version",
106
+ action="store_true",
107
+ dest="version",
108
+ default=False,
109
+ help="Print version and exit.",
110
+ )
97
111
  subparsers = parser.add_subparsers(help='commands', dest='command')
98
112
  parser_check = subparsers.add_parser('check', help='check for updated versions')
99
113
  parser_check.add_argument(
@@ -132,6 +146,12 @@ def handle_dependency_file(dep_file: str, optargs, constraint_file):
132
146
  packages = [canonicalize_name(name) for name in optargs.packages]
133
147
  else:
134
148
  packages = None
149
+ if optargs.exclude_packages:
150
+ exclude_packages = [
151
+ canonicalize_name(name) for name in optargs.exclude_packages
152
+ ]
153
+ else:
154
+ exclude_packages = None
135
155
  if dep_file_normalized == "pyproject.toml":
136
156
  # pyproject.toml format
137
157
  updatable = handle_pyproject_toml(
@@ -140,6 +160,7 @@ def handle_dependency_file(dep_file: str, optargs, constraint_file):
140
160
  command=optargs.command,
141
161
  exclude_newer=optargs.exclude_newer,
142
162
  exclude_newer_package=optargs.exclude_newer_package,
163
+ exclude_packages=exclude_packages,
143
164
  constraint_file=constraint_file,
144
165
  color=optargs.color,
145
166
  )
@@ -151,6 +172,7 @@ def handle_dependency_file(dep_file: str, optargs, constraint_file):
151
172
  command=optargs.command,
152
173
  exclude_newer=optargs.exclude_newer,
153
174
  exclude_newer_package=optargs.exclude_newer_package,
175
+ exclude_packages=exclude_packages,
154
176
  constraint_file=constraint_file,
155
177
  color=optargs.color,
156
178
  )
@@ -162,6 +184,7 @@ def handle_dependency_file(dep_file: str, optargs, constraint_file):
162
184
  command=optargs.command,
163
185
  exclude_newer=optargs.exclude_newer,
164
186
  exclude_newer_package=optargs.exclude_newer_package,
187
+ exclude_packages=exclude_packages,
165
188
  constraint_file=constraint_file,
166
189
  color=optargs.color,
167
190
  )
@@ -174,7 +197,6 @@ def handle_dependency_file(dep_file: str, optargs, constraint_file):
174
197
 
175
198
  def main() -> int:
176
199
  """Parse options and check or update dependencies."""
177
- logger.info(f"{shlex.join(sys.argv)}")
178
200
  # parse options
179
201
  try:
180
202
  optargs = get_option_parser().parse_args(sys.argv[1:])
@@ -182,8 +204,13 @@ def main() -> int:
182
204
  logger.exception(exc)
183
205
  usage()
184
206
  # handle options
207
+ if optargs.version:
208
+ print(__version__)
209
+ return 0
185
210
  if optargs.debug:
186
211
  logger.setLevel(logging.DEBUG)
212
+ logger.info(f"{shlex.join(sys.argv)}")
213
+ logger.debug(f"puc version {__version__}")
187
214
  remove_constraint_file = False
188
215
  constraints = optargs.constraints
189
216
  constraint_file = None
@@ -25,6 +25,7 @@ def handle_pyproject_toml(
25
25
  packages=None,
26
26
  exclude_newer: str | None = None,
27
27
  exclude_newer_package: str | None = None,
28
+ exclude_packages=None,
28
29
  constraint_file: str | None = None,
29
30
  color: bool = True,
30
31
  ) -> int:
@@ -57,6 +58,7 @@ def handle_pyproject_toml(
57
58
  packages=packages,
58
59
  exclude_newer=exclude_newer,
59
60
  exclude_newer_package=exclude_newer_package,
61
+ exclude_packages=exclude_packages,
60
62
  constraint_file=constraint_file,
61
63
  color=color,
62
64
  )
@@ -72,6 +74,7 @@ def handle_pyproject_toml(
72
74
  packages=packages,
73
75
  exclude_newer=exclude_newer,
74
76
  exclude_newer_package=exclude_newer_package,
77
+ exclude_packages=exclude_packages,
75
78
  constraint_file=constraint_file,
76
79
  color=color,
77
80
  )
@@ -86,6 +89,7 @@ def handle_pyproject_toml(
86
89
  packages=packages,
87
90
  exclude_newer=exclude_newer,
88
91
  exclude_newer_package=exclude_newer_package,
92
+ exclude_packages=exclude_packages,
89
93
  constraint_file=constraint_file,
90
94
  color=color,
91
95
  )
@@ -104,6 +108,7 @@ def update_pyproject_dependencies(
104
108
  packages=None,
105
109
  exclude_newer: str | None = None,
106
110
  exclude_newer_package: str | None = None,
111
+ exclude_packages=None,
107
112
  constraint_file: str | None = None,
108
113
  color: bool = True,
109
114
  ) -> int:
@@ -124,6 +129,10 @@ def update_pyproject_dependencies(
124
129
 
125
130
  # respect optional package filter
126
131
  if packages and canonicalize_name(pkg_req.name) not in packages:
132
+ logger.info(f"skip filtered package {pkg_req.name!r}")
133
+ continue
134
+ if exclude_packages and canonicalize_name(pkg_req.name) in exclude_packages:
135
+ logger.info(f"skip excluded package {pkg_req.name!r}")
127
136
  continue
128
137
  try:
129
138
  latest_version = get_latest_version(
@@ -28,6 +28,7 @@ def handle_requirements_txt(
28
28
  packages=None,
29
29
  exclude_newer: str | None = None,
30
30
  exclude_newer_package: str | None = None,
31
+ exclude_packages=None,
31
32
  constraint_file: str | None = None,
32
33
  color: bool = True,
33
34
  rec_level: int = 0,
@@ -65,6 +66,7 @@ def handle_requirements_txt(
65
66
  packages=packages,
66
67
  exclude_newer=exclude_newer,
67
68
  exclude_newer_package=exclude_newer_package,
69
+ exclude_packages=exclude_packages,
68
70
  constraint_file=constraint_file,
69
71
  color=color,
70
72
  rec_level=rec_level + 1,
@@ -90,6 +92,11 @@ def handle_requirements_txt(
90
92
  spec = next(s for s in pkg_req.specifier)
91
93
  if packages and canonicalize_name(pkg_req.name) not in packages:
92
94
  output.write(line)
95
+ elif (
96
+ exclude_packages
97
+ and canonicalize_name(pkg_req.name) in exclude_packages
98
+ ):
99
+ output.write(line)
93
100
  elif latest_version is not None and latest_version != spec.version:
94
101
  updatable += 1
95
102
  if not is_newer_version(spec.version, latest_version):
@@ -20,6 +20,7 @@ def handle_uv_lock(
20
20
  packages=None,
21
21
  exclude_newer: str | None = None,
22
22
  exclude_newer_package: str | None = None,
23
+ exclude_packages=None,
23
24
  constraint_file: str | None = None,
24
25
  color: bool = True,
25
26
  ) -> int:
@@ -51,12 +52,20 @@ def handle_uv_lock(
51
52
  continue
52
53
  # respect package filter
53
54
  if packages and canonicalize_name(name) not in packages:
54
- logger.info(f"skip package {name} due to package filter")
55
+ logger.info(f"skip filtered package {name!r}")
56
+ continue
57
+ if exclude_packages and canonicalize_name(name) in exclude_packages:
58
+ logger.info(f"skip excluded package {name!r}")
55
59
  continue
56
60
  # skip virtual packages (ie. the project itself)
57
61
  source = uvpackage.get("source", {})
62
+ # skip virtual packages (ie. the project itself)
58
63
  if "virtual" in source:
59
- logger.info(f"skip virtual package {name}")
64
+ logger.info(f"skip virtual package {name!r}")
65
+ continue
66
+ # skip editable packages
67
+ if "editable" in source:
68
+ logger.info(f"skip editable package {name!r}")
60
69
  continue
61
70
  updatable += update_uvlock_dependency(
62
71
  name,
@@ -9,6 +9,8 @@ dependencies = [
9
9
  "pywin32==310; os_name=='nt'",
10
10
  # arbitrary equality
11
11
  "certifi===2026.1.4",
12
+ # ignore this
13
+ "fragilicous==1.0",
12
14
 
13
15
  # unpinned
14
16
  "requests",
@@ -6,6 +6,8 @@ ruff ==0.15.9
6
6
  pywin32==310; os_name=='nt'
7
7
  # arbitrary equality
8
8
  certifi===2026.1.4
9
+ # ignore this
10
+ fragilicous==1.0
9
11
 
10
12
  # unpinned
11
13
  requests
@@ -55,3 +55,17 @@ wheels = [
55
55
  name = "imadoofus"
56
56
  version = "1.1.1"
57
57
  source = { virtual = "." }
58
+
59
+ [[package]]
60
+ name = "imabellend"
61
+ version = "1.1.1"
62
+ source = { editable = "." }
63
+
64
+ [[package]]
65
+ name = "fragilicous"
66
+ version = "0.0.29"
67
+ source = { registry = "https://pypi.org/simple" }
68
+ sdist = { url = "https://files.pythonhosted.org/packages/47/d5/853561de49fae38c519e905b2d8da9c531219608f1fccc47a0fc2c896980/ty-0.0.29.tar.gz", hash = "sha256:e7936cca2f691eeda631876c92809688dbbab68687c3473f526cd83b6a9228d8", size = 5469221, upload-time = "2026-04-05T15:01:21.328Z" }
69
+ wheels = [
70
+ { url = "https://files.pythonhosted.org/packages/2c/55/87280a994d6a2d2647c65e12abbc997ed49835794366153c04c4d9304d76/ty-0.0.29-py3-none-win_arm64.whl", hash = "sha256:f9794bbd1bb3ce13f78c191d0c89ae4c63f52c12b6daa0c6fe220b90d019d12c", size = 10428165, upload-time = "2026-04-05T15:01:34.665Z" },
71
+ ]
@@ -28,7 +28,15 @@ class PyprojectTomlTest(unittest.TestCase):
28
28
  def test_pyproject_check(self):
29
29
  """Run puc check"""
30
30
  filename = os.path.join(datadir, "pyproject.toml")
31
- cmd = ["uv", "run", "puc", "check", filename]
31
+ cmd = [
32
+ "uv",
33
+ "run",
34
+ "puc",
35
+ "--exclude-package",
36
+ "fragilicous",
37
+ "check",
38
+ filename,
39
+ ]
32
40
  result = subprocess.run(cmd, check=False, text=True, capture_output=True)
33
41
  output = result.stdout.strip()
34
42
  self.assertTrue(result.returncode > 0)
@@ -37,6 +45,7 @@ class PyprojectTomlTest(unittest.TestCase):
37
45
  self.assertIn("found update 'ruff ==", output)
38
46
  self.assertIn("found update 'tensorflow==", output)
39
47
  self.assertIn("found update 'certifi===", output)
48
+ self.assertIn("skip excluded package 'fragilicous'", output)
40
49
 
41
50
  @needs_program('uv')
42
51
  def test_pyproject_check_package(self):
@@ -72,7 +81,15 @@ class PyprojectTomlTest(unittest.TestCase):
72
81
  content = f.read()
73
82
  for dep in packagedeps:
74
83
  self.assertIn(dep, content)
75
- cmd = ["uv", "run", "puc", "update", filename]
84
+ cmd = [
85
+ "uv",
86
+ "run",
87
+ "puc",
88
+ "--exclude-package",
89
+ "fragilicous",
90
+ "update",
91
+ filename,
92
+ ]
76
93
  result = subprocess.run(cmd, check=True, text=True, capture_output=True)
77
94
  output = result.stdout.strip()
78
95
  self.assertIn("updating 'argcomplete==", output)
@@ -80,6 +97,7 @@ class PyprojectTomlTest(unittest.TestCase):
80
97
  self.assertIn("updating 'ruff ==", output)
81
98
  self.assertIn("updating 'tensorflow==", output)
82
99
  self.assertIn("updating 'certifi===", output)
100
+ self.assertIn("skip excluded package 'fragilicous'", output)
83
101
  # check that old package versions have been updated
84
102
  with open(filename) as f:
85
103
  content = f.read()
@@ -28,13 +28,23 @@ class UvLockTest(unittest.TestCase):
28
28
  def test_uvlock_check(self):
29
29
  """Run puc check"""
30
30
  filename = os.path.join(datadir, "uv.lock")
31
- cmd = ["uv", "run", "puc", "check", filename]
31
+ cmd = [
32
+ "uv",
33
+ "run",
34
+ "puc",
35
+ "--exclude-package",
36
+ "fragilicous",
37
+ "check",
38
+ filename,
39
+ ]
32
40
  result = subprocess.run(cmd, check=False, text=True, capture_output=True)
33
41
  output = result.stdout.strip()
34
42
  self.assertTrue(result.returncode > 0)
35
43
  self.assertIn("found update 'ty==", output)
36
44
  self.assertIn("found update 'ruff==", output)
37
- self.assertIn("skip virtual package imadoofus", output)
45
+ self.assertIn("skip virtual package 'imadoofus'", output)
46
+ self.assertIn("skip editable package 'imabellend'", output)
47
+ self.assertIn("skip excluded package 'fragilicous'", output)
38
48
 
39
49
  @needs_program('uv')
40
50
  def test_uvlock_check_package(self):
@@ -58,10 +68,19 @@ class UvLockTest(unittest.TestCase):
58
68
  shutil.copy(projectfile, os.path.join(tmpdir, "pyproject.toml"))
59
69
  try:
60
70
  filename = os.path.join(tmpdir, "uv.lock")
61
- cmd = ["uv", "run", "puc", "update", filename]
71
+ cmd = [
72
+ "uv",
73
+ "run",
74
+ "puc",
75
+ "--exclude-package",
76
+ "fragilicous",
77
+ "update",
78
+ filename,
79
+ ]
62
80
  result = subprocess.run(cmd, check=True, text=True, capture_output=True)
63
81
  output = result.stdout.strip()
64
82
  self.assertIn("updating 'ty==", output)
65
83
  self.assertIn("updating 'ruff==", output)
84
+ self.assertIn("skip excluded package 'fragilicous'", output)
66
85
  finally:
67
86
  shutil.rmtree(tmpdir)