instapaper-scraper 1.1.0__tar.gz → 1.1.1__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 (24) hide show
  1. {instapaper_scraper-1.1.0/src/instapaper_scraper.egg-info → instapaper_scraper-1.1.1}/PKG-INFO +22 -14
  2. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/README.md +20 -13
  3. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/pyproject.toml +7 -3
  4. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper/output.py +16 -4
  5. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1/src/instapaper_scraper.egg-info}/PKG-INFO +22 -14
  6. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper.egg-info/requires.txt +1 -0
  7. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/tests/test_output.py +34 -13
  8. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/LICENSE +0 -0
  9. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/setup.cfg +0 -0
  10. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper/__init__.py +0 -0
  11. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper/api.py +0 -0
  12. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper/auth.py +0 -0
  13. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper/cli.py +0 -0
  14. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper/constants.py +0 -0
  15. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper/exceptions.py +0 -0
  16. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper.egg-info/SOURCES.txt +0 -0
  17. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper.egg-info/dependency_links.txt +0 -0
  18. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper.egg-info/entry_points.txt +0 -0
  19. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/src/instapaper_scraper.egg-info/top_level.txt +0 -0
  20. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/tests/test_api.py +0 -0
  21. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/tests/test_auth.py +0 -0
  22. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/tests/test_cli.py +0 -0
  23. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/tests/test_cli_priority.py +0 -0
  24. {instapaper_scraper-1.1.0 → instapaper_scraper-1.1.1}/tests/test_init.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instapaper-scraper
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: A tool to scrape articles from Instapaper.
5
5
  Project-URL: Homepage, https://github.com/chriskyfung/InstapaperScraper
6
6
  Project-URL: Source, https://github.com/chriskyfung/InstapaperScraper
@@ -43,12 +43,19 @@ Requires-Dist: build; extra == "dev"
43
43
  Requires-Dist: twine; extra == "dev"
44
44
  Requires-Dist: mypy; extra == "dev"
45
45
  Requires-Dist: pre-commit; extra == "dev"
46
+ Requires-Dist: licensecheck; extra == "dev"
46
47
  Dynamic: license-file
47
48
 
48
49
  # Instapaper Scraper
49
50
 
50
51
  <!-- Badges -->
51
52
  <p align="center">
53
+ <a href="https://pypi.org/project/instapaper-scraper/">
54
+ <img src="https://img.shields.io/pypi/v/instapaper-scraper.svg" alt="PyPI version">
55
+ </a>
56
+ <a href="https://pepy.tech/projects/instapaper-scraper">
57
+ <img src="https://static.pepy.tech/personalized-badge/instapaper-scraper?period=total&left_text=downloads" alt="PyPI Downloads">
58
+ </a>
52
59
  <a href="https://github.com/chriskyfung/InstapaperScraper">
53
60
  <img src="https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fchriskyfung%2FInstapaperScraper%2Frefs%2Fheads%2Fmaster%2Fpyproject.toml" alt="Python Version from PEP 621 TOML">
54
61
  </a>
@@ -58,23 +65,18 @@ Dynamic: license-file
58
65
  <a href="https://codecov.io/gh/chriskyfung/InstapaperScraper">
59
66
  <img src="https://codecov.io/gh/chriskyfung/InstapaperScraper/graph/badge.svg" alt="Code Coverage">
60
67
  </a>
68
+ <wbr />
61
69
  <a href="https://github.com/chriskyfung/InstapaperScraper/actions/workflows/ci.yml">
62
70
  <img src="https://github.com/chriskyfung/InstapaperScraper/actions/workflows/ci.yml/badge.svg" alt="CI Status">
63
71
  </a>
64
- <a href="https://pypi.org/project/instapaper-scraper/">
65
- <img src="https://img.shields.io/pypi/v/instapaper-scraper.svg" alt="PyPI version">
66
- </a>
67
- <a href="https://pepy.tech/projects/instapaper-scraper">
68
- <img src="https://static.pepy.tech/personalized-badge/instapaper-scraper?period=total&left_text=downloads" alt="PyPI Downloads">
69
- </a>
70
72
  <a href="https://www.gnu.org/licenses/gpl-3.0.en.html">
71
73
  <img src="https://img.shields.io/github/license/chriskyfung/InstapaperScraper" alt="GitHub License">
72
74
  </a>
73
75
  <a href="https://github.com/sponsors/chriskyfung" title="Sponsor on GitHub">
74
- <img src="https://img.shields.io/badge/Sponsor-GitHub-purple?logo=github" alt="GitHub Sponsors Default">
76
+ <img src="https://img.shields.io/badge/Sponsor-GitHub-blue?logo=github-sponsors&colorA=263238&colorB=EC407A" alt="GitHub Sponsors Default">
75
77
  </a>
76
- <a href="https://www.buymeacoffee.com/chriskyfung" title="Buy Me A Coffee">
77
- <img src="https://img.shields.io/badge/Support%20Me-Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=white" alt="Buy Me A Coffee">
78
+ <a href="https://www.buymeacoffee.com/chriskyfung" title="Support Coffee">
79
+ <img src="https://img.shields.io/badge/Support-Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=ffdd00&colorA=263238" alt="Buy Me A Coffee">
78
80
  </a>
79
81
  </p>
80
82
 
@@ -336,6 +338,13 @@ To run static type checking with `mypy`:
336
338
  mypy src
337
339
  ```
338
340
 
341
+ To run license checks:
342
+
343
+ ```sh
344
+ licensecheck --show-only-failing
345
+ ```
346
+
347
+
339
348
  ## 📜 Disclaimer
340
349
 
341
350
  This script requires valid Instapaper credentials. Use it responsibly and in accordance with Instapaper’s Terms of Service.
@@ -344,9 +353,8 @@ This script requires valid Instapaper credentials. Use it responsibly and in acc
344
353
 
345
354
  This project is licensed under the terms of the **GNU General Public License v3.0**. See the [LICENSE](LICENSE) file for the full license text.
346
355
 
347
- ## 🙏 Support the Project
356
+ ## Contributors
348
357
 
349
- `Instapaper Scraper` is a free and open-source project that requires significant time and effort to maintain and improve. If you find this tool useful, please consider supporting its development. Your contribution helps ensure the project stays healthy, active, and continuously updated.
358
+ [![Contributors](https://contrib.rocks/image?repo=chriskyfung/InstapaperScraper)](https://github.com/chriskyfung/InstapaperScraper/graphs/contributors)
350
359
 
351
- - **[Sponsor on GitHub](https://github.com/sponsors/chriskyfung):** The best way to support the project with recurring monthly donations. Tiers with special rewards like priority support are available!
352
- - **[Buy Me a Coffee](https://www.buymeacoffee.com/chriskyfung):** Perfect for a one-time thank you.
360
+ Made with [contrib.rocks](https://contrib.rocks).
@@ -2,6 +2,12 @@
2
2
 
3
3
  <!-- Badges -->
4
4
  <p align="center">
5
+ <a href="https://pypi.org/project/instapaper-scraper/">
6
+ <img src="https://img.shields.io/pypi/v/instapaper-scraper.svg" alt="PyPI version">
7
+ </a>
8
+ <a href="https://pepy.tech/projects/instapaper-scraper">
9
+ <img src="https://static.pepy.tech/personalized-badge/instapaper-scraper?period=total&left_text=downloads" alt="PyPI Downloads">
10
+ </a>
5
11
  <a href="https://github.com/chriskyfung/InstapaperScraper">
6
12
  <img src="https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fchriskyfung%2FInstapaperScraper%2Frefs%2Fheads%2Fmaster%2Fpyproject.toml" alt="Python Version from PEP 621 TOML">
7
13
  </a>
@@ -11,23 +17,18 @@
11
17
  <a href="https://codecov.io/gh/chriskyfung/InstapaperScraper">
12
18
  <img src="https://codecov.io/gh/chriskyfung/InstapaperScraper/graph/badge.svg" alt="Code Coverage">
13
19
  </a>
20
+ <wbr />
14
21
  <a href="https://github.com/chriskyfung/InstapaperScraper/actions/workflows/ci.yml">
15
22
  <img src="https://github.com/chriskyfung/InstapaperScraper/actions/workflows/ci.yml/badge.svg" alt="CI Status">
16
23
  </a>
17
- <a href="https://pypi.org/project/instapaper-scraper/">
18
- <img src="https://img.shields.io/pypi/v/instapaper-scraper.svg" alt="PyPI version">
19
- </a>
20
- <a href="https://pepy.tech/projects/instapaper-scraper">
21
- <img src="https://static.pepy.tech/personalized-badge/instapaper-scraper?period=total&left_text=downloads" alt="PyPI Downloads">
22
- </a>
23
24
  <a href="https://www.gnu.org/licenses/gpl-3.0.en.html">
24
25
  <img src="https://img.shields.io/github/license/chriskyfung/InstapaperScraper" alt="GitHub License">
25
26
  </a>
26
27
  <a href="https://github.com/sponsors/chriskyfung" title="Sponsor on GitHub">
27
- <img src="https://img.shields.io/badge/Sponsor-GitHub-purple?logo=github" alt="GitHub Sponsors Default">
28
+ <img src="https://img.shields.io/badge/Sponsor-GitHub-blue?logo=github-sponsors&colorA=263238&colorB=EC407A" alt="GitHub Sponsors Default">
28
29
  </a>
29
- <a href="https://www.buymeacoffee.com/chriskyfung" title="Buy Me A Coffee">
30
- <img src="https://img.shields.io/badge/Support%20Me-Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=white" alt="Buy Me A Coffee">
30
+ <a href="https://www.buymeacoffee.com/chriskyfung" title="Support Coffee">
31
+ <img src="https://img.shields.io/badge/Support-Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=ffdd00&colorA=263238" alt="Buy Me A Coffee">
31
32
  </a>
32
33
  </p>
33
34
 
@@ -289,6 +290,13 @@ To run static type checking with `mypy`:
289
290
  mypy src
290
291
  ```
291
292
 
293
+ To run license checks:
294
+
295
+ ```sh
296
+ licensecheck --show-only-failing
297
+ ```
298
+
299
+
292
300
  ## 📜 Disclaimer
293
301
 
294
302
  This script requires valid Instapaper credentials. Use it responsibly and in accordance with Instapaper’s Terms of Service.
@@ -297,9 +305,8 @@ This script requires valid Instapaper credentials. Use it responsibly and in acc
297
305
 
298
306
  This project is licensed under the terms of the **GNU General Public License v3.0**. See the [LICENSE](LICENSE) file for the full license text.
299
307
 
300
- ## 🙏 Support the Project
308
+ ## Contributors
301
309
 
302
- `Instapaper Scraper` is a free and open-source project that requires significant time and effort to maintain and improve. If you find this tool useful, please consider supporting its development. Your contribution helps ensure the project stays healthy, active, and continuously updated.
310
+ [![Contributors](https://contrib.rocks/image?repo=chriskyfung/InstapaperScraper)](https://github.com/chriskyfung/InstapaperScraper/graphs/contributors)
303
311
 
304
- - **[Sponsor on GitHub](https://github.com/sponsors/chriskyfung):** The best way to support the project with recurring monthly donations. Tiers with special rewards like priority support are available!
305
- - **[Buy Me a Coffee](https://www.buymeacoffee.com/chriskyfung):** Perfect for a one-time thank you.
312
+ Made with [contrib.rocks](https://contrib.rocks).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "instapaper-scraper"
7
- version = "1.1.0"
7
+ version = "1.1.1"
8
8
  description = "A tool to scrape articles from Instapaper."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -35,7 +35,7 @@ dependencies = [
35
35
  "soupsieve~=2.8",
36
36
  "typing_extensions~=4.15.0",
37
37
  "urllib3>=2.5,<2.7",
38
- "tomli~=2.0.1; python_version < '3.11'",
38
+ "tomli~=2.0.1; python_version < '3.11'"
39
39
  ]
40
40
 
41
41
  [project.urls]
@@ -46,6 +46,9 @@ Issues = "https://github.com/chriskyfung/InstapaperScraper/issues"
46
46
  [project.scripts]
47
47
  instapaper-scraper = "instapaper_scraper.cli:main"
48
48
 
49
+ [tool.licensecheck]
50
+ license = "GPL-3.0"
51
+
49
52
  [tool.pytest.ini_options]
50
53
  pythonpath = "src"
51
54
 
@@ -77,5 +80,6 @@ dev = [
77
80
  "build",
78
81
  "twine",
79
82
  "mypy",
80
- "pre-commit"
83
+ "pre-commit",
84
+ "licensecheck"
81
85
  ]
@@ -1,9 +1,6 @@
1
1
  import os
2
- import json
3
- import sqlite3
4
2
  import logging
5
- import csv
6
- from typing import List, Dict, Any
3
+ from typing import List, Dict, Any, TYPE_CHECKING
7
4
 
8
5
  from .constants import INSTAPAPER_READ_URL, KEY_ID, KEY_TITLE, KEY_URL
9
6
 
@@ -19,6 +16,13 @@ LOG_NO_ARTICLES = "No articles found to save."
19
16
  LOG_SAVED_ARTICLES = "Saved {count} articles to {filename}"
20
17
  LOG_UNKNOWN_FORMAT = "Unknown output format: {format}"
21
18
 
19
+ if TYPE_CHECKING:
20
+ # Import for type-checking purposes, and use an alias
21
+ # to signal to linters like ruff that it is being used.
22
+ import sqlite3 as sqlite3
23
+
24
+ __all__ = ["sqlite3"]
25
+
22
26
 
23
27
  def get_sqlite_create_table_sql(add_instapaper_url: bool = False) -> str:
24
28
  """Returns the SQL statement to create the articles table."""
@@ -28,6 +32,8 @@ def get_sqlite_create_table_sql(add_instapaper_url: bool = False) -> str:
28
32
  f"{KEY_URL} TEXT NOT NULL",
29
33
  ]
30
34
  if add_instapaper_url:
35
+ import sqlite3
36
+
31
37
  # The GENERATED ALWAYS AS syntax was added in SQLite 3.31.0
32
38
  if sqlite3.sqlite_version_info >= (3, 31, 0):
33
39
  columns.append(
@@ -55,6 +61,8 @@ def save_to_csv(
55
61
  data: List[Dict[str, Any]], filename: str, add_instapaper_url: bool = False
56
62
  ) -> None:
57
63
  """Saves a list of articles to a CSV file."""
64
+ import csv
65
+
58
66
  os.makedirs(os.path.dirname(filename), exist_ok=True)
59
67
  with open(filename, "w", newline="", encoding="utf-8") as f:
60
68
  fieldnames = [KEY_ID, KEY_TITLE, KEY_URL]
@@ -71,6 +79,8 @@ def save_to_csv(
71
79
 
72
80
  def save_to_json(data: List[Dict[str, Any]], filename: str) -> None:
73
81
  """Saves a list of articles to a JSON file."""
82
+ import json
83
+
74
84
  os.makedirs(os.path.dirname(filename), exist_ok=True)
75
85
  with open(filename, "w", encoding="utf-8") as f:
76
86
  json.dump(data, f, indent=JSON_INDENT, ensure_ascii=False)
@@ -81,6 +91,8 @@ def save_to_sqlite(
81
91
  data: List[Dict[str, Any]], db_name: str, add_instapaper_url: bool = False
82
92
  ) -> None:
83
93
  """Saves a list of articles to a SQLite database."""
94
+ import sqlite3
95
+
84
96
  os.makedirs(os.path.dirname(db_name), exist_ok=True)
85
97
  conn = sqlite3.connect(db_name)
86
98
  cursor = conn.cursor()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instapaper-scraper
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: A tool to scrape articles from Instapaper.
5
5
  Project-URL: Homepage, https://github.com/chriskyfung/InstapaperScraper
6
6
  Project-URL: Source, https://github.com/chriskyfung/InstapaperScraper
@@ -43,12 +43,19 @@ Requires-Dist: build; extra == "dev"
43
43
  Requires-Dist: twine; extra == "dev"
44
44
  Requires-Dist: mypy; extra == "dev"
45
45
  Requires-Dist: pre-commit; extra == "dev"
46
+ Requires-Dist: licensecheck; extra == "dev"
46
47
  Dynamic: license-file
47
48
 
48
49
  # Instapaper Scraper
49
50
 
50
51
  <!-- Badges -->
51
52
  <p align="center">
53
+ <a href="https://pypi.org/project/instapaper-scraper/">
54
+ <img src="https://img.shields.io/pypi/v/instapaper-scraper.svg" alt="PyPI version">
55
+ </a>
56
+ <a href="https://pepy.tech/projects/instapaper-scraper">
57
+ <img src="https://static.pepy.tech/personalized-badge/instapaper-scraper?period=total&left_text=downloads" alt="PyPI Downloads">
58
+ </a>
52
59
  <a href="https://github.com/chriskyfung/InstapaperScraper">
53
60
  <img src="https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fchriskyfung%2FInstapaperScraper%2Frefs%2Fheads%2Fmaster%2Fpyproject.toml" alt="Python Version from PEP 621 TOML">
54
61
  </a>
@@ -58,23 +65,18 @@ Dynamic: license-file
58
65
  <a href="https://codecov.io/gh/chriskyfung/InstapaperScraper">
59
66
  <img src="https://codecov.io/gh/chriskyfung/InstapaperScraper/graph/badge.svg" alt="Code Coverage">
60
67
  </a>
68
+ <wbr />
61
69
  <a href="https://github.com/chriskyfung/InstapaperScraper/actions/workflows/ci.yml">
62
70
  <img src="https://github.com/chriskyfung/InstapaperScraper/actions/workflows/ci.yml/badge.svg" alt="CI Status">
63
71
  </a>
64
- <a href="https://pypi.org/project/instapaper-scraper/">
65
- <img src="https://img.shields.io/pypi/v/instapaper-scraper.svg" alt="PyPI version">
66
- </a>
67
- <a href="https://pepy.tech/projects/instapaper-scraper">
68
- <img src="https://static.pepy.tech/personalized-badge/instapaper-scraper?period=total&left_text=downloads" alt="PyPI Downloads">
69
- </a>
70
72
  <a href="https://www.gnu.org/licenses/gpl-3.0.en.html">
71
73
  <img src="https://img.shields.io/github/license/chriskyfung/InstapaperScraper" alt="GitHub License">
72
74
  </a>
73
75
  <a href="https://github.com/sponsors/chriskyfung" title="Sponsor on GitHub">
74
- <img src="https://img.shields.io/badge/Sponsor-GitHub-purple?logo=github" alt="GitHub Sponsors Default">
76
+ <img src="https://img.shields.io/badge/Sponsor-GitHub-blue?logo=github-sponsors&colorA=263238&colorB=EC407A" alt="GitHub Sponsors Default">
75
77
  </a>
76
- <a href="https://www.buymeacoffee.com/chriskyfung" title="Buy Me A Coffee">
77
- <img src="https://img.shields.io/badge/Support%20Me-Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=white" alt="Buy Me A Coffee">
78
+ <a href="https://www.buymeacoffee.com/chriskyfung" title="Support Coffee">
79
+ <img src="https://img.shields.io/badge/Support-Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=ffdd00&colorA=263238" alt="Buy Me A Coffee">
78
80
  </a>
79
81
  </p>
80
82
 
@@ -336,6 +338,13 @@ To run static type checking with `mypy`:
336
338
  mypy src
337
339
  ```
338
340
 
341
+ To run license checks:
342
+
343
+ ```sh
344
+ licensecheck --show-only-failing
345
+ ```
346
+
347
+
339
348
  ## 📜 Disclaimer
340
349
 
341
350
  This script requires valid Instapaper credentials. Use it responsibly and in accordance with Instapaper’s Terms of Service.
@@ -344,9 +353,8 @@ This script requires valid Instapaper credentials. Use it responsibly and in acc
344
353
 
345
354
  This project is licensed under the terms of the **GNU General Public License v3.0**. See the [LICENSE](LICENSE) file for the full license text.
346
355
 
347
- ## 🙏 Support the Project
356
+ ## Contributors
348
357
 
349
- `Instapaper Scraper` is a free and open-source project that requires significant time and effort to maintain and improve. If you find this tool useful, please consider supporting its development. Your contribution helps ensure the project stays healthy, active, and continuously updated.
358
+ [![Contributors](https://contrib.rocks/image?repo=chriskyfung/InstapaperScraper)](https://github.com/chriskyfung/InstapaperScraper/graphs/contributors)
350
359
 
351
- - **[Sponsor on GitHub](https://github.com/sponsors/chriskyfung):** The best way to support the project with recurring monthly donations. Tiers with special rewards like priority support are available!
352
- - **[Buy Me a Coffee](https://www.buymeacoffee.com/chriskyfung):** Perfect for a one-time thank you.
360
+ Made with [contrib.rocks](https://contrib.rocks).
@@ -4,7 +4,7 @@ import sqlite3
4
4
  import logging
5
5
  import io
6
6
  import csv
7
- from unittest.mock import patch, MagicMock
7
+ from unittest.mock import patch, MagicMock, PropertyMock
8
8
 
9
9
  from instapaper_scraper.output import (
10
10
  _correct_ext,
@@ -47,6 +47,25 @@ def output_dir(tmp_path):
47
47
  return tmp_path / "output"
48
48
 
49
49
 
50
+ @pytest.fixture
51
+ def mock_sqlite3():
52
+ """
53
+ Fixture to mock the sqlite3 module for dynamic imports.
54
+ This fixture patches sys.modules to replace the sqlite3 module with a mock.
55
+ It yields a function that allows tests to configure the mock's version info.
56
+ """
57
+ with patch.dict("sys.modules", {"sqlite3": MagicMock()}) as mock_modules:
58
+ mock_sqlite = mock_modules["sqlite3"]
59
+
60
+ def _configure_mock(version_info):
61
+ # Use PropertyMock to ensure the version comparison works correctly
62
+ type(mock_sqlite).sqlite_version_info = PropertyMock(
63
+ return_value=version_info
64
+ )
65
+
66
+ yield _configure_mock, mock_sqlite
67
+
68
+
50
69
  class TestCorrectExt:
51
70
  @pytest.mark.parametrize(
52
71
  "filename, format, expected",
@@ -73,16 +92,18 @@ def test_get_sqlite_create_table_sql_without_url():
73
92
  assert "instapaper_url" not in sql
74
93
 
75
94
 
76
- @patch("sqlite3.sqlite_version_info", (3, 31, 0))
77
- def test_get_sqlite_create_table_sql_with_url_modern_sqlite():
95
+ def test_get_sqlite_create_table_sql_with_url_modern_sqlite(mock_sqlite3):
78
96
  """Test CREATE TABLE SQL with instapaper_url on modern SQLite."""
97
+ configure_mock, _ = mock_sqlite3
98
+ configure_mock((3, 31, 0))
79
99
  sql = get_sqlite_create_table_sql(add_instapaper_url=True)
80
100
  assert "instapaper_url TEXT GENERATED ALWAYS AS" in sql
81
101
 
82
102
 
83
- @patch("sqlite3.sqlite_version_info", (3, 30, 0))
84
- def test_get_sqlite_create_table_sql_with_url_old_sqlite():
103
+ def test_get_sqlite_create_table_sql_with_url_old_sqlite(mock_sqlite3):
85
104
  """Test CREATE TABLE SQL with instapaper_url on old SQLite."""
105
+ configure_mock, _ = mock_sqlite3
106
+ configure_mock((3, 30, 0))
86
107
  sql = get_sqlite_create_table_sql(add_instapaper_url=True)
87
108
  assert "instapaper_url TEXT" in sql
88
109
  assert "GENERATED" not in sql
@@ -188,15 +209,15 @@ def test_save_to_sqlite(sample_articles, output_dir):
188
209
  conn.close()
189
210
 
190
211
 
191
- @patch("sqlite3.connect")
192
- @patch("sqlite3.sqlite_version_info", (3, 31, 0))
193
212
  def test_save_to_sqlite_with_instapaper_url_modern_sqlite(
194
- mock_connect, sample_articles, output_dir
213
+ mock_sqlite3, sample_articles, output_dir
195
214
  ):
196
215
  """Test saving with instapaper_url on modern SQLite."""
216
+ configure_mock, mock_sqlite = mock_sqlite3
217
+ configure_mock((3, 31, 0))
197
218
  mock_conn = MagicMock()
198
219
  mock_cursor = MagicMock()
199
- mock_connect.return_value = mock_conn
220
+ mock_sqlite.connect.return_value = mock_conn
200
221
  mock_conn.cursor.return_value = mock_cursor
201
222
 
202
223
  articles = sample_articles()
@@ -217,15 +238,15 @@ def test_save_to_sqlite_with_instapaper_url_modern_sqlite(
217
238
  assert "instapaper_url" not in executed_data[0]
218
239
 
219
240
 
220
- @patch("sqlite3.connect")
221
- @patch("sqlite3.sqlite_version_info", (3, 30, 0))
222
241
  def test_save_to_sqlite_with_instapaper_url_old_sqlite(
223
- mock_connect, sample_articles, output_dir
242
+ mock_sqlite3, sample_articles, output_dir
224
243
  ):
225
244
  """Test saving with instapaper_url on old SQLite."""
245
+ configure_mock, mock_sqlite = mock_sqlite3
246
+ configure_mock((3, 30, 0))
226
247
  mock_conn = MagicMock()
227
248
  mock_cursor = MagicMock()
228
- mock_connect.return_value = mock_conn
249
+ mock_sqlite.connect.return_value = mock_conn
229
250
  mock_conn.cursor.return_value = mock_cursor
230
251
 
231
252
  articles = sample_articles()