reader 3.18__tar.gz → 3.19__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 (240) hide show
  1. {reader-3.18 → reader-3.19}/CHANGES.rst +22 -0
  2. {reader-3.18/src/reader.egg-info → reader-3.19}/PKG-INFO +8 -37
  3. {reader-3.18 → reader-3.19}/docs/conf.py +0 -14
  4. {reader-3.18 → reader-3.19}/docs/dev.rst +16 -10
  5. {reader-3.18 → reader-3.19}/docs/install.rst +3 -1
  6. {reader-3.18 → reader-3.19}/pyproject.toml +27 -28
  7. reader-3.19/run.sh +125 -0
  8. {reader-3.18 → reader-3.19}/scripts/release.py +9 -19
  9. {reader-3.18 → reader-3.19}/src/reader/__init__.py +1 -1
  10. {reader-3.18 → reader-3.19}/src/reader/_plugins/enclosure_tags.py +4 -2
  11. {reader-3.18 → reader-3.19}/src/reader/_storage/_sqlite_utils.py +4 -2
  12. {reader-3.18 → reader-3.19}/src/reader/types.py +1 -1
  13. {reader-3.18 → reader-3.19/src/reader.egg-info}/PKG-INFO +8 -37
  14. {reader-3.18 → reader-3.19}/src/reader.egg-info/SOURCES.txt +0 -6
  15. reader-3.19/src/reader.egg-info/requires.txt +27 -0
  16. {reader-3.18 → reader-3.19}/tests/conftest.py +1 -0
  17. {reader-3.18 → reader-3.19}/tests/test_parser.py +10 -29
  18. {reader-3.18 → reader-3.19}/tests/test_plugins_cli_status.py +1 -1
  19. {reader-3.18 → reader-3.19}/tests/utils.py +33 -43
  20. reader-3.19/tox.ini +40 -0
  21. reader-3.18/docs/.DS_Store +0 -0
  22. reader-3.18/docs/Makefile +0 -20
  23. reader-3.18/docs/make.bat +0 -36
  24. reader-3.18/run.sh +0 -188
  25. reader-3.18/scripts/.coverage +0 -0
  26. reader-3.18/scripts/generate_import_all.py +0 -15
  27. reader-3.18/src/reader.egg-info/requires.txt +0 -67
  28. reader-3.18/tests/test_test_utils.py +0 -36
  29. reader-3.18/tox.ini +0 -40
  30. {reader-3.18 → reader-3.19}/LICENSE +0 -0
  31. {reader-3.18 → reader-3.19}/MANIFEST.in +0 -0
  32. {reader-3.18 → reader-3.19}/README.rst +0 -0
  33. {reader-3.18 → reader-3.19}/docs/api.rst +0 -0
  34. {reader-3.18 → reader-3.19}/docs/app.rst +0 -0
  35. {reader-3.18 → reader-3.19}/docs/changelog.rst +0 -0
  36. {reader-3.18 → reader-3.19}/docs/cli.rst +0 -0
  37. {reader-3.18 → reader-3.19}/docs/config.rst +0 -0
  38. {reader-3.18 → reader-3.19}/docs/contributing.rst +0 -0
  39. {reader-3.18 → reader-3.19}/docs/dev-app.rst +0 -0
  40. {reader-3.18 → reader-3.19}/docs/guide.rst +0 -0
  41. {reader-3.18 → reader-3.19}/docs/images/redesign-01.png +0 -0
  42. {reader-3.18 → reader-3.19}/docs/index.rst +0 -0
  43. {reader-3.18 → reader-3.19}/docs/internal.rst +0 -0
  44. {reader-3.18 → reader-3.19}/docs/plugins.rst +0 -0
  45. {reader-3.18 → reader-3.19}/docs/screenshots/dillo.png +0 -0
  46. {reader-3.18 → reader-3.19}/docs/screenshots/entries-feed.png +0 -0
  47. {reader-3.18 → reader-3.19}/docs/screenshots/entries-v2-dark.png +0 -0
  48. {reader-3.18 → reader-3.19}/docs/screenshots/entries-v2-filters-light.png +0 -0
  49. {reader-3.18 → reader-3.19}/docs/screenshots/entries.png +0 -0
  50. {reader-3.18 → reader-3.19}/docs/screenshots/entry-one.png +0 -0
  51. {reader-3.18 → reader-3.19}/docs/screenshots/entry-two.png +0 -0
  52. {reader-3.18 → reader-3.19}/docs/screenshots/feeds.png +0 -0
  53. {reader-3.18 → reader-3.19}/docs/screenshots/lynx.png +0 -0
  54. {reader-3.18 → reader-3.19}/docs/screenshots/search.png +0 -0
  55. {reader-3.18 → reader-3.19}/docs/tutorial.rst +0 -0
  56. {reader-3.18 → reader-3.19}/docs/why.rst +0 -0
  57. {reader-3.18 → reader-3.19}/examples/config.yaml +0 -0
  58. {reader-3.18 → reader-3.19}/examples/custom_headers.py +0 -0
  59. {reader-3.18 → reader-3.19}/examples/feed_slugs.py +0 -0
  60. {reader-3.18 → reader-3.19}/examples/parser_only.py +0 -0
  61. {reader-3.18 → reader-3.19}/examples/podcast.py +0 -0
  62. {reader-3.18 → reader-3.19}/examples/terminal.py +0 -0
  63. {reader-3.18 → reader-3.19}/scripts/backup.sh +0 -0
  64. {reader-3.18 → reader-3.19}/scripts/bench.py +0 -0
  65. {reader-3.18 → reader-3.19}/scripts/debug_storage_stats.py +0 -0
  66. {reader-3.18 → reader-3.19}/scripts/jscontrols.html +0 -0
  67. {reader-3.18 → reader-3.19}/scripts/jscontrols.py +0 -0
  68. {reader-3.18 → reader-3.19}/scripts/lines.sh +0 -0
  69. {reader-3.18 → reader-3.19}/setup.cfg +0 -0
  70. {reader-3.18 → reader-3.19}/src/reader/__main__.py +0 -0
  71. {reader-3.18 → reader-3.19}/src/reader/_app/__init__.py +0 -0
  72. {reader-3.18 → reader-3.19}/src/reader/_app/api_thing.py +0 -0
  73. {reader-3.18 → reader-3.19}/src/reader/_app/cli.py +0 -0
  74. {reader-3.18 → reader-3.19}/src/reader/_app/static/controls.js +0 -0
  75. {reader-3.18 → reader-3.19}/src/reader/_app/static/style.css +0 -0
  76. {reader-3.18 → reader-3.19}/src/reader/_app/templates/add_entry.html +0 -0
  77. {reader-3.18 → reader-3.19}/src/reader/_app/templates/entries.html +0 -0
  78. {reader-3.18 → reader-3.19}/src/reader/_app/templates/entry.html +0 -0
  79. {reader-3.18 → reader-3.19}/src/reader/_app/templates/feeds.html +0 -0
  80. {reader-3.18 → reader-3.19}/src/reader/_app/templates/layout.html +0 -0
  81. {reader-3.18 → reader-3.19}/src/reader/_app/templates/macros.html +0 -0
  82. {reader-3.18 → reader-3.19}/src/reader/_app/templates/metadata.html +0 -0
  83. {reader-3.18 → reader-3.19}/src/reader/_app/templates/tags.html +0 -0
  84. {reader-3.18 → reader-3.19}/src/reader/_app/v2/__init__.py +0 -0
  85. {reader-3.18 → reader-3.19}/src/reader/_app/v2/forms.py +0 -0
  86. {reader-3.18 → reader-3.19}/src/reader/_app/v2/static/style.css +0 -0
  87. {reader-3.18 → reader-3.19}/src/reader/_app/v2/static/theme.js +0 -0
  88. {reader-3.18 → reader-3.19}/src/reader/_app/v2/templates/v2/entries.html +0 -0
  89. {reader-3.18 → reader-3.19}/src/reader/_app/v2/templates/v2/layout.html +0 -0
  90. {reader-3.18 → reader-3.19}/src/reader/_app/v2/templates/v2/macros.html +0 -0
  91. {reader-3.18 → reader-3.19}/src/reader/_app/wsgi.py +0 -0
  92. {reader-3.18 → reader-3.19}/src/reader/_cli.py +0 -0
  93. {reader-3.18 → reader-3.19}/src/reader/_config.py +0 -0
  94. {reader-3.18 → reader-3.19}/src/reader/_hash_utils.py +0 -0
  95. {reader-3.18 → reader-3.19}/src/reader/_parser/__init__.py +0 -0
  96. {reader-3.18 → reader-3.19}/src/reader/_parser/_http_utils.py +0 -0
  97. {reader-3.18 → reader-3.19}/src/reader/_parser/_lazy.py +0 -0
  98. {reader-3.18 → reader-3.19}/src/reader/_parser/_url_utils.py +0 -0
  99. {reader-3.18 → reader-3.19}/src/reader/_parser/feedparser.py +0 -0
  100. {reader-3.18 → reader-3.19}/src/reader/_parser/file.py +0 -0
  101. {reader-3.18 → reader-3.19}/src/reader/_parser/http.py +0 -0
  102. {reader-3.18 → reader-3.19}/src/reader/_parser/jsonfeed.py +0 -0
  103. {reader-3.18 → reader-3.19}/src/reader/_parser/requests/__init__.py +0 -0
  104. {reader-3.18 → reader-3.19}/src/reader/_parser/requests/_lazy.py +0 -0
  105. {reader-3.18 → reader-3.19}/src/reader/_plugins/__init__.py +0 -0
  106. {reader-3.18 → reader-3.19}/src/reader/_plugins/cli_status.py +0 -0
  107. {reader-3.18 → reader-3.19}/src/reader/_plugins/preview_feed_list.py +0 -0
  108. {reader-3.18 → reader-3.19}/src/reader/_plugins/share.py +0 -0
  109. {reader-3.18 → reader-3.19}/src/reader/_plugins/sqlite_releases.py +0 -0
  110. {reader-3.18 → reader-3.19}/src/reader/_plugins/templates/preview_feed_list.html +0 -0
  111. {reader-3.18 → reader-3.19}/src/reader/_plugins/timer.py +0 -0
  112. {reader-3.18 → reader-3.19}/src/reader/_storage/__init__.py +0 -0
  113. {reader-3.18 → reader-3.19}/src/reader/_storage/_base.py +0 -0
  114. {reader-3.18 → reader-3.19}/src/reader/_storage/_changes.py +0 -0
  115. {reader-3.18 → reader-3.19}/src/reader/_storage/_entries.py +0 -0
  116. {reader-3.18 → reader-3.19}/src/reader/_storage/_feeds.py +0 -0
  117. {reader-3.18 → reader-3.19}/src/reader/_storage/_html_utils.py +0 -0
  118. {reader-3.18 → reader-3.19}/src/reader/_storage/_schema.py +0 -0
  119. {reader-3.18 → reader-3.19}/src/reader/_storage/_search.py +0 -0
  120. {reader-3.18 → reader-3.19}/src/reader/_storage/_sql_utils.py +0 -0
  121. {reader-3.18 → reader-3.19}/src/reader/_storage/_tags.py +0 -0
  122. {reader-3.18 → reader-3.19}/src/reader/_types.py +0 -0
  123. {reader-3.18 → reader-3.19}/src/reader/_update.py +0 -0
  124. {reader-3.18 → reader-3.19}/src/reader/_utils.py +0 -0
  125. {reader-3.18 → reader-3.19}/src/reader/_vendor/__init__.py +0 -0
  126. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/__init__.py +0 -0
  127. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/api.py +0 -0
  128. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/__init__.py +0 -0
  129. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/asctime.py +0 -0
  130. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/greek.py +0 -0
  131. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/hungarian.py +0 -0
  132. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/iso8601.py +0 -0
  133. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/korean.py +0 -0
  134. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/perforce.py +0 -0
  135. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/rfc822.py +0 -0
  136. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/datetimes/w3dtf.py +0 -0
  137. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/encodings.py +0 -0
  138. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/exceptions.py +0 -0
  139. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/html.py +0 -0
  140. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/http.py +0 -0
  141. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/mixin.py +0 -0
  142. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/__init__.py +0 -0
  143. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/_base.py +0 -0
  144. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/admin.py +0 -0
  145. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/cc.py +0 -0
  146. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/dc.py +0 -0
  147. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/georss.py +0 -0
  148. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/itunes.py +0 -0
  149. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/mediarss.py +0 -0
  150. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/namespaces/psc.py +0 -0
  151. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/parsers/__init__.py +0 -0
  152. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/parsers/json.py +0 -0
  153. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/parsers/loose.py +0 -0
  154. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/parsers/strict.py +0 -0
  155. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/py.typed +0 -0
  156. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/sanitizer.py +0 -0
  157. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/sgml.py +0 -0
  158. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/urls.py +0 -0
  159. {reader-3.18 → reader-3.19}/src/reader/_vendor/feedparser/util.py +0 -0
  160. {reader-3.18 → reader-3.19}/src/reader/core.py +0 -0
  161. {reader-3.18 → reader-3.19}/src/reader/exceptions.py +0 -0
  162. {reader-3.18 → reader-3.19}/src/reader/plugins/__init__.py +0 -0
  163. {reader-3.18 → reader-3.19}/src/reader/plugins/enclosure_dedupe.py +0 -0
  164. {reader-3.18 → reader-3.19}/src/reader/plugins/entry_dedupe.py +0 -0
  165. {reader-3.18 → reader-3.19}/src/reader/plugins/mark_as_read.py +0 -0
  166. {reader-3.18 → reader-3.19}/src/reader/plugins/readtime.py +0 -0
  167. {reader-3.18 → reader-3.19}/src/reader/plugins/ua_fallback.py +0 -0
  168. {reader-3.18 → reader-3.19}/src/reader/py.typed +0 -0
  169. {reader-3.18 → reader-3.19}/src/reader/utils.py +0 -0
  170. {reader-3.18 → reader-3.19}/src/reader.egg-info/dependency_links.txt +0 -0
  171. {reader-3.18 → reader-3.19}/src/reader.egg-info/top_level.txt +0 -0
  172. {reader-3.18 → reader-3.19}/tests/data/10.json +0 -0
  173. {reader-3.18 → reader-3.19}/tests/data/10.json.py +0 -0
  174. {reader-3.18 → reader-3.19}/tests/data/custom +0 -0
  175. {reader-3.18 → reader-3.19}/tests/data/empty.atom +0 -0
  176. {reader-3.18 → reader-3.19}/tests/data/empty.atom.py +0 -0
  177. {reader-3.18 → reader-3.19}/tests/data/empty.json +0 -0
  178. {reader-3.18 → reader-3.19}/tests/data/empty.json.py +0 -0
  179. {reader-3.18 → reader-3.19}/tests/data/empty.rss +0 -0
  180. {reader-3.18 → reader-3.19}/tests/data/empty.rss.py +0 -0
  181. {reader-3.18 → reader-3.19}/tests/data/full.atom +0 -0
  182. {reader-3.18 → reader-3.19}/tests/data/full.atom.py +0 -0
  183. {reader-3.18 → reader-3.19}/tests/data/full.json +0 -0
  184. {reader-3.18 → reader-3.19}/tests/data/full.json.py +0 -0
  185. {reader-3.18 → reader-3.19}/tests/data/full.rss +0 -0
  186. {reader-3.18 → reader-3.19}/tests/data/full.rss.py +0 -0
  187. {reader-3.18 → reader-3.19}/tests/data/invalid.json +0 -0
  188. {reader-3.18 → reader-3.19}/tests/data/invalid.json.py +0 -0
  189. {reader-3.18 → reader-3.19}/tests/data/relative.atom +0 -0
  190. {reader-3.18 → reader-3.19}/tests/data/relative.atom.py +0 -0
  191. {reader-3.18 → reader-3.19}/tests/data/relative.rss +0 -0
  192. {reader-3.18 → reader-3.19}/tests/data/relative.rss.py +0 -0
  193. {reader-3.18 → reader-3.19}/tests/data/sqlite_releases.html +0 -0
  194. {reader-3.18 → reader-3.19}/tests/data/unknown.json +0 -0
  195. {reader-3.18 → reader-3.19}/tests/data/unknown.json.py +0 -0
  196. {reader-3.18 → reader-3.19}/tests/fakeparser.py +0 -0
  197. {reader-3.18 → reader-3.19}/tests/reader_methods.py +0 -0
  198. {reader-3.18 → reader-3.19}/tests/reader_test_plugins/__init__.py +0 -0
  199. {reader-3.18 → reader-3.19}/tests/reader_test_plugins/good.py +0 -0
  200. {reader-3.18 → reader-3.19}/tests/reader_test_plugins/init_error.py +0 -0
  201. {reader-3.18 → reader-3.19}/tests/reader_test_plugins/missing_dependency.py +0 -0
  202. {reader-3.18 → reader-3.19}/tests/reader_test_plugins/missing_entry_point.py +0 -0
  203. {reader-3.18 → reader-3.19}/tests/test__types.py +0 -0
  204. {reader-3.18 → reader-3.19}/tests/test__utils.py +0 -0
  205. {reader-3.18 → reader-3.19}/tests/test_app.py +0 -0
  206. {reader-3.18 → reader-3.19}/tests/test_app_wsgi.py +0 -0
  207. {reader-3.18 → reader-3.19}/tests/test_bench.py +0 -0
  208. {reader-3.18 → reader-3.19}/tests/test_changes.py +0 -0
  209. {reader-3.18 → reader-3.19}/tests/test_cli.py +0 -0
  210. {reader-3.18 → reader-3.19}/tests/test_config.py +0 -0
  211. {reader-3.18 → reader-3.19}/tests/test_exceptions.py +0 -0
  212. {reader-3.18 → reader-3.19}/tests/test_hash_utils.py +0 -0
  213. {reader-3.18 → reader-3.19}/tests/test_html_utils.py +0 -0
  214. {reader-3.18 → reader-3.19}/tests/test_lazy_imports.py +0 -0
  215. {reader-3.18 → reader-3.19}/tests/test_plugins_enclosure_dedupe.py +0 -0
  216. {reader-3.18 → reader-3.19}/tests/test_plugins_entry_dedupe.py +0 -0
  217. {reader-3.18 → reader-3.19}/tests/test_plugins_mark_as_read.py +0 -0
  218. {reader-3.18 → reader-3.19}/tests/test_plugins_preview_feed_list.py +0 -0
  219. {reader-3.18 → reader-3.19}/tests/test_plugins_readtime.py +0 -0
  220. {reader-3.18 → reader-3.19}/tests/test_plugins_sqlite_releases.py +0 -0
  221. {reader-3.18 → reader-3.19}/tests/test_plugins_ua_fallback.py +0 -0
  222. {reader-3.18 → reader-3.19}/tests/test_reader.py +0 -0
  223. {reader-3.18 → reader-3.19}/tests/test_reader_context.py +0 -0
  224. {reader-3.18 → reader-3.19}/tests/test_reader_counts.py +0 -0
  225. {reader-3.18 → reader-3.19}/tests/test_reader_deprecations.py +0 -0
  226. {reader-3.18 → reader-3.19}/tests/test_reader_filter.py +0 -0
  227. {reader-3.18 → reader-3.19}/tests/test_reader_hooks.py +0 -0
  228. {reader-3.18 → reader-3.19}/tests/test_reader_integration.py +0 -0
  229. {reader-3.18 → reader-3.19}/tests/test_reader_plugins.py +0 -0
  230. {reader-3.18 → reader-3.19}/tests/test_reader_private.py +0 -0
  231. {reader-3.18 → reader-3.19}/tests/test_reader_search.py +0 -0
  232. {reader-3.18 → reader-3.19}/tests/test_reader_sort.py +0 -0
  233. {reader-3.18 → reader-3.19}/tests/test_reader_update.py +0 -0
  234. {reader-3.18 → reader-3.19}/tests/test_reader_utils.py +0 -0
  235. {reader-3.18 → reader-3.19}/tests/test_search.py +0 -0
  236. {reader-3.18 → reader-3.19}/tests/test_sql_utils.py +0 -0
  237. {reader-3.18 → reader-3.19}/tests/test_sqlite_utils.py +0 -0
  238. {reader-3.18 → reader-3.19}/tests/test_storage.py +0 -0
  239. {reader-3.18 → reader-3.19}/tests/test_tags.py +0 -0
  240. {reader-3.18 → reader-3.19}/tests/test_types.py +0 -0
@@ -6,6 +6,28 @@ Changelog
6
6
  :no-index:
7
7
 
8
8
 
9
+ Version 3.19
10
+ ------------
11
+
12
+ Released 2025-10-01
13
+
14
+ * Support Python 3.14.
15
+ * Remove deprecated empty ``search`` and ``readtime`` extras.
16
+ * Remove undocumented development ``tests``, ``docs``, and ``dev`` extras.
17
+
18
+ * :mod:`~reader._plugins.enclosure_tags` plugin:
19
+ strip HTML tags from values used as MP3 tags
20
+ (for example, ``<b>`` tags used to highlight search result matches).
21
+
22
+ * Project infrastructure improvements.
23
+
24
+ * Clean up :doc:`contributing` documentation and :gh:`run.sh <run.sh>` script.
25
+ * Use GitHub Actions to publish releases to PyPI.
26
+ * Split type checking and testing jobs in the ``build`` GHA workflow.
27
+ * Make tox runs slightly faster.
28
+ * Use dependency groups instead of extras for development dependencies.
29
+
30
+
9
31
  Version 3.18
10
32
  ------------
11
33
 
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reader
3
- Version: 3.18
3
+ Version: 3.19
4
4
  Summary: A Python feed reader library.
5
5
  Author: lemon24
6
6
  License: BSD-3-Clause
7
7
  Project-URL: Documentation, https://reader.readthedocs.io/
8
- Project-URL: Changes, https://reader.readthedocs.io/en/latest/changelog.html
9
- Project-URL: Source Code, https://github.com/lemon24/reader
10
- Project-URL: Issue tracker, https://github.com/lemon24/reader/issues
8
+ Project-URL: Repository, https://github.com/lemon24/reader
9
+ Project-URL: Issues, https://github.com/lemon24/reader/issues
10
+ Project-URL: Changelog, https://reader.readthedocs.io/en/latest/changelog.html
11
11
  Keywords: atom,cdf,feed,rdf,rss,json feed,web feed,podcast,feed reader,feed aggregator
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Environment :: Console
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
25
26
  Classifier: Programming Language :: Python :: Implementation :: CPython
26
27
  Classifier: Programming Language :: Python :: Implementation :: PyPy
27
28
  Classifier: Topic :: Internet
@@ -40,14 +41,14 @@ Requires-Dist: requests>=2.18
40
41
  Requires-Dist: werkzeug>2
41
42
  Requires-Dist: iso8601>=1
42
43
  Requires-Dist: beautifulsoup4>=4.5
43
- Provides-Extra: search
44
- Provides-Extra: readtime
44
+ Provides-Extra: all
45
+ Requires-Dist: reader[app,cli,unstable-plugins]; extra == "all"
45
46
  Provides-Extra: cli
46
47
  Requires-Dist: click>=7; extra == "cli"
47
48
  Requires-Dist: PyYAML; extra == "cli"
48
49
  Provides-Extra: app
49
50
  Requires-Dist: flask>=0.10; extra == "app"
50
- Requires-Dist: humanize!=4.7.*,>=4; extra == "app"
51
+ Requires-Dist: humanize>=4; extra == "app"
51
52
  Requires-Dist: PyYAML; extra == "app"
52
53
  Requires-Dist: WTForms; extra == "app"
53
54
  Requires-Dist: jinja2-fragments; extra == "app"
@@ -59,36 +60,6 @@ Requires-Dist: beautifulsoup4; extra == "unstable-plugins"
59
60
  Requires-Dist: blinker>=1.4; extra == "unstable-plugins"
60
61
  Requires-Dist: beautifulsoup4; extra == "unstable-plugins"
61
62
  Requires-Dist: tabulate; extra == "unstable-plugins"
62
- Provides-Extra: tests
63
- Requires-Dist: pytest>=4; extra == "tests"
64
- Requires-Dist: pytest-randomly; extra == "tests"
65
- Requires-Dist: pytest-subtests; extra == "tests"
66
- Requires-Dist: pytest-rerunfailures; extra == "tests"
67
- Requires-Dist: coverage; extra == "tests"
68
- Requires-Dist: pytest-cov; extra == "tests"
69
- Requires-Dist: requests-mock; extra == "tests"
70
- Requires-Dist: mechanicalsoup; (implementation_name != "pypy" and python_version <= "3.12") and extra == "tests"
71
- Requires-Dist: requests-wsgi-adapter; extra == "tests"
72
- Requires-Dist: lxml; (implementation_name != "pypy" and python_version <= "3.12") and extra == "tests"
73
- Requires-Dist: html5lib; extra == "tests"
74
- Requires-Dist: numpy; (implementation_name != "pypy" and os_name == "posix" and python_version <= "3.12") and extra == "tests"
75
- Requires-Dist: mypy; implementation_name != "pypy" and extra == "tests"
76
- Requires-Dist: types-requests; extra == "tests"
77
- Requires-Dist: types-beautifulsoup4; extra == "tests"
78
- Provides-Extra: docs
79
- Requires-Dist: sphinx; extra == "docs"
80
- Requires-Dist: sphinx-rtd-theme>=1.3.0rc1; extra == "docs"
81
- Requires-Dist: click>=7; extra == "docs"
82
- Requires-Dist: sphinx-click; extra == "docs"
83
- Requires-Dist: sphinx-hoverxref; extra == "docs"
84
- Requires-Dist: sphinxcontrib-log-cabinet; extra == "docs"
85
- Requires-Dist: setuptools; extra == "docs"
86
- Provides-Extra: dev
87
- Requires-Dist: reader[app,cli,docs,tests,unstable-plugins]; extra == "dev"
88
- Requires-Dist: tox; extra == "dev"
89
- Requires-Dist: pre-commit; extra == "dev"
90
- Requires-Dist: build; extra == "dev"
91
- Requires-Dist: twine; extra == "dev"
92
63
  Dynamic: license-file
93
64
 
94
65
  .. begin-intro
@@ -33,7 +33,6 @@ extensions = [
33
33
  'sphinx.ext.intersphinx',
34
34
  'sphinx.ext.extlinks',
35
35
  'sphinx_click.ext',
36
- 'hoverxref.extension',
37
36
  'sphinxcontrib.log_cabinet',
38
37
  ]
39
38
 
@@ -112,19 +111,6 @@ extlinks = {
112
111
  }
113
112
 
114
113
 
115
- hoverxref_auto_ref = True
116
- hoverxref_domains = ["py"]
117
- hoverxref_role_types = {
118
- 'ref': 'tooltip',
119
- 'mod': 'tooltip',
120
- 'class': 'tooltip',
121
- 'meth': 'tooltip',
122
- 'attr': 'tooltip',
123
- 'exc': 'tooltip',
124
- 'func': 'tooltip',
125
- 'data': 'tooltip',
126
- }
127
-
128
114
  pygments_style = 'friendly'
129
115
 
130
116
  html_theme = 'sphinx_rtd_theme'
@@ -226,25 +226,31 @@ Making a release
226
226
 
227
227
  .. note::
228
228
 
229
- :gh:`scripts/release.py <scripts/release.py>` already does most of these.
229
+ The :gh:`release.py <scripts/release.py>` script together with
230
+ the :gh:`publish <.github/workflows/publish.yaml>` GitHub Actions workflow
231
+ already do most of these.
230
232
 
231
233
  Making a release (from ``x`` to ``y`` == ``x + 1``):
232
234
 
233
235
  * (release.py) bump version in ``src/reader/__init__.py`` to ``y``
234
236
  * (release.py) update changelog with release version and date
235
237
  * (release.py) make sure tests pass / docs build
236
- * (release.py) clean up dist/: ``rm -rf dist/``
237
- * (release.py) build tarball and wheel: ``python -m build``
238
238
  * (release.py) push to GitHub
239
- * (release.py prompts) wait for GitHub Actions / Codecov / Read the Docs builds to pass
240
- * upload to test PyPI and check: ``twine upload --repository-url https://test.pypi.org/legacy/ dist/*``
241
- * (release.py) upload to PyPI: ``twine upload dist/*``
239
+ * (release.py prompts) wait for GitHub Actions / Read the Docs builds to pass
242
240
  * (release.py) tag current commit with `<major>.<minor>` and `<major>.x`
243
- (e.g. when releasing `1.20`: `1.20` and `1.x`)
244
- * (release.py prompts) create release in GitHub
245
- * build docs from latest and enable ``y`` docs version (should happen automatically after the first time)
241
+ (e.g. when releasing `1.20`: `1.20` and `1.x`) and push tags to GitHub
242
+ * (release.py prompts) approve publish workflow
243
+ * (publish workflow) build and upload to PyPI
244
+ * alternatively, to make a manual release (should not be needed)
245
+
246
+ * clean up dist/: ``rm -rf dist/``
247
+ * build tarball and wheel: ``python -m build``
248
+ * upload to test PyPI and check: ``twine upload --repository-url https://test.pypi.org/legacy/ dist/*``
249
+ * upload to PyPI: ``twine upload dist/*``
250
+
251
+ * (publish workflow) create draft release in GitHub
252
+ * (release.py prompts) publish release in GitHub
246
253
  * (release.py) bump versions from ``y`` to ``(y + 1).dev0``, add ``(y + 1)`` changelog section
247
- * (release.py prompts) trigger Read the Docs build for `<major>.x` (doesn't happen automatically)
248
254
 
249
255
 
250
256
 
@@ -109,11 +109,13 @@ Use the following command to update *reader*
109
109
  pip install --upgrade reader
110
110
 
111
111
 
112
+ .. _install-pre-release:
113
+
112
114
  Living on the edge
113
115
  ~~~~~~~~~~~~~~~~~~
114
116
 
115
117
  If you want to use the latest *reader* code before it’s released,
116
- install or update from the master branch:
118
+ install or update from the ``master`` branch:
117
119
 
118
120
  .. code-block:: bash
119
121
 
@@ -1,7 +1,3 @@
1
- [build-system]
2
- requires = ["setuptools", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
1
  [project]
6
2
  name = "reader"
7
3
  authors = [{name = "lemon24"}]
@@ -26,6 +22,7 @@ classifiers = [
26
22
  "Programming Language :: Python :: 3.11",
27
23
  "Programming Language :: Python :: 3.12",
28
24
  "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
29
26
  "Programming Language :: Python :: Implementation :: CPython",
30
27
  "Programming Language :: Python :: Implementation :: PyPy",
31
28
  "Topic :: Internet",
@@ -52,19 +49,16 @@ dynamic = ["version"]
52
49
 
53
50
  [project.urls]
54
51
  Documentation = "https://reader.readthedocs.io/"
55
- Changes = "https://reader.readthedocs.io/en/latest/changelog.html"
56
- "Source Code" = "https://github.com/lemon24/reader"
57
- "Issue tracker" = "https://github.com/lemon24/reader/issues"
52
+ Repository = "https://github.com/lemon24/reader"
53
+ Issues = "https://github.com/lemon24/reader/issues"
54
+ Changelog = "https://reader.readthedocs.io/en/latest/changelog.html"
58
55
 
59
56
  [project.optional-dependencies]
60
57
 
61
- # STABLE
62
-
63
- search = [] # empty since 2.4, kept to avoid breaking dependents
64
- readtime = [] # empty since 3.1, kept to avoid breaking dependents
65
-
66
58
  # UNSTABLE
67
59
 
60
+ all = ["reader[cli,app,unstable-plugins]"]
61
+
68
62
  cli = [
69
63
  "click>=7",
70
64
  # for config
@@ -72,8 +66,7 @@ cli = [
72
66
  ]
73
67
  app = [
74
68
  "flask>=0.10",
75
- # https://github.com/python-humanize/humanize/issues/122
76
- "humanize>=4,!=4.7.*",
69
+ "humanize>=4",
77
70
  # for config
78
71
  "PyYAML",
79
72
  # for v2
@@ -81,8 +74,6 @@ app = [
81
74
  "jinja2-fragments",
82
75
  ]
83
76
 
84
- # UNSTABLE PLUGINS
85
-
86
77
  # mushed together for convenience
87
78
  unstable-plugins = [
88
79
  # enclosure-tags
@@ -98,16 +89,14 @@ unstable-plugins = [
98
89
  "tabulate",
99
90
  ]
100
91
 
101
- # DEVELOPMENT
92
+ [dependency-groups]
102
93
 
103
- # run tests under one interpreter
104
94
  tests = [
105
95
  "pytest>=4",
106
96
  "pytest-randomly",
107
97
  "pytest-subtests",
108
98
  "pytest-rerunfailures",
109
99
  "coverage",
110
- "pytest-cov",
111
100
  "requests-mock",
112
101
  # mechanicalsoup hard-depends on lxml (see below)
113
102
  'mechanicalsoup; (implementation_name != "pypy" and python_version <= "3.12")',
@@ -119,35 +108,38 @@ tests = [
119
108
  "html5lib",
120
109
  # for bench.py
121
110
  'numpy; (implementation_name != "pypy" and os_name == "posix" and python_version <= "3.12")',
122
- # mypy does not work on pypy (yet).
123
- 'mypy; implementation_name != "pypy"',
111
+ ]
112
+
113
+ typing = [
114
+ 'mypy',
124
115
  "types-requests",
125
116
  "types-beautifulsoup4",
126
117
  ]
127
118
 
128
- # build docs
129
119
  docs = [
130
120
  "sphinx",
131
- # listing 1.3.0rc1 explicitly until 1.3 is released
132
- # https://github.com/readthedocs/sphinx_rtd_theme/pull/1464#issuecomment-1664799982
133
- "sphinx-rtd-theme>=1.3.0rc1",
121
+ "sphinx-rtd-theme",
134
122
  "click>=7",
135
123
  "sphinx-click",
136
- "sphinx-hoverxref",
137
124
  "sphinxcontrib-log-cabinet",
138
125
  # for read_configuration used in conf.py
139
126
  "setuptools",
140
127
  ]
141
128
 
142
- # things needed to develop / test locally / make releases
143
129
  dev = [
144
- "reader[cli,app,unstable-plugins,tests,docs]",
130
+ {include-group = "tests"},
131
+ {include-group = "typing"},
132
+ {include-group = "docs"},
145
133
  "tox",
146
134
  "pre-commit",
147
135
  "build",
148
136
  "twine",
149
137
  ]
150
138
 
139
+ [build-system]
140
+ requires = ["setuptools", "wheel"]
141
+ build-backend = "setuptools.build_meta"
142
+
151
143
  [tool.setuptools.dynamic]
152
144
  version = {attr = "reader.__version__"}
153
145
 
@@ -170,9 +162,16 @@ source = ["reader", "tests"]
170
162
  omit = [
171
163
  "src/reader/_vendor/*",
172
164
  ]
165
+ # TODO: remove when https://github.com/nedbat/coveragepy/issues/1987 is solved
166
+ core = "ctrace"
173
167
  [tool.coverage.paths]
174
168
  source = ["src", "*/site-packages"]
175
169
 
170
+ [tool.mypy]
171
+ files = ["src"]
172
+ show_error_codes = true
173
+ pretty = true
174
+ strict = true
176
175
  [[tool.mypy.overrides]]
177
176
  module = [
178
177
  "reader.__main__",
reader-3.19/run.sh ADDED
@@ -0,0 +1,125 @@
1
+ #!/bin/bash
2
+ #
3
+ # usage: ./run.sh command [argument ...]
4
+ #
5
+ # Executable documentation for the development workflow.
6
+ #
7
+ # See https://death.andgravity.com/run-sh for how this works.
8
+
9
+
10
+ # preamble
11
+
12
+ set -o nounset
13
+ set -o pipefail
14
+ set -o errexit
15
+
16
+ PROJECT_ROOT=${0%/*}
17
+ if [[ $0 != $PROJECT_ROOT && $PROJECT_ROOT != "" ]]; then
18
+ cd "$PROJECT_ROOT"
19
+ fi
20
+ readonly PROJECT_ROOT=$( pwd )
21
+ readonly SCRIPT="$PROJECT_ROOT/$( basename "$0" )"
22
+
23
+
24
+ # main development workflow
25
+
26
+ function install {
27
+ pip install -e '.[all]' --group dev --upgrade --upgrade-strategy eager
28
+ pre-commit install --install-hooks
29
+ }
30
+
31
+ function test {
32
+ pytest --runslow "$@"
33
+ }
34
+
35
+ function test-all {
36
+ tox run-parallel "$@"
37
+ }
38
+
39
+ function coverage {
40
+ unset -f coverage
41
+ coverage run -m pytest --runslow "$@"
42
+ coverage html
43
+ coverage-report
44
+ }
45
+
46
+ function typing {
47
+ mypy "$@"
48
+ }
49
+
50
+ function docs {
51
+ sphinx-build -E -W docs docs/_build/html "$@"
52
+ }
53
+
54
+
55
+ # "watch" versions of the main commands
56
+
57
+ function test-dev {
58
+ watch pytest "$@"
59
+ }
60
+
61
+ function typing-dev {
62
+ watch typing "$@"
63
+ }
64
+
65
+ function docs-dev {
66
+ rm -r docs/_build/html
67
+ watch sphinx-build -W docs docs/_build/html "$@"
68
+ }
69
+
70
+ function serve-dev {
71
+ export READER_DB=db.sqlite
72
+ flask -A reader._app.wsgi --debug run "$@"
73
+ }
74
+
75
+
76
+ # low level commands
77
+
78
+ function coverage-report {
79
+ # --fail-under only for the library, not the CLI or the web app
80
+ unset -f coverage
81
+ coverage report \
82
+ --omit "$(
83
+ echo "
84
+ */reader/_vendor/*
85
+ */reader/__main__.py
86
+ */reader/_cli*
87
+ */reader/_config*
88
+ */reader/_app/*
89
+ */reader/_plugins/*
90
+ tests/*
91
+ " | xargs echo | sed 's/ /,/g'
92
+ )" \
93
+ --skip-covered \
94
+ --show-missing \
95
+ --fail-under $( on-pypy && echo 99 || echo 100 )
96
+ }
97
+
98
+
99
+ # utilities
100
+
101
+ function on-pypy {
102
+ [[ $( python -c 'import sys; print(sys.implementation.name)' ) == pypy ]]
103
+ }
104
+
105
+ function watch {
106
+ entr-project-files -cdr "$SCRIPT" "$@"
107
+ }
108
+
109
+ function entr-project-files {
110
+ set +o errexit
111
+ while true; do
112
+ ls-project-files | entr "$@"
113
+ if [[ $? -eq 0 ]]; then
114
+ break
115
+ fi
116
+ done
117
+ }
118
+
119
+ function ls-project-files {
120
+ git ls-files "$@"
121
+ git ls-files --exclude-standard --others "$@"
122
+ }
123
+
124
+
125
+ "$@"
@@ -34,11 +34,6 @@ def run_tox():
34
34
  run_no_venv('tox -p all', shell=True)
35
35
 
36
36
 
37
- def build():
38
- run('rm -rf dist/', shell=True)
39
- run('python -m build', shell=True)
40
-
41
-
42
37
  def path_sub(pattern, repl, path):
43
38
  with open(path) as f:
44
39
  text = f.read()
@@ -112,10 +107,6 @@ def check_unpushed():
112
107
  abort("unpushed changes\n\n{}\n", p.stdout.strip('\n'))
113
108
 
114
109
 
115
- def upload_to_pypi():
116
- run('twine upload dist/*', shell=True)
117
-
118
-
119
110
  def add_and_push_tags(tags):
120
111
  for tag in tags:
121
112
  run(['git', 'tag', '--force', tag])
@@ -150,10 +141,6 @@ def main(version, new_version, date, tox, changelog):
150
141
 
151
142
  confirm("Wait for GitHub Actions / Read the Docs builds to pass.")
152
143
 
153
- confirm("Upload to PyPI?")
154
- build()
155
- upload_to_pypi()
156
-
157
144
  tags = [version]
158
145
  version_x = version.partition('.')[0] + '.x'
159
146
  if new_version_is_final:
@@ -162,7 +149,15 @@ def main(version, new_version, date, tox, changelog):
162
149
  confirm(f"Add and push tags ({', '.join(tags)})?")
163
150
  add_and_push_tags(tags)
164
151
 
165
- confirm("Create release {} in GitHub (doesn't happen automatically).", version)
152
+ confirm(
153
+ "Approve publish workflow to upload to PyPI.\n {}\nDone?",
154
+ "https://github.com/lemon24/reader/actions/workflows/publish.yaml",
155
+ )
156
+ confirm(
157
+ "Publish draft release {} in GitHub.\n {}\nDone?",
158
+ version,
159
+ "https://github.com/lemon24/reader/releases",
160
+ )
166
161
 
167
162
  new_version_full = f"{new_version}.dev0"
168
163
  update_init_version(new_version_full)
@@ -173,11 +168,6 @@ def main(version, new_version, date, tox, changelog):
173
168
  confirm("Push version {}?", new_version_full)
174
169
  push()
175
170
 
176
- # TODO: I just enabled branch or tag creation/deletion for the RtD webhook in GitHub, this might not be needed next time.
177
- confirm(
178
- f"Trigger Read the Docs build for {version_x} (doesn't happen automatically)."
179
- )
180
-
181
171
 
182
172
  if __name__ == '__main__':
183
173
  main()
@@ -37,7 +37,7 @@ Print the titles of the unread entries::
37
37
 
38
38
  """
39
39
 
40
- __version__ = '3.18'
40
+ __version__ = '3.19'
41
41
 
42
42
  # isort: off
43
43
 
@@ -38,6 +38,7 @@ from flask import request
38
38
  from flask import Response
39
39
  from flask import stream_with_context
40
40
  from flask import url_for
41
+ from jinja2.filters import do_striptags as striptags
41
42
 
42
43
 
43
44
  blueprint = Blueprint('enclosure_tags', __name__)
@@ -146,12 +147,13 @@ def enclosure_tags_filter(enclosure, entry, feed_tags):
146
147
 
147
148
  args = {'url': enclosure.href, 'filename': filename}
148
149
  if entry.title:
149
- args['title'] = entry.title
150
+ args['title'] = striptags(entry.title)
150
151
  if album := (entry.feed.user_title or entry.feed.title):
152
+ album = striptags(album)
151
153
  args['album'] = album
152
154
  args['artist'] = album
153
155
  elif artist := (entry.author or entry.feed.author):
154
- args['artist'] = artist
156
+ args['artist'] = striptags(artist)
155
157
 
156
158
  for tag in feed_tags:
157
159
  if 'podcast' in tag.lower():
@@ -23,6 +23,7 @@ from dataclasses import dataclass
23
23
  from datetime import datetime
24
24
  from datetime import timezone
25
25
  from typing import Any
26
+ from typing import cast
26
27
  from typing import no_type_check
27
28
  from typing import TypeVar
28
29
 
@@ -471,8 +472,9 @@ class LocalConnectionFactory:
471
472
  if not self._local.is_creating_thread:
472
473
  self._other_threads = True
473
474
 
474
- self._local.db = db = sqlite3.connect(self.path, **self.kwargs)
475
- assert db is not None, "for mypy"
475
+ # TODO: remove needless cast (needed after mypy 1.16.0 update)
476
+ db = cast(sqlite3.Connection, sqlite3.connect(self.path, **self.kwargs))
477
+ self._local.db = db
476
478
  self._local.call_count = 0
477
479
 
478
480
  try:
@@ -757,7 +757,7 @@ class EntrySort(enum.StrEnum):
757
757
  #:
758
758
  #: .. note::
759
759
  #:
760
- #: The algorithm for "recent" is a heuristic and may change over time.
760
+ #: The algorithm for "recent" is a heuristic and may change over time.
761
761
  #:
762
762
  #: .. versionchanged:: 3.1
763
763
  #: Sort entries by added date most of the time,
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reader
3
- Version: 3.18
3
+ Version: 3.19
4
4
  Summary: A Python feed reader library.
5
5
  Author: lemon24
6
6
  License: BSD-3-Clause
7
7
  Project-URL: Documentation, https://reader.readthedocs.io/
8
- Project-URL: Changes, https://reader.readthedocs.io/en/latest/changelog.html
9
- Project-URL: Source Code, https://github.com/lemon24/reader
10
- Project-URL: Issue tracker, https://github.com/lemon24/reader/issues
8
+ Project-URL: Repository, https://github.com/lemon24/reader
9
+ Project-URL: Issues, https://github.com/lemon24/reader/issues
10
+ Project-URL: Changelog, https://reader.readthedocs.io/en/latest/changelog.html
11
11
  Keywords: atom,cdf,feed,rdf,rss,json feed,web feed,podcast,feed reader,feed aggregator
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Environment :: Console
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
24
  Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
25
26
  Classifier: Programming Language :: Python :: Implementation :: CPython
26
27
  Classifier: Programming Language :: Python :: Implementation :: PyPy
27
28
  Classifier: Topic :: Internet
@@ -40,14 +41,14 @@ Requires-Dist: requests>=2.18
40
41
  Requires-Dist: werkzeug>2
41
42
  Requires-Dist: iso8601>=1
42
43
  Requires-Dist: beautifulsoup4>=4.5
43
- Provides-Extra: search
44
- Provides-Extra: readtime
44
+ Provides-Extra: all
45
+ Requires-Dist: reader[app,cli,unstable-plugins]; extra == "all"
45
46
  Provides-Extra: cli
46
47
  Requires-Dist: click>=7; extra == "cli"
47
48
  Requires-Dist: PyYAML; extra == "cli"
48
49
  Provides-Extra: app
49
50
  Requires-Dist: flask>=0.10; extra == "app"
50
- Requires-Dist: humanize!=4.7.*,>=4; extra == "app"
51
+ Requires-Dist: humanize>=4; extra == "app"
51
52
  Requires-Dist: PyYAML; extra == "app"
52
53
  Requires-Dist: WTForms; extra == "app"
53
54
  Requires-Dist: jinja2-fragments; extra == "app"
@@ -59,36 +60,6 @@ Requires-Dist: beautifulsoup4; extra == "unstable-plugins"
59
60
  Requires-Dist: blinker>=1.4; extra == "unstable-plugins"
60
61
  Requires-Dist: beautifulsoup4; extra == "unstable-plugins"
61
62
  Requires-Dist: tabulate; extra == "unstable-plugins"
62
- Provides-Extra: tests
63
- Requires-Dist: pytest>=4; extra == "tests"
64
- Requires-Dist: pytest-randomly; extra == "tests"
65
- Requires-Dist: pytest-subtests; extra == "tests"
66
- Requires-Dist: pytest-rerunfailures; extra == "tests"
67
- Requires-Dist: coverage; extra == "tests"
68
- Requires-Dist: pytest-cov; extra == "tests"
69
- Requires-Dist: requests-mock; extra == "tests"
70
- Requires-Dist: mechanicalsoup; (implementation_name != "pypy" and python_version <= "3.12") and extra == "tests"
71
- Requires-Dist: requests-wsgi-adapter; extra == "tests"
72
- Requires-Dist: lxml; (implementation_name != "pypy" and python_version <= "3.12") and extra == "tests"
73
- Requires-Dist: html5lib; extra == "tests"
74
- Requires-Dist: numpy; (implementation_name != "pypy" and os_name == "posix" and python_version <= "3.12") and extra == "tests"
75
- Requires-Dist: mypy; implementation_name != "pypy" and extra == "tests"
76
- Requires-Dist: types-requests; extra == "tests"
77
- Requires-Dist: types-beautifulsoup4; extra == "tests"
78
- Provides-Extra: docs
79
- Requires-Dist: sphinx; extra == "docs"
80
- Requires-Dist: sphinx-rtd-theme>=1.3.0rc1; extra == "docs"
81
- Requires-Dist: click>=7; extra == "docs"
82
- Requires-Dist: sphinx-click; extra == "docs"
83
- Requires-Dist: sphinx-hoverxref; extra == "docs"
84
- Requires-Dist: sphinxcontrib-log-cabinet; extra == "docs"
85
- Requires-Dist: setuptools; extra == "docs"
86
- Provides-Extra: dev
87
- Requires-Dist: reader[app,cli,docs,tests,unstable-plugins]; extra == "dev"
88
- Requires-Dist: tox; extra == "dev"
89
- Requires-Dist: pre-commit; extra == "dev"
90
- Requires-Dist: build; extra == "dev"
91
- Requires-Dist: twine; extra == "dev"
92
63
  Dynamic: license-file
93
64
 
94
65
  .. begin-intro