webchanges 3.31.3__tar.gz → 3.31.4__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.31.4}/PKG-INFO +9 -5
  2. {webchanges-3.31.3 → webchanges-3.31.4}/README.rst +5 -2
  3. {webchanges-3.31.3 → webchanges-3.31.4}/pyproject.toml +86 -141
  4. {webchanges-3.31.3 → webchanges-3.31.4}/requirements.txt +1 -1
  5. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/__init__.py +3 -3
  6. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/cli.py +16 -17
  7. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/command.py +52 -72
  8. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/config.py +1 -1
  9. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/differs.py +210 -187
  10. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/filters.py +64 -101
  11. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/handler.py +15 -21
  12. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/jobs.py +107 -132
  13. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/mailer.py +7 -4
  14. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/main.py +11 -9
  15. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/reporters.py +99 -122
  16. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/storage.py +70 -106
  17. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/storage_minidb.py +1 -1
  18. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/util.py +10 -18
  19. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/worker.py +6 -10
  20. {webchanges-3.31.3 → webchanges-3.31.4/webchanges.egg-info}/PKG-INFO +9 -5
  21. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges.egg-info/SOURCES.txt +1 -1
  22. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges.egg-info/requires.txt +2 -0
  23. /webchanges-3.31.3/LICENSE → /webchanges-3.31.4/LICENSE.md +0 -0
  24. {webchanges-3.31.3 → webchanges-3.31.4}/MANIFEST.in +0 -0
  25. {webchanges-3.31.3 → webchanges-3.31.4}/setup.cfg +0 -0
  26. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/__main__.py +0 -0
  27. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/_vendored/__init__.py +0 -0
  28. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/_vendored/headers.py +0 -0
  29. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/_vendored/packaging_version.py +0 -0
  30. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges/py.typed +0 -0
  31. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges.egg-info/dependency_links.txt +0 -0
  32. {webchanges-3.31.3 → webchanges-3.31.4}/webchanges.egg-info/entry_points.txt +0 -0
  33. {webchanges-3.31.3 → webchanges-3.31.4}/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.31.4
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>
@@ -92,6 +92,7 @@ Classifier: Programming Language :: Python :: 3.10
92
92
  Classifier: Programming Language :: Python :: 3.11
93
93
  Classifier: Programming Language :: Python :: 3.12
94
94
  Classifier: Programming Language :: Python :: 3.13
95
+ Classifier: Programming Language :: Python :: 3.14
95
96
  Classifier: Programming Language :: Python :: Implementation :: CPython
96
97
  Classifier: Topic :: Internet
97
98
  Classifier: Topic :: Internet :: WWW/HTTP
@@ -101,7 +102,7 @@ Classifier: Topic :: Utilities
101
102
  Classifier: Typing :: Typed
102
103
  Requires-Python: >=3.10
103
104
  Description-Content-Type: text/x-rst
104
- License-File: LICENSE
105
+ License-File: LICENSE.md
105
106
  Requires-Dist: colorama; sys_platform == "win32"
106
107
  Requires-Dist: cssselect
107
108
  Requires-Dist: h2
@@ -113,7 +114,7 @@ Requires-Dist: msgpack
113
114
  Requires-Dist: platformdirs
114
115
  Requires-Dist: pyyaml
115
116
  Requires-Dist: tzdata; sys_platform == "win32"
116
- Requires-Dist: zstandard
117
+ Requires-Dist: zstandard; python_version < "3.14"
117
118
  Provides-Extra: use-browser
118
119
  Requires-Dist: playwright; extra == "use-browser"
119
120
  Requires-Dist: psutil; extra == "use-browser"
@@ -266,7 +267,7 @@ Schedule
266
267
 
267
268
  Code
268
269
  ====
269
- |coveralls| |snyk| |issues| |code_style|
270
+ |coveralls| |snyk| |issues| |code_style| |OpenSSF_Scorecard|
270
271
 
271
272
  The code, issues tracker, and discussions are hosted on `GitHub <https://github.com/mborsetti/webchanges>`__.
272
273
 
@@ -285,7 +286,7 @@ License
285
286
  =======
286
287
  |license|
287
288
 
288
- See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE>`__ (released
289
+ See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE.md>`__ (released
289
290
  under the `MIT License <https://opensource.org/licenses/MIT>`__ but redistributing modified source code, dated 30
290
291
  July 2020, from `urlwatch 2.21 <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__
291
292
  licensed under a `BSD 3-Clause License
@@ -346,3 +347,6 @@ configuration files), see `here <https://webchanges.readthedocs.io/en/stable/upg
346
347
  .. |snyk| image:: https://snyk.io/advisor/python/holidays/badge.svg
347
348
  :target: https://snyk.io/advisor/python/holidays
348
349
  :alt: Snyk Package Health Score
350
+ .. |OpenSSF_Scorecard| image:: https://api.scorecard.dev/projects/github.com/mborsetti/webchanges/badge
351
+ :target: https://scorecard.dev/viewer/?uri=github.com/mborsetti/webchanges
352
+ :alt: OpenSSF Scoreard
@@ -100,7 +100,7 @@ Schedule
100
100
 
101
101
  Code
102
102
  ====
103
- |coveralls| |snyk| |issues| |code_style|
103
+ |coveralls| |snyk| |issues| |code_style| |OpenSSF_Scorecard|
104
104
 
105
105
  The code, issues tracker, and discussions are hosted on `GitHub <https://github.com/mborsetti/webchanges>`__.
106
106
 
@@ -119,7 +119,7 @@ License
119
119
  =======
120
120
  |license|
121
121
 
122
- See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE>`__ (released
122
+ See the `complete licenses <https://raw.githubusercontent.com/mborsetti/webchanges/refs/heads/main/LICENSE.md>`__ (released
123
123
  under the `MIT License <https://opensource.org/licenses/MIT>`__ but redistributing modified source code, dated 30
124
124
  July 2020, from `urlwatch 2.21 <https://github.com/thp/urlwatch/tree/346b25914b0418342ffe2fb0529bed702fddc01f>`__
125
125
  licensed under a `BSD 3-Clause License
@@ -180,3 +180,6 @@ configuration files), see `here <https://webchanges.readthedocs.io/en/stable/upg
180
180
  .. |snyk| image:: https://snyk.io/advisor/python/holidays/badge.svg
181
181
  :target: https://snyk.io/advisor/python/holidays
182
182
  :alt: Snyk Package Health Score
183
+ .. |OpenSSF_Scorecard| image:: https://api.scorecard.dev/projects/github.com/mborsetti/webchanges/badge
184
+ :target: https://scorecard.dev/viewer/?uri=github.com/mborsetti/webchanges
185
+ :alt: OpenSSF Scoreard
@@ -6,7 +6,7 @@
6
6
 
7
7
  [build-system]
8
8
  # Minimum requirements for the build system to execute.
9
- requires = ['setuptools'] # PEP 508 specifications.
9
+ requires = ['setuptools'] # PEP 508 specifications.
10
10
  # Setuptools specification
11
11
  build-backend = "setuptools.build_meta"
12
12
 
@@ -17,12 +17,10 @@ name = 'webchanges'
17
17
  description = 'Web Changes Delivered. AI-Summarized. Totally Anonymous.'
18
18
  readme = { file = 'README.rst', content-type = 'text/x-rst' }
19
19
  requires-python = '>=3.10'
20
- license = {file = 'LICENSE'}
21
- authors = [
22
- {name = 'Mike Borsetti', email = 'mike+webchanges@borsetti.com'},
23
- ]
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 = [
@@ -42,6 +40,7 @@ classifiers = [
42
40
  'Programming Language :: Python :: 3.11',
43
41
  'Programming Language :: Python :: 3.12',
44
42
  'Programming Language :: Python :: 3.13',
43
+ 'Programming Language :: Python :: 3.14',
45
44
  'Programming Language :: Python :: Implementation :: CPython',
46
45
  'Topic :: Internet',
47
46
  'Topic :: Internet :: WWW/HTTP',
@@ -90,56 +89,27 @@ redis = ['redis']
90
89
  requests = ['requests']
91
90
  safe_password = ['keyring']
92
91
  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]'
92
+ 'webchanges[use_browser,beautify,bs4,html5lib,ical2text,jq,ocr,pdf2text,pypdf_crypto,deepdiff_xml,imagediff,matrix,pushbullet,pushover,xmpp,redis,requests,safe_password]',
94
93
  ]
95
94
 
96
95
 
97
96
  # -------------------------- setuptools --------------------------
97
+ [tool.setuptools]
98
98
  # Called by build
99
99
  # 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
100
+
101
+ # Explicit package listing is required for building in tox -e new-install
102
102
  # https://setuptools.pypa.io/en/latest/userguide/package_discovery.html
103
103
  packages = ['webchanges', 'webchanges._vendored']
104
104
 
105
105
  [tool.setuptools.dynamic]
106
- version = {attr = 'webchanges.__version__'}
107
- dependencies = {file = 'requirements.txt'}
106
+ version = { attr = 'webchanges.__version__' }
107
+ dependencies = { file = 'requirements.txt' }
108
108
 
109
109
  [tool.setuptools.package-data]
110
110
  'webchanges' = ['py.typed']
111
111
 
112
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
-
142
-
143
113
  # -------------------------- coverage --------------------------
144
114
  [tool.coverage.run]
145
115
  # Runs in Github Actions (see .github/workflows/ci-cd.yaml) and in tox (see tox.ini).
@@ -193,50 +163,13 @@ exclude_lines = [
193
163
  'except ImportError:',
194
164
 
195
165
  # Don't cover IDE code:
196
- 'if TYPE_CHECKING:'
166
+ 'if TYPE_CHECKING:',
197
167
  ]
198
168
  # Exclude entire files if they have a custom # pragma: exclude file remark
199
169
  # exclude_also = '(?s)\A.*# pragma: exclude file.*\Z'
200
170
  # ignore_errors = true
201
171
 
202
172
 
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
173
  # -------------------------- mypy --------------------------
241
174
  [tool.mypy]
242
175
  # Static Typing for Python
@@ -244,7 +177,7 @@ color_output = true
244
177
  # Config file documentation at https://mypy.readthedocs.io/en/stable/config_file.html
245
178
 
246
179
  # Disables import discovery of namespace packages (see PEP 420)
247
- namespace_packages = false
180
+ namespace_packages = true
248
181
 
249
182
  # Specifies the Python version used to parse and check the target program.
250
183
  # python_version = 3.12
@@ -253,7 +186,7 @@ namespace_packages = false
253
186
  ignore_missing_imports = true
254
187
 
255
188
  # Disallows calling functions without type annotations from functions with type annotations.
256
- disallow_untyped_calls = true
189
+ disallow_untyped_calls = false
257
190
 
258
191
  # Disallows defining functions without type annotations or with incomplete type annotations.
259
192
  disallow_untyped_defs = true
@@ -310,13 +243,9 @@ report_level = 'WARNING'
310
243
  log_auto_indent = true
311
244
  # Enable log display during test run (aka "live logging" https://docs.pytest.org/en/stable/logging.html#live-logs)
312
245
  log_cli = true
313
- minversion = '8.3.3'
246
+ minversion = '8.4.1'
314
247
  testpaths = ['tests']
315
248
 
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'
319
-
320
249
  # Adds pytest-cov functionality (see https://pytest-cov.readthedocs.io/en/latest/config.html)
321
250
  # Note: --cov moved to .github/workflows/ci-cd.yaml and tox.ini due to interference with PyCharm breakpoints (see
322
251
  # https://github.com/pytest-dev/pytest-cov/issues/131) and to enable running tox --parallel
@@ -326,37 +255,10 @@ testpaths = ['tests']
326
255
 
327
256
  # -------------------------- ruff --------------------------
328
257
  [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",
258
+ # Config file documentation at https://docs.astral.sh/ruff/configuration/ and https://docs.astral.sh/ruff/settings/
359
259
 
260
+ # File patterns to omit from formatting and linting, in addition to those specified by exclude.
261
+ extend-exclude = [
360
262
  "webchanges/storage_minidb.py",
361
263
  "webchanges/_vendored",
362
264
  ]
@@ -367,7 +269,6 @@ exclude = [
367
269
 
368
270
  # Set the maximum line length.
369
271
  line-length = 120
370
- indent-width = 4
371
272
 
372
273
  # Target Python version
373
274
  # target-version = "py311" # Commented out to infer from [project] requires-python
@@ -376,27 +277,55 @@ indent-width = 4
376
277
  # By default, Ruff enables Flake8's F rules, along with a subset of the E rules
377
278
  # Enable rules not enabled by default, and ignore specific rules.
378
279
  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",
280
+ "ANN", # flake8-annotations
281
+ "S", # flake8-bandit
282
+ "BLE", # flake8-blind-except
283
+ "B", # flake8-bugbear
284
+ "A", # flake8-builtin
285
+ "C4", # flake8-comprehensions
286
+ "DTZ", # flake8-datetimez
287
+ # "EM", # flake8-errmsg # TODO
288
+ "FA", # flake8-future-annotations
289
+ "INT", # flake8-gettext
290
+ "ISC", # flake8-implicit-str-concat
291
+ "LOG", # flake8-logging
292
+ # "G", # flake8-logging-format # TODO
293
+ "PIE", # flake8-pie
294
+ "PYI", # flake8-pyi
295
+ "PT", # flake8-pytest-style
296
+ "Q", # flake8-quotes
297
+ "RSE", # flake8-raise
298
+ "RET", # flake8-return
299
+ "SIM", # flake8-simplify
300
+ "TID", # flake8-tidy-imports
301
+ "TD", # flake8-todos
302
+ "TC", # flake8-type-checking
303
+ # "ARG", # flake8-unused-arguments # TODO
304
+ "PTH", # flake8-use-pathlib
305
+ # "FLY", # flynt # TODO
306
+ "I", # isort
307
+ "C90", # mccabe
308
+ "N", # pep8-naming
309
+ "PERF", # Perflint
310
+ "E", # pycodestyle errors
311
+ "W", # pycodestyle warnings
312
+ # "DOC", # pydoclint # TODO
313
+ # "D", # pydocstyle # TODO
314
+ "F", # Pyflakes
315
+ # "PL", # Pylint # TODO
316
+ "FURB", # refurb
317
+ "RUF", # Ruff-specific rules
318
+ # "TRY", # tryceratops # TODO
397
319
  ]
320
+
398
321
  ignore = [
322
+ "PT011", # pytest.raises({exception}) is too broad, set the match parameter or use a more specific exception
323
+ "PT030", # pytest.warns({warning}) is too broad, set the match parameter or use a more specific warning
399
324
  "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
325
+ "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass
326
+ "SIM115", # Use a context manager for opening files
327
+ "TD002", # Missing author in TODO
328
+ "TD003", # Missing issue link for this TODO
400
329
  ]
401
330
 
402
331
  # Allow fix for all enabled rules (when `--fix`) is provided.
@@ -406,6 +335,28 @@ unfixable = []
406
335
  # Allow unused variables when underscore-prefixed.
407
336
  dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
408
337
 
338
+ [tool.ruff.lint.flake8-annotations]
339
+ allow-star-arg-any = true # TODO
340
+
341
+ [tool.ruff.lint.flake8-quotes]
342
+ inline-quotes = "single"
343
+
344
+ [tool.ruff.lint.mccabe]
345
+ # Flag errors (`C901`) whenever the complexity level exceeds below.
346
+ max-complexity = 30
347
+
348
+ [tool.ruff.lint.per-file-ignores]
349
+ "tests/*" = [
350
+ "S101", # Use of `assert` detected
351
+ ]
352
+
353
+ [tool.ruff.lint.pydoclint]
354
+ # Skip docstrings which fit on a single line.
355
+ ignore-one-line-docstrings = true
356
+
357
+ [tool.ruff.lint.pydocstyle]
358
+ convention = "google"
359
+
409
360
  [tool.ruff.format]
410
361
  # Enable the formatter, which is a drop-in replacement for Black.
411
362
  # No extra configuration is needed for basic formatting.
@@ -438,9 +389,3 @@ docstring-code-format = true
438
389
  # This only has an effect when the `docstring-code-format` setting is
439
390
  # enabled.
440
391
  docstring-code-line-length = "dynamic"
441
-
442
- [tool.ruff.lint.per-file-ignores]
443
- "tests/*" = [
444
- # Use of `assert` detected
445
- "S101"
446
- ]
@@ -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.
@@ -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.31.4'
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__':
@@ -4,7 +4,7 @@
4
4
 
5
5
  # See config module for the command line arguments.
6
6
 
7
- # The code below is subject to the license contained in the LICENSE file, which is part of the source code.
7
+ # The code below is subject to the license contained in the LICENSE.md file, which is part of the source code.
8
8
 
9
9
  from __future__ import annotations
10
10
 
@@ -33,7 +33,8 @@ logger = logging.getLogger(__name__)
33
33
 
34
34
  def python_version_warning() -> None:
35
35
  """Check if we're running on the minimum Python version supported and if so print and issue a pending deprecation
36
- warning."""
36
+ warning.
37
+ """
37
38
  if sys.version_info[0:2] == __min_python_version__:
38
39
  current_minor_version = '.'.join(str(n) for n in sys.version_info[0:2])
39
40
  next_minor_version = f'{__min_python_version__[0]}.{__min_python_version__[1] + 1}'
@@ -126,10 +127,9 @@ def teardown_logger(verbose: int | None = None) -> None:
126
127
 
127
128
  :param verbose: the verbosity level (1 = INFO, 2 = ERROR).
128
129
  """
129
- if verbose is not None:
130
- if verbose >= 2:
131
- # https://playwright.dev/python/docs/debug#verbose-api-logs
132
- os.environ.pop('DEBUG', None)
130
+ if verbose is not None and verbose >= 2:
131
+ # https://playwright.dev/python/docs/debug#verbose-api-logs
132
+ os.environ.pop('DEBUG', None)
133
133
 
134
134
 
135
135
  def _expand_glob_files(
@@ -167,7 +167,7 @@ def _expand_glob_files(
167
167
  return file_list
168
168
 
169
169
  # no directory specified (and not in current one): add default one
170
- if not file.is_absolute() and not Path(file).parent == Path.cwd():
170
+ if not file.is_absolute() and Path(file).parent != Path.cwd():
171
171
  file_list = list(default_path.glob(str(file)))
172
172
  if any(f.is_file() for f in file_list):
173
173
  return file_list
@@ -222,7 +222,7 @@ def locate_storage_file(
222
222
  return file
223
223
 
224
224
  # no directory specified (and not in current one): add default one
225
- if file.parent == PurePath('.'):
225
+ if file.parent == PurePath():
226
226
  new_file = default_path.joinpath(file)
227
227
  if new_file.is_file():
228
228
  return new_file
@@ -307,7 +307,8 @@ def load_hooks(hooks_file: Path, is_default: bool = False) -> None:
307
307
 
308
308
  def handle_unitialized_actions(urlwatch_config: CommandConfig) -> None:
309
309
  """Handles CLI actions that do not require all classes etc. to be initialized (and command.py loaded). For speed
310
- purposes."""
310
+ purposes.
311
+ """
311
312
 
312
313
  def _exit(arg: str | int | None) -> None:
313
314
  logger.info(f'Exiting with exit code {arg}')
@@ -323,16 +324,14 @@ def handle_unitialized_actions(urlwatch_config: CommandConfig) -> None:
323
324
  f"'pip install -U {__project_name__}'."
324
325
  )
325
326
  return 0
326
- elif new_release == '':
327
+ if new_release == '':
327
328
  print(' You are running the latest release.')
328
329
  return 0
329
- else:
330
- print(' Error contacting PyPI to determine the latest release.')
331
- return 1
330
+ print(' Error contacting PyPI to determine the latest release.')
331
+ return 1
332
332
 
333
333
  def playwright_install_chrome() -> int: # pragma: no cover
334
- """
335
- Replicates playwright.___main__.main() function, which is called by the playwright executable, in order to
334
+ """Replicates playwright.___main__.main() function, which is called by the playwright executable, in order to
336
335
  install the browser executable.
337
336
 
338
337
  :return: Playwright's executable return code.
@@ -347,7 +346,7 @@ def handle_unitialized_actions(urlwatch_config: CommandConfig) -> None:
347
346
  env['PW_CLI_TARGET_LANG'] = 'python'
348
347
  cmd = [str(driver_executable), 'install', 'chrome']
349
348
  logger.info(f'Running playwright CLI: {" ".join(cmd)}')
350
- completed_process = subprocess.run(cmd, env=env, capture_output=True, text=True) # noqa: S603
349
+ completed_process = subprocess.run(cmd, check=False, env=env, capture_output=True, text=True) # noqa: S603
351
350
  if completed_process.returncode:
352
351
  print(completed_process.stderr)
353
352
  return completed_process.returncode
@@ -478,7 +477,7 @@ def main() -> None: # pragma: no cover
478
477
  max_snapshots = command_config.max_snapshots or config_storage.config.get('database', {}).get('max_snapshots') or 4
479
478
  if database_engine == 'sqlite3':
480
479
  ssdb_storage: SsdbStorage = SsdbSQLite3Storage(command_config.ssdb_file, max_snapshots) # storage.py
481
- elif any(str(command_config.ssdb_file).startswith(prefix) for prefix in {'redis://', 'rediss://'}):
480
+ elif any(str(command_config.ssdb_file).startswith(prefix) for prefix in ('redis://', 'rediss://')):
482
481
  ssdb_storage = SsdbRedisStorage(command_config.ssdb_file) # storage.py
483
482
  elif database_engine.startswith('redis'):
484
483
  ssdb_storage = SsdbRedisStorage(database_engine)