webchanges 3.36.0__tar.gz → 3.37.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 (69) hide show
  1. {webchanges-3.36.0/webchanges.egg-info → webchanges-3.37.0}/PKG-INFO +33 -30
  2. {webchanges-3.36.0 → webchanges-3.37.0}/README.rst +30 -27
  3. {webchanges-3.36.0 → webchanges-3.37.0}/pyproject.toml +13 -4
  4. {webchanges-3.36.0 → webchanges-3.37.0}/tools/get_manpages_parser.py +6 -2
  5. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/__init__.py +1 -1
  6. webchanges-3.37.0/webchanges/__main__.py +11 -0
  7. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/_resources/config.schema.json +22 -5
  8. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/_resources/jobs.schema.json +66 -19
  9. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/_vendored/headers.py +1 -1
  10. webchanges-3.37.0/webchanges/_vendored/packaging_version.py +1469 -0
  11. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/_vendored/typeguard.py +70 -54
  12. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/cli.py +461 -29
  13. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/command.py +89 -305
  14. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/differs.py +308 -85
  15. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/filters/__init__.py +2 -0
  16. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/filters/_base.py +4 -4
  17. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/filters/_document.py +1 -1
  18. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/filters/_text.py +46 -1
  19. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/handler.py +14 -8
  20. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/jobs/_base.py +22 -14
  21. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/jobs/_browser.py +229 -99
  22. webchanges-3.37.0/webchanges/jobs/_shell.py +258 -0
  23. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/jobs/_url.py +9 -8
  24. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/main.py +8 -2
  25. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/__init__.py +0 -3
  26. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/_base.py +27 -16
  27. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/_config.py +34 -1
  28. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/_yaml.py +37 -22
  29. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/util.py +114 -34
  30. webchanges-3.37.0/webchanges/worker.py +313 -0
  31. {webchanges-3.36.0 → webchanges-3.37.0/webchanges.egg-info}/PKG-INFO +33 -30
  32. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges.egg-info/SOURCES.txt +0 -1
  33. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges.egg-info/requires.txt +2 -2
  34. webchanges-3.36.0/webchanges/__main__.py +0 -10
  35. webchanges-3.36.0/webchanges/_vendored/packaging_version.py +0 -807
  36. webchanges-3.36.0/webchanges/jobs/_shell.py +0 -91
  37. webchanges-3.36.0/webchanges/reporters/_filter.py +0 -59
  38. webchanges-3.36.0/webchanges/worker.py +0 -245
  39. {webchanges-3.36.0 → webchanges-3.37.0}/LICENSE.md +0 -0
  40. {webchanges-3.36.0 → webchanges-3.37.0}/MANIFEST.in +0 -0
  41. {webchanges-3.36.0 → webchanges-3.37.0}/requirements.txt +0 -0
  42. {webchanges-3.36.0 → webchanges-3.37.0}/setup.cfg +0 -0
  43. {webchanges-3.36.0 → webchanges-3.37.0}/setup.py +0 -0
  44. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/_resources/__init__.py +0 -0
  45. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/_vendored/__init__.py +0 -0
  46. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/config.py +0 -0
  47. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/filters/_encoding.py +0 -0
  48. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/filters/_html.py +0 -0
  49. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/filters/_shell.py +0 -0
  50. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/jobs/__init__.py +0 -0
  51. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/jobs/_exceptions.py +0 -0
  52. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/mailer.py +0 -0
  53. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/py.typed +0 -0
  54. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/_browser.py +0 -0
  55. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/_chat.py +0 -0
  56. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/_email.py +0 -0
  57. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/_stdout.py +0 -0
  58. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/_system.py +0 -0
  59. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/reporters/_web.py +0 -0
  60. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/__init__.py +0 -0
  61. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/_base.py +0 -0
  62. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/_minidb.py +0 -0
  63. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/_redis.py +0 -0
  64. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/_sqlite3.py +0 -0
  65. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage/_ssdb.py +0 -0
  66. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges/storage_minidb.py +0 -0
  67. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges.egg-info/dependency_links.txt +0 -0
  68. {webchanges-3.36.0 → webchanges-3.37.0}/webchanges.egg-info/entry_points.txt +0 -0
  69. {webchanges-3.36.0 → webchanges-3.37.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.36.0
3
+ Version: 3.37.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>
@@ -76,9 +76,9 @@ Provides-Extra: pypdf-crypto
76
76
  Requires-Dist: pypdf; extra == "pypdf-crypto"
77
77
  Requires-Dist: cryptography; extra == "pypdf-crypto"
78
78
  Provides-Extra: deepdiff
79
- Requires-Dist: deepdiff; extra == "deepdiff"
79
+ Requires-Dist: deepdiff>=8.0; extra == "deepdiff"
80
80
  Provides-Extra: deepdiff-xml
81
- Requires-Dist: deepdiff; extra == "deepdiff-xml"
81
+ Requires-Dist: deepdiff>=8.0; extra == "deepdiff-xml"
82
82
  Requires-Dist: xmltodict; extra == "deepdiff-xml"
83
83
  Provides-Extra: imagediff
84
84
  Requires-Dist: Pillow; extra == "imagediff"
@@ -118,26 +118,35 @@ notifications and AI-powered summaries to your favorite `platform
118
118
  <https://webchanges.readthedocs.io/en/stable/introduction.html#reporters>`__.
119
119
 
120
120
 
121
- Requirements
122
- ============
123
- **webchanges** requires |support|.
121
+ Highlights
122
+ ==========
123
+ - **AI-powered change summaries** via Gemini (`ai_google differ
124
+ <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google-diff>`__, BETA).
125
+ - **Structural diffs for JSON and XML** via the `deepdiff differ
126
+ <https://webchanges.readthedocs.io/en/stable/differs.html#deepdiff-diff>`__.
127
+ - **Visual diffs for images** via the `image differ
128
+ <https://webchanges.readthedocs.io/en/stable/differs.html#image-diff>`__ (BETA).
129
+ - **Bypass TLS/JA3 fingerprinting** with the optional `curl_cffi
130
+ <https://webchanges.readthedocs.io/en/stable/jobs.html#http-client>`__ HTTP client.
131
+ - **JSON-Schema-validated** ``jobs.yaml`` and ``config.yaml`` for editor autocompletion and inline validation.
124
132
 
125
- For the best experience, use the current version of `Python <https://www.python.org/downloads/>`__. We also support
126
- 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
127
- bug and security fix release from that older version.
128
133
 
129
- While **webchanges** supports free-threated Python, certain optional dependencies may not.
134
+ Requirements
135
+ ============
136
+ **webchanges** requires |support|. For the best experience, use the current version of `Python
137
+ <https://www.python.org/downloads/>`__; older Python versions are supported for 3 years after they're replaced. Free-threaded Python is supported, though some
138
+ optional dependencies may not be.
130
139
 
131
140
  For Generative AI summaries (BETA), you need a free `API Key from Google Cloud AI Studio
132
141
  <https://aistudio.google.com/app/apikey>`__ (see `here
133
- <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google>`__).
142
+ <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google-diff>`__).
134
143
 
135
144
 
136
145
  Installation
137
146
  ============
138
147
  |pypi_version| |format| |status|
139
148
 
140
- Install **webchanges** with ``uv`` (recommended):
149
+ Install **webchanges** with `uv <https://docs.astral.sh/uv/>`__ (recommended):
141
150
 
142
151
  .. code-block:: bash
143
152
 
@@ -149,22 +158,18 @@ or with ``pip``:
149
158
 
150
159
  pip install webchanges
151
160
 
152
- Running in Docker
161
+ Other ways to run
153
162
  -----------------
154
- **webchanges** can easily run in a `Docker <https://www.docker.com/>`__ container! You will find a minimal
155
- implementation (no browser) `here <https://github.com/yubiuser/webchanges-docker>`__, and one with a browser
156
- `here <https://github.com/jhedlund/webchanges-docker>`__.
157
-
158
- As a GitHub Action
159
- ------------------
160
- **webchanges** can easily run as a `GitHub Action <https://www.docker.com/>`__! You will find an implementation
161
- `here <https://github.com/swimmwatch/webchanges-action>`__.
163
+ - In a `Docker <https://www.docker.com/>`__ container: a minimal implementation (no browser) is `here
164
+ <https://github.com/yubiuser/webchanges-docker>`__, and one with a browser is `here
165
+ <https://github.com/jhedlund/webchanges-docker>`__.
166
+ - As a `GitHub Action <https://docs.github.com/en/actions>`__: an implementation is `here
167
+ <https://github.com/swimmwatch/webchanges-action>`__.
162
168
 
163
169
 
164
170
  Documentation |readthedocs|
165
171
  ===========================
166
- The documentation is hosted on `Read the Docs <https://webchanges.readthedocs.io/>`__. A (very good) AI-generated
167
- high-level architectural overview is available on `DeepWiki <https://deepwiki.com/mborsetti/webchanges>`__.
172
+ The documentation is hosted on `Read the Docs <https://webchanges.readthedocs.io/>`__.
168
173
 
169
174
 
170
175
  Quick Start
@@ -238,13 +243,11 @@ licensed under a `BSD 3-Clause License
238
243
  <https://raw.githubusercontent.com/thp/urlwatch/346b25914b0418342ffe2fb0529bed702fddc01f/COPYING>`__).
239
244
 
240
245
 
241
- Compatibility with **urlwatch**
242
- ===============================
243
-
244
- This project is based on code from `urlwatch 2.21
245
- <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__ dated 30 July 2020.
246
- For upgrading from **urlwatch** to **webchanges** (using the same job and
247
- configuration files), see `here <https://webchanges.readthedocs.io/en/stable/upgrading.html>`__.
246
+ Relationship with **urlwatch**
247
+ ==============================
248
+ This project is a fork of `urlwatch 2.21
249
+ <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__ and has since added many features;
250
+ see the `comparison <https://webchanges.readthedocs.io/en/stable/_static/webchanges-vs-urlwatch.html>`__.
248
251
 
249
252
 
250
253
  .. |support| image:: https://img.shields.io/pypi/pyversions/webchanges.svg
@@ -14,26 +14,35 @@ notifications and AI-powered summaries to your favorite `platform
14
14
  <https://webchanges.readthedocs.io/en/stable/introduction.html#reporters>`__.
15
15
 
16
16
 
17
- Requirements
18
- ============
19
- **webchanges** requires |support|.
17
+ Highlights
18
+ ==========
19
+ - **AI-powered change summaries** via Gemini (`ai_google differ
20
+ <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google-diff>`__, BETA).
21
+ - **Structural diffs for JSON and XML** via the `deepdiff differ
22
+ <https://webchanges.readthedocs.io/en/stable/differs.html#deepdiff-diff>`__.
23
+ - **Visual diffs for images** via the `image differ
24
+ <https://webchanges.readthedocs.io/en/stable/differs.html#image-diff>`__ (BETA).
25
+ - **Bypass TLS/JA3 fingerprinting** with the optional `curl_cffi
26
+ <https://webchanges.readthedocs.io/en/stable/jobs.html#http-client>`__ HTTP client.
27
+ - **JSON-Schema-validated** ``jobs.yaml`` and ``config.yaml`` for editor autocompletion and inline validation.
20
28
 
21
- For the best experience, use the current version of `Python <https://www.python.org/downloads/>`__. We also support
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
- bug and security fix release from that older version.
24
29
 
25
- While **webchanges** supports free-threated Python, certain optional dependencies may not.
30
+ Requirements
31
+ ============
32
+ **webchanges** requires |support|. For the best experience, use the current version of `Python
33
+ <https://www.python.org/downloads/>`__; older Python versions are supported for 3 years after they're replaced. Free-threaded Python is supported, though some
34
+ optional dependencies may not be.
26
35
 
27
36
  For Generative AI summaries (BETA), you need a free `API Key from Google Cloud AI Studio
28
37
  <https://aistudio.google.com/app/apikey>`__ (see `here
29
- <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google>`__).
38
+ <https://webchanges.readthedocs.io/en/stable/differs.html#ai-google-diff>`__).
30
39
 
31
40
 
32
41
  Installation
33
42
  ============
34
43
  |pypi_version| |format| |status|
35
44
 
36
- Install **webchanges** with ``uv`` (recommended):
45
+ Install **webchanges** with `uv <https://docs.astral.sh/uv/>`__ (recommended):
37
46
 
38
47
  .. code-block:: bash
39
48
 
@@ -45,22 +54,18 @@ or with ``pip``:
45
54
 
46
55
  pip install webchanges
47
56
 
48
- Running in Docker
57
+ Other ways to run
49
58
  -----------------
50
- **webchanges** can easily run in a `Docker <https://www.docker.com/>`__ container! You will find a minimal
51
- implementation (no browser) `here <https://github.com/yubiuser/webchanges-docker>`__, and one with a browser
52
- `here <https://github.com/jhedlund/webchanges-docker>`__.
53
-
54
- As a GitHub Action
55
- ------------------
56
- **webchanges** can easily run as a `GitHub Action <https://www.docker.com/>`__! You will find an implementation
57
- `here <https://github.com/swimmwatch/webchanges-action>`__.
59
+ - In a `Docker <https://www.docker.com/>`__ container: a minimal implementation (no browser) is `here
60
+ <https://github.com/yubiuser/webchanges-docker>`__, and one with a browser is `here
61
+ <https://github.com/jhedlund/webchanges-docker>`__.
62
+ - As a `GitHub Action <https://docs.github.com/en/actions>`__: an implementation is `here
63
+ <https://github.com/swimmwatch/webchanges-action>`__.
58
64
 
59
65
 
60
66
  Documentation |readthedocs|
61
67
  ===========================
62
- The documentation is hosted on `Read the Docs <https://webchanges.readthedocs.io/>`__. A (very good) AI-generated
63
- high-level architectural overview is available on `DeepWiki <https://deepwiki.com/mborsetti/webchanges>`__.
68
+ The documentation is hosted on `Read the Docs <https://webchanges.readthedocs.io/>`__.
64
69
 
65
70
 
66
71
  Quick Start
@@ -134,13 +139,11 @@ licensed under a `BSD 3-Clause License
134
139
  <https://raw.githubusercontent.com/thp/urlwatch/346b25914b0418342ffe2fb0529bed702fddc01f/COPYING>`__).
135
140
 
136
141
 
137
- Compatibility with **urlwatch**
138
- ===============================
139
-
140
- This project is based on code from `urlwatch 2.21
141
- <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__ dated 30 July 2020.
142
- For upgrading from **urlwatch** to **webchanges** (using the same job and
143
- configuration files), see `here <https://webchanges.readthedocs.io/en/stable/upgrading.html>`__.
142
+ Relationship with **urlwatch**
143
+ ==============================
144
+ This project is a fork of `urlwatch 2.21
145
+ <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__ and has since added many features;
146
+ see the `comparison <https://webchanges.readthedocs.io/en/stable/_static/webchanges-vs-urlwatch.html>`__.
144
147
 
145
148
 
146
149
  .. |support| image:: https://img.shields.io/pypi/pyversions/webchanges.svg
@@ -65,7 +65,7 @@ webchanges = 'webchanges.cli:main'
65
65
  use_browser = ['playwright', 'psutil']
66
66
  # filters
67
67
  # lxml is now optional with beautifulsoup4
68
- # note: also update the list of all possible dependencies in show_detailed_versions() in command.py!
68
+ # note: also update the list of all possible dependencies in show_detailed_versions() in cli.py!
69
69
  beautify = ['beautifulsoup4', 'jsbeautifier', 'cssbeautifier']
70
70
  bs4 = ['beautifulsoup4']
71
71
  html5lib = ['html5lib']
@@ -76,8 +76,9 @@ pdf2text = ['pdftotext']
76
76
  pypdf = ['pypdf']
77
77
  pypdf_crypto = ['pypdf', 'cryptography']
78
78
  # differs
79
- deepdiff = ['deepdiff']
80
- deepdiff_xml = ['deepdiff', 'xmltodict']
79
+ # deepdiff >= 8.0 is required for threshold_to_diff_deeper
80
+ deepdiff = ['deepdiff >= 8.0']
81
+ deepdiff_xml = ['deepdiff >= 8.0', 'xmltodict']
81
82
  imagediff = ['Pillow']
82
83
  # reporters
83
84
  matrix = ['matrix_client']
@@ -210,6 +211,14 @@ report_level = 'WARNING'
210
211
  # log_cli = true
211
212
  minversion = '9.0.2'
212
213
  testpaths = ['tests']
214
+ filterwarnings = [
215
+ # Fail tests that leak sqlite3 connections. Scoped by message so third-party ResourceWarnings (e.g.
216
+ # asyncio/playwright transports) don't destabilize the suite. The second entry is required because the warning
217
+ # is emitted during garbage collection: the error raised there is unraisable, and only surfaces as a test
218
+ # failure through pytest's unraisable-exception plugin (as a PytestUnraisableExceptionWarning).
219
+ 'error:unclosed database:ResourceWarning',
220
+ 'error:(?s).*unclosed database:pytest.PytestUnraisableExceptionWarning',
221
+ ]
213
222
 
214
223
  # pytest-playwright
215
224
  # addopts = ['--browser', 'chromium', '--browser-channel', 'chrome']
@@ -293,7 +302,7 @@ ignore = [
293
302
  'RUF012', # Mutable class attributes should be annotated with `typing.ClassVar`
294
303
  'SIM105', # Use contextlib.suppress({exception}) instead of try-except-pass
295
304
  'SIM115', # Use a context manager for opening files
296
- 'TD002', # Missing author in TODO
305
+ 'TD002', # Missing author in TODO
297
306
  'TD003', # Missing issue link for this TODO
298
307
  ]
299
308
 
@@ -1,8 +1,12 @@
1
- # pragma: exclude file
1
+ from __future__ import annotations
2
2
 
3
3
  import sys
4
- from argparse import ArgumentParser
5
4
  from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from argparse import ArgumentParser
9
+
6
10
 
7
11
  sys.path.insert(0, str(Path.cwd()))
8
12
 
@@ -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.36.0'
25
+ __version__ = '3.37.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
  )
@@ -0,0 +1,11 @@
1
+ if __name__ == '__main__':
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ cwd = str(Path.cwd())
6
+ if cwd not in sys.path:
7
+ sys.path.insert(0, cwd)
8
+
9
+ from webchanges.cli import main
10
+
11
+ main()
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/webchanges/_resources/config.schema.json",
3
4
  "title": "Webchanges Configuration File",
4
5
  "description": "JSON Schema for the webchanges config.yaml configuration file.",
5
6
  "type": "object",
6
- "required": ["display", "report", "job_defaults", "differ_defaults", "database", "footnote"],
7
+ "required": ["display", "report", "job_defaults", "differ_defaults", "database", "footnote", "version_check"],
7
8
  "properties": {
8
9
  "display": {
9
10
  "$ref": "#/$defs/_ConfigDisplay",
@@ -29,6 +30,10 @@
29
30
  "type": ["string", "null"],
30
31
  "description": "Optional text appended to the footer of every report (text, html and markdown). Set to null to disable.",
31
32
  "default": null
33
+ },
34
+ "version_check": {
35
+ "$ref": "#/$defs/_ConfigVersionCheck",
36
+ "description": "Settings for the once-a-day check against PyPi for a newer release of webchanges."
32
37
  }
33
38
  },
34
39
  "additionalProperties": false,
@@ -37,7 +42,7 @@
37
42
  "title": "Display",
38
43
  "description": "Which job outcomes appear in reports. Changes are always reported.",
39
44
  "type": "object",
40
- "required": ["new", "error", "unchanged", "empty-diff"],
45
+ "required": ["new", "error", "unchanged"],
41
46
  "properties": {
42
47
  "new": {
43
48
  "type": "boolean",
@@ -55,9 +60,8 @@
55
60
  "default": false
56
61
  },
57
62
  "empty-diff": {
58
- "type": "boolean",
59
- "description": "Deprecated. Whether to report a 'changed' job whose diff was reduced to an empty string by a diff_filter. Use the additions_only job directive instead.",
60
- "default": false,
63
+ "enum": [false],
64
+ "description": "Deprecated; use additions_only in the job instead.",
61
65
  "deprecated": true
62
66
  }
63
67
  }
@@ -928,6 +932,19 @@
928
932
  "default": 4
929
933
  }
930
934
  }
935
+ },
936
+ "_ConfigVersionCheck": {
937
+ "title": "Version Check",
938
+ "description": "Settings for the once-a-day check against PyPi for a newer release of webchanges.",
939
+ "type": "object",
940
+ "required": ["enabled"],
941
+ "properties": {
942
+ "enabled": {
943
+ "type": "boolean",
944
+ "description": "Whether to query PyPi for a newer release. The result is cached on disk for 24 hours, so this hits the network at most once per day. Set to false to disable the check entirely (no network call, no upgrade nudge in reports).",
945
+ "default": true
946
+ }
947
+ }
931
948
  }
932
949
  }
933
950
  }
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/webchanges/_resources/jobs.schema.json",
3
4
  "title": "Webchanges Jobs Configuration",
4
- "description": "JSON Schema for each job in Webchanges' jobs.yaml configuration file",
5
+ "description": "JSON Schema for each job in webchanges' jobs.yaml configuration file",
5
6
  "type": "object",
6
7
  "oneOf": [{ "required": ["url"] }, { "required": ["command"] }],
7
8
  "properties": {
@@ -58,10 +59,23 @@
58
59
  "type": ["integer", "number"],
59
60
  "description": "Request timeout in seconds."
60
61
  },
62
+ "same_site_delay": {
63
+ "type": ["integer", "number"],
64
+ "description": "Number of seconds to wait before retrieving the URL when an earlier job in the list already targets the same site (network location). The first (or only) job for a given site is never delayed. Use it to stagger jobs that would otherwise hit the same site at the same instant and get rate-limited or blocked."
65
+ },
66
+ "delay": {
67
+ "type": ["integer", "number"],
68
+ "description": "Number of seconds to wait before retrieving the URL (e.g. to stagger jobs that hit the same site, or to space out requests to a slow or rate-limited server)."
69
+ },
61
70
  "ignore_connection_errors": {
62
71
  "type": "boolean",
63
72
  "description": "If true, connection errors are ignored (no report sent)."
64
73
  },
74
+ "empty_as_transient": {
75
+ "type": "boolean",
76
+ "description": "If true, an empty response is treated as a transient HTTP error (synthetic status code 999) instead of valid content, preserving the last non-empty snapshot. Combine with max_tries or 'ignore_http_error_codes: 999' to control error reporting. Not for jobs with use_browser.",
77
+ "default": false
78
+ },
65
79
  "no_redirects": {
66
80
  "type": "boolean",
67
81
  "description": "If true, do not follow HTTP redirects."
@@ -170,6 +184,11 @@
170
184
  },
171
185
  "description": "Resource types to block from loading (browser jobs)."
172
186
  },
187
+ "connect_over_cdp": {
188
+ "oneOf": [{ "type": "boolean" }, { "type": "string", "format": "uri" }],
189
+ "description": "Attach Playwright to an already-running Chromium browser via the Chrome DevTools Protocol (browser jobs). 'true' uses the default endpoint ws://127.0.0.1:58489/devtools/browser; a string is used as the CDP WebSocket URL.",
190
+ "default": false
191
+ },
173
192
  "evaluate": {
174
193
  "type": "string",
175
194
  "description": "JavaScript code to execute in the page context (browser jobs)."
@@ -180,7 +199,7 @@
180
199
  },
181
200
  "ignore_default_args": {
182
201
  "oneOf": [{ "type": "boolean" }, { "type": "array", "items": { "type": "string" } }],
183
- "description": "Override Chrome default arguments (browser jobs).",
202
+ "description": "Override browser default arguments (browser jobs).",
184
203
  "default": false
185
204
  },
186
205
  "ignore_https_errors": {
@@ -204,14 +223,18 @@
204
223
  "type": "string",
205
224
  "description": "Referer header value (browser jobs)."
206
225
  },
226
+ "stealth": {
227
+ "type": "boolean",
228
+ "description": "Use stealth mode (browser jobs)."
229
+ },
207
230
  "switches": {
208
231
  "type": "array",
209
232
  "items": { "type": "string" },
210
- "description": "Additional Chrome command-line switches (browser jobs)."
233
+ "description": "Additional browser command-line switches (browser jobs)."
211
234
  },
212
235
  "user_data_dir": {
213
236
  "type": "string",
214
- "description": "Path to a Chrome user data directory (for persistence/auth with use_browser)."
237
+ "description": "Path to a browser user data directory (for persistence/auth with use_browser)."
215
238
  },
216
239
  "wait_for_function": {
217
240
  "oneOf": [
@@ -444,6 +467,27 @@
444
467
  "type": "boolean",
445
468
  "description": "Use compact output format.",
446
469
  "default": false
470
+ },
471
+ "cutoff_distance_for_pairs": {
472
+ "type": "number",
473
+ "minimum": 0,
474
+ "maximum": 1,
475
+ "description": "With ignore_order, two changed items are paired and compared in depth only if their deep distance is below this value; otherwise they are reported as one item removed plus one added (deepdiff library's own default is 0.3).",
476
+ "default": 1.0
477
+ },
478
+ "cutoff_intersection_for_pairs": {
479
+ "type": "number",
480
+ "minimum": 0,
481
+ "maximum": 1,
482
+ "description": "With ignore_order, pairing of changed items is attempted only if the intersection distance of the two iterables is below this value; otherwise each changed item is reported in its entirety (deepdiff library's own default is 0.7).",
483
+ "default": 1.0
484
+ },
485
+ "threshold_to_diff_deeper": {
486
+ "type": "number",
487
+ "minimum": 0,
488
+ "maximum": 1,
489
+ "description": "The fraction of keys two dictionaries must share for them to be compared key by key instead of being reported as one whole new value (deepdiff library's own default is 0.33).",
490
+ "default": 0
447
491
  }
448
492
  },
449
493
  "required": ["name"],
@@ -644,21 +688,9 @@
644
688
  },
645
689
  "definitions": {
646
690
  "filterList": {
647
- "oneOf": [
648
- {
649
- "type": "string",
650
- "description": "A single filter name (e.g. 'html2text', 'sha1sum')."
651
- },
652
- {
653
- "type": "array",
654
- "items": { "$ref": "#/definitions/filterItem" },
655
- "description": "List of filters to apply in order."
656
- },
657
- {
658
- "$ref": "#/definitions/filterItemObject",
659
- "description": "A single filter as an object."
660
- }
661
- ]
691
+ "type": "array",
692
+ "items": { "$ref": "#/definitions/filterItem" },
693
+ "description": "List of filters to apply in order."
662
694
  },
663
695
  "filterItem": {
664
696
  "oneOf": [
@@ -735,6 +767,21 @@
735
767
  ],
736
768
  "description": "Beautifies HTML using BeautifulSoup."
737
769
  },
770
+ "between": {
771
+ "type": "object",
772
+ "properties": {
773
+ "start": {
774
+ "type": "string",
775
+ "description": "Regular expression matching the line that opens the block (this line is not kept)."
776
+ },
777
+ "end": {
778
+ "type": "string",
779
+ "description": "Regular expression matching the line that closes the block (this line is not kept)."
780
+ }
781
+ },
782
+ "additionalProperties": false,
783
+ "description": "Extracts the block of lines between a starting and an ending regular expression."
784
+ },
738
785
  "css": {
739
786
  "oneOf": [
740
787
  { "type": "string", "description": "CSS selector expression." },
@@ -182,7 +182,7 @@ class Headers(typing.MutableMapping[str, str]):
182
182
  """
183
183
  return [(key.decode(self.encoding), value.decode(self.encoding)) for _, key, value in self._list]
184
184
 
185
- def get(self, key: str, default: typing.Any = None) -> typing.Any:
185
+ def get(self, key: str, default: typing.Any = None) -> typing.Any: # ty:ignore[invalid-method-override]
186
186
  """
187
187
  Return a header value. If multiple occurrences of the header occur
188
188
  then concatenate them together with commas.