fastapi-rtk 2.11.4__tar.gz → 2.12.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 (308) hide show
  1. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/PKG-INFO +1 -1
  2. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/__init__.py +17 -0
  3. fastapi_rtk-2.12.0/fastapi_rtk/_version.py +1 -0
  4. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/auth.py +22 -1
  5. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/__init__.py +3 -0
  6. fastapi_rtk-2.12.0/fastapi_rtk/bases/cache.py +85 -0
  7. fastapi_rtk-2.12.0/fastapi_rtk/caches/__init__.py +10 -0
  8. fastapi_rtk-2.12.0/fastapi_rtk/caches/cache.py +78 -0
  9. fastapi_rtk-2.12.0/fastapi_rtk/caches/serializers.py +95 -0
  10. fastapi_rtk-2.12.0/fastapi_rtk/caches/storages.py +78 -0
  11. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/db.py +26 -2
  12. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/dependencies.py +5 -1
  13. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/fastapi_react_toolkit.py +28 -0
  14. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/globals.py +14 -0
  15. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/manager.py +45 -4
  16. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/apis.py +62 -1
  17. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/models.py +7 -0
  18. fastapi_rtk-2.12.0/fastapi_rtk/security/sqla/user_cache.py +178 -0
  19. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/setting.py +15 -0
  20. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/test_auth.py +151 -26
  21. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/test_apis.py +63 -0
  22. fastapi_rtk-2.12.0/tests/unit/test_caches.py +166 -0
  23. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_db.py +56 -0
  24. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_dependencies.py +24 -4
  25. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_manager.py +85 -0
  26. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_setting.py +2 -0
  27. fastapi_rtk-2.12.0/tests/unit/test_user_cache.py +199 -0
  28. fastapi_rtk-2.11.4/fastapi_rtk/_version.py +0 -1
  29. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/.gitignore +0 -0
  30. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/LICENSE +0 -0
  31. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/README.md +0 -0
  32. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/__init__.py +0 -0
  33. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/base_api.py +0 -0
  34. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
  35. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_add_mixin.py +0 -0
  36. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_bulk_mixin.py +0 -0
  37. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_columns_mixin.py +0 -0
  38. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_delete_mixin.py +0 -0
  39. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_download_mixin.py +0 -0
  40. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_edit_mixin.py +0 -0
  41. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_file_mixin.py +0 -0
  42. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_info_mixin.py +0 -0
  43. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_list_mixin.py +0 -0
  44. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_params.py +0 -0
  45. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_show_mixin.py +0 -0
  46. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_typed_base.py +0 -0
  47. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/api/model_rest_api/_upload_mixin.py +0 -0
  48. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/apis/__init__.py +0 -0
  49. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/apis/info.py +0 -0
  50. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/apis/license.py +0 -0
  51. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/__init__.py +0 -0
  52. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  53. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  54. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  55. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/ldap.py +0 -0
  56. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  57. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  58. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  59. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/config.py +0 -0
  60. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/db.py +0 -0
  61. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  62. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/__init__.py +0 -0
  63. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/__init__.py +0 -0
  64. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/column.py +0 -0
  65. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/db.py +0 -0
  66. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  67. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/filters.py +0 -0
  68. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/interface.py +0 -0
  69. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/model.py +0 -0
  70. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/generic/session.py +0 -0
  71. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/__init__.py +0 -0
  72. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/column.py +0 -0
  73. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/db.py +0 -0
  74. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  75. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  76. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  77. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_mixin.py +0 -0
  78. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_mixin.py +0 -0
  79. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
  80. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/_processing_mixin.py +0 -0
  81. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  82. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  83. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  84. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  85. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  86. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/filters.py +0 -0
  87. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/interface.py +0 -0
  88. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/model.py +0 -0
  89. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/backends/sqla/session.py +0 -0
  90. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/db.py +0 -0
  91. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/file_manager.py +0 -0
  92. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/filter.py +0 -0
  93. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/interface.py +0 -0
  94. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/model.py +0 -0
  95. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/bases/session.py +0 -0
  96. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/__init__.py +0 -0
  97. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/cli.py +0 -0
  98. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/__init__.py +0 -0
  99. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
  100. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  101. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  102. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  103. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  104. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  105. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  106. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  107. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  108. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  109. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/export.py +0 -0
  110. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/security.py +0 -0
  111. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/commands/translate.py +0 -0
  112. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/const.py +0 -0
  113. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/decorators.py +0 -0
  114. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/types.py +0 -0
  115. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/cli/utils.py +0 -0
  116. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/config.py +0 -0
  117. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/const.py +0 -0
  118. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/decorators.py +0 -0
  119. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/exceptions.py +0 -0
  120. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/__init__.py +0 -0
  121. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/file_manager.py +0 -0
  122. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/image_manager.py +0 -0
  123. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
  124. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  125. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/filters.py +0 -0
  126. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/__init__.py +0 -0
  127. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel/__init__.py +0 -0
  128. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel/cli.py +0 -0
  129. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel/config.py +0 -0
  130. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/babel.cfg +0 -0
  131. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/lazy_text.py +0 -0
  132. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/messages.pot +0 -0
  133. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  134. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
  135. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  136. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
  137. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/middlewares.py +0 -0
  138. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/mixins.py +0 -0
  139. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/models.py +0 -0
  140. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/py.typed +0 -0
  141. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/registry.py +0 -0
  142. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/routers.py +0 -0
  143. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/schemas.py +0 -0
  144. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/__init__.py +0 -0
  145. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/__init__.py +0 -0
  146. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
  147. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_association_mixin.py +0 -0
  148. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_builtin_role_mixin.py +0 -0
  149. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_cleanup_mixin.py +0 -0
  150. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
  151. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
  152. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
  153. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_role_mixin.py +0 -0
  154. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/security/sqla/security_manager/_user_mixin.py +0 -0
  155. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/types.py +0 -0
  156. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/__init__.py +0 -0
  157. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/async_task_runner.py +0 -0
  158. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/class_factory.py +0 -0
  159. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  160. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/deep_merge.py +0 -0
  161. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/extender_mixin.py +0 -0
  162. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  163. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/formatter.py +0 -0
  164. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/hooks.py +0 -0
  165. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/lazy.py +0 -0
  166. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/merge_schema.py +0 -0
  167. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  168. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/pydantic.py +0 -0
  169. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/run_utils.py +0 -0
  170. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/self_dependencies.py +0 -0
  171. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  172. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/sqla.py +0 -0
  173. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/timezone.py +0 -0
  174. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/update_signature.py +0 -0
  175. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  176. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/fastapi_rtk/version.py +0 -0
  177. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/pyproject.toml +0 -0
  178. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/conftest.py +0 -0
  179. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/docker-compose.gis.yml +0 -0
  180. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/__init__.py +0 -0
  181. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/api/__init__.py +0 -0
  182. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/api/test_model_rest_api.py +0 -0
  183. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/apis/__init__.py +0 -0
  184. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/apis/test_info_callable_schema.py +0 -0
  185. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/apis/test_info_contract.py +0 -0
  186. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/auth/__init__.py +0 -0
  187. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/auth/test_auth_flow.py +0 -0
  188. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/auth/test_oauth.py +0 -0
  189. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/conftest.py +0 -0
  190. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/file_managers/__init__.py +0 -0
  191. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/file_managers/test_file_routes.py +0 -0
  192. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/security/__init__.py +0 -0
  193. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/security/sqla/__init__.py +0 -0
  194. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/security/sqla/test_security_manager.py +0 -0
  195. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/integration/test_app_smoke.py +0 -0
  196. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/__init__.py +0 -0
  197. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/__init__.py +0 -0
  198. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/conftest.py +0 -0
  199. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/__init__.py +0 -0
  200. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_columns.py +0 -0
  201. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
  202. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_init.py +0 -0
  203. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/model_rest_api/test_typed_base_consistency.py +0 -0
  204. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/api/test_base_api.py +0 -0
  205. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/apis/__init__.py +0 -0
  206. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/apis/test_info.py +0 -0
  207. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/apis/test_license.py +0 -0
  208. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/__init__.py +0 -0
  209. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/hashers/__init__.py +0 -0
  210. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
  211. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/hashers/test_scrypt.py +0 -0
  212. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/password_helpers/__init__.py +0 -0
  213. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/password_helpers/test_fab.py +0 -0
  214. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/__init__.py +0 -0
  215. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/test_config.py +0 -0
  216. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/test_db.py +0 -0
  217. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/auth/strategies/test_jwt.py +0 -0
  218. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/__init__.py +0 -0
  219. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/__init__.py +0 -0
  220. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_column.py +0 -0
  221. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_db.py +0 -0
  222. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_exceptions.py +0 -0
  223. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_filters.py +0 -0
  224. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_interface.py +0 -0
  225. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_model.py +0 -0
  226. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/generic/test_session.py +0 -0
  227. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/__init__.py +0 -0
  228. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/conftest.py +0 -0
  229. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_audit.py +0 -0
  230. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_audit_types.py +0 -0
  231. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_column.py +0 -0
  232. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_db.py +0 -0
  233. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_filters.py +0 -0
  234. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
  235. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_interface.py +0 -0
  236. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_model.py +0 -0
  237. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/backends/sqla/test_session.py +0 -0
  238. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/__init__.py +0 -0
  239. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_db.py +0 -0
  240. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_file_manager.py +0 -0
  241. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_filter.py +0 -0
  242. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_interface.py +0 -0
  243. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_model.py +0 -0
  244. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/bases/test_session.py +0 -0
  245. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/__init__.py +0 -0
  246. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/__init__.py +0 -0
  247. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/conftest.py +0 -0
  248. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_db.py +0 -0
  249. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_export.py +0 -0
  250. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_security.py +0 -0
  251. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_security_crypto.py +0 -0
  252. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/commands/test_translate.py +0 -0
  253. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/conftest.py +0 -0
  254. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_cli.py +0 -0
  255. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_decorators.py +0 -0
  256. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_types.py +0 -0
  257. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/cli/test_utils.py +0 -0
  258. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/__init__.py +0 -0
  259. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/conftest.py +0 -0
  260. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_file_manager.py +0 -0
  261. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_image_manager.py +0 -0
  262. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
  263. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
  264. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/__init__.py +0 -0
  265. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/test_babel_cli.py +0 -0
  266. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/test_babel_config.py +0 -0
  267. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/lang/test_lazy_text.py +0 -0
  268. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/__init__.py +0 -0
  269. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/__init__.py +0 -0
  270. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/conftest.py +0 -0
  271. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/test_models.py +0 -0
  272. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/security/sqla/test_security_manager.py +0 -0
  273. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_config.py +0 -0
  274. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_const.py +0 -0
  275. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_decorators.py +0 -0
  276. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_exceptions.py +0 -0
  277. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_fastapi_react_toolkit.py +0 -0
  278. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_filters.py +0 -0
  279. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_globals.py +0 -0
  280. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_middlewares.py +0 -0
  281. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_mixins.py +0 -0
  282. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_models.py +0 -0
  283. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_registry.py +0 -0
  284. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_routers.py +0 -0
  285. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_schemas.py +0 -0
  286. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_types.py +0 -0
  287. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/test_version.py +0 -0
  288. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/__init__.py +0 -0
  289. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_async_task_runner.py +0 -0
  290. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_class_factory.py +0 -0
  291. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_csv_json_converter.py +0 -0
  292. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_deep_merge.py +0 -0
  293. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_extender_mixin.py +0 -0
  294. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
  295. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_formatter.py +0 -0
  296. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_hooks.py +0 -0
  297. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_lazy.py +0 -0
  298. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_merge_schema.py +0 -0
  299. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
  300. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_pydantic.py +0 -0
  301. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_run_utils.py +0 -0
  302. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_self_dependencies.py +0 -0
  303. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_smartdefaultdict.py +0 -0
  304. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_sqla.py +0 -0
  305. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_timezone.py +0 -0
  306. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_update_signature.py +0 -0
  307. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/tests/unit/utils/test_use_default_when_none.py +0 -0
  308. {fastapi_rtk-2.11.4 → fastapi_rtk-2.12.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-rtk
3
- Version: 2.11.4
3
+ Version: 2.12.0
4
4
  Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
5
5
  Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
6
6
  Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
@@ -7,6 +7,7 @@ from .auth import *
7
7
  from .backends.generic import *
8
8
  from .backends.sqla import *
9
9
  from .bases import *
10
+ from .caches import *
10
11
  from .const import *
11
12
  from .db import *
12
13
  from .decorators import *
@@ -22,6 +23,7 @@ from .routers import *
22
23
  from .schemas import *
23
24
  from .security import *
24
25
  from .security.sqla.apis import * #! Manually import security APIs to prevent circular imports
26
+ from .security.sqla.user_cache import *
25
27
  from .setting import *
26
28
  from .types import *
27
29
  from .utils import *
@@ -143,6 +145,8 @@ __all__ = [
143
145
  "SQLASessionMaker",
144
146
  "SQLAScopedSession",
145
147
  # .bases
148
+ "AbstractCacheSerializer",
149
+ "AbstractCacheStorage",
146
150
  "DBQueryParams",
147
151
  "AbstractQueryBuilder",
148
152
  "BaseFileException",
@@ -157,6 +161,11 @@ __all__ = [
157
161
  "MissingGreenletError",
158
162
  "BasicModel",
159
163
  "AbstractSession",
164
+ # .caches
165
+ "Cache",
166
+ "JSONCacheSerializer",
167
+ "PickleCacheSerializer",
168
+ "InMemoryCacheStorage",
160
169
  # .const
161
170
  "logger",
162
171
  # .db
@@ -236,6 +245,14 @@ __all__ = [
236
245
  "PermissionViewApi",
237
246
  "UsersApi",
238
247
  "RolesApi",
248
+ # .security.sqla.user_cache
249
+ "USER_CACHE_KEY_PREFIX",
250
+ "CachedUser",
251
+ "CachedUserDatabase",
252
+ "get_cached_user",
253
+ "set_cached_user",
254
+ "invalidate_user_cache",
255
+ "clear_user_cache",
239
256
  # .setting
240
257
  "Setting",
241
258
  # .types
@@ -0,0 +1 @@
1
+ __version__ = "2.12.0"
@@ -12,6 +12,7 @@ import makefun
12
12
  from ..const import DEFAULT_COOKIE_NAME, DEFAULT_SECRET, DEFAULT_TOKEN_URL, logger
13
13
  from ..db import User, UserDatabase, get_user_db
14
14
  from ..manager import UserManager
15
+ from ..security.sqla.user_cache import CachedUserDatabase
15
16
  from .strategies import Strategy, StrategyConfig
16
17
  from .strategies.jwt import get_jwt_strategy_generator
17
18
 
@@ -85,9 +86,10 @@ class Authenticator(fastapi_users.authentication.Authenticator):
85
86
  get_enabled_backends: fastapi_users.authentication.authenticator.EnabledBackendsDependency
86
87
  | None = None,
87
88
  default_to_none: bool = False,
89
+ use_cache: bool = False,
88
90
  ):
89
91
  """
90
- Modified version of the default `current_user` function. This adds `default_to_none` parameter.
92
+ Modified version of the default `current_user` function. This adds `default_to_none` and `use_cache` parameters.
91
93
 
92
94
  Return a dependency callable to retrieve currently authenticated user.
93
95
 
@@ -108,6 +110,9 @@ class Authenticator(fastapi_users.authentication.Authenticator):
108
110
  By default, all specified authentication backends are enabled.
109
111
  Please note however that every backend will appear in the OpenAPI documentation,
110
112
  as FastAPI resolves it statically.
113
+ :param use_cache: If `True`, the user is looked up through the user cache honoring `AUTH_USER_CACHE_TTL`,
114
+ leaving the session untouched on a hit. Only for read paths: a cached user is detached and carries no
115
+ fresher state than the entry. Defaults to `False`.
111
116
  """
112
117
  signature = self._get_dependency_signature(get_enabled_backends)
113
118
 
@@ -120,6 +125,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
120
125
  verified=verified,
121
126
  superuser=superuser,
122
127
  default_to_none=default_to_none,
128
+ use_cache=use_cache,
123
129
  **kwargs,
124
130
  )
125
131
  return user
@@ -164,6 +170,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
164
170
  verified: bool = False,
165
171
  superuser: bool = False,
166
172
  default_to_none: bool = False,
173
+ use_cache: bool = False,
167
174
  strategy_params: tuple[tuple, dict[str, typing.Any]] | None = None,
168
175
  ):
169
176
  """
@@ -178,6 +185,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
178
185
  verified (bool, optional): If `True`, the authenticated user must be verified. Defaults to False.
179
186
  superuser (bool, optional): If `True`, the authenticated user must be a superuser. Defaults to False.
180
187
  default_to_none (bool, optional): If `True`, return `None` if there is no authenticated user instead of raising a HTTPException. Defaults to False.
188
+ use_cache (bool, optional): If `True`, the user is looked up through the user cache honoring `AUTH_USER_CACHE_TTL`, leaving the session untouched on a hit. Only for read paths: a cached user is detached and carries no fresher state than the entry. Defaults to False.
181
189
  strategy_params (tuple[tuple, dict[str, typing.Any]] | None, optional): Parameters for the authentication strategy. Defaults to None.
182
190
 
183
191
  Returns:
@@ -191,6 +199,7 @@ class Authenticator(fastapi_users.authentication.Authenticator):
191
199
  verified=verified,
192
200
  superuser=superuser,
193
201
  default_to_none=default_to_none,
202
+ use_cache=use_cache,
194
203
  **(strategy_params[1] if strategy_params else {}),
195
204
  )
196
205
 
@@ -205,8 +214,15 @@ class Authenticator(fastapi_users.authentication.Authenticator):
205
214
  verified: bool = False,
206
215
  superuser: bool = False,
207
216
  default_to_none: bool = False,
217
+ use_cache: bool = False,
208
218
  **kwargs,
209
219
  ) -> tuple[fastapi_users.models.UP | None, str | None]:
220
+ user_db = user_manager.user_db
221
+ if use_cache:
222
+ user_manager.user_db = CachedUserDatabase(
223
+ user_db.session, user_db.user_table, user_db.oauth_account_table
224
+ )
225
+
210
226
  user, token = None, None
211
227
  try:
212
228
  user, token = await super()._authenticate(
@@ -221,6 +237,11 @@ class Authenticator(fastapi_users.authentication.Authenticator):
221
237
  except fastapi.HTTPException as e:
222
238
  if not default_to_none:
223
239
  raise e
240
+ finally:
241
+ # The user manager is shared with the rest of the request, and only this
242
+ # lookup may answer from the cache: a cached user is detached, so a later
243
+ # write through it would clash with the session's own copy of that row.
244
+ user_manager.user_db = user_db
224
245
  return user, token
225
246
 
226
247
 
@@ -1,3 +1,4 @@
1
+ from .cache import *
1
2
  from .db import *
2
3
  from .file_manager import *
3
4
  from .filter import *
@@ -6,6 +7,8 @@ from .model import *
6
7
  from .session import *
7
8
 
8
9
  __all__ = [
10
+ "AbstractCacheSerializer",
11
+ "AbstractCacheStorage",
9
12
  "DBQueryParams",
10
13
  "AbstractQueryBuilder",
11
14
  "BaseFileException",
@@ -0,0 +1,85 @@
1
+ import abc
2
+ import typing
3
+
4
+ __all__ = ["AbstractCacheSerializer", "AbstractCacheStorage"]
5
+
6
+
7
+ class AbstractCacheSerializer(abc.ABC):
8
+ """
9
+ Converts a cached value to and from the form a storage can hold.
10
+
11
+ Implement this to control the wire format of a storage that cannot hold Python objects, e.g. JSON or pickle for redis. The signature picks how `Cache` schedules the call: an `async def` is awaited on the event loop, while a plain `def` is treated as heavy and runs in a thread. Declare `async def` unless the conversion is costly enough to be worth a thread hop.
12
+ """
13
+
14
+ @abc.abstractmethod
15
+ async def dumps(self, value: typing.Any) -> typing.Any:
16
+ """
17
+ Convert a value into its stored form.
18
+
19
+ Args:
20
+ value (typing.Any): The value handed to `Cache.set`.
21
+
22
+ Returns:
23
+ typing.Any: The value to hand to the storage.
24
+ """
25
+
26
+ @abc.abstractmethod
27
+ async def loads(self, value: typing.Any) -> typing.Any:
28
+ """
29
+ Convert a stored value back into its original form.
30
+
31
+ Args:
32
+ value (typing.Any): The value read from the storage.
33
+
34
+ Returns:
35
+ typing.Any: The value to hand back to the caller.
36
+ """
37
+
38
+
39
+ class AbstractCacheStorage(abc.ABC):
40
+ """
41
+ Keeps already-serialized values under string keys, with a per-entry expiry.
42
+
43
+ Implement this to hold entries somewhere other than the process memory, e.g. redis or memcached.
44
+ """
45
+
46
+ @abc.abstractmethod
47
+ async def get(self, key: str) -> typing.Any | None:
48
+ """
49
+ Read the stored value under the given key.
50
+
51
+ Args:
52
+ key (str): The key to read.
53
+
54
+ Returns:
55
+ typing.Any | None: The stored value, or `None` when absent or expired.
56
+ """
57
+
58
+ @abc.abstractmethod
59
+ async def set(self, key: str, value: typing.Any, ttl: int) -> None:
60
+ """
61
+ Write a value under the given key.
62
+
63
+ Args:
64
+ key (str): The key to write.
65
+ value (typing.Any): The already-serialized value to store.
66
+ ttl (int): Seconds the entry stays valid.
67
+ """
68
+
69
+ @abc.abstractmethod
70
+ async def delete(self, key: str) -> None:
71
+ """
72
+ Drop the entry under the given key.
73
+
74
+ Args:
75
+ key (str): The key to drop.
76
+ """
77
+
78
+ @abc.abstractmethod
79
+ async def clear(self, prefix: str | None = None) -> None:
80
+ """
81
+ Drop every entry, or every entry whose key starts with the given prefix.
82
+
83
+ Args:
84
+ prefix (str | None, optional): Only drop keys starting with this. Defaults to None, which drops everything.
85
+ """
@@ -0,0 +1,10 @@
1
+ from .cache import *
2
+ from .serializers import *
3
+ from .storages import *
4
+
5
+ __all__ = [
6
+ "Cache",
7
+ "JSONCacheSerializer",
8
+ "PickleCacheSerializer",
9
+ "InMemoryCacheStorage",
10
+ ]
@@ -0,0 +1,78 @@
1
+ import typing
2
+
3
+ from ..bases.cache import AbstractCacheSerializer, AbstractCacheStorage
4
+ from ..utils import smart_run
5
+ from .storages import InMemoryCacheStorage
6
+
7
+ __all__ = ["Cache"]
8
+
9
+
10
+ class Cache:
11
+ """
12
+ Cache pairing a storage with an optional serializer.
13
+
14
+ Swap the storage to move entries out of the process, and the serializer to match what that storage can hold.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ storage: AbstractCacheStorage | None = None,
20
+ serializer: AbstractCacheSerializer | None = None,
21
+ ):
22
+ """
23
+ Initialize the cache.
24
+
25
+ Args:
26
+ storage (AbstractCacheStorage | None, optional): Where entries are kept. Defaults to None, which uses an `InMemoryCacheStorage`.
27
+ serializer (AbstractCacheSerializer | None, optional): Applied on write and reversed on read. Defaults to None, which stores values as handed in and only works for a storage holding Python objects.
28
+ """
29
+ self.storage = storage or InMemoryCacheStorage()
30
+ self.serializer = serializer
31
+
32
+ async def get(self, key: str) -> typing.Any | None:
33
+ """
34
+ Read the value cached under the given key.
35
+
36
+ Args:
37
+ key (str): The key to read.
38
+
39
+ Returns:
40
+ typing.Any | None: The cached value, or `None` when absent or expired.
41
+ """
42
+ value = await self.storage.get(key)
43
+ if value is None or self.serializer is None:
44
+ return value
45
+
46
+ return await smart_run(self.serializer.loads, value)
47
+
48
+ async def set(self, key: str, value: typing.Any, ttl: int) -> None:
49
+ """
50
+ Cache a value under the given key.
51
+
52
+ Args:
53
+ key (str): The key to write.
54
+ value (typing.Any): The value to cache.
55
+ ttl (int): Seconds the entry stays valid.
56
+ """
57
+ if self.serializer is not None:
58
+ value = await smart_run(self.serializer.dumps, value)
59
+
60
+ await self.storage.set(key, value, ttl)
61
+
62
+ async def delete(self, key: str) -> None:
63
+ """
64
+ Drop the entry cached under the given key.
65
+
66
+ Args:
67
+ key (str): The key to drop.
68
+ """
69
+ await self.storage.delete(key)
70
+
71
+ async def clear(self, prefix: str | None = None) -> None:
72
+ """
73
+ Drop every cached entry, or every entry whose key starts with the given prefix.
74
+
75
+ Args:
76
+ prefix (str | None, optional): Only drop keys starting with this. Defaults to None, which drops everything.
77
+ """
78
+ await self.storage.clear(prefix)
@@ -0,0 +1,95 @@
1
+ import json
2
+ import pickle
3
+ import typing
4
+
5
+ from ..bases.cache import AbstractCacheSerializer
6
+
7
+ __all__ = ["JSONCacheSerializer", "PickleCacheSerializer"]
8
+
9
+
10
+ class JSONCacheSerializer(AbstractCacheSerializer):
11
+ """
12
+ Stores values as JSON text.
13
+
14
+ Values must be JSON-native; a payload holding `datetime` or `Decimal` needs `PickleCacheSerializer` or a custom encoder.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ dumps_kwargs: dict[str, typing.Any] | None = None,
20
+ loads_kwargs: dict[str, typing.Any] | None = None,
21
+ ):
22
+ """
23
+ Initialize the serializer.
24
+
25
+ Args:
26
+ dumps_kwargs (dict[str, typing.Any] | None, optional): Keyword arguments passed to `json.dumps`, e.g. a custom `cls` encoder. Defaults to None.
27
+ loads_kwargs (dict[str, typing.Any] | None, optional): Keyword arguments passed to `json.loads`. Defaults to None.
28
+ """
29
+ self.dumps_kwargs = dumps_kwargs or {}
30
+ self.loads_kwargs = loads_kwargs or {}
31
+
32
+ async def dumps(self, value: typing.Any) -> str:
33
+ """
34
+ Convert a value into JSON text.
35
+
36
+ Args:
37
+ value (typing.Any): The value to store.
38
+
39
+ Returns:
40
+ str: The JSON text to hand to the storage.
41
+ """
42
+ return json.dumps(value, **self.dumps_kwargs)
43
+
44
+ async def loads(self, value: str) -> typing.Any:
45
+ """
46
+ Convert JSON text back into a value.
47
+
48
+ Args:
49
+ value (str): The JSON text read from the storage.
50
+
51
+ Returns:
52
+ typing.Any: The decoded value.
53
+ """
54
+ return json.loads(value, **self.loads_kwargs)
55
+
56
+
57
+ class PickleCacheSerializer(AbstractCacheSerializer):
58
+ """
59
+ Stores values as pickled bytes, keeping types JSON cannot express.
60
+
61
+ Only point this at a store the application itself owns: unpickling attacker-controlled bytes executes arbitrary code.
62
+ """
63
+
64
+ def __init__(self, protocol: int = pickle.HIGHEST_PROTOCOL):
65
+ """
66
+ Initialize the serializer.
67
+
68
+ Args:
69
+ protocol (int, optional): The pickle protocol to write with. Defaults to `pickle.HIGHEST_PROTOCOL`.
70
+ """
71
+ self.protocol = protocol
72
+
73
+ async def dumps(self, value: typing.Any) -> bytes:
74
+ """
75
+ Convert a value into pickled bytes.
76
+
77
+ Args:
78
+ value (typing.Any): The value to store.
79
+
80
+ Returns:
81
+ bytes: The pickled value to hand to the storage.
82
+ """
83
+ return pickle.dumps(value, protocol=self.protocol)
84
+
85
+ async def loads(self, value: bytes) -> typing.Any:
86
+ """
87
+ Convert pickled bytes back into a value.
88
+
89
+ Args:
90
+ value (bytes): The pickled value read from the storage.
91
+
92
+ Returns:
93
+ typing.Any: The unpickled value.
94
+ """
95
+ return pickle.loads(value)
@@ -0,0 +1,78 @@
1
+ import time
2
+ import typing
3
+
4
+ from ..bases.cache import AbstractCacheStorage
5
+
6
+ __all__ = ["InMemoryCacheStorage"]
7
+
8
+
9
+ class InMemoryCacheStorage(AbstractCacheStorage):
10
+ """
11
+ Storage backed by a dictionary in the worker process.
12
+
13
+ Each worker keeps its own entries, so a deletion only reaches the worker that performed it and the others fall back to the TTL.
14
+ """
15
+
16
+ def __init__(self):
17
+ """
18
+ Initialize the storage with no entries.
19
+ """
20
+ self._entries: dict[str, tuple[float, typing.Any]] = {}
21
+
22
+ async def get(self, key: str) -> typing.Any | None:
23
+ """
24
+ Read the stored value under the given key, dropping it when it expired.
25
+
26
+ Args:
27
+ key (str): The key to read.
28
+
29
+ Returns:
30
+ typing.Any | None: The stored value, or `None` when absent or expired.
31
+ """
32
+ entry = self._entries.get(key)
33
+ if entry is None:
34
+ return None
35
+
36
+ expires_at, value = entry
37
+ if expires_at <= time.monotonic():
38
+ self._entries.pop(key, None)
39
+ return None
40
+
41
+ return value
42
+
43
+ async def set(self, key: str, value: typing.Any, ttl: int) -> None:
44
+ """
45
+ Write a value under the given key, dropping entries that expired meanwhile.
46
+
47
+ Args:
48
+ key (str): The key to write.
49
+ value (typing.Any): The already-serialized value to store.
50
+ ttl (int): Seconds the entry stays valid.
51
+ """
52
+ now = time.monotonic()
53
+ for expired in [k for k, (exp, _) in self._entries.items() if exp <= now]:
54
+ self._entries.pop(expired, None)
55
+ self._entries[key] = (now + ttl, value)
56
+
57
+ async def delete(self, key: str) -> None:
58
+ """
59
+ Drop the entry under the given key.
60
+
61
+ Args:
62
+ key (str): The key to drop.
63
+ """
64
+ self._entries.pop(key, None)
65
+
66
+ async def clear(self, prefix: str | None = None) -> None:
67
+ """
68
+ Drop every entry, or every entry whose key starts with the given prefix.
69
+
70
+ Args:
71
+ prefix (str | None, optional): Only drop keys starting with this. Defaults to None, which drops everything.
72
+ """
73
+ if prefix is None:
74
+ self._entries.clear()
75
+ return
76
+
77
+ for key in [k for k in self._entries if k.startswith(prefix)]:
78
+ self._entries.pop(key, None)
@@ -71,7 +71,7 @@ class UserDatabase(
71
71
  statement = sqlalchemy.select(self.user_table).where(
72
72
  sqlalchemy.func.lower(self.user_table.email) == sqlalchemy.func.lower(email)
73
73
  )
74
- return await self._get_user(statement)
74
+ return await self._get_first_user(statement, field="email")
75
75
 
76
76
  async def get_by_oauth_account(
77
77
  self, oauth: str, account_id: str
@@ -149,7 +149,7 @@ class UserDatabase(
149
149
  sqlalchemy.func.lower(self.user_table.username)
150
150
  == sqlalchemy.func.lower(username)
151
151
  )
152
- return await self._get_user(statement)
152
+ return await self._get_first_user(statement, field="username")
153
153
 
154
154
  async def _get_user(
155
155
  self, statement: sqlalchemy.Select
@@ -157,6 +157,30 @@ class UserDatabase(
157
157
  results = await smart_run(self.session.execute, statement)
158
158
  return results.unique().scalar_one_or_none()
159
159
 
160
+ async def _get_first_user(
161
+ self, statement: sqlalchemy.Select, *, field: str
162
+ ) -> fastapi_users.models.UP | None:
163
+ """
164
+ Resolve a case-insensitive user lookup deterministically.
165
+
166
+ Legacy databases may contain rows that differ only in the casing of
167
+ `email`/`username` (the unique constraint is case-sensitive). Instead
168
+ of raising `MultipleResultsFound` (which surfaced as HTTP 500 on
169
+ login), the lowest-id matching row wins and a warning is logged.
170
+ """
171
+ statement = statement.order_by(self.user_table.id)
172
+ results = await smart_run(self.session.execute, statement)
173
+ users = results.unique().scalars().all()
174
+ if len(users) > 1:
175
+ logger.warning(
176
+ "Multiple users match %s case-insensitively (ids=%s). "
177
+ "Using the lowest-id row (id=%s); please de-duplicate the user table.",
178
+ field,
179
+ [user.id for user in users],
180
+ users[0].id,
181
+ )
182
+ return users[0] if users else None
183
+
160
184
 
161
185
  class lazy(lazy_self["DatabaseSessionManager", T]): ...
162
186
 
@@ -19,6 +19,8 @@ def set_global_user():
19
19
  """
20
20
  A dependency for FastAPI that will set the current user to the global variable `g.user`.
21
21
 
22
+ The user is read through `g.cache` when `AUTH_USER_CACHE_TTL` is set, so a cached request issues no query and never checks out a database connection for the lookup.
23
+
22
24
  Usage:
23
25
  ```python
24
26
  async def get_info(
@@ -31,7 +33,9 @@ def set_global_user():
31
33
 
32
34
  async def set_global_user_dependency(
33
35
  user: User | None = fastapi.Depends(
34
- g.auth.fastapi_users.current_user(active=True, default_to_none=True)
36
+ g.auth.fastapi_users.current_user(
37
+ active=True, default_to_none=True, use_cache=True
38
+ )
35
39
  ),
36
40
  ):
37
41
  g.user = user
@@ -20,6 +20,7 @@ from .api import BaseApi, ModelRestApi
20
20
  from .apis import InfoApi, LicenseApi
21
21
  from .auth import Auth
22
22
  from .backends.sqla.session import SQLASession
23
+ from .caches import Cache
23
24
  from .cli.commands.db import upgrade
24
25
  from .cli.commands.translate import init_babel_cli
25
26
  from .const import (
@@ -90,6 +91,7 @@ class FastAPIReactToolkit:
90
91
  exclude_apis (list[BASE_APIS] | None, optional): List of security APIs to be excluded when initializing the FastAPI application. Defaults to None.
91
92
  global_user_dependency (bool, optional): Whether to add the `set_global_user` dependency to the FastAPI application. This allows you to access the current user with the `g.user` object. Defaults to True.
92
93
  global_background_tasks (bool, optional): Whether to add the `set_global_background_tasks` dependency to the FastAPI application. This allows you to access the background tasks with the `g.background_tasks` object. Defaults to True.
94
+ cache (Cache | None, optional): The cache backend used by caching features such as `AUTH_USER_CACHE_TTL`, also reachable as `g.cache`. Defaults to None, which uses the `CACHE` config key or a `Cache` backed by an `InMemoryCacheStorage`.
93
95
  instrumentator (Instrumentator | None, optional): The instrumentator to use for monitoring the FastAPI application. Defaults to `Instrumentator(**Setting.INSTRUMENTATOR_CONFIG)`.
94
96
  on_startup (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is starting up. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
95
97
  on_shutdown (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is shutting down. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
@@ -212,6 +214,7 @@ class FastAPIReactToolkit:
212
214
  global_user_dependency: bool = True,
213
215
  global_background_tasks: bool = True,
214
216
  global_request: bool = True,
217
+ cache: Cache | None = None,
215
218
  instrumentator: prometheus_fastapi_instrumentator.Instrumentator | None = None,
216
219
  on_startup: (
217
220
  collections.abc.Callable[[fastapi.FastAPI], None]
@@ -246,6 +249,7 @@ class FastAPIReactToolkit:
246
249
  global_user_dependency (bool, optional): Whether to add the `set_global_user` dependency to the FastAPI application. This allows you to access the current user with the `g.user` object. Defaults to True.
247
250
  global_background_tasks (bool, optional): Whether to add the `set_global_background_tasks` dependency to the FastAPI application. This allows you to access the background tasks with the `g.background_tasks` object. Defaults to True.
248
251
  global_request (bool, optional): Whether to add the `set_global_request` dependency to the FastAPI application. This allows you to access the current request with the `g.request` object. Defaults to True.
252
+ cache (Cache | None, optional): The cache backend used by caching features such as `AUTH_USER_CACHE_TTL`, also reachable as `g.cache`. Defaults to None, which uses the `CACHE` config key or a `Cache` backed by an `InMemoryCacheStorage`.
249
253
  instrumentator (Instrumentator | None, optional): The instrumentator to use for monitoring the FastAPI application. Defaults to `Instrumentator(**Setting.INSTRUMENTATOR_CONFIG)`.
250
254
  on_startup (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is starting up. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
251
255
  on_shutdown (Callable[[FastAPI], None] | Awaitable[Callable[[FastAPI], None]] | None, optional): Function to call when the app is shutting down. Can either be a regular function or an async function. If the function takes a `FastAPI` instance as an argument, it will be passed to the function. Defaults to None.
@@ -265,6 +269,8 @@ class FastAPIReactToolkit:
265
269
  self.global_user_dependency = global_user_dependency
266
270
  self.global_background_tasks = global_background_tasks
267
271
  self.global_request = global_request
272
+ if cache is not None:
273
+ self.cache = cache
268
274
  if instrumentator is not None:
269
275
  self.instrumentator = instrumentator
270
276
  self.on_startup = on_startup
@@ -798,3 +804,25 @@ class FastAPIReactToolkit:
798
804
  @property
799
805
  def config(self):
800
806
  return g.config
807
+
808
+ @property
809
+ def cache(self) -> Cache:
810
+ """
811
+ The cache used by caching features such as `AUTH_USER_CACHE_TTL`.
812
+
813
+ Reads and writes `g.cache`, so the toolkit and the rest of the framework always see the same instance.
814
+
815
+ Returns:
816
+ Cache: The active cache, built from the `CACHE` config key or defaulting to an `InMemoryCacheStorage`.
817
+ """
818
+ return g.cache
819
+
820
+ @cache.setter
821
+ def cache(self, value: Cache) -> None:
822
+ """
823
+ Replace the cache used by caching features.
824
+
825
+ Args:
826
+ value (Cache): The cache to use.
827
+ """
828
+ g.cache = value