reader 3.21__tar.gz → 3.23__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 (278) hide show
  1. {reader-3.21 → reader-3.23}/CHANGES.rst +73 -4
  2. {reader-3.21 → reader-3.23}/MANIFEST.in +3 -1
  3. {reader-3.21/src/reader.egg-info → reader-3.23}/PKG-INFO +10 -13
  4. {reader-3.21 → reader-3.23}/README.rst +8 -11
  5. {reader-3.21 → reader-3.23}/docs/api.rst +21 -0
  6. reader-3.23/docs/app.rst +153 -0
  7. {reader-3.21 → reader-3.23}/docs/cli.rst +4 -10
  8. {reader-3.21 → reader-3.23}/docs/conf.py +2 -2
  9. reader-3.23/docs/config.rst +30 -0
  10. {reader-3.21 → reader-3.23}/docs/dev.rst +30 -38
  11. {reader-3.21 → reader-3.23}/docs/guide.rst +55 -26
  12. {reader-3.21 → reader-3.23}/docs/plugins.rst +17 -16
  13. reader-3.23/docs/screenshots/entries-filters-light.png +0 -0
  14. reader-3.23/docs/screenshots/entries.png +0 -0
  15. reader-3.23/docs/screenshots/entry-image-light.png +0 -0
  16. reader-3.23/docs/screenshots/entry.png +0 -0
  17. reader-3.23/docs/screenshots/feed.png +0 -0
  18. reader-3.23/docs/screenshots/feeds.png +0 -0
  19. reader-3.23/docs/screenshots/import-result.png +0 -0
  20. reader-3.23/docs/screenshots/import-select.png +0 -0
  21. reader-3.23/examples/config.toml +26 -0
  22. {reader-3.21 → reader-3.23}/examples/podcast.py +0 -1
  23. {reader-3.21 → reader-3.23}/pyproject.toml +7 -5
  24. {reader-3.21 → reader-3.23}/run.sh +1 -1
  25. {reader-3.21 → reader-3.23}/scripts/bench.py +1 -3
  26. {reader-3.21 → reader-3.23}/scripts/debug_storage_stats.py +0 -1
  27. {reader-3.21 → reader-3.23}/scripts/entry_dedupe_backtest.py +0 -1
  28. {reader-3.21 → reader-3.23}/scripts/jscontrols.py +0 -2
  29. reader-3.23/scripts/vendor_app_stuff.py +40 -0
  30. {reader-3.21 → reader-3.23}/src/reader/__init__.py +7 -12
  31. {reader-3.21 → reader-3.23}/src/reader/__main__.py +0 -1
  32. reader-3.23/src/reader/_app/__init__.py +399 -0
  33. {reader-3.21 → reader-3.23}/src/reader/_app/cli.py +14 -10
  34. reader-3.23/src/reader/_app/ext.py +51 -0
  35. {reader-3.21/src/reader/_app/v2 → reader-3.23/src/reader/_app}/forms.py +73 -21
  36. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/__init__.py +27 -60
  37. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/layout.html +4 -5
  38. reader-3.23/src/reader/_app/legacy/wsgi.py +27 -0
  39. reader-3.23/src/reader/_app/static/style.css +70 -0
  40. reader-3.23/src/reader/_app/static/vendor/README +1 -0
  41. reader-3.23/src/reader/_app/static/vendor/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css +5 -0
  42. reader-3.23/src/reader/_app/static/vendor/bootstrap-icons@1.13.1/font/fonts/bootstrap-icons.woff +0 -0
  43. reader-3.23/src/reader/_app/static/vendor/bootstrap-icons@1.13.1/font/fonts/bootstrap-icons.woff2 +0 -0
  44. reader-3.23/src/reader/_app/static/vendor/bootstrap@5.3.8/dist/css/bootstrap.min.css +6 -0
  45. reader-3.23/src/reader/_app/static/vendor/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js +7 -0
  46. reader-3.23/src/reader/_app/static/vendor/htmx-ext-response-targets@2.0.4/dist/response-targets.min.js +1 -0
  47. reader-3.23/src/reader/_app/static/vendor/htmx.org@2.0.8/dist/htmx.min.js +1 -0
  48. reader-3.23/src/reader/_app/templates/add_feed.html +54 -0
  49. reader-3.23/src/reader/_app/templates/change_feed_title.html +56 -0
  50. reader-3.23/src/reader/_app/templates/delete_feed.html +37 -0
  51. reader-3.23/src/reader/_app/templates/entries.html +189 -0
  52. reader-3.23/src/reader/_app/templates/entry.html +135 -0
  53. reader-3.23/src/reader/_app/templates/feeds.html +88 -0
  54. reader-3.23/src/reader/_app/templates/import_feeds.html +121 -0
  55. reader-3.23/src/reader/_app/templates/layout.html +1 -0
  56. reader-3.21/src/reader/_app/v2/templates/v2/layout.html → reader-3.23/src/reader/_app/templates/layout_base.html +30 -29
  57. reader-3.23/src/reader/_app/templates/macros.html +319 -0
  58. reader-3.23/src/reader/_app/wsgi.py +27 -0
  59. {reader-3.21 → reader-3.23}/src/reader/_cli.py +102 -132
  60. reader-3.23/src/reader/_config_utils.py +166 -0
  61. {reader-3.21 → reader-3.23}/src/reader/_hash_utils.py +0 -1
  62. {reader-3.21 → reader-3.23}/src/reader/_parser/__init__.py +15 -1
  63. {reader-3.21 → reader-3.23}/src/reader/_parser/_http_utils.py +0 -1
  64. {reader-3.21 → reader-3.23}/src/reader/_parser/_lazy.py +0 -1
  65. {reader-3.21 → reader-3.23}/src/reader/_parser/feedparser.py +0 -1
  66. {reader-3.21 → reader-3.23}/src/reader/_parser/jsonfeed.py +0 -1
  67. {reader-3.21 → reader-3.23}/src/reader/_parser/requests/__init__.py +0 -2
  68. {reader-3.21 → reader-3.23}/src/reader/_parser/requests/_lazy.py +0 -1
  69. reader-3.23/src/reader/_plugins/__init__.py +7 -0
  70. {reader-3.21 → reader-3.23}/src/reader/_plugins/cli_status.py +8 -4
  71. reader-3.23/src/reader/_plugins/legacy/__init__.py +3 -0
  72. {reader-3.21/src/reader/_plugins → reader-3.23/src/reader/_plugins/legacy}/enclosure_tags.py +2 -3
  73. {reader-3.21/src/reader/_plugins → reader-3.23/src/reader/_plugins/legacy}/preview_feed_list.py +4 -5
  74. {reader-3.21/src/reader/_plugins → reader-3.23/src/reader/_plugins/legacy}/share.py +2 -3
  75. {reader-3.21 → reader-3.23}/src/reader/_plugins/sqlite_releases.py +2 -3
  76. {reader-3.21 → reader-3.23}/src/reader/_plugins/timer.py +1 -1
  77. {reader-3.21 → reader-3.23}/src/reader/_storage/__init__.py +0 -3
  78. {reader-3.21 → reader-3.23}/src/reader/_storage/_base.py +0 -1
  79. {reader-3.21 → reader-3.23}/src/reader/_storage/_changes.py +2 -5
  80. {reader-3.21 → reader-3.23}/src/reader/_storage/_entries.py +8 -13
  81. {reader-3.21 → reader-3.23}/src/reader/_storage/_feeds.py +1 -2
  82. {reader-3.21 → reader-3.23}/src/reader/_storage/_html_utils.py +0 -1
  83. {reader-3.21 → reader-3.23}/src/reader/_storage/_schema.py +4 -9
  84. {reader-3.21 → reader-3.23}/src/reader/_storage/_search.py +13 -22
  85. {reader-3.21 → reader-3.23}/src/reader/_storage/_sql_utils.py +0 -1
  86. {reader-3.21 → reader-3.23}/src/reader/_storage/_sqlite_utils.py +1 -2
  87. {reader-3.21 → reader-3.23}/src/reader/_storage/_tags.py +0 -1
  88. {reader-3.21 → reader-3.23}/src/reader/_types.py +2 -3
  89. {reader-3.21 → reader-3.23}/src/reader/_update.py +0 -1
  90. {reader-3.21 → reader-3.23}/src/reader/_utils.py +8 -1
  91. {reader-3.21 → reader-3.23}/src/reader/core.py +120 -26
  92. {reader-3.21 → reader-3.23}/src/reader/exceptions.py +8 -0
  93. reader-3.23/src/reader/opml.py +178 -0
  94. reader-3.23/src/reader/plugins/__init__.py +52 -0
  95. reader-3.23/src/reader/plugins/_loader.py +81 -0
  96. {reader-3.21 → reader-3.23}/src/reader/plugins/enclosure_dedupe.py +4 -2
  97. {reader-3.21 → reader-3.23}/src/reader/plugins/entry_dedupe.py +6 -7
  98. {reader-3.21 → reader-3.23}/src/reader/plugins/mark_as_read.py +4 -3
  99. {reader-3.21 → reader-3.23}/src/reader/plugins/readtime.py +4 -3
  100. {reader-3.21 → reader-3.23}/src/reader/plugins/ua_fallback.py +4 -3
  101. {reader-3.21 → reader-3.23}/src/reader/types.py +70 -1
  102. {reader-3.21 → reader-3.23}/src/reader/utils.py +16 -2
  103. {reader-3.21 → reader-3.23/src/reader.egg-info}/PKG-INFO +10 -13
  104. {reader-3.21 → reader-3.23}/src/reader.egg-info/SOURCES.txt +54 -37
  105. {reader-3.21 → reader-3.23}/src/reader.egg-info/requires.txt +1 -1
  106. reader-3.23/tests/_plugins/builtin.py +2 -0
  107. reader-3.23/tests/_plugins/error_during_import.py +1 -0
  108. reader-3.23/tests/_plugins/error_import_error.py +1 -0
  109. {reader-3.21 → reader-3.23}/tests/data/10.json.py +0 -1
  110. {reader-3.21 → reader-3.23}/tests/data/empty.atom.py +0 -1
  111. {reader-3.21 → reader-3.23}/tests/data/empty.json.py +0 -1
  112. {reader-3.21 → reader-3.23}/tests/data/empty.rss.py +0 -1
  113. {reader-3.21 → reader-3.23}/tests/data/full.atom.py +0 -1
  114. {reader-3.21 → reader-3.23}/tests/data/full.json.py +0 -1
  115. {reader-3.21 → reader-3.23}/tests/data/full.rss.py +0 -1
  116. {reader-3.21 → reader-3.23}/tests/data/invalid.json.py +0 -1
  117. {reader-3.21 → reader-3.23}/tests/data/relative.atom.py +0 -1
  118. {reader-3.21 → reader-3.23}/tests/data/relative.rss.py +0 -1
  119. {reader-3.21 → reader-3.23}/tests/data/unknown.json.py +0 -1
  120. {reader-3.21 → reader-3.23}/tests/test__types.py +0 -1
  121. {reader-3.21 → reader-3.23}/tests/test__utils.py +0 -1
  122. reader-3.21/tests/test_app.py → reader-3.23/tests/test_app_legacy.py +9 -10
  123. {reader-3.21 → reader-3.23}/tests/test_app_wsgi.py +3 -3
  124. {reader-3.21 → reader-3.23}/tests/test_bench.py +0 -2
  125. {reader-3.21 → reader-3.23}/tests/test_changes.py +0 -1
  126. {reader-3.21 → reader-3.23}/tests/test_cli.py +37 -151
  127. reader-3.23/tests/test_config_utils.py +105 -0
  128. {reader-3.21 → reader-3.23}/tests/test_html_utils.py +0 -1
  129. {reader-3.21 → reader-3.23}/tests/test_lazy_imports.py +5 -6
  130. reader-3.23/tests/test_opml.py +153 -0
  131. {reader-3.21 → reader-3.23}/tests/test_plugins_cli_status.py +2 -4
  132. {reader-3.21 → reader-3.23}/tests/test_plugins_enclosure_dedupe.py +1 -1
  133. {reader-3.21 → reader-3.23}/tests/test_plugins_entry_dedupe.py +1 -2
  134. reader-3.23/tests/test_plugins_loader.py +103 -0
  135. {reader-3.21 → reader-3.23}/tests/test_plugins_mark_as_read.py +5 -8
  136. {reader-3.21 → reader-3.23}/tests/test_plugins_preview_feed_list.py +6 -7
  137. {reader-3.21 → reader-3.23}/tests/test_plugins_readtime.py +4 -5
  138. {reader-3.21 → reader-3.23}/tests/test_plugins_sqlite_releases.py +2 -2
  139. {reader-3.21 → reader-3.23}/tests/test_plugins_ua_fallback.py +2 -2
  140. {reader-3.21 → reader-3.23}/tests/test_reader.py +2 -6
  141. {reader-3.21 → reader-3.23}/tests/test_reader_deprecations.py +0 -1
  142. {reader-3.21 → reader-3.23}/tests/test_reader_filter.py +29 -2
  143. {reader-3.21 → reader-3.23}/tests/test_reader_hooks.py +0 -1
  144. {reader-3.21 → reader-3.23}/tests/test_reader_lifecycle.py +0 -1
  145. {reader-3.21 → reader-3.23}/tests/test_reader_private.py +0 -1
  146. {reader-3.21 → reader-3.23}/tests/test_reader_search.py +12 -9
  147. {reader-3.21 → reader-3.23}/tests/test_reader_sort.py +7 -1
  148. {reader-3.21 → reader-3.23}/tests/test_reader_update.py +0 -1
  149. {reader-3.21 → reader-3.23}/tests/test_reader_utils.py +14 -2
  150. {reader-3.21 → reader-3.23}/tests/test_search.py +0 -1
  151. {reader-3.21 → reader-3.23}/tests/test_sql_utils.py +12 -22
  152. {reader-3.21 → reader-3.23}/tests/test_sqlite_utils.py +0 -1
  153. reader-3.21/docs/app.rst +0 -155
  154. reader-3.21/docs/config.rst +0 -37
  155. reader-3.21/docs/dev-app.rst +0 -138
  156. reader-3.21/docs/images/redesign-01.png +0 -0
  157. reader-3.21/docs/screenshots/dillo.png +0 -0
  158. reader-3.21/docs/screenshots/entries-feed.png +0 -0
  159. reader-3.21/docs/screenshots/entries-v2-dark.png +0 -0
  160. reader-3.21/docs/screenshots/entries-v2-filters-light.png +0 -0
  161. reader-3.21/docs/screenshots/entries.png +0 -0
  162. reader-3.21/docs/screenshots/entry-one.png +0 -0
  163. reader-3.21/docs/screenshots/entry-two.png +0 -0
  164. reader-3.21/docs/screenshots/feeds.png +0 -0
  165. reader-3.21/docs/screenshots/lynx.png +0 -0
  166. reader-3.21/docs/screenshots/search.png +0 -0
  167. reader-3.21/examples/config.yaml +0 -100
  168. reader-3.21/src/reader/_app/v2/__init__.py +0 -123
  169. reader-3.21/src/reader/_app/v2/static/style.css +0 -27
  170. reader-3.21/src/reader/_app/v2/templates/v2/entries.html +0 -234
  171. reader-3.21/src/reader/_app/v2/templates/v2/macros.html +0 -47
  172. reader-3.21/src/reader/_app/wsgi.py +0 -42
  173. reader-3.21/src/reader/_config.py +0 -161
  174. reader-3.21/src/reader/_plugins/__init__.py +0 -65
  175. reader-3.21/src/reader/plugins/__init__.py +0 -64
  176. reader-3.21/tests/reader_test_plugins/good.py +0 -2
  177. reader-3.21/tests/reader_test_plugins/init_error.py +0 -2
  178. reader-3.21/tests/reader_test_plugins/missing_dependency.py +0 -1
  179. reader-3.21/tests/reader_test_plugins/missing_entry_point.py +0 -1
  180. reader-3.21/tests/test_config.py +0 -116
  181. reader-3.21/tests/test_reader_plugins.py +0 -87
  182. {reader-3.21 → reader-3.23}/LICENSE +0 -0
  183. {reader-3.21 → reader-3.23}/docs/changelog.rst +0 -0
  184. {reader-3.21 → reader-3.23}/docs/contributing.rst +0 -0
  185. {reader-3.21 → reader-3.23}/docs/index.rst +0 -0
  186. {reader-3.21 → reader-3.23}/docs/install.rst +0 -0
  187. {reader-3.21 → reader-3.23}/docs/internal.rst +0 -0
  188. {reader-3.21 → reader-3.23}/docs/tutorial.rst +0 -0
  189. {reader-3.21 → reader-3.23}/docs/why.rst +0 -0
  190. {reader-3.21 → reader-3.23}/examples/custom_headers.py +0 -0
  191. {reader-3.21 → reader-3.23}/examples/feed_slugs.py +0 -0
  192. {reader-3.21 → reader-3.23}/examples/parser_only.py +0 -0
  193. {reader-3.21 → reader-3.23}/examples/terminal.py +0 -0
  194. {reader-3.21 → reader-3.23}/scripts/backup.sh +0 -0
  195. {reader-3.21 → reader-3.23}/scripts/jscontrols.html +0 -0
  196. {reader-3.21 → reader-3.23}/scripts/lines.sh +0 -0
  197. {reader-3.21 → reader-3.23}/scripts/release.py +0 -0
  198. {reader-3.21 → reader-3.23}/setup.cfg +0 -0
  199. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/api_thing.py +0 -0
  200. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/static/controls.js +0 -0
  201. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/static/style.css +0 -0
  202. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/add_entry.html +0 -0
  203. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/entries.html +0 -0
  204. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/entry.html +0 -0
  205. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/feeds.html +0 -0
  206. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/macros.html +0 -0
  207. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/metadata.html +0 -0
  208. {reader-3.21/src/reader/_app → reader-3.23/src/reader/_app/legacy}/templates/tags.html +0 -0
  209. {reader-3.21/src/reader/_app/v2 → reader-3.23/src/reader/_app}/static/theme.js +0 -0
  210. {reader-3.21 → reader-3.23}/src/reader/_parser/_url_utils.py +0 -0
  211. {reader-3.21 → reader-3.23}/src/reader/_parser/file.py +0 -0
  212. {reader-3.21 → reader-3.23}/src/reader/_parser/http.py +0 -0
  213. {reader-3.21/src/reader/_plugins → reader-3.23/src/reader/_plugins/legacy}/templates/preview_feed_list.html +0 -0
  214. {reader-3.21 → reader-3.23}/src/reader/_vendor/__init__.py +0 -0
  215. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/__init__.py +0 -0
  216. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/api.py +0 -0
  217. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/__init__.py +0 -0
  218. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/asctime.py +0 -0
  219. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/greek.py +0 -0
  220. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/hungarian.py +0 -0
  221. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/iso8601.py +0 -0
  222. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/korean.py +0 -0
  223. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/perforce.py +0 -0
  224. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/rfc822.py +0 -0
  225. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/datetimes/w3dtf.py +0 -0
  226. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/encodings.py +0 -0
  227. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/exceptions.py +0 -0
  228. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/html.py +0 -0
  229. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/http.py +0 -0
  230. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/mixin.py +0 -0
  231. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/__init__.py +0 -0
  232. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/_base.py +0 -0
  233. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/admin.py +0 -0
  234. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/cc.py +0 -0
  235. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/dc.py +0 -0
  236. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/georss.py +0 -0
  237. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/itunes.py +0 -0
  238. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/mediarss.py +0 -0
  239. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/namespaces/psc.py +0 -0
  240. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/parsers/__init__.py +0 -0
  241. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/parsers/json.py +0 -0
  242. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/parsers/loose.py +0 -0
  243. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/parsers/strict.py +0 -0
  244. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/py.typed +0 -0
  245. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/sanitizer.py +0 -0
  246. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/sgml.py +0 -0
  247. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/urls.py +0 -0
  248. {reader-3.21 → reader-3.23}/src/reader/_vendor/feedparser/util.py +0 -0
  249. {reader-3.21 → reader-3.23}/src/reader/py.typed +0 -0
  250. {reader-3.21 → reader-3.23}/src/reader.egg-info/dependency_links.txt +0 -0
  251. {reader-3.21 → reader-3.23}/src/reader.egg-info/top_level.txt +0 -0
  252. {reader-3.21/tests/reader_test_plugins → reader-3.23/tests/_plugins}/__init__.py +0 -0
  253. {reader-3.21 → reader-3.23}/tests/conftest.py +0 -0
  254. {reader-3.21 → reader-3.23}/tests/data/10.json +0 -0
  255. {reader-3.21 → reader-3.23}/tests/data/custom +0 -0
  256. {reader-3.21 → reader-3.23}/tests/data/empty.atom +0 -0
  257. {reader-3.21 → reader-3.23}/tests/data/empty.json +0 -0
  258. {reader-3.21 → reader-3.23}/tests/data/empty.rss +0 -0
  259. {reader-3.21 → reader-3.23}/tests/data/full.atom +0 -0
  260. {reader-3.21 → reader-3.23}/tests/data/full.json +0 -0
  261. {reader-3.21 → reader-3.23}/tests/data/full.rss +0 -0
  262. {reader-3.21 → reader-3.23}/tests/data/invalid.json +0 -0
  263. {reader-3.21 → reader-3.23}/tests/data/relative.atom +0 -0
  264. {reader-3.21 → reader-3.23}/tests/data/relative.rss +0 -0
  265. {reader-3.21 → reader-3.23}/tests/data/sqlite_releases.html +0 -0
  266. {reader-3.21 → reader-3.23}/tests/data/unknown.json +0 -0
  267. {reader-3.21 → reader-3.23}/tests/fakeparser.py +0 -0
  268. {reader-3.21 → reader-3.23}/tests/reader_methods.py +0 -0
  269. {reader-3.21 → reader-3.23}/tests/test_exceptions.py +0 -0
  270. {reader-3.21 → reader-3.23}/tests/test_hash_utils.py +0 -0
  271. {reader-3.21 → reader-3.23}/tests/test_parser.py +0 -0
  272. {reader-3.21 → reader-3.23}/tests/test_reader_counts.py +0 -0
  273. {reader-3.21 → reader-3.23}/tests/test_reader_integration.py +0 -0
  274. {reader-3.21 → reader-3.23}/tests/test_storage.py +0 -0
  275. {reader-3.21 → reader-3.23}/tests/test_tags.py +0 -0
  276. {reader-3.21 → reader-3.23}/tests/test_types.py +0 -0
  277. {reader-3.21 → reader-3.23}/tests/utils.py +0 -0
  278. {reader-3.21 → reader-3.23}/tox.ini +0 -0
@@ -6,6 +6,75 @@ Changelog
6
6
  :no-index:
7
7
 
8
8
 
9
+ Version 3.23
10
+ ------------
11
+
12
+ Released 2026-04-24
13
+
14
+ * Add support for :ref:`opml`. (:issue:`165`)
15
+
16
+ * Add support for import / export in the web app; :ref:`screenshots <screenshots-import>`.
17
+
18
+ * Deprecate the :func:`~reader.utils.archive_entries` ``feed_user_title`` argument.
19
+ * Fix :meth:`get_feeds(feed='') <Reader.get_feeds>` and
20
+ :meth:`get_entries(entry=('', '')) <Reader.get_entries>`
21
+ returning wrong entries.
22
+ (:issue:`392`)
23
+
24
+
25
+ Version 3.22
26
+ ------------
27
+
28
+ Released 2026-04-01
29
+
30
+ * :func:`make_reader` plugin loading improvements. (:issue:`395`)
31
+
32
+ * ``plugins`` now supports arbitrary import paths.
33
+ * :ref:`Built-in plugins <built-in plugins>` now use the
34
+ ``.<plugin>`` naming scheme, instead of ``reader.<plugin>``;
35
+ support for the latter is deprecated and will be removed in version 4.0.
36
+
37
+ * :meth:`~Reader.get_feeds`, :meth:`~Reader.get_entries`, and
38
+ :meth:`~Reader.search_entries` now raise exceptions
39
+ for invalid ``query`` and ``starting_after`` eagerly,
40
+ before the iterable is consumed.
41
+ Thanks to `Omprakash Choudhary`_ for the PR.
42
+ (:issue:`388`)
43
+
44
+ * :doc:`app` improvements.
45
+
46
+ * Finish the web app re-design MVP,
47
+ including:
48
+ add / list / filter / delete feeds,
49
+ list / filter articles,
50
+ article view,
51
+ mark articles as read / (un)important,
52
+ article view,
53
+ custom feed titles,
54
+ dark mode,
55
+ CSRF protection,
56
+ vendored CSS / JS dependencies.
57
+
58
+ * Make the new web app the default (entry point ``reader._app.wsgi:app``);
59
+ the :ref:`legacy web app <legacy web app>` remains available
60
+ at ``reader._app.legacy.wsgi:app``.
61
+
62
+ * Update both web apps to use the new configuration infrastructure (see below).
63
+
64
+ * :doc:`CLI <cli>` and :doc:`config file <config>` breaking changes. (:issue:`395`)
65
+
66
+ * The config file format changed from YAML to TOML,
67
+ and follows the shape of the CLI.
68
+ * The ``reader serve`` command was renamed to ``reader web``.
69
+ * Plugin loading environment variables changed:
70
+ ``READER_PLUGIN`` -> ``READER_PLUGINS``,
71
+ ``READER_CLI_PLUGIN`` -> ``READER_CLI_PLUGINS``,
72
+ ``READER_APP_PLUGIN`` -> ``READER_WEB_PLUGINS``.
73
+
74
+
75
+ .. _Omprakash Choudhary: https://github.com/omseervi098
76
+
77
+
9
78
  Version 3.21
10
79
  ------------
11
80
 
@@ -251,7 +320,7 @@ Released 2024-07-23
251
320
  * Fix bug in the :mod:`~reader.plugins.entry_dedupe` plugin causing entries
252
321
  to flip-flop if there were multiple *new* duplicates of the same issue
253
322
  (on the first update, one entry remains, on the second update, the other);
254
- related to the bug fixed in `version 3.2 <Version 3.2_>`_.
323
+ related to the bug fixed in `version 3.2 <Version 3.2_>`__.
255
324
  (:issue:`340`)
256
325
 
257
326
  * :mod:`~reader._plugins.enclosure_tags` plugin improvements. (:issue:`344`)
@@ -294,7 +363,7 @@ Released 2024-06-19
294
363
  * Update an entry whenever its :attr:`~Entry.updated` changes
295
364
  (don't require it to be newer).
296
365
 
297
- * Fix bug introduced in `version 3.12 <Version 3.12_>`_ causing an assertion error
366
+ * Fix bug introduced in `version 3.12 <Version 3.12_>`__ causing an assertion error
298
367
  when there are multiple entries with the same id in the same feed,
299
368
  or when parallel :meth:`~Reader.update_feeds` calls add the same entry.
300
369
  The fix restores the pre-3.12 first-entry-wins / last-write-wins behavior.
@@ -445,7 +514,7 @@ Released 2023-08-20
445
514
 
446
515
  * Add werkzeug dependency,
447
516
  instead of vendoring selected :mod:`werkzeug.http` utilities.
448
- * Rework lazy imports introduced in `version 3.3 <Version 3.3_>`_.
517
+ * Rework lazy imports introduced in `version 3.3 <Version 3.3_>`__.
449
518
  (:issue:`316`)
450
519
  * Make :mod:`reader._parser` a package, and move parsing-related modules into it.
451
520
  (:issue:`316`)
@@ -506,7 +575,7 @@ Released 2023-03-19
506
575
  so one can express "explicitly unimportant" (*don't care*)
507
576
  by setting it to :const:`False`.
508
577
  This replaces the semantics for *don't care* introduced
509
- in `version 2.2 <Version 2.2_>`_.
578
+ in `version 2.2 <Version 2.2_>`__.
510
579
  (:issue:`254`)
511
580
 
512
581
  .. warning::
@@ -1,7 +1,9 @@
1
1
  recursive-include src/reader/_app/templates *
2
2
  recursive-include src/reader/_app/static *
3
- recursive-include src/reader/_app/v2 *
4
3
  recursive-include src/reader/_plugins/templates *
4
+ recursive-include src/reader/_app/legacy/templates *
5
+ recursive-include src/reader/_app/legacy/static *
6
+ recursive-include src/reader/_plugins/legacy/templates *
5
7
  recursive-include src/reader *.pyi
6
8
  recursive-include src/reader py.typed
7
9
  include run.sh CHANGES.rst LICENSE tox.ini
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reader
3
- Version: 3.21
3
+ Version: 3.23
4
4
  Summary: A Python feed reader library.
5
5
  Author: lemon24
6
6
  License: BSD-3-Clause
@@ -45,12 +45,12 @@ Provides-Extra: all
45
45
  Requires-Dist: reader[app,cli,unstable-plugins]; extra == "all"
46
46
  Provides-Extra: cli
47
47
  Requires-Dist: click>=7; extra == "cli"
48
- Requires-Dist: PyYAML; extra == "cli"
49
48
  Provides-Extra: app
50
49
  Requires-Dist: flask>=0.10; extra == "app"
51
50
  Requires-Dist: humanize>=4; extra == "app"
52
51
  Requires-Dist: PyYAML; extra == "app"
53
52
  Requires-Dist: WTForms; extra == "app"
53
+ Requires-Dist: Flask-WTF; extra == "app"
54
54
  Requires-Dist: jinja2-fragments; extra == "app"
55
55
  Provides-Extra: unstable-plugins
56
56
  Requires-Dist: requests; extra == "unstable-plugins"
@@ -68,7 +68,9 @@ Dynamic: license-file
68
68
 
69
69
  It is designed to allow writing feed reader applications
70
70
  without any business code,
71
- and without depending on a particular framework.
71
+ and without depending on a particular framework;
72
+ in MVC terms,
73
+ you'd probably call it a fat model.
72
74
 
73
75
  .. end-intro
74
76
 
@@ -111,6 +113,7 @@ and without depending on a particular framework.
111
113
  * filter feeds and articles
112
114
  * full-text search articles
113
115
  * get statistics on feed and user activity
116
+ * import / export feeds as OPML
114
117
  * write plugins to extend its functionality
115
118
  * skip all the low level stuff and focus on what makes your feed reader different
116
119
 
@@ -122,19 +125,13 @@ and without depending on a particular framework.
122
125
 
123
126
  What *reader* doesn't do:
124
127
 
125
- * provide an UI
126
- * provide a REST API (yet)
127
128
  * depend on a web framework
128
- * have an opinion of how/where you use it
129
+ * have an opinion of how or where you use it
129
130
 
130
- The following exist, but are optional (and frankly, a bit unpolished):
131
+ In addition, *reader* provides:
131
132
 
132
- * a minimal web interface
133
-
134
- * that works even with text-only browsers
135
- * with automatic tag fixing for podcasts (MP3 enclosures)
136
-
137
- * a command-line interface
133
+ * an optional web application based on Flask, Bootstrap, and htmx
134
+ * an optional command-line interface
138
135
 
139
136
  .. end-features
140
137
 
@@ -4,7 +4,9 @@
4
4
 
5
5
  It is designed to allow writing feed reader applications
6
6
  without any business code,
7
- and without depending on a particular framework.
7
+ and without depending on a particular framework;
8
+ in MVC terms,
9
+ you'd probably call it a fat model.
8
10
 
9
11
  .. end-intro
10
12
 
@@ -47,6 +49,7 @@ and without depending on a particular framework.
47
49
  * filter feeds and articles
48
50
  * full-text search articles
49
51
  * get statistics on feed and user activity
52
+ * import / export feeds as OPML
50
53
  * write plugins to extend its functionality
51
54
  * skip all the low level stuff and focus on what makes your feed reader different
52
55
 
@@ -58,19 +61,13 @@ and without depending on a particular framework.
58
61
 
59
62
  What *reader* doesn't do:
60
63
 
61
- * provide an UI
62
- * provide a REST API (yet)
63
64
  * depend on a web framework
64
- * have an opinion of how/where you use it
65
+ * have an opinion of how or where you use it
65
66
 
66
- The following exist, but are optional (and frankly, a bit unpolished):
67
+ In addition, *reader* provides:
67
68
 
68
- * a minimal web interface
69
-
70
- * that works even with text-only browsers
71
- * with automatic tag fixing for podcasts (MP3 enclosures)
72
-
73
- * a command-line interface
69
+ * an optional web application based on Flask, Bootstrap, and htmx
70
+ * an optional command-line interface
74
71
 
75
72
  .. end-features
76
73
 
@@ -67,6 +67,17 @@ Data objects
67
67
  .. autoclass:: EntryUpdateStatus
68
68
  :members:
69
69
 
70
+ .. autoclass:: FeedToImport
71
+ :members:
72
+ :undoc-members:
73
+
74
+ .. autoclass:: FeedImportResult
75
+ :members:
76
+
77
+ .. autoclass:: FeedExport
78
+ :members:
79
+
80
+
70
81
 
71
82
  Exceptions
72
83
  ----------
@@ -155,6 +166,9 @@ Exceptions
155
166
  .. autoexception:: PluginInitError
156
167
  :show-inheritance:
157
168
 
169
+ .. autoexception:: FeedImportError
170
+ :show-inheritance:
171
+
158
172
 
159
173
  .. autoexception:: ReaderWarning
160
174
  :show-inheritance:
@@ -214,3 +228,10 @@ Utilities
214
228
  ---------
215
229
 
216
230
  .. autofunction:: reader.utils.archive_entries
231
+
232
+
233
+
234
+ ``reader.opml``
235
+ ---------------
236
+
237
+ .. automodule:: reader.opml
@@ -0,0 +1,153 @@
1
+
2
+ Web application
3
+ ===============
4
+
5
+ *reader* comes with a web application
6
+ based on `Flask`_, `Bootstrap`_, and `htmx`_,
7
+ intended to work across all browsers,
8
+ including mobile and light-weight / text-only ones.
9
+
10
+ .. _Flask: https://flask.palletsprojects.com/
11
+ .. _Bootstrap: https://getbootstrap.com/
12
+ .. _htmx: https://htmx.org/
13
+
14
+ Features, as of *reader* version |version|:
15
+
16
+ * add / list / filter / delete feeds
17
+ * list / filter articles
18
+ * mark articles as read / (un)important
19
+ * article view
20
+ * custom feed titles
21
+ * dark mode
22
+
23
+ More features already implemented in the library
24
+ and the :ref:`legacy web app <legacy web app>`
25
+ should be added in the next releases;
26
+ see the :ref:`roadmap <app roadmap>` for details.
27
+
28
+
29
+
30
+ .. _app screenshots:
31
+
32
+ Screenshots
33
+ -----------
34
+
35
+ .. figure:: screenshots/entries.png
36
+ :width: 240px
37
+
38
+ main page (dark mode)
39
+
40
+ .. figure:: screenshots/entries-filters-light.png
41
+ :width: 240px
42
+
43
+ main page – more filters (light mode)
44
+
45
+ .. figure:: screenshots/feed.png
46
+ :width: 240px
47
+
48
+ feed page (dark mode)
49
+
50
+ .. figure:: screenshots/feeds.png
51
+ :width: 240px
52
+
53
+ feeds page (dark mode)
54
+
55
+ .. figure:: screenshots/entry.png
56
+ :width: 240px
57
+
58
+ article view (dark mode)
59
+
60
+ .. figure:: screenshots/entry-image-light.png
61
+ :width: 240px
62
+
63
+ article view (light mode)
64
+
65
+ .. _screenshots-import:
66
+
67
+ .. figure:: screenshots/import-select.png
68
+ :width: 240px
69
+
70
+ import feeds (select)
71
+
72
+ .. figure:: screenshots/import-result.png
73
+ :width: 240px
74
+
75
+ import feeds (result)
76
+
77
+
78
+
79
+ Serving the web app
80
+ -------------------
81
+
82
+ .. note::
83
+
84
+ The web application is optional, use the ``app`` extra to install
85
+ its :ref:`dependencies <Optional dependencies>`.
86
+
87
+
88
+ *reader* exposes a standard WSGI application as ``reader._app.wsgi:app``.
89
+ See the `Flask documentation`_ for more details on how to deploy it.
90
+ The path to the reader database can be configured through the
91
+ :doc:`config file <config>`
92
+ or the ``READER_DB`` environment variable.
93
+
94
+ .. warning::
95
+
96
+ The web application has no authentication / authorization whatsoever;
97
+ it is expected a server / middleware will provide that.
98
+
99
+
100
+ An example uWSGI configuration file (probably not idiomatic, from `here`_)::
101
+
102
+ [uwsgi]
103
+ socket = /apps/reader/uwsgi/sock
104
+ manage-script-name = true
105
+ mount = /reader=reader._app.wsgi:app
106
+ plugin = python3
107
+ virtualenv = /apps/reader/
108
+ env = READER_CONFIG=/apps/reader/reader.toml
109
+
110
+
111
+ You can also run the web application with the ``web`` command.
112
+ ``web`` uses `Werkzeug's development server`_,
113
+ so it probably won't scale well past a single user.
114
+
115
+ .. note::
116
+
117
+ For privacy reasons,
118
+ you may want to configure your web server to not send a ``Referer`` header
119
+ (by setting ``Referrer-Policy`` header to ``same-origin``
120
+ for all responses; `nginx example`_).
121
+ The ``web`` command does it by default.
122
+
123
+
124
+ If running on a personal computer, you can use cron to run it at boot::
125
+
126
+ @reboot sleep 60; reader web -p 8080 2>&1 ) >>"/tmp/$LOGNAME.reader.web.run.boot.log"
127
+
128
+
129
+ .. _here: https://github.com/lemon24/owncloud/blob/8009f227ef60ebaab621e7bb3363ec9071d8a2e8/reader.yaml#L103-L116
130
+ .. _nginx example: https://github.com/lemon24/owncloud/blob/8009f227ef60ebaab621e7bb3363ec9071d8a2e8/secure.conf#L23
131
+ .. _Flask documentation: https://flask.palletsprojects.com/en/stable/deploying/
132
+ .. _Werkzeug's development server: https://werkzeug.palletsprojects.com/en/stable/serving/#werkzeug.serving.run_simple
133
+
134
+
135
+
136
+ .. _legacy web app:
137
+
138
+ Legacy web app
139
+ --------------
140
+
141
+ Before the current web application,
142
+ there was another one,
143
+ serving as test bed for new features.
144
+ It is usable, if a bit unpolished,
145
+ but was never meant to be a long-term thing,
146
+ hence the new app;
147
+ you can find screenshots
148
+ `here <https://github.com/lemon24/reader/blob/3.21/docs/app.rst#screenshots-legacy>`_.
149
+
150
+ The WSGI entry point is ``reader._app.legacy.wsgi:app``.
151
+
152
+ The legacy web app should remain available
153
+ until the new app reaches feature parity.
@@ -2,18 +2,12 @@
2
2
  Command-line interface
3
3
  ======================
4
4
 
5
- *reader* comes with a command-line interface
6
- that exposes basic management functionality.
7
-
8
-
9
- .. warning::
10
-
11
- The CLI is is not fully stable,
12
- see the :ref:`roadmap <cli roadmap>` for details.
5
+ *reader* comes with a CLI
6
+ that exposes management functionality.
13
7
 
14
8
  .. note::
15
9
 
16
- The command-line interface is optional, use the ``cli`` extra to install
10
+ The CLI is optional, use the ``cli`` extra to install
17
11
  its :ref:`dependencies <Optional dependencies>`.
18
12
 
19
13
  Most commands need a database to work. The following are equivalent:
@@ -42,7 +36,7 @@ Serve the web application locally (at http://localhost:8080/):
42
36
 
43
37
  .. code-block:: bash
44
38
 
45
- python -m reader serve
39
+ python -m reader web
46
40
 
47
41
 
48
42
  .. _cli-update:
@@ -6,7 +6,6 @@ import unittest.mock
6
6
  import packaging.version
7
7
  from setuptools.config.pyprojecttoml import read_configuration
8
8
 
9
-
10
9
  sys.path.insert(0, os.path.abspath('../src'))
11
10
 
12
11
  # mock some things "by hand", so we can import reader below without any dependencies
@@ -17,6 +16,7 @@ for name in [
17
16
  'flask.signals',
18
17
  'werkzeug',
19
18
  'werkzeug.datastructures',
19
+ 'werkzeug.exceptions',
20
20
  'werkzeug.http',
21
21
  'yaml',
22
22
  ]:
@@ -24,7 +24,6 @@ for name in [
24
24
 
25
25
  import reader
26
26
 
27
-
28
27
  extensions = [
29
28
  'sphinx_rtd_theme',
30
29
  'sphinx.ext.autodoc',
@@ -45,6 +44,7 @@ autodoc_mock_imports = [
45
44
  'bs4',
46
45
  'mutagen',
47
46
  'flask',
47
+ 'flask_wtf',
48
48
  'werkzeug',
49
49
  'humanize',
50
50
  'markupsafe',
@@ -0,0 +1,30 @@
1
+
2
+ Configuration
3
+ =============
4
+
5
+ Both the :doc:`CLI <cli>` and the :doc:`web application <app>` can
6
+ be configured from a TOML file.
7
+
8
+ .. note::
9
+
10
+ Configuration file loading dependencies get installed automatically when
11
+ installing the CLI or the web application
12
+ :ref:`extras <Optional dependencies>`.
13
+
14
+
15
+ The configuration file path can be specified either through the ``--config``
16
+ CLI option or through the ``READER_CONFIG`` environment variable
17
+ (also usable with the web application).
18
+
19
+ The configuration file matches the shape of the :doc:`CLI <cli>`
20
+ (the ``reader`` section has options for the ``reader`` command,
21
+ the ``reader.update`` section has options for the ``reader update`` command, etc.).
22
+ In general, option names match those in the CLI
23
+ (``--feed-root`` -> ``feed_root``);
24
+ options that can be passed multiple times in the CLI
25
+ are pluralized in the config
26
+ (``--cli-plugin`` -> ``cli_plugins``).
27
+
28
+ Example:
29
+
30
+ .. literalinclude:: ../examples/config.toml
@@ -32,7 +32,6 @@ in my spare time,
32
32
  but I will prioritize supporting :doc:`contributors <contributing>`
33
33
  (discussions, reviews and so on).
34
34
 
35
- * OPML support, :issue:`165`
36
35
  * :ref:`deleting entries <deleting entries>`
37
36
 
38
37
  * archiving important entries of deleted feeds, :issue:`290`
@@ -59,22 +58,22 @@ but I will prioritize supporting :doc:`contributors <contributing>`
59
58
  * resource tags
60
59
 
61
60
  * :ref:`searchable tag values <searchable tags>`, e.g. for comments
62
- * :ref:`unification with entry.read/important <entry flag unification>`
63
61
  * optimistic locking, :issue:`308`
64
62
  * filter tags by prefix, :issue:`309`
65
63
 
66
-
67
- * HTTP compliance, likely as plugins
64
+ * HTTP behavior / compliance, likely as plugins, :issue:`377`
68
65
 
69
66
  * 301 Moved Permanently, :issue:`246`
70
67
  * 410 Gone, :issue:`246`
71
- * 429 Too Many Requests, :issue:`307`
68
+ * slow down on throttling / max-age, :issue:`378`
69
+ * match the rhythm of the feed, :issue:`382`
72
70
 
73
71
  * add more fields to data objects
74
72
 
73
+ * .links attribute, :issue:`320`
74
+ * .authors attribute, :issue:`391`
75
75
  * extra data, as an escape hatch, :issue:`277`
76
76
 
77
- * :ref:`multiple storage implementations <multiple storage implementations>`
78
77
  * :ref:`batch get methods <batch get methods>`
79
78
  * :doc:`internal` stabilization
80
79
  * arbitrary website scraping, :issue:`222`
@@ -93,15 +92,16 @@ but I will prioritize supporting :doc:`contributors <contributing>`
93
92
  Command-line interface
94
93
  ~~~~~~~~~~~~~~~~~~~~~~
95
94
 
96
- The :doc:`cli` is more or less stable,\ [*]_
97
- although both the output and config loading need more polish
98
- and additional tests.
95
+ The :doc:`cli` is more or less stable,
96
+ although it could use additional tests.
99
97
 
100
- A full-blown terminal feed reader is *not* in scope,
98
+ A terminal feed reader is *not* in scope,
101
99
  since I don't need one,
102
- but I'm not opposed to the idea.
100
+ but I'm not opposed to the idea;
101
+ note that the web app should already
102
+ work with text-based browsers such as `Lynx`_.
103
103
 
104
- .. [*] With the exception of ``serve``, which is provided by the web app.
104
+ .. _Lynx: https://en.wikipedia.org/wiki/Lynx_(web_browser)
105
105
 
106
106
 
107
107
  .. _app roadmap:
@@ -109,27 +109,23 @@ but I'm not opposed to the idea.
109
109
  Web application
110
110
  ~~~~~~~~~~~~~~~
111
111
 
112
- The :doc:`app` is "unsupported",
113
- in that it's not all that polished,
114
- and I don't have time to do major improvments.
115
- But, I am using it daily,
116
- and it will keep working until a better one exists.
117
-
118
- Long term, I'd like to:
119
-
120
- * re-design it from scratch to improve usability
121
- (see :issue:`318` for a wishlist)
122
- * switch to `htmx`_ instead of using a home-grown solution
123
- * spin it off into a separate package/project
112
+ The new :doc:`app` is under active development.
113
+ I am working on adding features
114
+ already implemented in the library
115
+ and the :ref:`legacy web app <legacy web app>`,
116
+ and possibly on a hosted version.
124
117
 
125
- **2025 update**:
126
- I've started working on a re-design based on `htmx`_ and `Bootstrap`_;
127
- some :ref:`screenshots <app screenshots>`.
128
- The new app will be available in parallel with the old one
129
- until it reaches feature parity.
118
+ Like with the library,
119
+ the goal is to keep it simple to use
120
+ and sustainable to maintain long-term;
121
+ the decision to use `Flask`_, `Bootstrap`_, and `htmx`_ is part of this
122
+ (if they were around for ~15 years,
123
+ they should be around for 15 more).
130
124
 
131
- .. _htmx: https://htmx.org/
125
+ .. _Flask: https://flask.palletsprojects.com/
132
126
  .. _Bootstrap: https://getbootstrap.com/
127
+ .. _htmx: https://htmx.org/
128
+
133
129
 
134
130
 
135
131
  .. _compat:
@@ -677,7 +673,10 @@ Why I want to postpone batch update/set methods:
677
673
  tl:dr: Performance is likely a non-issue with SQLite,
678
674
  convenience can be added on top as a plugin.
679
675
 
680
- (2025) Why it may be worth adding batch interfaces anyway (even if underneath the storage implementation doesn't actually batch) – it allows for future optimization: https://blog.glyph.im/2022/12/potato-programming.html
676
+ (2025 update) Why it may be worth adding batch interfaces anyway
677
+ (even if underneath the storage implementation doesn't actually batch)
678
+ – it allows for future optimization:
679
+ https://blog.glyph.im/2022/12/potato-programming.html
681
680
 
682
681
  See the 2.12 reader._app.ResourceTags class for an idea of how to
683
682
  represent a bunch of tags in a reserved-name-scheme-agnostic way
@@ -771,10 +770,3 @@ REST API
771
770
 
772
771
  Some early thoughts: :issue:`192#issuecomment-700773138`
773
772
  (closed with `wontfix`, for now).
774
-
775
-
776
- Web application
777
- ~~~~~~~~~~~~~~~
778
-
779
- .. toctree::
780
- dev-app