webchanges 3.31.3__tar.gz → 3.33.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 (33) hide show
  1. {webchanges-3.31.3/webchanges.egg-info → webchanges-3.33.0}/PKG-INFO +14 -7
  2. {webchanges-3.31.3 → webchanges-3.33.0}/README.rst +7 -2
  3. {webchanges-3.31.3 → webchanges-3.33.0}/pyproject.toml +118 -208
  4. {webchanges-3.31.3 → webchanges-3.33.0}/requirements.txt +1 -1
  5. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/__init__.py +4 -4
  6. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/_vendored/headers.py +5 -5
  7. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/cli.py +26 -24
  8. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/command.py +115 -102
  9. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/config.py +2 -3
  10. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/differs.py +255 -211
  11. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/filters.py +83 -130
  12. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/handler.py +71 -98
  13. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/jobs.py +482 -387
  14. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/mailer.py +7 -4
  15. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/main.py +11 -9
  16. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/reporters.py +125 -149
  17. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/storage.py +115 -126
  18. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/storage_minidb.py +1 -1
  19. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/util.py +26 -25
  20. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/worker.py +26 -21
  21. {webchanges-3.31.3 → webchanges-3.33.0/webchanges.egg-info}/PKG-INFO +14 -7
  22. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges.egg-info/SOURCES.txt +1 -1
  23. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges.egg-info/requires.txt +2 -0
  24. /webchanges-3.31.3/LICENSE → /webchanges-3.33.0/LICENSE.md +0 -0
  25. {webchanges-3.31.3 → webchanges-3.33.0}/MANIFEST.in +0 -0
  26. {webchanges-3.31.3 → webchanges-3.33.0}/setup.cfg +0 -0
  27. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/__main__.py +0 -0
  28. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/_vendored/__init__.py +0 -0
  29. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/_vendored/packaging_version.py +0 -0
  30. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges/py.typed +0 -0
  31. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges.egg-info/dependency_links.txt +0 -0
  32. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges.egg-info/entry_points.txt +0 -0
  33. {webchanges-3.31.3 → webchanges-3.33.0}/webchanges.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webchanges
3
- Version: 3.31.3
3
+ Version: 3.33.0
4
4
  Summary: Web Changes Delivered. AI-Summarized. Totally Anonymous.
5
5
  Author-email: Mike Borsetti <mike+webchanges@borsetti.com>
6
6
  Maintainer-email: Mike Borsetti <mike+webchanges@borsetti.com>
@@ -88,10 +88,12 @@ Classifier: Operating System :: OS Independent
88
88
  Classifier: Programming Language :: Python
89
89
  Classifier: Programming Language :: Python :: 3
90
90
  Classifier: Programming Language :: Python :: 3 :: Only
91
- Classifier: Programming Language :: Python :: 3.10
92
91
  Classifier: Programming Language :: Python :: 3.11
93
92
  Classifier: Programming Language :: Python :: 3.12
94
93
  Classifier: Programming Language :: Python :: 3.13
94
+ Classifier: Programming Language :: Python :: 3.14
95
+ Classifier: Programming Language :: Python :: Free Threading
96
+ Classifier: Programming Language :: Python :: Free Threading :: 4 - Resilient
95
97
  Classifier: Programming Language :: Python :: Implementation :: CPython
96
98
  Classifier: Topic :: Internet
97
99
  Classifier: Topic :: Internet :: WWW/HTTP
@@ -99,9 +101,9 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
99
101
  Classifier: Topic :: System :: Monitoring
100
102
  Classifier: Topic :: Utilities
101
103
  Classifier: Typing :: Typed
102
- Requires-Python: >=3.10
104
+ Requires-Python: >=3.11
103
105
  Description-Content-Type: text/x-rst
104
- License-File: LICENSE
106
+ License-File: LICENSE.md
105
107
  Requires-Dist: colorama; sys_platform == "win32"
106
108
  Requires-Dist: cssselect
107
109
  Requires-Dist: h2
@@ -113,7 +115,7 @@ Requires-Dist: msgpack
113
115
  Requires-Dist: platformdirs
114
116
  Requires-Dist: pyyaml
115
117
  Requires-Dist: tzdata; sys_platform == "win32"
116
- Requires-Dist: zstandard
118
+ Requires-Dist: zstandard; python_version < "3.14"
117
119
  Provides-Extra: use-browser
118
120
  Requires-Dist: playwright; extra == "use-browser"
119
121
  Requires-Dist: psutil; extra == "use-browser"
@@ -188,6 +190,8 @@ For the best experience, use the current version of `Python <https://www.python.
188
190
  older Python versions for 3 years after they're replaced by a newer one; we just ask that you use the most up-to-date
189
191
  bug and security fix release from that older version.
190
192
 
193
+ While **webchanges** supports free-threated Python, certain optional dependencies may not.
194
+
191
195
  For Generative AI summaries (BETA), you need a free `API Key from Google Cloud AI Studio
192
196
  <https://aistudio.google.com/app/apikey>`__ (see `here
193
197
  <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google>`__).
@@ -266,7 +270,7 @@ Schedule
266
270
 
267
271
  Code
268
272
  ====
269
- |coveralls| |snyk| |issues| |code_style|
273
+ |coveralls| |snyk| |issues| |code_style| |OpenSSF_Scorecard|
270
274
 
271
275
  The code, issues tracker, and discussions are hosted on `GitHub <https://github.com/mborsetti/webchanges>`__.
272
276
 
@@ -285,7 +289,7 @@ License
285
289
  =======
286
290
  |license|
287
291
 
288
- See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE>`__ (released
292
+ See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE.md>`__ (released
289
293
  under the `MIT License <https://opensource.org/licenses/MIT>`__ but redistributing modified source code, dated 30
290
294
  July 2020, from `urlwatch 2.21 <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__
291
295
  licensed under a `BSD 3-Clause License
@@ -346,3 +350,6 @@ configuration files), see `here <https://webchanges.readthedocs.io/en/stable/upg
346
350
  .. |snyk| image:: https://snyk.io/advisor/python/holidays/badge.svg
347
351
  :target: https://snyk.io/advisor/python/holidays
348
352
  :alt: Snyk Package Health Score
353
+ .. |OpenSSF_Scorecard| image:: https://api.scorecard.dev/projects/github.com/mborsetti/webchanges/badge
354
+ :target: https://scorecard.dev/viewer/?uri=github.com/mborsetti/webchanges
355
+ :alt: OpenSSF Scoreard
@@ -22,6 +22,8 @@ For the best experience, use the current version of `Python <https://www.python.
22
22
  older Python versions for 3 years after they're replaced by a newer one; we just ask that you use the most up-to-date
23
23
  bug and security fix release from that older version.
24
24
 
25
+ While **webchanges** supports free-threated Python, certain optional dependencies may not.
26
+
25
27
  For Generative AI summaries (BETA), you need a free `API Key from Google Cloud AI Studio
26
28
  <https://aistudio.google.com/app/apikey>`__ (see `here
27
29
  <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google>`__).
@@ -100,7 +102,7 @@ Schedule
100
102
 
101
103
  Code
102
104
  ====
103
- |coveralls| |snyk| |issues| |code_style|
105
+ |coveralls| |snyk| |issues| |code_style| |OpenSSF_Scorecard|
104
106
 
105
107
  The code, issues tracker, and discussions are hosted on `GitHub <https://github.com/mborsetti/webchanges>`__.
106
108
 
@@ -119,7 +121,7 @@ License
119
121
  =======
120
122
  |license|
121
123
 
122
- See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE>`__ (released
124
+ See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE.md>`__ (released
123
125
  under the `MIT License <https://opensource.org/licenses/MIT>`__ but redistributing modified source code, dated 30
124
126
  July 2020, from `urlwatch 2.21 <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__
125
127
  licensed under a `BSD 3-Clause License
@@ -180,3 +182,6 @@ configuration files), see `here <https://webchanges.readthedocs.io/en/stable/upg
180
182
  .. |snyk| image:: https://snyk.io/advisor/python/holidays/badge.svg
181
183
  :target: https://snyk.io/advisor/python/holidays
182
184
  :alt: Snyk Package Health Score
185
+ .. |OpenSSF_Scorecard| image:: https://api.scorecard.dev/projects/github.com/mborsetti/webchanges/badge
186
+ :target: https://scorecard.dev/viewer/?uri=github.com/mborsetti/webchanges
187
+ :alt: OpenSSF Scoreard
@@ -6,9 +6,9 @@
6
6
 
7
7
  [build-system]
8
8
  # Minimum requirements for the build system to execute.
9
- requires = ['setuptools'] # PEP 508 specifications.
9
+ requires = ['argparse-manpage[setuptools]'] # PEP 508 specifications.
10
10
  # Setuptools specification
11
- build-backend = "setuptools.build_meta"
11
+ build-backend = 'setuptools.build_meta'
12
12
 
13
13
  [project]
14
14
  # See https://packaging.python.org/en/latest/specifications/declaring-project-metadata/
@@ -16,13 +16,11 @@ dynamic = ['version', 'dependencies']
16
16
  name = 'webchanges'
17
17
  description = 'Web Changes Delivered. AI-Summarized. Totally Anonymous.'
18
18
  readme = { file = 'README.rst', content-type = 'text/x-rst' }
19
- requires-python = '>=3.10'
20
- license = {file = 'LICENSE'}
21
- authors = [
22
- {name = 'Mike Borsetti', email = 'mike+webchanges@borsetti.com'},
23
- ]
19
+ requires-python = '>=3.11'
20
+ license = { file = 'LICENSE.md' }
21
+ authors = [{ name = 'Mike Borsetti', email = 'mike+webchanges@borsetti.com' }]
24
22
  maintainers = [
25
- {name = 'Mike Borsetti', email = 'mike+webchanges@borsetti.com'},
23
+ { name = 'Mike Borsetti', email = 'mike+webchanges@borsetti.com' },
26
24
  ]
27
25
  keywords = ['webmonitoring', 'monitoring']
28
26
  classifiers = [
@@ -38,10 +36,12 @@ classifiers = [
38
36
  'Programming Language :: Python',
39
37
  'Programming Language :: Python :: 3',
40
38
  'Programming Language :: Python :: 3 :: Only',
41
- 'Programming Language :: Python :: 3.10',
42
39
  'Programming Language :: Python :: 3.11',
43
40
  'Programming Language :: Python :: 3.12',
44
41
  'Programming Language :: Python :: 3.13',
42
+ 'Programming Language :: Python :: 3.14',
43
+ 'Programming Language :: Python :: Free Threading',
44
+ 'Programming Language :: Python :: Free Threading :: 4 - Resilient',
45
45
  'Programming Language :: Python :: Implementation :: CPython',
46
46
  'Topic :: Internet',
47
47
  'Topic :: Internet :: WWW/HTTP',
@@ -89,56 +89,32 @@ xmpp = ['aioxmpp']
89
89
  redis = ['redis']
90
90
  requests = ['requests']
91
91
  safe_password = ['keyring']
92
+ # all
92
93
  all = [
93
- 'webchanges[use_browser,beautify,bs4,html5lib,ical2text,jq,ocr,pdf2text,pypdf_crypto,deepdiff_xml,imagediff,matrix,pushbullet,pushover,xmpp,redis,requests,safe_password]'
94
+ 'webchanges[use_browser,beautify,bs4,html5lib,ical2text,jq,ocr,pdf2text,pypdf_crypto,deepdiff_xml,imagediff,matrix,pushbullet,pushover,xmpp,redis,requests,safe_password]',
94
95
  ]
95
96
 
96
97
 
97
98
  # -------------------------- setuptools --------------------------
99
+ [tool.setuptools]
98
100
  # Called by build
99
101
  # See https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
100
- [tool.setuptools]
101
- # explicit package listing is required for building in tox -e new-install
102
+
103
+ # Explicit package listing is required for building in tox -e new-install
102
104
  # https://setuptools.pypa.io/en/latest/userguide/package_discovery.html
103
105
  packages = ['webchanges', 'webchanges._vendored']
104
106
 
105
107
  [tool.setuptools.dynamic]
106
- version = {attr = 'webchanges.__version__'}
107
- dependencies = {file = 'requirements.txt'}
108
+ version = { attr = 'webchanges.__version__' }
109
+ dependencies = { file = 'requirements.txt' }
108
110
 
109
111
  [tool.setuptools.package-data]
110
112
  'webchanges' = ['py.typed']
111
113
 
112
-
113
- # -------------------------- bandit --------------------------
114
- [tool.bandit]
115
- # Find common security issues in Python code.
116
- # Runs as part of pre-commit.
117
- # Config file documentation at https://bandit.readthedocs.io/en/latest/config.html
118
-
119
- targets = ['webchanges', 'tests']
120
- skips = [
121
- 'B101', # Use of assert detected.
122
- 'B404', # Consider possible security implications associated with subprocess module.
123
- 'B602', # subprocess call with shell=True identified, security issue.
124
- 'B603' # subprocess call - check for execution of untrusted input.
125
- ]
126
-
127
-
128
- # -------------------------- black --------------------------
129
- [tool.black]
130
- # Uncompromising code formatting
131
- # Runs as part of pre-commit
132
- # Config file documentation
133
- # https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#configuration-via-a-file
134
-
135
- # What's in here overrides the command-line options shown by running $ black --help.
136
- line-length = 120
137
- target-version = ['py310']
138
- skip-string-normalization = true
139
- extend-exclude = '/(\.idea|\.pytest_cache|\__pycache__|\venv.*|\webchanges.egg-info)/'
140
- color = true
141
-
114
+ # -------------------------- manpages --------------------------
115
+ # https://github.com/praiskup/argparse-manpage/blob/main/README.md
116
+ [tool.build_manpages]
117
+ manpages = ['man/webchanges.1:function=get_parser:pyfile=get_parser']
142
118
 
143
119
  # -------------------------- coverage --------------------------
144
120
  [tool.coverage.run]
@@ -193,104 +169,13 @@ exclude_lines = [
193
169
  'except ImportError:',
194
170
 
195
171
  # Don't cover IDE code:
196
- 'if TYPE_CHECKING:'
172
+ 'if TYPE_CHECKING:',
197
173
  ]
198
174
  # Exclude entire files if they have a custom # pragma: exclude file remark
199
175
  # exclude_also = '(?s)\A.*# pragma: exclude file.*\Z'
200
176
  # ignore_errors = true
201
177
 
202
178
 
203
- # -------------------------- isort --------------------------
204
- [tool.isort]
205
- # isort your imports, so you don't have to.
206
- # Runs as part of pre-commit.
207
- # Config file documentation https://pycqa.github.io/isort/docs/configuration/options.html
208
-
209
- profile = 'black'
210
- # multi_line_output = 3
211
- # use_parentheses = True
212
- # include_trailing_comma = True
213
- # force_grid_wrap = 0
214
- # ensure_newline_before_comments = True
215
- line_length = 120
216
-
217
- # Treat project as a git repository and ignore files listed in .gitignore.
218
- skip_gitignore = true
219
-
220
- # Force isort to recognize a module as being part of the current python project.
221
- known_first_party = 'webchanges'
222
-
223
- # Force isort to recognize a module as being a local folder.
224
- # Generally, this is reserved for relative imports (from . import module).
225
- known_local_folder = 'webchanges'
226
-
227
- # Force all imports to be sorted alphabetically within a section.
228
- force_alphabetical_sort_within_sections = true
229
-
230
- # Automatically create section groups by the top-level package they come from.
231
- group_by_package = true
232
-
233
- # Remove redundant aliases from imports, such as import os as os.
234
- remove_redundant_aliases = true
235
-
236
- # Use color in terminal output.
237
- color_output = true
238
-
239
-
240
- # -------------------------- mypy --------------------------
241
- [tool.mypy]
242
- # Static Typing for Python
243
- # Runs as part of pre-commit
244
- # Config file documentation at https://mypy.readthedocs.io/en/stable/config_file.html
245
-
246
- # Disables import discovery of namespace packages (see PEP 420)
247
- namespace_packages = false
248
-
249
- # Specifies the Python version used to parse and check the target program.
250
- # python_version = 3.12
251
-
252
- # Suppresses error messages about imports that cannot be resolved.
253
- ignore_missing_imports = true
254
-
255
- # Disallows calling functions without type annotations from functions with type annotations.
256
- disallow_untyped_calls = true
257
-
258
- # Disallows defining functions without type annotations or with incomplete type annotations.
259
- disallow_untyped_defs = true
260
-
261
- # Reports an error whenever a function with type annotations is decorated with a decorator without annotations.
262
- disallow_untyped_decorators = true
263
-
264
- # Warns about casting an expression to its inferred type.
265
- warn_redundant_casts = true
266
-
267
- # Warns about unneeded # type: ignore comments.
268
- # May behave differently in GitHub Actions than it does on Windows.
269
- # warn_unused_ignores = true
270
-
271
- # Shows a warning when returning a value with type Any from a function declared with a non-Any return type.
272
- warn_return_any = true
273
-
274
- # Shows a warning when encountering any code inferred to be unreachable or redundant after performing type analysis.
275
- warn_unreachable = false
276
-
277
- # Enables additional checks that are technically correct but may be impractical in real code.
278
- extra_checks = true
279
-
280
- # Shows documentation link to corresponding error code.
281
- show_error_code_links = true
282
-
283
- # Use visually nicer output in error messages: use soft word wrap, show source code snippets, and show error location
284
- # markers.
285
- pretty = true
286
-
287
- # Use an SQLite database to store the cache.
288
- sqlite_cache = true
289
-
290
- # Warns about per-module sections in the config file that do not match any files processed when invoking mypy.
291
- warn_unused_configs = true
292
-
293
-
294
179
  # -------------------------- rstcheck --------------------------
295
180
  [tool.rstcheck]
296
181
  # Checks syntax of reStructuredText and code blocks nested within it.
@@ -307,15 +192,14 @@ report_level = 'WARNING'
307
192
  # Testing framework
308
193
  # Config file documentation at https://docs.pytest.org/en/stable/reference/reference.html#ini-options-ref
309
194
 
310
- log_auto_indent = true
195
+ # log_auto_indent = 2
311
196
  # Enable log display during test run (aka "live logging" https://docs.pytest.org/en/stable/logging.html#live-logs)
312
- log_cli = true
313
- minversion = '8.3.3'
197
+ # log_cli = true
198
+ minversion = '9.0.2'
314
199
  testpaths = ['tests']
315
200
 
316
- # the below is for pytest-asyncio (used to be required due to Playwright)
317
- # asyncio_mode = 'auto'
318
- # asyncio_default_fixture_loop_scope = 'function'
201
+ # pytest-playwright
202
+ # addopts = ['--browser', 'chromium', '--browser-channel', 'chrome']
319
203
 
320
204
  # Adds pytest-cov functionality (see https://pytest-cov.readthedocs.io/en/latest/config.html)
321
205
  # Note: --cov moved to .github/workflows/ci-cd.yaml and tox.ini due to interference with PyCharm breakpoints (see
@@ -326,40 +210,10 @@ testpaths = ['tests']
326
210
 
327
211
  # -------------------------- ruff --------------------------
328
212
  [tool.ruff]
329
- # Config file documentation at https://docs.astral.sh/ruff/configuration/
330
-
331
- # Exclude a variety of commonly ignored directories.
332
- exclude = [
333
- ".bzr",
334
- ".direnv",
335
- ".eggs",
336
- ".git",
337
- ".git-rewrite",
338
- ".hg",
339
- ".ipynb_checkpoints",
340
- ".mypy_cache",
341
- ".nox",
342
- ".pants.d",
343
- ".pyenv",
344
- ".pytest_cache",
345
- ".pytype",
346
- ".ruff_cache",
347
- ".svn",
348
- ".tox",
349
- ".venv",
350
- ".vscode",
351
- "__pypackages__",
352
- "_build",
353
- "buck-out",
354
- "build",
355
- "dist",
356
- "node_modules",
357
- "site-packages",
358
- "venv",
359
-
360
- "webchanges/storage_minidb.py",
361
- "webchanges/_vendored",
362
- ]
213
+ # Config file documentation at https://docs.astral.sh/ruff/configuration/ and https://docs.astral.sh/ruff/settings/
214
+
215
+ # File patterns to omit from formatting and linting, in addition to those specified by exclude.
216
+ extend-exclude = ['webchanges/storage_minidb.py', 'webchanges/_vendored']
363
217
 
364
218
  # By default, Ruff will discover files matching *.py, *.pyi, *.ipynb, or pyproject.toml.
365
219
  # Include additional files
@@ -367,44 +221,97 @@ exclude = [
367
221
 
368
222
  # Set the maximum line length.
369
223
  line-length = 120
370
- indent-width = 4
371
224
 
372
225
  # Target Python version
373
- # target-version = "py311" # Commented out to infer from [project] requires-python
226
+ # target-version = 'py311' # Commented out to infer from [project] requires-python
374
227
 
375
228
  [tool.ruff.lint]
376
229
  # By default, Ruff enables Flake8's F rules, along with a subset of the E rules
377
230
  # Enable rules not enabled by default, and ignore specific rules.
378
231
  select = [
379
- # flake8-builtins
380
- "B",
381
- # flake8-bandit
382
- "S",
383
- # flake8-datetimez
384
- "DTZ",
385
- # isort
386
- "I",
387
- # McCabe complexity
388
- # "C901",
389
- # pepe8-naming
390
- "N",
391
- # Pyflakes
392
- "F",
393
- # pycodestyle errors and warnings
394
- "E", "W",
395
- # Ruff-specific rules
396
- "RUF",
232
+ 'ANN', # flake8-annotations
233
+ 'S', # flake8-bandit
234
+ 'BLE', # flake8-blind-except
235
+ 'B', # flake8-bugbear
236
+ 'A', # flake8-builtin
237
+ 'C4', # flake8-comprehensions
238
+ 'DTZ', # flake8-datetimez
239
+ # 'EM', # flake8-errmsg # TODO
240
+ 'FA', # flake8-future-annotations
241
+ 'INT', # flake8-gettext
242
+ 'ISC', # flake8-implicit-str-concat
243
+ 'LOG', # flake8-logging
244
+ # 'G', # flake8-logging-format # Prefer f-string to lazy `%` formatting and OK with '+'
245
+ 'PIE', # flake8-pie
246
+ 'PYI', # flake8-pyi
247
+ 'PT', # flake8-pytest-style
248
+ 'Q', # flake8-quotes
249
+ 'RSE', # flake8-raise
250
+ 'RET', # flake8-return
251
+ 'SIM', # flake8-simplify
252
+ 'TID', # flake8-tidy-imports
253
+ 'TD', # flake8-todos
254
+ 'TC', # flake8-type-checking
255
+ # 'ARG', # flake8-unused-arguments # Code loses clarity
256
+ 'PTH', # flake8-use-pathlib
257
+ 'FLY', # flynt
258
+ 'I', # isort
259
+ 'C90', # mccabe
260
+ 'N', # pep8-naming
261
+ 'PERF', # Perflint
262
+ 'E', # pycodestyle errors
263
+ 'W', # pycodestyle warnings
264
+ # 'DOC', # pydoclint # TODO
265
+ # 'D', # pydocstyle # TODO
266
+ 'F', # Pyflakes
267
+ # 'PL', # Pylint # TODO
268
+ 'FURB', # refurb
269
+ 'RUF', # Ruff-specific rules
270
+ # 'TRY', # tryceratops # TODO
397
271
  ]
272
+
398
273
  ignore = [
399
- "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
274
+ 'FLY002', # Consider f-string instead of string join
275
+ # 'G003', # Logging statement uses `+`'
276
+ # 'G004', # Logging statement uses f-string (Prefer f-string to lazy `%` formatting)
277
+ 'PLC0415', # `import` should be at the top-level of a file
278
+ 'PT011', # pytest.raises({exception}) is too broad, set the match parameter or use a more specific exception
279
+ 'PT030', # pytest.warns({warning}) is too broad, set the match parameter or use a more specific warning
280
+ 'RUF012', # Mutable class attributes should be annotated with `typing.ClassVar`
281
+ 'SIM105', # Use contextlib.suppress({exception}) instead of try-except-pass
282
+ 'SIM115', # Use a context manager for opening files
283
+ 'TD002', # Missing author in TODO
284
+ 'TD003', # Missing issue link for this TODO
400
285
  ]
401
286
 
402
287
  # Allow fix for all enabled rules (when `--fix`) is provided.
403
- fixable = ["ALL"]
288
+ fixable = ['ALL']
404
289
  unfixable = []
405
290
 
406
291
  # Allow unused variables when underscore-prefixed.
407
- dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
292
+ dummy-variable-rgx = '^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$'
293
+
294
+ [tool.ruff.lint.flake8-annotations]
295
+ allow-star-arg-any = true # TODO
296
+
297
+ [tool.ruff.lint.flake8-quotes]
298
+ inline-quotes = 'single'
299
+
300
+ [tool.ruff.lint.mccabe]
301
+ # Flag errors (`C901`) whenever the complexity level exceeds below.
302
+ max-complexity = 30
303
+
304
+ [tool.ruff.lint.per-file-ignores]
305
+ 'tests/*' = [
306
+ 'S101', # Use of `assert` detected
307
+ ]
308
+
309
+ [tool.ruff.lint.pydoclint]
310
+ # Skip docstrings which fit on a single line.
311
+ ignore-one-line-docstrings = true
312
+
313
+ [tool.ruff.lint.pydocstyle]
314
+ convention = 'google'
408
315
 
409
316
  [tool.ruff.format]
410
317
  # Enable the formatter, which is a drop-in replacement for Black.
@@ -414,16 +321,16 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
414
321
  exclude = []
415
322
 
416
323
  # Use single quotes for strings.
417
- quote-style = "single"
324
+ quote-style = 'single'
418
325
 
419
326
  # Like Black, indent with spaces, rather than tabs.
420
- indent-style = "space"
327
+ indent-style = 'space'
421
328
 
422
329
  # Like Black, respect magic trailing commas.
423
330
  skip-magic-trailing-comma = false
424
331
 
425
332
  # Like Black, automatically detect the appropriate line ending.
426
- line-ending = "auto"
333
+ line-ending = 'auto'
427
334
 
428
335
  # Enable auto-formatting of code examples in docstrings. Markdown,
429
336
  # reStructuredText code/literal blocks and doctests are all supported.
@@ -437,10 +344,13 @@ docstring-code-format = true
437
344
  #
438
345
  # This only has an effect when the `docstring-code-format` setting is
439
346
  # enabled.
440
- docstring-code-line-length = "dynamic"
347
+ docstring-code-line-length = 'dynamic'
441
348
 
442
- [tool.ruff.lint.per-file-ignores]
443
- "tests/*" = [
444
- # Use of `assert` detected
445
- "S101"
446
- ]
349
+ # -------------------------- ty --------------------------
350
+ # Config file documentation at https://docs.astral.sh/ty/reference/configuration/
351
+
352
+ [tool.ty.environment]
353
+ extra-paths = ['./webchanges']
354
+
355
+ [tool.ty.src]
356
+ exclude = ['./webchanges/storage_minidb.py']
@@ -9,4 +9,4 @@ msgpack
9
9
  platformdirs
10
10
  pyyaml
11
11
  tzdata; sys_platform == 'win32'
12
- zstandard
12
+ zstandard; python_version < '3.14'
@@ -3,7 +3,7 @@ it summarizes (including with Gen AI) what changed ('diff') and displays it and/
3
3
  supported services. Can check the output of local commands as well.
4
4
  """
5
5
 
6
- # The code below is subject to the license contained in the LICENSE file, which is part of the source code.
6
+ # The code below is subject to the license contained in the LICENSE.md file, which is part of the source code.
7
7
 
8
8
  # The docstring above (__doc__) and the variables below are used in the program and for builds, including in building
9
9
  # documentation with Sphinx.
@@ -12,7 +12,7 @@ supported services. Can check the output of local commands as well.
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
- __min_python_version__ = (3, 10) # minimum version of Python required to run; supported until fall 2025
15
+ __min_python_version__ = (3, 11) # minimum version of Python required to run; 3.11 supported until fall 2026
16
16
 
17
17
 
18
18
  __project_name__ = str(__package__)
@@ -22,7 +22,7 @@ __project_name__ = str(__package__)
22
22
  # * MINOR version when you add functionality in a backwards compatible manner, and
23
23
  # * MICRO or PATCH version when you make backwards compatible bug fixes. We no longer use '0'
24
24
  # If unsure on increments, use pkg_resources.parse_version to parse
25
- __version__ = '3.31.3'
25
+ __version__ = '3.33.0'
26
26
  __description__ = (
27
27
  'Check web (or command output) for changes since last run and notify.\n\nAnonymously alerts you of web changes.'
28
28
  )
@@ -40,7 +40,7 @@ def init_data() -> dict[str, str | tuple]:
40
40
 
41
41
  :returns: dict of globals()
42
42
  """
43
- return {k: v for k, v in globals().items()}
43
+ return dict(globals().items())
44
44
 
45
45
 
46
46
  if __name__ == '__main__':
@@ -50,7 +50,7 @@ def to_str(value: str | bytes, encoding: str = 'utf-8') -> str:
50
50
 
51
51
 
52
52
  def to_bytes_or_str(value: str, match_type_of: typing.AnyStr) -> typing.AnyStr:
53
- return value if isinstance(match_type_of, str) else value.encode()
53
+ return value if isinstance(match_type_of, str) else value.encode() # ty:ignore[invalid-return-type]
54
54
 
55
55
 
56
56
  # from https://github.com/encode/httpx/blob/master/httpx/_models.py
@@ -81,8 +81,8 @@ def _obfuscate_sensitive_headers(
81
81
  items: typing.Iterable[tuple[typing.AnyStr, typing.AnyStr]],
82
82
  ) -> typing.Iterator[tuple[typing.AnyStr, typing.AnyStr]]:
83
83
  for k, v in items:
84
- if to_str(k.lower()) in SENSITIVE_HEADERS:
85
- v = to_bytes_or_str('[secure]', match_type_of=v)
84
+ if to_str(k.lower()) in SENSITIVE_HEADERS: # ty:ignore[no-matching-overload, invalid-argument-type]
85
+ v = to_bytes_or_str('[secure]', match_type_of=v) # ty:ignore[invalid-argument-type]
86
86
  yield k, v
87
87
 
88
88
 
@@ -102,8 +102,8 @@ class Headers(typing.MutableMapping[str, str]):
102
102
  self._list = list(headers._list)
103
103
  elif isinstance(headers, Mapping):
104
104
  for k, v in headers.items():
105
- bytes_key = _normalize_header_key(k, encoding)
106
- bytes_value = _normalize_header_value(v, encoding)
105
+ bytes_key = _normalize_header_key(k, encoding) # ty:ignore[invalid-argument-type]
106
+ bytes_value = _normalize_header_value(v, encoding) # ty:ignore[invalid-argument-type]
107
107
  self._list.append((bytes_key, bytes_key.lower(), bytes_value))
108
108
  elif headers is not None:
109
109
  for k, v in headers: