reader 3.10__tar.gz → 3.12__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 (226) hide show
  1. {reader-3.10 → reader-3.12}/CHANGES.rst +31 -0
  2. {reader-3.10/src/reader.egg-info → reader-3.12}/PKG-INFO +3 -1
  3. {reader-3.10 → reader-3.12}/docs/api.rst +1 -0
  4. {reader-3.10 → reader-3.12}/docs/dev.rst +46 -3
  5. {reader-3.10 → reader-3.12}/docs/guide.rst +24 -12
  6. {reader-3.10 → reader-3.12}/docs/internal.rst +26 -3
  7. {reader-3.10 → reader-3.12}/docs/plugins.rst +4 -4
  8. {reader-3.10 → reader-3.12}/pyproject.toml +3 -0
  9. {reader-3.10 → reader-3.12}/run.sh +1 -1
  10. reader-3.12/scripts/bench.py +453 -0
  11. {reader-3.10 → reader-3.12}/src/reader/__init__.py +1 -1
  12. {reader-3.10 → reader-3.12}/src/reader/_app/cli.py +0 -3
  13. {reader-3.10 → reader-3.12}/src/reader/_cli.py +3 -30
  14. reader-3.12/src/reader/_storage/__init__.py +39 -0
  15. reader-3.12/src/reader/_storage/_base.py +110 -0
  16. reader-3.12/src/reader/_storage/_changes.py +213 -0
  17. reader-3.12/src/reader/_storage/_entries.py +654 -0
  18. reader-3.12/src/reader/_storage/_feeds.py +404 -0
  19. {reader-3.10/src/reader → reader-3.12/src/reader/_storage}/_html_utils.py +3 -1
  20. reader-3.12/src/reader/_storage/_schema.py +284 -0
  21. reader-3.12/src/reader/_storage/_search.py +643 -0
  22. {reader-3.10/src/reader → reader-3.12/src/reader/_storage}/_sql_utils.py +99 -31
  23. {reader-3.10/src/reader → reader-3.12/src/reader/_storage}/_sqlite_utils.py +123 -104
  24. reader-3.12/src/reader/_storage/_tags.py +250 -0
  25. {reader-3.10 → reader-3.12}/src/reader/_types.py +191 -7
  26. {reader-3.10 → reader-3.12}/src/reader/_utils.py +0 -60
  27. {reader-3.10 → reader-3.12}/src/reader/core.py +50 -70
  28. {reader-3.10 → reader-3.12}/src/reader/exceptions.py +12 -0
  29. {reader-3.10 → reader-3.12}/src/reader/plugins/readtime.py +2 -2
  30. {reader-3.10 → reader-3.12}/src/reader/types.py +77 -15
  31. {reader-3.10 → reader-3.12/src/reader.egg-info}/PKG-INFO +3 -1
  32. {reader-3.10 → reader-3.12}/src/reader.egg-info/SOURCES.txt +14 -5
  33. {reader-3.10 → reader-3.12}/src/reader.egg-info/requires.txt +3 -0
  34. {reader-3.10 → reader-3.12}/tests/conftest.py +19 -3
  35. {reader-3.10 → reader-3.12}/tests/fakeparser.py +40 -30
  36. {reader-3.10 → reader-3.12}/tests/reader_methods.py +6 -11
  37. {reader-3.10 → reader-3.12}/tests/test__types.py +14 -1
  38. {reader-3.10 → reader-3.12}/tests/test_app.py +0 -6
  39. {reader-3.10 → reader-3.12}/tests/test_bench.py +17 -6
  40. reader-3.12/tests/test_changes.py +427 -0
  41. {reader-3.10 → reader-3.12}/tests/test_html_utils.py +1 -1
  42. {reader-3.10 → reader-3.12}/tests/test_reader.py +25 -617
  43. {reader-3.10 → reader-3.12}/tests/test_reader_context.py +4 -2
  44. reader-3.12/tests/test_reader_counts.py +288 -0
  45. reader-3.12/tests/test_reader_filter.py +326 -0
  46. {reader-3.10 → reader-3.12}/tests/test_reader_hooks.py +4 -9
  47. {reader-3.10 → reader-3.12}/tests/test_reader_search.py +114 -272
  48. reader-3.12/tests/test_search.py +218 -0
  49. {reader-3.10 → reader-3.12}/tests/test_sql_utils.py +2 -2
  50. {reader-3.10 → reader-3.12}/tests/test_sqlite_utils.py +124 -89
  51. {reader-3.10 → reader-3.12}/tests/test_storage.py +41 -60
  52. {reader-3.10 → reader-3.12}/tox.ini +4 -4
  53. reader-3.10/scripts/bench.py +0 -498
  54. reader-3.10/src/reader/_search.py +0 -990
  55. reader-3.10/src/reader/_storage.py +0 -1769
  56. reader-3.10/tests/test_search.py +0 -204
  57. {reader-3.10 → reader-3.12}/LICENSE +0 -0
  58. {reader-3.10 → reader-3.12}/MANIFEST.in +0 -0
  59. {reader-3.10 → reader-3.12}/README.rst +0 -0
  60. {reader-3.10 → reader-3.12}/docs/Makefile +0 -0
  61. {reader-3.10 → reader-3.12}/docs/app.rst +0 -0
  62. {reader-3.10 → reader-3.12}/docs/changelog.rst +0 -0
  63. {reader-3.10 → reader-3.12}/docs/cli.rst +0 -0
  64. {reader-3.10 → reader-3.12}/docs/conf.py +0 -0
  65. {reader-3.10 → reader-3.12}/docs/config.rst +0 -0
  66. {reader-3.10 → reader-3.12}/docs/contributing.rst +0 -0
  67. {reader-3.10 → reader-3.12}/docs/dev-app.rst +0 -0
  68. {reader-3.10 → reader-3.12}/docs/images/redesign-01.png +0 -0
  69. {reader-3.10 → reader-3.12}/docs/index.rst +0 -0
  70. {reader-3.10 → reader-3.12}/docs/install.rst +0 -0
  71. {reader-3.10 → reader-3.12}/docs/make.bat +0 -0
  72. {reader-3.10 → reader-3.12}/docs/screenshots/dillo.png +0 -0
  73. {reader-3.10 → reader-3.12}/docs/screenshots/entries-feed.png +0 -0
  74. {reader-3.10 → reader-3.12}/docs/screenshots/entries.png +0 -0
  75. {reader-3.10 → reader-3.12}/docs/screenshots/entry-one.png +0 -0
  76. {reader-3.10 → reader-3.12}/docs/screenshots/entry-two.png +0 -0
  77. {reader-3.10 → reader-3.12}/docs/screenshots/feeds.png +0 -0
  78. {reader-3.10 → reader-3.12}/docs/screenshots/lynx.png +0 -0
  79. {reader-3.10 → reader-3.12}/docs/screenshots/search.png +0 -0
  80. {reader-3.10 → reader-3.12}/docs/tutorial.rst +0 -0
  81. {reader-3.10 → reader-3.12}/docs/why.rst +0 -0
  82. {reader-3.10 → reader-3.12}/examples/config.yaml +0 -0
  83. {reader-3.10 → reader-3.12}/examples/parser_only.py +0 -0
  84. {reader-3.10 → reader-3.12}/examples/podcast.py +0 -0
  85. {reader-3.10 → reader-3.12}/examples/terminal.py +0 -0
  86. {reader-3.10 → reader-3.12}/scripts/.coverage +0 -0
  87. {reader-3.10 → reader-3.12}/scripts/backup.sh +0 -0
  88. {reader-3.10 → reader-3.12}/scripts/debug_storage_stats.py +0 -0
  89. {reader-3.10 → reader-3.12}/scripts/generate_import_all.py +0 -0
  90. {reader-3.10 → reader-3.12}/scripts/jscontrols.html +0 -0
  91. {reader-3.10 → reader-3.12}/scripts/jscontrols.py +0 -0
  92. {reader-3.10 → reader-3.12}/scripts/lines.sh +0 -0
  93. {reader-3.10 → reader-3.12}/scripts/release.py +0 -0
  94. {reader-3.10 → reader-3.12}/setup.cfg +0 -0
  95. {reader-3.10 → reader-3.12}/src/reader/__main__.py +0 -0
  96. {reader-3.10 → reader-3.12}/src/reader/_app/__init__.py +0 -0
  97. {reader-3.10 → reader-3.12}/src/reader/_app/api_thing.py +0 -0
  98. {reader-3.10 → reader-3.12}/src/reader/_app/static/controls.js +0 -0
  99. {reader-3.10 → reader-3.12}/src/reader/_app/static/style.css +0 -0
  100. {reader-3.10 → reader-3.12}/src/reader/_app/templates/add_entry.html +0 -0
  101. {reader-3.10 → reader-3.12}/src/reader/_app/templates/entries.html +0 -0
  102. {reader-3.10 → reader-3.12}/src/reader/_app/templates/entry.html +0 -0
  103. {reader-3.10 → reader-3.12}/src/reader/_app/templates/feeds.html +0 -0
  104. {reader-3.10 → reader-3.12}/src/reader/_app/templates/layout.html +0 -0
  105. {reader-3.10 → reader-3.12}/src/reader/_app/templates/macros.html +0 -0
  106. {reader-3.10 → reader-3.12}/src/reader/_app/templates/metadata.html +0 -0
  107. {reader-3.10 → reader-3.12}/src/reader/_app/templates/tags.html +0 -0
  108. {reader-3.10 → reader-3.12}/src/reader/_app/wsgi.py +0 -0
  109. {reader-3.10 → reader-3.12}/src/reader/_config.py +0 -0
  110. {reader-3.10 → reader-3.12}/src/reader/_hash_utils.py +0 -0
  111. {reader-3.10 → reader-3.12}/src/reader/_parser/__init__.py +0 -0
  112. {reader-3.10 → reader-3.12}/src/reader/_parser/_http_utils.py +0 -0
  113. {reader-3.10 → reader-3.12}/src/reader/_parser/_lazy.py +0 -0
  114. {reader-3.10 → reader-3.12}/src/reader/_parser/_url_utils.py +0 -0
  115. {reader-3.10 → reader-3.12}/src/reader/_parser/feedparser.py +0 -0
  116. {reader-3.10 → reader-3.12}/src/reader/_parser/file.py +0 -0
  117. {reader-3.10 → reader-3.12}/src/reader/_parser/http.py +0 -0
  118. {reader-3.10 → reader-3.12}/src/reader/_parser/jsonfeed.py +0 -0
  119. {reader-3.10 → reader-3.12}/src/reader/_parser/requests/__init__.py +0 -0
  120. {reader-3.10 → reader-3.12}/src/reader/_parser/requests/_lazy.py +0 -0
  121. {reader-3.10 → reader-3.12}/src/reader/_plugins/__init__.py +0 -0
  122. {reader-3.10 → reader-3.12}/src/reader/_plugins/cli_status.py +0 -0
  123. {reader-3.10 → reader-3.12}/src/reader/_plugins/enclosure_tags.py +0 -0
  124. {reader-3.10 → reader-3.12}/src/reader/_plugins/preview_feed_list.py +0 -0
  125. {reader-3.10 → reader-3.12}/src/reader/_plugins/share.py +0 -0
  126. {reader-3.10 → reader-3.12}/src/reader/_plugins/sqlite_releases.py +0 -0
  127. {reader-3.10 → reader-3.12}/src/reader/_plugins/templates/preview_feed_list.html +0 -0
  128. {reader-3.10 → reader-3.12}/src/reader/_plugins/timer.py +0 -0
  129. {reader-3.10 → reader-3.12}/src/reader/_update.py +0 -0
  130. {reader-3.10 → reader-3.12}/src/reader/_vendor/__init__.py +0 -0
  131. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/__init__.py +0 -0
  132. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/api.py +0 -0
  133. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/__init__.py +0 -0
  134. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/asctime.py +0 -0
  135. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/greek.py +0 -0
  136. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/hungarian.py +0 -0
  137. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/iso8601.py +0 -0
  138. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/korean.py +0 -0
  139. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/perforce.py +0 -0
  140. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/rfc822.py +0 -0
  141. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/datetimes/w3dtf.py +0 -0
  142. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/encodings.py +0 -0
  143. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/exceptions.py +0 -0
  144. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/html.py +0 -0
  145. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/http.py +0 -0
  146. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/mixin.py +0 -0
  147. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/__init__.py +0 -0
  148. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/_base.py +0 -0
  149. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/admin.py +0 -0
  150. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/cc.py +0 -0
  151. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/dc.py +0 -0
  152. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/georss.py +0 -0
  153. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/itunes.py +0 -0
  154. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/mediarss.py +0 -0
  155. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/namespaces/psc.py +0 -0
  156. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/parsers/__init__.py +0 -0
  157. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/parsers/json.py +0 -0
  158. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/parsers/loose.py +0 -0
  159. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/parsers/strict.py +0 -0
  160. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/py.typed +0 -0
  161. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/sanitizer.py +0 -0
  162. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/sgml.py +0 -0
  163. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/urls.py +0 -0
  164. {reader-3.10 → reader-3.12}/src/reader/_vendor/feedparser/util.py +0 -0
  165. {reader-3.10 → reader-3.12}/src/reader/plugins/__init__.py +0 -0
  166. {reader-3.10 → reader-3.12}/src/reader/plugins/enclosure_dedupe.py +0 -0
  167. {reader-3.10 → reader-3.12}/src/reader/plugins/entry_dedupe.py +0 -0
  168. {reader-3.10 → reader-3.12}/src/reader/plugins/mark_as_read.py +0 -0
  169. {reader-3.10 → reader-3.12}/src/reader/plugins/ua_fallback.py +0 -0
  170. {reader-3.10 → reader-3.12}/src/reader/py.typed +0 -0
  171. {reader-3.10 → reader-3.12}/src/reader.egg-info/dependency_links.txt +0 -0
  172. {reader-3.10 → reader-3.12}/src/reader.egg-info/top_level.txt +0 -0
  173. {reader-3.10 → reader-3.12}/tests/data/10.json +0 -0
  174. {reader-3.10 → reader-3.12}/tests/data/10.json.py +0 -0
  175. {reader-3.10 → reader-3.12}/tests/data/custom +0 -0
  176. {reader-3.10 → reader-3.12}/tests/data/empty.atom +0 -0
  177. {reader-3.10 → reader-3.12}/tests/data/empty.atom.py +0 -0
  178. {reader-3.10 → reader-3.12}/tests/data/empty.json +0 -0
  179. {reader-3.10 → reader-3.12}/tests/data/empty.json.py +0 -0
  180. {reader-3.10 → reader-3.12}/tests/data/empty.rss +0 -0
  181. {reader-3.10 → reader-3.12}/tests/data/empty.rss.py +0 -0
  182. {reader-3.10 → reader-3.12}/tests/data/full.atom +0 -0
  183. {reader-3.10 → reader-3.12}/tests/data/full.atom.py +0 -0
  184. {reader-3.10 → reader-3.12}/tests/data/full.json +0 -0
  185. {reader-3.10 → reader-3.12}/tests/data/full.json.py +0 -0
  186. {reader-3.10 → reader-3.12}/tests/data/full.rss +0 -0
  187. {reader-3.10 → reader-3.12}/tests/data/full.rss.py +0 -0
  188. {reader-3.10 → reader-3.12}/tests/data/invalid.json +0 -0
  189. {reader-3.10 → reader-3.12}/tests/data/invalid.json.py +0 -0
  190. {reader-3.10 → reader-3.12}/tests/data/relative.atom +0 -0
  191. {reader-3.10 → reader-3.12}/tests/data/relative.atom.py +0 -0
  192. {reader-3.10 → reader-3.12}/tests/data/relative.rss +0 -0
  193. {reader-3.10 → reader-3.12}/tests/data/relative.rss.py +0 -0
  194. {reader-3.10 → reader-3.12}/tests/data/sqlite_releases.html +0 -0
  195. {reader-3.10 → reader-3.12}/tests/data/unknown.json +0 -0
  196. {reader-3.10 → reader-3.12}/tests/data/unknown.json.py +0 -0
  197. {reader-3.10 → reader-3.12}/tests/reader_test_plugins/__init__.py +0 -0
  198. {reader-3.10 → reader-3.12}/tests/reader_test_plugins/good.py +0 -0
  199. {reader-3.10 → reader-3.12}/tests/reader_test_plugins/init_error.py +0 -0
  200. {reader-3.10 → reader-3.12}/tests/reader_test_plugins/missing_dependency.py +0 -0
  201. {reader-3.10 → reader-3.12}/tests/reader_test_plugins/missing_entry_point.py +0 -0
  202. {reader-3.10 → reader-3.12}/tests/test_app_wsgi.py +0 -0
  203. {reader-3.10 → reader-3.12}/tests/test_cli.py +0 -0
  204. {reader-3.10 → reader-3.12}/tests/test_config.py +0 -0
  205. {reader-3.10 → reader-3.12}/tests/test_exceptions.py +0 -0
  206. {reader-3.10 → reader-3.12}/tests/test_hash_utils.py +0 -0
  207. {reader-3.10 → reader-3.12}/tests/test_lazy_imports.py +0 -0
  208. {reader-3.10 → reader-3.12}/tests/test_parser.py +0 -0
  209. {reader-3.10 → reader-3.12}/tests/test_plugins_cli_status.py +0 -0
  210. {reader-3.10 → reader-3.12}/tests/test_plugins_enclosure_dedupe.py +0 -0
  211. {reader-3.10 → reader-3.12}/tests/test_plugins_entry_dedupe.py +0 -0
  212. {reader-3.10 → reader-3.12}/tests/test_plugins_mark_as_read.py +0 -0
  213. {reader-3.10 → reader-3.12}/tests/test_plugins_preview_feed_list.py +0 -0
  214. {reader-3.10 → reader-3.12}/tests/test_plugins_readtime.py +0 -0
  215. {reader-3.10 → reader-3.12}/tests/test_plugins_sqlite_releases.py +0 -0
  216. {reader-3.10 → reader-3.12}/tests/test_plugins_ua_fallback.py +0 -0
  217. {reader-3.10 → reader-3.12}/tests/test_reader_deprecations.py +0 -0
  218. {reader-3.10 → reader-3.12}/tests/test_reader_integration.py +0 -0
  219. {reader-3.10 → reader-3.12}/tests/test_reader_plugins.py +0 -0
  220. {reader-3.10 → reader-3.12}/tests/test_reader_private.py +0 -0
  221. {reader-3.10 → reader-3.12}/tests/test_reader_sort.py +0 -0
  222. {reader-3.10 → reader-3.12}/tests/test_tags.py +0 -0
  223. {reader-3.10 → reader-3.12}/tests/test_test_utils.py +0 -0
  224. {reader-3.10 → reader-3.12}/tests/test_types.py +0 -0
  225. {reader-3.10 → reader-3.12}/tests/test_utils.py +0 -0
  226. {reader-3.10 → reader-3.12}/tests/utils.py +0 -0
@@ -6,6 +6,37 @@ Changelog
6
6
  :noindex:
7
7
 
8
8
 
9
+ Version 3.12
10
+ ------------
11
+
12
+ Released 2024-03-05
13
+
14
+ * Split the :ref:`full-text search <fts>` index into a separate,
15
+ attached database.
16
+ (:issue:`323`)
17
+ * Require at least SQLite 3.18.
18
+ Previously, *reader* core required 3.15,
19
+ and only :meth:`~Reader.update_search` required 3.18.
20
+ (:issue:`323`)
21
+ * Enable `write-ahead logging`_ only once, when the database is created,
22
+ instead of every time it is opened.
23
+ (:issue:`323`)
24
+ * Vacuum the main database after migrations. (:issue:`323`)
25
+ * Add an internal :ref:`change tracking API <changes>`
26
+ to formalize how search keeps in sync with storage.
27
+ (:issue:`323`)
28
+ * Refactor storage internals. (:issue:`323`)
29
+
30
+
31
+ Version 3.11
32
+ ------------
33
+
34
+ Released 2023-12-30
35
+
36
+ * Allow filtering entries by their (entry) tags. (:issue:`328`)
37
+ * Support Python 3.12. (:issue:`326`)
38
+
39
+
9
40
  Version 3.10
10
41
  ------------
11
42
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: reader
3
- Version: 3.10
3
+ Version: 3.12
4
4
  Summary: A Python feed reader library.
5
5
  Author: lemon24
6
6
  License: BSD-3-Clause
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3
21
21
  Classifier: Programming Language :: Python :: 3 :: Only
22
22
  Classifier: Programming Language :: Python :: 3.10
23
23
  Classifier: Programming Language :: Python :: 3.11
24
+ Classifier: Programming Language :: Python :: 3.12
24
25
  Classifier: Programming Language :: Python :: Implementation :: CPython
25
26
  Classifier: Programming Language :: Python :: Implementation :: PyPy
26
27
  Classifier: Topic :: Internet
@@ -68,6 +69,7 @@ Requires-Dist: mechanicalsoup; (implementation_name != "pypy" or python_version
68
69
  Requires-Dist: requests-wsgi-adapter; extra == "tests"
69
70
  Requires-Dist: lxml; (implementation_name != "pypy" or python_version <= "3.9") and extra == "tests"
70
71
  Requires-Dist: html5lib; extra == "tests"
72
+ Requires-Dist: numpy; (implementation_name != "pypy" and os_name == "posix") and extra == "tests"
71
73
  Requires-Dist: mypy; implementation_name != "pypy" and extra == "tests"
72
74
  Requires-Dist: types-requests; extra == "tests"
73
75
  Requires-Dist: types-beautifulsoup4; extra == "tests"
@@ -173,6 +173,7 @@ The class hierarchy for :mod:`reader` exceptions is:
173
173
  Type aliases
174
174
  ------------
175
175
 
176
+ .. autodata:: reader.types.TagFilterInput
176
177
  .. autodata:: reader.types.TristateFilterInput
177
178
 
178
179
 
@@ -34,7 +34,7 @@ but I will prioritize supporting :doc:`contributors <contributing>`
34
34
 
35
35
  * OPML support, :issue:`165`
36
36
  * :ref:`deleting entries <deleting entries>`
37
- * more feed interaction statistics, :issue:`254`
37
+ * :ref:`feed interaction statistics <counts api>`
38
38
 
39
39
  * security
40
40
 
@@ -57,7 +57,6 @@ but I will prioritize supporting :doc:`contributors <contributing>`
57
57
 
58
58
  * :ref:`searchable tag values <searchable tags>`, e.g. for comments
59
59
  * :ref:`unification with entry.read/important <entry flag unification>`
60
- * filter entries by entry tags
61
60
  * optimistic locking, :issue:`308`
62
61
  * filter tags by prefix, :issue:`309`
63
62
 
@@ -339,6 +338,28 @@ Detailed requirements and API discussion: :issue:`168#issuecomment-642002049`.
339
338
 
340
339
  Minimal work needed to support alternate storages: :issue:`168#issuecomment-1383127564`.
341
340
 
341
+ Storage internal API documented in version 3.10 (November 2023) in :issue:`325`.
342
+
343
+
344
+ Database optimization
345
+ ~~~~~~~~~~~~~~~~~~~~~
346
+
347
+ Optimization lessons learned while fixing "database is locked" errors: :issue:`175#issuecomment-657495233`.
348
+
349
+ Some general guidance on schema/index design: :issue:`327#issuecomment-1859147186`.
350
+
351
+ Speeding up ``get_entries(sort='recent')``:
352
+
353
+ * first attempt at adding indexes: :issue:`134`
354
+ * using a computed column (``recent_sort``) didn't change things very much: :issue:`279`
355
+ * an index on ``recent_sort`` alone is not enough for pagination,
356
+ the index needs to match 1:1 the WHERE clause: :issue:`330`.
357
+
358
+ Speeding up ``get_entry_counts(feed=...)``:
359
+
360
+ * having an index on entries(feed) yielded a 4x improvement: :issue:`251`
361
+ * even better, we should cache commonly-used counts: :issue:`306#issuecomment-1694655504`
362
+
342
363
 
343
364
  Parser
344
365
  ~~~~~~
@@ -429,7 +450,8 @@ https://death.andgravity.com/query-builder
429
450
  Pagination for methods that return iterators
430
451
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
431
452
 
432
- Why do it for the private implementation: :issue:`167#issuecomment-626753299` (also a comment in storage code).
453
+ Why do it for the private implementation: :issue:`167#issuecomment-626753299`;
454
+
433
455
 
434
456
  Detailed requirements and API discussion for public pagination: :issue:`196#issuecomment-706038363`.
435
457
 
@@ -444,6 +466,9 @@ From the initial issue:
444
466
 
445
467
  Enabling search by default, and alternative search APIs: :issue:`252`.
446
468
 
469
+ Change tracking API: :issue:`323#issuecomment-1930756417`, model validated in
470
+ `this gist <https://gist.github.com/lemon24/558955ad82ba2e4f50c0184c630c668c>`_.
471
+
447
472
  External resources:
448
473
 
449
474
  * Comprehensive, although a bit old (2017): `What every software engineer should know about search <https://medium.com/startup-grind/what-every-software-engineer-should-know-about-search-27d1df99f80d>`_ (`full version <http://webcache.googleusercontent.com/search?q=cache:https://medium.com/startup-grind/what-every-software-engineer-should-know-about-search-27d1df99f80d&sca_esv=570067020&prmd=ivn&strip=1&vwsrc=0>`_)
@@ -522,12 +547,17 @@ and why I want more use-cases before implementing them (basically, YAGNI):
522
547
 
523
548
  :issue:`253` discusses using entry tags to implement the current entry flags
524
549
  (read, important); tl;dr: it's not worth adding entry tags just for this.
550
+ :issue:`327` discusses using entry tags for ``has_enclosures``; tl;dr:
551
+ it wouldn't save a lot of code, it would be only *a bit* slower,
552
+ and it reconfirms that read and important are integral to the data model,
553
+ so we still want them as regular columns.
525
554
 
526
555
  After closing :issue:`228` with `wontfix` in late 2021,
527
556
  in early 2022 (following the :issue:`266` tag/metadata unification)
528
557
  I implemented entry and global tags in :issue:`272`;
529
558
  there's a list of known use cases in the issue description.
530
559
 
560
+
531
561
  Resource tags
532
562
  ^^^^^^^^^^^^^
533
563
 
@@ -581,6 +611,8 @@ Requirements, open questions, and how it interacts with :mod:`~reader.plugins.en
581
611
  A summary of why it isn't easy to do: :issue:`301#issuecomment-1442423151`.
582
612
 
583
613
 
614
+ .. _counts api:
615
+
584
616
  Counts API
585
617
  ~~~~~~~~~~
586
618
 
@@ -589,6 +621,11 @@ Detailed requirements and API discussion: :issue:`185#issuecomment-731743327`.
589
621
  Tracking additional statistics (e.g. :attr:`~Entry.read_modified`): :issue:`254`;
590
622
  how to expose said statistics: :issue:`254#issuecomment-1807064610`.
591
623
 
624
+ Notebook with a successful attempt to determine a feed "usefulness" score
625
+ based on how many entries I mark as read / important / don't care;
626
+ highlights a number of gaps in the *reader* API:
627
+ https://gist.github.com/lemon24/93222ef4bc4a775092b56546a6e6cd0f
628
+
592
629
 
593
630
  Using None as a special argument value
594
631
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -622,6 +659,12 @@ Using a single Reader objects from multiple threads
622
659
 
623
660
  Some thoughts on why it's difficult to do: :issue:`206#issuecomment-751383418`.
624
661
 
662
+ Requirements and use cases: :issue:`206#issuecomment-1179739301`.
663
+
664
+ When/how to run ``pragma optimize``: :issue:`206#issuecomment-1183660880`.
665
+
666
+ Full support added in version 2.16 (July 2022).
667
+
625
668
 
626
669
  Plugins
627
670
  ~~~~~~~
@@ -127,6 +127,8 @@ since each connection would be to a *different* database::
127
127
  reader.exceptions.StorageError: usage error: cannot use a private database from threads other than the creating thread
128
128
 
129
129
 
130
+ .. _backups:
131
+
130
132
  Back-ups
131
133
  ~~~~~~~~
132
134
 
@@ -439,16 +441,27 @@ the entries that changed since the last call,
439
441
  so it is OK to call it relatively often.
440
442
 
441
443
 
442
- Because search adds minor overhead to other :class:`Reader` methods
443
- and can almost double the size of the database,
444
- it can be turned on/off through the
445
- :meth:`~Reader.enable_search()` / :meth:`~Reader.disable_search()` methods.
446
- This is persistent across instances using the same database,
447
- and only needs to be done once.
448
- You can also use the ``search_enabled`` :func:`make_reader` argument
449
- for the same purpose.
450
- By default, search is disabled,
451
- and enabled automatically on the first :meth:`~Reader.update_search()` call.
444
+ Search can be turned on/off through the
445
+ :meth:`~Reader.enable_search()` / :meth:`~Reader.disable_search()` methods
446
+ (persistent across instances using the same database),
447
+ or the ``search_enabled`` argument of :func:`make_reader`;
448
+ by default, search is enabled automatically
449
+ on the first :meth:`~Reader.update_search()` call.
450
+ If search is enabled,
451
+ you should call :meth:`~Reader.update_search()` regularly
452
+ to prevent unprocesses changes from accumulating over time.
453
+
454
+
455
+ Because the search index can be almost as large as the main database,
456
+ the default implementation splits it into a separate, attached database,
457
+ which allows :ref:`backing up <backups>` the main database separately;
458
+ for a reader created with ``make_reader('db.sqlite')``,
459
+ the search index will be in ``db.sqlite.search``.
460
+
461
+
462
+ .. versionchanged:: 3.12
463
+ Split the full-text search index into a separate database.
464
+
452
465
 
453
466
 
454
467
 
@@ -493,8 +506,7 @@ in which case the behavior is to ensure the tag exists
493
506
 
494
507
  Besides storing resource metadata,
495
508
  tags can be used for filtering feeds and entries
496
- (as of version |version|, only by feed tags;
497
- see the :meth:`~Reader.get_feeds()` documentation for more complex examples)::
509
+ (see :data:`.TagFilterInput` for more complex examples)::
498
510
 
499
511
  >>> # feeds that have the tag "one"
500
512
  >>> [f.title for f in reader.get_feeds(tags=['one'])]
@@ -143,9 +143,11 @@ but at least one other implementation needs to exists before that.
143
143
 
144
144
  Currently, search is tightly-bound to a storage implementation
145
145
  (see :meth:`~.BoundSearchStorageType.make_search`).
146
- In the future, it may be possible to mix and match
147
- main storage and search implementations,
148
- but :class:`StorageType` will likely need
146
+ While the :ref:`change tracking API <changes>` allows
147
+ search implementations to keep in sync with text content changes,
148
+ there is no convenient way for :meth:`SearchType.search_entries`
149
+ to filter/sort results without storage cooperation;
150
+ :class:`StorageType` will need
149
151
  additional capabilities to support this.
150
152
 
151
153
 
@@ -167,6 +169,27 @@ but at least one other implementation needs to exists before that.
167
169
  :members:
168
170
 
169
171
 
172
+ .. _changes:
173
+
174
+ .. autoclass:: ChangeTrackingStorageType()
175
+ :members:
176
+ :show-inheritance:
177
+
178
+ .. autoclass:: ChangeTrackerType()
179
+ :members:
180
+
181
+ .. autoclass:: Change
182
+ :members:
183
+
184
+ .. autoclass:: Action
185
+ :members:
186
+
187
+ .. autoattribute:: reader.Entry._sequence
188
+
189
+ .. autoexception:: reader.exceptions.ChangeTrackingNotEnabledError
190
+ :show-inheritance:
191
+
192
+
170
193
  Data objects
171
194
  ~~~~~~~~~~~~
172
195
 
@@ -67,8 +67,8 @@ However, the plugin used the internal :ref:`parser` API
67
67
  and stored old tweets alongside the rendered HTML content
68
68
  to avoid retrieving them again when updating the thread/entry.
69
69
 
70
- You can still find the code
71
- `on GitHub <https://github.com/lemon24/reader/blob/3.6/src/reader/_plugins/twitter.py>`_.
70
+ You can still find the code on GitHub:
71
+ `twitter.py <https://github.com/lemon24/reader/blob/3.6/src/reader/_plugins/twitter.py>`_.
72
72
 
73
73
 
74
74
  .. _tumblr_gdpr:
@@ -84,8 +84,8 @@ including machine-readable RSS feeds).
84
84
  This plugin is a good example of how to set cookies
85
85
  on the Requests session used to retrieve feeds.
86
86
 
87
- You can still find the code
88
- `on GitHub <https://github.com/lemon24/reader/blob/3.6/src/reader/_plugins/tumblr_gdpr.py>`_.
87
+ You can still find the code on GitHub:
88
+ `tumblr_gdpr.py <https://github.com/lemon24/reader/blob/3.6/src/reader/_plugins/tumblr_gdpr.py>`_.
89
89
 
90
90
 
91
91
 
@@ -25,6 +25,7 @@ classifiers = [
25
25
  "Programming Language :: Python :: 3 :: Only",
26
26
  "Programming Language :: Python :: 3.10",
27
27
  "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
28
29
  "Programming Language :: Python :: Implementation :: CPython",
29
30
  "Programming Language :: Python :: Implementation :: PyPy",
30
31
  "Topic :: Internet",
@@ -112,6 +113,8 @@ tests = [
112
113
  # lxml does not have a PyPy 3.10+ wheel.
113
114
  'lxml; (implementation_name != "pypy" or python_version <= "3.9")',
114
115
  "html5lib",
116
+ # for bench.py
117
+ 'numpy; (implementation_name != "pypy" and os_name == "posix")',
115
118
  # mypy does not work on pypy (yet).
116
119
  'mypy; implementation_name != "pypy"',
117
120
  "types-requests",
@@ -99,7 +99,7 @@ function typing {
99
99
 
100
100
 
101
101
  function docs {
102
- make -C docs html "$@"
102
+ make -C docs html SPHINXOPTS="-W" "$@"
103
103
  }
104
104
 
105
105