WuttaWeb 0.17.2__tar.gz → 0.18.0__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 (193) hide show
  1. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/CHANGELOG.md +7 -0
  2. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/PKG-INFO +4 -2
  3. wuttaweb-0.18.0/docs/api/wuttaweb.cli.rst +6 -0
  4. wuttaweb-0.18.0/docs/api/wuttaweb.cli.webapp.rst +6 -0
  5. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/conf.py +1 -0
  6. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/index.rst +3 -0
  7. wuttaweb-0.18.0/docs/narr/cli/builtin.rst +28 -0
  8. wuttaweb-0.18.0/docs/narr/cli/index.rst +14 -0
  9. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/pyproject.toml +7 -6
  10. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/app.py +99 -24
  11. wuttaweb-0.18.0/src/wuttaweb/cli/__init__.py +28 -0
  12. wuttaweb-0.18.0/src/wuttaweb/cli/webapp.py +88 -0
  13. wuttaweb-0.18.0/tests/cli/test_webapp.py +104 -0
  14. wuttaweb-0.18.0/tests/test_app.py +131 -0
  15. wuttaweb-0.18.0/tests/views/__init__.py +0 -0
  16. wuttaweb-0.17.2/tests/test_app.py +0 -61
  17. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/.gitignore +0 -0
  18. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/COPYING.txt +0 -0
  19. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/README.md +0 -0
  20. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/Makefile +0 -0
  21. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/_static/.keepme +0 -0
  22. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.app.rst +0 -0
  23. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.auth.rst +0 -0
  24. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.conf.rst +0 -0
  25. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.db.continuum.rst +0 -0
  26. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.db.rst +0 -0
  27. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.db.sess.rst +0 -0
  28. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.base.rst +0 -0
  29. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.rst +0 -0
  30. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.schema.rst +0 -0
  31. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.forms.widgets.rst +0 -0
  32. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.grids.base.rst +0 -0
  33. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.grids.filters.rst +0 -0
  34. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.grids.rst +0 -0
  35. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.handler.rst +0 -0
  36. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.helpers.rst +0 -0
  37. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.menus.rst +0 -0
  38. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.progress.rst +0 -0
  39. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.rst +0 -0
  40. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.static.rst +0 -0
  41. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.subscribers.rst +0 -0
  42. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.util.rst +0 -0
  43. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.auth.rst +0 -0
  44. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.base.rst +0 -0
  45. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.batch.rst +0 -0
  46. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.common.rst +0 -0
  47. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.essential.rst +0 -0
  48. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.master.rst +0 -0
  49. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.people.rst +0 -0
  50. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.progress.rst +0 -0
  51. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.roles.rst +0 -0
  52. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.rst +0 -0
  53. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.settings.rst +0 -0
  54. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.upgrades.rst +0 -0
  55. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/api/wuttaweb.views.users.rst +0 -0
  56. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/glossary.rst +0 -0
  57. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/make.bat +0 -0
  58. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/narr/templates/base.rst +0 -0
  59. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/narr/templates/index.rst +0 -0
  60. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/narr/templates/lookup.rst +0 -0
  61. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/docs/narr/templates/overview.rst +0 -0
  62. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/__init__.py +0 -0
  63. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/_version.py +0 -0
  64. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/auth.py +0 -0
  65. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/conf.py +0 -0
  66. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/db/__init__.py +0 -0
  67. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/db/continuum.py +0 -0
  68. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/db/sess.py +0 -0
  69. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/email/templates/feedback.html.mako +0 -0
  70. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/email/templates/feedback.txt.mako +0 -0
  71. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/forms/__init__.py +0 -0
  72. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/forms/base.py +0 -0
  73. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/forms/schema.py +0 -0
  74. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/forms/widgets.py +0 -0
  75. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/grids/__init__.py +0 -0
  76. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/grids/base.py +0 -0
  77. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/grids/filters.py +0 -0
  78. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/handler.py +0 -0
  79. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/helpers.py +0 -0
  80. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/menus.py +0 -0
  81. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/progress.py +0 -0
  82. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/static/__init__.py +0 -0
  83. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/static/img/favicon.ico +0 -0
  84. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/static/img/logo.png +0 -0
  85. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/static/img/testing.png +0 -0
  86. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/subscribers.py +0 -0
  87. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/appinfo/configure.mako +0 -0
  88. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/appinfo/index.mako +0 -0
  89. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/auth/change_password.mako +0 -0
  90. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/auth/login.mako +0 -0
  91. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/base.mako +0 -0
  92. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/base_meta.mako +0 -0
  93. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/batch/view.mako +0 -0
  94. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/configure.mako +0 -0
  95. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/checkbox.pt +0 -0
  96. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/checkbox_choice.pt +0 -0
  97. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/checked_password.pt +0 -0
  98. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/dateinput.pt +0 -0
  99. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/datetimeinput.pt +0 -0
  100. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/moneyinput.pt +0 -0
  101. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/password.pt +0 -0
  102. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/permissions.pt +0 -0
  103. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/checkbox.pt +0 -0
  104. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/filedownload.pt +0 -0
  105. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/notes.pt +0 -0
  106. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/objectref.pt +0 -0
  107. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/permissions.pt +0 -0
  108. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/readonly/rolerefs.pt +0 -0
  109. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/select.pt +0 -0
  110. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/textarea.pt +0 -0
  111. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/deform/textinput.pt +0 -0
  112. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/forbidden.mako +0 -0
  113. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/form.mako +0 -0
  114. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/forms/vue_template.mako +0 -0
  115. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/grids/table_element.mako +0 -0
  116. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/grids/vue_template.mako +0 -0
  117. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/home.mako +0 -0
  118. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/configure.mako +0 -0
  119. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/create.mako +0 -0
  120. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/delete.mako +0 -0
  121. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/edit.mako +0 -0
  122. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/form.mako +0 -0
  123. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/index.mako +0 -0
  124. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/master/view.mako +0 -0
  125. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/notfound.mako +0 -0
  126. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/page.mako +0 -0
  127. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/people/view_profile.mako +0 -0
  128. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/progress.mako +0 -0
  129. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/setup.mako +0 -0
  130. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/upgrade.mako +0 -0
  131. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/upgrades/configure.mako +0 -0
  132. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/upgrades/view.mako +0 -0
  133. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/templates/wutta-components.mako +0 -0
  134. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/util.py +0 -0
  135. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/__init__.py +0 -0
  136. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/auth.py +0 -0
  137. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/base.py +0 -0
  138. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/batch.py +0 -0
  139. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/common.py +0 -0
  140. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/essential.py +0 -0
  141. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/master.py +0 -0
  142. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/people.py +0 -0
  143. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/progress.py +0 -0
  144. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/roles.py +0 -0
  145. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/settings.py +0 -0
  146. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/upgrades.py +0 -0
  147. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/src/wuttaweb/views/users.py +0 -0
  148. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tasks.py +0 -0
  149. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/__init__.py +0 -0
  150. {wuttaweb-0.17.2/tests/db → wuttaweb-0.18.0/tests/cli}/__init__.py +0 -0
  151. {wuttaweb-0.17.2/tests/forms → wuttaweb-0.18.0/tests/db}/__init__.py +0 -0
  152. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/db/test_continuum.py +0 -0
  153. {wuttaweb-0.17.2/tests/grids → wuttaweb-0.18.0/tests/forms}/__init__.py +0 -0
  154. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/forms/test_base.py +0 -0
  155. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/forms/test_schema.py +0 -0
  156. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/forms/test_widgets.py +0 -0
  157. {wuttaweb-0.17.2/tests/views → wuttaweb-0.18.0/tests/grids}/__init__.py +0 -0
  158. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/grids/test_base.py +0 -0
  159. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/grids/test_filters.py +0 -0
  160. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/bb_fontawesome_svg_core.js +0 -0
  161. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/bb_free_solid_svg_icons.js +0 -0
  162. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/bb_oruga.js +0 -0
  163. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/bb_oruga_bulma.css +0 -0
  164. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/bb_oruga_bulma.js +0 -0
  165. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/bb_vue.js +0 -0
  166. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/bb_vue_fontawesome.js +0 -0
  167. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/buefy.css +0 -0
  168. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/buefy.js +0 -0
  169. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/fontawesome.js +0 -0
  170. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/vue.js +0 -0
  171. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/libcache/vue_resource.js +0 -0
  172. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_auth.py +0 -0
  173. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_handler.py +0 -0
  174. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_helpers.py +0 -0
  175. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_menus.py +0 -0
  176. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_progress.py +0 -0
  177. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_static.py +0 -0
  178. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_subscribers.py +0 -0
  179. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/test_util.py +0 -0
  180. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/util.py +0 -0
  181. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test___init__.py +0 -0
  182. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_auth.py +0 -0
  183. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_base.py +0 -0
  184. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_batch.py +0 -0
  185. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_common.py +0 -0
  186. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_master.py +0 -0
  187. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_people.py +0 -0
  188. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_progress.py +0 -0
  189. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_roles.py +0 -0
  190. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_settings.py +0 -0
  191. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_upgrades.py +0 -0
  192. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tests/views/test_users.py +0 -0
  193. {wuttaweb-0.17.2 → wuttaweb-0.18.0}/tox.ini +0 -0
@@ -5,6 +5,13 @@ All notable changes to wuttaweb will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v0.18.0 (2024-12-18)
9
+
10
+ ### Feat
11
+
12
+ - add basic support for running in ASGI context
13
+ - add support for running via uvicorn; `wutta webapp` command
14
+
8
15
  ## v0.17.2 (2024-12-17)
9
16
 
10
17
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: WuttaWeb
3
- Version: 0.17.2
3
+ Version: 0.18.0
4
4
  Summary: Web App for Wutta Framework
5
5
  Project-URL: Homepage, https://wuttaproject.org/
6
6
  Project-URL: Repository, https://forgejo.wuttaproject.org/wutta/wuttaweb
@@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.11
25
25
  Classifier: Topic :: Internet :: WWW/HTTP
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Requires-Python: >=3.8
28
+ Requires-Dist: asgiref
28
29
  Requires-Dist: colanderalchemy
29
30
  Requires-Dist: humanize
30
31
  Requires-Dist: markdown
@@ -38,13 +39,14 @@ Requires-Dist: pyramid-tm
38
39
  Requires-Dist: pyramid>=2
39
40
  Requires-Dist: waitress
40
41
  Requires-Dist: webhelpers2
41
- Requires-Dist: wuttjamaican[db]>=0.18.0
42
+ Requires-Dist: wuttjamaican[db]>=0.18.1
42
43
  Requires-Dist: zope-sqlalchemy>=1.5
43
44
  Provides-Extra: continuum
44
45
  Requires-Dist: wutta-continuum; extra == 'continuum'
45
46
  Provides-Extra: docs
46
47
  Requires-Dist: furo; extra == 'docs'
47
48
  Requires-Dist: sphinx; extra == 'docs'
49
+ Requires-Dist: sphinxcontrib-programoutput; extra == 'docs'
48
50
  Provides-Extra: tests
49
51
  Requires-Dist: pytest-cov; extra == 'tests'
50
52
  Requires-Dist: tox; extra == 'tests'
@@ -0,0 +1,6 @@
1
+
2
+ ``wuttaweb.cli``
3
+ ================
4
+
5
+ .. automodule:: wuttaweb.cli
6
+ :members:
@@ -0,0 +1,6 @@
1
+
2
+ ``wuttaweb.cli.webapp``
3
+ =======================
4
+
5
+ .. automodule:: wuttaweb.cli.webapp
6
+ :members:
@@ -21,6 +21,7 @@ extensions = [
21
21
  'sphinx.ext.intersphinx',
22
22
  'sphinx.ext.viewcode',
23
23
  'sphinx.ext.todo',
24
+ 'sphinxcontrib.programoutput',
24
25
  ]
25
26
 
26
27
  templates_path = ['_templates']
@@ -19,6 +19,7 @@ the narrative docs are pretty scant. That will eventually change.
19
19
  :caption: Documentation:
20
20
 
21
21
  glossary
22
+ narr/cli/index
22
23
  narr/templates/index
23
24
 
24
25
  .. toctree::
@@ -28,6 +29,8 @@ the narrative docs are pretty scant. That will eventually change.
28
29
  api/wuttaweb
29
30
  api/wuttaweb.app
30
31
  api/wuttaweb.auth
32
+ api/wuttaweb.cli
33
+ api/wuttaweb.cli.webapp
31
34
  api/wuttaweb.conf
32
35
  api/wuttaweb.db
33
36
  api/wuttaweb.db.continuum
@@ -0,0 +1,28 @@
1
+
2
+ ===================
3
+ Built-in Commands
4
+ ===================
5
+
6
+ Below are the :term:`subcommands <subcommand>` which come with
7
+ WuttaWeb.
8
+
9
+
10
+ .. _wutta-webapp:
11
+
12
+ ``wutta webapp``
13
+ ----------------
14
+
15
+ Run the web app, according to config file(s).
16
+
17
+ This command is a convenience only; under the hood it can run `uvicorn
18
+ <https://www.uvicorn.org/#uvicornrun>`_ but by default will run
19
+ whatever :ref:`pserve <pyramid:pserve_script>` is setup to do (which
20
+ usually is `waitress
21
+ <https://docs.pylonsproject.org/projects/waitress/en/latest/index.html>`_).
22
+
23
+ Ultimately it's all up to config, so run different web apps with
24
+ different config files.
25
+
26
+ Defined in: :mod:`wuttaweb.cli.webapp`
27
+
28
+ .. program-output:: wutta webapp --help
@@ -0,0 +1,14 @@
1
+
2
+ ==============
3
+ Command Line
4
+ ==============
5
+
6
+ There isn't much to the command line for WuttaWeb, but here it is.
7
+
8
+ For more general info about CLI see
9
+ :doc:`wuttjamaican:narr/cli/index`.
10
+
11
+ .. toctree::
12
+ :maxdepth: 2
13
+
14
+ builtin
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "WuttaWeb"
9
- version = "0.17.2"
9
+ version = "0.18.0"
10
10
  description = "Web App for Wutta Framework"
11
11
  readme = "README.md"
12
12
  authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@@ -30,6 +30,7 @@ classifiers = [
30
30
  ]
31
31
  requires-python = ">= 3.8"
32
32
  dependencies = [
33
+ "asgiref",
33
34
  "ColanderAlchemy",
34
35
  "humanize",
35
36
  "markdown",
@@ -43,32 +44,32 @@ dependencies = [
43
44
  "pyramid_tm",
44
45
  "waitress",
45
46
  "WebHelpers2",
46
- "WuttJamaican[db]>=0.18.0",
47
+ "WuttJamaican[db]>=0.18.1",
47
48
  "zope.sqlalchemy>=1.5",
48
49
  ]
49
50
 
50
51
 
51
52
  [project.optional-dependencies]
52
53
  continuum = ["Wutta-Continuum"]
53
- docs = ["Sphinx", "furo"]
54
+ docs = ["Sphinx", "furo", "sphinxcontrib-programoutput"]
54
55
  tests = ["pytest-cov", "tox"]
55
56
 
56
57
 
57
58
  [project.entry-points."fanstatic.libraries"]
58
59
  wuttaweb_img = "wuttaweb.static:img"
59
60
 
60
-
61
61
  [project.entry-points."paste.app_factory"]
62
62
  main = "wuttaweb.app:main"
63
63
 
64
-
65
64
  [project.entry-points."wutta.app.providers"]
66
65
  wuttaweb = "wuttaweb.app:WebAppProvider"
67
66
 
68
-
69
67
  [project.entry-points."wutta.config.extensions"]
70
68
  wuttaweb = "wuttaweb.conf:WuttaWebConfigExtension"
71
69
 
70
+ [project.entry-points."wutta.typer_imports"]
71
+ wuttaweb = "wuttaweb.cli"
72
+
72
73
 
73
74
  [project.urls]
74
75
  Homepage = "https://wuttaproject.org/"
@@ -24,17 +24,22 @@
24
24
  Application
25
25
  """
26
26
 
27
+ import logging
27
28
  import os
28
29
 
29
30
  from wuttjamaican.app import AppProvider
30
31
  from wuttjamaican.conf import make_config
31
32
 
33
+ from asgiref.wsgi import WsgiToAsgi
32
34
  from pyramid.config import Configurator
33
35
 
34
36
  import wuttaweb.db
35
37
  from wuttaweb.auth import WuttaSecurityPolicy
36
38
 
37
39
 
40
+ log = logging.getLogger(__name__)
41
+
42
+
38
43
  class WebAppProvider(AppProvider):
39
44
  """
40
45
  The :term:`app provider` for WuttaWeb. This adds some methods to
@@ -87,17 +92,20 @@ def make_wutta_config(settings, config_maker=None, **kwargs):
87
92
 
88
93
  If this config file path cannot be discovered, an error is raised.
89
94
  """
90
- # validate config file path
91
- path = settings.get('wutta.config')
92
- if not path or not os.path.exists(path):
93
- raise ValueError("Please set 'wutta.config' in [app:main] "
94
- "section of config to the path of your "
95
- "config file. Lame, but necessary.")
96
-
97
- # make config, add to settings
98
- config_maker = config_maker or make_config
99
- wutta_config = config_maker(path, **kwargs)
100
- settings['wutta_config'] = wutta_config
95
+ wutta_config = settings.get('wutta_config')
96
+ if not wutta_config:
97
+
98
+ # validate config file path
99
+ path = settings.get('wutta.config')
100
+ if not path or not os.path.exists(path):
101
+ raise ValueError("Please set 'wutta.config' in [app:main] "
102
+ "section of config to the path of your "
103
+ "config file. Lame, but necessary.")
104
+
105
+ # make config, add to settings
106
+ config_maker = config_maker or make_config
107
+ wutta_config = config_maker(path, **kwargs)
108
+ settings['wutta_config'] = wutta_config
101
109
 
102
110
  # configure database sessions
103
111
  if hasattr(wutta_config, 'appdb_engine'):
@@ -148,19 +156,18 @@ def make_pyramid_config(settings):
148
156
 
149
157
  def main(global_config, **settings):
150
158
  """
151
- This function returns a Pyramid WSGI application.
152
-
153
- Typically there is no need to call this function directly, but it
154
- may be configured as the web app entry point like so:
155
-
156
- .. code-block:: ini
157
-
158
- [app:main]
159
- use = egg:wuttaweb
160
-
161
- The app returned by this function is quite minimal, so most apps
162
- will need to define their own ``main()`` function, and use that
163
- instead.
159
+ Make and return the WSGI application, per given settings.
160
+
161
+ This function is designed to be called via Paste, hence it does
162
+ require params and therefore can't be used directly as app factory
163
+ for general WSGI servers. For the latter see
164
+ :func:`make_wsgi_app()` instead.
165
+
166
+ And this *particular* function is not even that useful, it only
167
+ constructs an app with minimal views built-in to WuttaWeb. Most
168
+ apps will define their own ``main()`` function (e.g. as
169
+ ``poser.web.app:main``), similar to this one but with additional
170
+ views and other config.
164
171
  """
165
172
  wutta_config = make_wutta_config(settings)
166
173
  pyramid_config = make_pyramid_config(settings)
@@ -170,3 +177,71 @@ def main(global_config, **settings):
170
177
  pyramid_config.include('wuttaweb.views')
171
178
 
172
179
  return pyramid_config.make_wsgi_app()
180
+
181
+
182
+ def make_wsgi_app(main_app=None, config=None):
183
+ """
184
+ Make and return a WSGI app, using the given Paste app factory.
185
+
186
+ See also :func:`make_asgi_app()` for the ASGI equivalent.
187
+
188
+ This function could be used directly for general WSGI servers
189
+ (e.g. uvicorn), ***if*** you just want the built-in :func:`main()`
190
+ app factory.
191
+
192
+ But most likely you do not, in which case you must define your own
193
+ function and call this one with your preferred app factory::
194
+
195
+ from wuttaweb.app import make_wsgi_app
196
+
197
+ def my_main(global_config, **settings):
198
+ # TODO: build your app
199
+ pass
200
+
201
+ def make_my_wsgi_app():
202
+ return make_wsgi_app(my_main)
203
+
204
+ So ``make_my_wsgi_app()`` could then be used as-is for general
205
+ WSGI servers. However, note that this approach will require
206
+ setting the ``WUTTA_CONFIG_FILES`` environment variable, unless
207
+ running via :ref:`wutta-webapp`.
208
+
209
+ :param main_app: Either a Paste-compatible app factory, or
210
+ :term:`spec` for one. If not specified, the built-in
211
+ :func:`main()` is assumed.
212
+
213
+ :param config: Optional :term:`config object`. If not specified,
214
+ one is created based on ``WUTTA_CONFIG_FILES`` environment
215
+ variable.
216
+ """
217
+ if not config:
218
+ config = make_config()
219
+ app = config.get_app()
220
+
221
+ # extract pyramid settings
222
+ settings = config.get_dict('app:main')
223
+
224
+ # keep same config object
225
+ settings['wutta_config'] = config
226
+
227
+ # determine the app factory
228
+ if isinstance(main_app, str):
229
+ make_wsgi_app = app.load_object(main_app)
230
+ elif callable(main_app):
231
+ make_wsgi_app = main_app
232
+ else:
233
+ raise ValueError("main_app must be spec or callable")
234
+
235
+ # construct a pyramid app "per usual"
236
+ return make_wsgi_app({}, **settings)
237
+
238
+
239
+ def make_asgi_app(main_app=None, config=None):
240
+ """
241
+ Make and return a ASGI app, using the given Paste app factory.
242
+
243
+ This works the same as :func:`make_wsgi_app()` and should be
244
+ called in the same way etc.
245
+ """
246
+ wsgi_app = make_wsgi_app(main_app, config=config)
247
+ return WsgiToAsgi(wsgi_app)
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8; -*-
2
+ ################################################################################
3
+ #
4
+ # wuttaweb -- Web App for Wutta Framework
5
+ # Copyright © 2024 Lance Edgar
6
+ #
7
+ # This file is part of Wutta Framework.
8
+ #
9
+ # Wutta Framework is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU General Public License as published by the Free
11
+ # Software Foundation, either version 3 of the License, or (at your option) any
12
+ # later version.
13
+ #
14
+ # Wutta Framework is distributed in the hope that it will be useful, but
15
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17
+ # more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License along with
20
+ # Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ ################################################################################
23
+ """
24
+ WuttaWeb - ``wutta`` subcommands
25
+ """
26
+
27
+ # nb. must bring in all modules for discovery to work
28
+ from . import webapp
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8; -*-
2
+ ################################################################################
3
+ #
4
+ # wuttaweb -- Web App for Wutta Framework
5
+ # Copyright © 2024 Lance Edgar
6
+ #
7
+ # This file is part of Wutta Framework.
8
+ #
9
+ # Wutta Framework is free software: you can redistribute it and/or modify it
10
+ # under the terms of the GNU General Public License as published by the Free
11
+ # Software Foundation, either version 3 of the License, or (at your option) any
12
+ # later version.
13
+ #
14
+ # Wutta Framework is distributed in the hope that it will be useful, but
15
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17
+ # more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License along with
20
+ # Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
21
+ #
22
+ ################################################################################
23
+ """
24
+ See also: :ref:`wutta-webapp`
25
+ """
26
+
27
+ import os
28
+ import sys
29
+ from typing_extensions import Annotated
30
+
31
+ import typer
32
+ from pyramid.scripts import pserve
33
+
34
+ from wuttjamaican.cli import wutta_typer
35
+
36
+
37
+ @wutta_typer.command()
38
+ def webapp(
39
+ ctx: typer.Context,
40
+ auto_reload: Annotated[
41
+ bool,
42
+ typer.Option('--reload', '-r',
43
+ help="Auto-reload web app when files change.")] = False,
44
+ ):
45
+ """
46
+ Run the configured web app
47
+ """
48
+ config = ctx.parent.wutta_config
49
+
50
+ # we'll need config file(s) to specify for web app
51
+ if not config.files_read:
52
+ sys.stderr.write("no config files found!\n")
53
+ sys.exit(1)
54
+
55
+ runner = config.get(f'{config.appname}.web.app.runner', default='pserve')
56
+ if runner == 'pserve':
57
+
58
+ # run pserve
59
+ argv = ['pserve', f'file+ini:{config.files_read[0]}']
60
+ if ctx.params['auto_reload']:
61
+ argv.append('--reload')
62
+ pserve.main(argv=argv)
63
+
64
+ elif runner == 'uvicorn':
65
+
66
+ import uvicorn
67
+
68
+ # need service details from config
69
+ spec = config.require(f'{config.appname}.web.app.spec')
70
+ kw = {
71
+ 'host': config.get(f'{config.appname}.web.app.host', default='127.0.0.1'),
72
+ 'port': config.get_int(f'{config.appname}.web.app.port', default=8000),
73
+ 'reload': ctx.params['auto_reload'],
74
+ 'reload_dirs': config.get_list(f'{config.appname}.web.app.reload_dirs'),
75
+ 'factory': config.get_bool(f'{config.appname}.web.app.factory', default=False),
76
+ 'interface': config.get(f'{config.appname}.web.app.interface', default='auto'),
77
+ }
78
+
79
+ # also must inject our config files to env, since there is no
80
+ # other way to specify when running via uvicorn
81
+ os.environ['WUTTA_CONFIG_FILES'] = os.pathsep.join(config.files_read)
82
+
83
+ # run uvicorn
84
+ uvicorn.run(spec, **kw)
85
+
86
+ else:
87
+ sys.stderr.write(f"unknown web app runner: {runner}\n")
88
+ sys.exit(2)
@@ -0,0 +1,104 @@
1
+ # -*- coding: utf-8; -*-
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ from wuttjamaican.testing import ConfigTestCase
6
+
7
+ from wuttaweb.cli import webapp as mod
8
+
9
+
10
+ class TestWebapp(ConfigTestCase):
11
+
12
+ def make_context(self, **kwargs):
13
+ params = {'auto_reload': False}
14
+ params.update(kwargs.get('params', {}))
15
+ ctx = MagicMock(params=params)
16
+ ctx.parent.wutta_config = self.config
17
+ return ctx
18
+
19
+ def test_missing_config_file(self):
20
+ # nb. our default config has no files, so can test w/ that
21
+ ctx = self.make_context()
22
+ with patch.object(mod, 'sys') as sys:
23
+ sys.exit.side_effect = RuntimeError
24
+ self.assertRaises(RuntimeError, mod.webapp, ctx)
25
+ sys.stderr.write.assert_called_once_with("no config files found!\n")
26
+ sys.exit.assert_called_once_with(1)
27
+
28
+ def test_invalid_runner(self):
29
+
30
+ # make new config from file, with bad setting
31
+ path = self.write_file('my.conf', """
32
+ [wutta.web]
33
+ app.runner = bogus
34
+ """)
35
+ self.config = self.make_config(files=[path])
36
+
37
+ ctx = self.make_context()
38
+ with patch.object(mod, 'sys') as sys:
39
+ sys.exit.side_effect = RuntimeError
40
+ self.assertRaises(RuntimeError, mod.webapp, ctx)
41
+ sys.stderr.write.assert_called_once_with("unknown web app runner: bogus\n")
42
+ sys.exit.assert_called_once_with(2)
43
+
44
+ def test_pserve(self):
45
+
46
+ path = self.write_file('my.conf', """
47
+ [wutta.web]
48
+ app.runner = pserve
49
+ """)
50
+ self.config = self.make_config(files=[path])
51
+
52
+ # normal
53
+ with patch.object(mod, 'pserve') as pserve:
54
+ ctx = self.make_context()
55
+ mod.webapp(ctx)
56
+ pserve.main.assert_called_once_with(argv=['pserve', f'file+ini:{path}'])
57
+
58
+ # with reload
59
+ with patch.object(mod, 'pserve') as pserve:
60
+ ctx = self.make_context(params={'auto_reload': True})
61
+ mod.webapp(ctx)
62
+ pserve.main.assert_called_once_with(argv=['pserve', f'file+ini:{path}', '--reload'])
63
+
64
+ def test_uvicorn(self):
65
+
66
+ path = self.write_file('my.conf', """
67
+ [wutta.web]
68
+ app.runner = uvicorn
69
+ app.spec = wuttaweb.app:make_wsgi_app
70
+ """)
71
+ self.config = self.make_config(files=[path])
72
+
73
+ orig_import = __import__
74
+ uvicorn = MagicMock()
75
+
76
+ def mock_import(name, *args, **kwargs):
77
+ if name == 'uvicorn':
78
+ return uvicorn
79
+ return orig_import(name, *args, **kwargs)
80
+
81
+ # normal
82
+ with patch('builtins.__import__', side_effect=mock_import):
83
+ ctx = self.make_context()
84
+ mod.webapp(ctx)
85
+ uvicorn.run.assert_called_once_with('wuttaweb.app:make_wsgi_app',
86
+ host='127.0.0.1',
87
+ port=8000,
88
+ reload=False,
89
+ reload_dirs=None,
90
+ factory=False,
91
+ interface='auto')
92
+
93
+ # with reload
94
+ uvicorn.run.reset_mock()
95
+ with patch('builtins.__import__', side_effect=mock_import):
96
+ ctx = self.make_context(params={'auto_reload': True})
97
+ mod.webapp(ctx)
98
+ uvicorn.run.assert_called_once_with('wuttaweb.app:make_wsgi_app',
99
+ host='127.0.0.1',
100
+ port=8000,
101
+ reload=True,
102
+ reload_dirs=None,
103
+ factory=False,
104
+ interface='auto')