django-pfx 1.7.6.dev3__tar.gz → 1.7.6.dev7__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 (295) hide show
  1. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/PKG-INFO +2 -1
  2. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/django_pfx.egg-info/PKG-INFO +2 -1
  3. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/django_pfx.egg-info/SOURCES.txt +2 -0
  4. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/django_pfx.egg-info/requires.txt +1 -0
  5. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/django_pfx.egg-info/scm_file_list.json +2 -0
  6. django_pfx-1.7.6.dev7/django_pfx.egg-info/scm_version.json +8 -0
  7. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/settings.md +30 -0
  8. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/apps.py +3 -0
  9. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/default_settings.py +2 -0
  10. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  11. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +5 -5
  12. django_pfx-1.7.6.dev7/pfx/pfxcore/version_check.py +121 -0
  13. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/setup.cfg +1 -0
  14. django_pfx-1.7.6.dev7/tests/tests/test_version_check.py +96 -0
  15. django_pfx-1.7.6.dev3/django_pfx.egg-info/scm_version.json +0 -8
  16. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/.gitignore +0 -0
  17. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/.gitlab-ci.yml +0 -0
  18. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/.pre-commit-config.yaml +0 -0
  19. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/LICENSE +0 -0
  20. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/MANIFEST.in +0 -0
  21. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/README.md +0 -0
  22. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/django_pfx.egg-info/dependency_links.txt +0 -0
  23. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/django_pfx.egg-info/top_level.txt +0 -0
  24. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/Makefile +0 -0
  25. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/conf.py +0 -0
  26. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/index.rst +0 -0
  27. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/api.views.rst +0 -0
  28. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/authentication.md +0 -0
  29. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/decorator.md +0 -0
  30. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/generate_openapi.md +0 -0
  31. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/getting_started.md +0 -0
  32. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/internationalisation.md +0 -0
  33. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/model.md +0 -0
  34. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/pfx_views.md +0 -0
  35. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/profiling.md +0 -0
  36. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/doc/source/testing.md +0 -0
  37. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/img/pfx.png +0 -0
  38. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/img/pfx.svg +0 -0
  39. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/make_messages +0 -0
  40. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/manage.py +0 -0
  41. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/__init__.py +0 -0
  42. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/__init__.py +0 -0
  43. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/apps.py +0 -0
  44. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/email_backend/__init__.py +0 -0
  45. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/email_backend/q_mailjet_email_backend.py +0 -0
  46. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/exceptions.py +0 -0
  47. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/mailjet/__init__.py +0 -0
  48. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/mailjet/mailjet.py +0 -0
  49. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/mailjet/message_result.py +0 -0
  50. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/migrations/0001_initial.py +0 -0
  51. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/migrations/0002_alter_email_headers.py +0 -0
  52. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/migrations/0003_alter_email_options.py +0 -0
  53. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/migrations/0004_alter_email_mail_from.py +0 -0
  54. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/migrations/__init__.py +0 -0
  55. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/models/__init__.py +0 -0
  56. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/models/email.py +0 -0
  57. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/providers/__init__.py +0 -0
  58. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/providers/email_django_q_executor.py +0 -0
  59. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/shortcuts.py +0 -0
  60. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/tasks.py +0 -0
  61. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/templates/emails/base_template.html +0 -0
  62. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/templates/emails/base_template.txt +0 -0
  63. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/templates/emails/base_template_subject.txt +0 -0
  64. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/urls.py +0 -0
  65. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/views/__init__.py +0 -0
  66. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/emails/views/email_rest_view.py +0 -0
  67. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/__init__.py +0 -0
  68. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/apidoc/__init__.py +0 -0
  69. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/apidoc/parameters.py +0 -0
  70. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/apidoc/schema.py +0 -0
  71. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/apidoc/tags.py +0 -0
  72. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/decorator/__init__.py +0 -0
  73. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/decorator/rest.py +0 -0
  74. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/exceptions.py +0 -0
  75. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/fields/__init__.py +0 -0
  76. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/fields/decimal_field.py +0 -0
  77. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/fields/media_field.py +0 -0
  78. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
  79. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/fields/rich_text_field.py +0 -0
  80. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/http/__init__.py +0 -0
  81. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/http/json_response.py +0 -0
  82. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/management/__init__.py +0 -0
  83. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/management/commands/__init__.py +0 -0
  84. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  85. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/management/commands/profile.py +0 -0
  86. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/mfa/__init__.py +0 -0
  87. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/mfa/authenticator.py +0 -0
  88. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/mfa/base.py +0 -0
  89. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/mfa/email.py +0 -0
  90. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/mfa/legacy.py +0 -0
  91. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/mfa/sms.py +0 -0
  92. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/middleware/__init__.py +0 -0
  93. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/middleware/authentication.py +0 -0
  94. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/middleware/locale.py +0 -0
  95. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/middleware/profiling.py +0 -0
  96. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/middleware/request_too_big_middleware.py +0 -0
  97. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/middleware/ws_cookie_authentication_middleware.py +0 -0
  98. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  99. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
  100. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
  101. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/0004_alter_loginban_failed_counter_and_more.py +0 -0
  102. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/0005_mfadevice_mfadevice_mfa_device_unique_backend.py +0 -0
  103. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/0006_alter_pfxuser_managers.py +0 -0
  104. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/0007_alter_pfxuser_options.py +0 -0
  105. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/__init__.py +0 -0
  106. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
  107. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
  108. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/__init__.py +0 -0
  109. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  110. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/attachment_mixin.py +0 -0
  111. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/cache_mixins.py +0 -0
  112. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/login_ban.py +0 -0
  113. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/mfa_device.py +0 -0
  114. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/mfa_user_mixin.py +0 -0
  115. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/not_null_fields.py +0 -0
  116. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
  117. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/pfx_models.py +0 -0
  118. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/pfx_user.py +0 -0
  119. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  120. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/serializers/__init__.py +0 -0
  121. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/serializers/json.py +0 -0
  122. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/settings.py +0 -0
  123. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/shortcuts.py +0 -0
  124. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/sms/__init__.py +0 -0
  125. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/sms/backends/__init__.py +0 -0
  126. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/sms/backends/base.py +0 -0
  127. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/sms/backends/console.py +0 -0
  128. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/storage/__init__.py +0 -0
  129. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/storage/exceptions.py +0 -0
  130. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/storage/local_storage.py +0 -0
  131. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/storage/s3_storage.py +0 -0
  132. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  133. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  134. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  135. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  136. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  137. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  138. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/test.py +0 -0
  139. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/urls.py +0 -0
  140. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/__init__.py +0 -0
  141. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/authentication_views.py +0 -0
  142. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/fields.py +0 -0
  143. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/filters_views.py +0 -0
  144. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/locale_views.py +0 -0
  145. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
  146. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/mfa_authentication_views.py +0 -0
  147. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
  148. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  149. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  150. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/groups.py +0 -0
  151. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  152. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  153. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  154. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  155. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  156. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  157. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  158. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  159. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  160. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/subset.py +0 -0
  161. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  162. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  163. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  164. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  165. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  166. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/pfxcore/views/rest_views.py +0 -0
  167. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/settings/__init__.py +0 -0
  168. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/settings/dev.py +0 -0
  169. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/__init__.py +0 -0
  170. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/apps.py +0 -0
  171. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/locale/fr/LC_MESSAGES/django.mo +0 -0
  172. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/locale/fr/LC_MESSAGES/django.po +0 -0
  173. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/migrations/0001_initial.py +0 -0
  174. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py +0 -0
  175. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/migrations/0003_clean_scheduled_tasks.py +0 -0
  176. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/migrations/0004_userjob_summary_url.py +0 -0
  177. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/migrations/__init__.py +0 -0
  178. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/models/__init__.py +0 -0
  179. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/models/user_job.py +0 -0
  180. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/models/user_job_attachment.py +0 -0
  181. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/providers/__init__.py +0 -0
  182. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/providers/user_job_django_q_executor.py +0 -0
  183. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/providers/user_job_websocket_channel_provider.py +0 -0
  184. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/tasks.py +0 -0
  185. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/urls.py +0 -0
  186. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/views/__init__.py +0 -0
  187. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/views/user_job_attachment_view.py +0 -0
  188. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/views/user_job_view.py +0 -0
  189. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pfx/userjobs/views/user_websocket_consumer.py +0 -0
  190. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/pyproject.toml +0 -0
  191. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/requirements.txt +0 -0
  192. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/serve-doc +0 -0
  193. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/setup.py +0 -0
  194. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/__init__.py +0 -0
  195. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/apps.py +0 -0
  196. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/channel_group_send_calls.py +0 -0
  197. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  198. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/mfa_urls.py +0 -0
  199. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/migrations/0001_initial.py +0 -0
  200. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/migrations/__init__.py +0 -0
  201. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/models.py +0 -0
  202. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/settings/__init__.py +0 -0
  203. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/settings/ci.py +0 -0
  204. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/settings/common.py +0 -0
  205. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/settings/dev.py +0 -0
  206. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/settings/dev_custom_example.py +0 -0
  207. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/settings/dev_default.py +0 -0
  208. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/__init__.py +0 -0
  209. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/basic_api_errors.py +0 -0
  210. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/basic_api_test.py +0 -0
  211. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/mfa_test_utils.py +0 -0
  212. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_api_doc.py +0 -0
  213. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_api_doc_search.py +0 -0
  214. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_auth_api.py +0 -0
  215. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_body_mixin.py +0 -0
  216. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_cache.py +0 -0
  217. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_client.py +0 -0
  218. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_emails_email_api.py +0 -0
  219. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_emails_email_model.py +0 -0
  220. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_emails_q_mailjet_email_backend.py +0 -0
  221. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_emails_send_email_message.py +0 -0
  222. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_fields_choices.py +0 -0
  223. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_fields_date.py +0 -0
  224. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_fields_decimal.py +0 -0
  225. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_fields_minutes_duration.py +0 -0
  226. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_fields_one2many.py +0 -0
  227. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_fields_rich_text.py +0 -0
  228. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_fields_time.py +0 -0
  229. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_filters.py +0 -0
  230. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_locale_api.py +0 -0
  231. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_mfa_legacy.py +0 -0
  232. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_mfa_login_api.py +0 -0
  233. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_mfa_setup_api.py +0 -0
  234. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
  235. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_perm_tests.py +0 -0
  236. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_permissions.py +0 -0
  237. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_perms_api.py +0 -0
  238. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_post_migrate_groups_update.py +0 -0
  239. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_profiling_middleware.py +0 -0
  240. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_settings.py +0 -0
  241. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_shortcuts.py +0 -0
  242. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_timezone_middleware.py +0 -0
  243. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_tools.py +0 -0
  244. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_user_job_api.py +0 -0
  245. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_user_job_messages.py +0 -0
  246. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_user_job_model.py +0 -0
  247. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_user_queryset.py +0 -0
  248. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_user_websocket.py +0 -0
  249. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_view_decorators.py +0 -0
  250. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/tests/test_view_fields.py +0 -0
  251. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/urls.py +0 -0
  252. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests/views.py +0 -0
  253. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/__init__.py +0 -0
  254. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/migrations/__init__.py +0 -0
  255. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/settings/__init__.py +0 -0
  256. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/settings/ci.py +0 -0
  257. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/settings/common.py +0 -0
  258. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/settings/dev.py +0 -0
  259. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/settings/dev_custom_example.py +0 -0
  260. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/settings/dev_default.py +0 -0
  261. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/tests/__init__.py +0 -0
  262. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/tests/test_api.py +0 -0
  263. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/tests/test_auth_api.py +0 -0
  264. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/urls.py +0 -0
  265. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_base_user/views.py +0 -0
  266. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/__init__.py +0 -0
  267. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/migrations/0001_initial.py +0 -0
  268. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/migrations/__init__.py +0 -0
  269. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/models.py +0 -0
  270. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/settings/__init__.py +0 -0
  271. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/settings/ci.py +0 -0
  272. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/settings/common.py +0 -0
  273. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/settings/dev.py +0 -0
  274. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/settings/dev_custom_example.py +0 -0
  275. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/settings/dev_default.py +0 -0
  276. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/tests/__init__.py +0 -0
  277. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/tests/test_api.py +0 -0
  278. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/tests/test_auth_api.py +0 -0
  279. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/urls.py +0 -0
  280. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_custom_user/views.py +0 -0
  281. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/__init__.py +0 -0
  282. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/migrations/0001_initial.py +0 -0
  283. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/migrations/__init__.py +0 -0
  284. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/models.py +0 -0
  285. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/settings/__init__.py +0 -0
  286. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/settings/ci.py +0 -0
  287. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/settings/common.py +0 -0
  288. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/settings/dev.py +0 -0
  289. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/settings/dev_custom_example.py +0 -0
  290. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/settings/dev_default.py +0 -0
  291. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/tests/__init__.py +0 -0
  292. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/tests/test_api.py +0 -0
  293. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/tests/test_auth_api.py +0 -0
  294. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/urls.py +0 -0
  295. {django_pfx-1.7.6.dev3 → django_pfx-1.7.6.dev7}/tests_user_email_login/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-pfx
3
- Version: 1.7.6.dev3
3
+ Version: 1.7.6.dev7
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -35,6 +35,7 @@ Requires-Dist: pyyaml
35
35
  Requires-Dist: pytz
36
36
  Requires-Dist: dill
37
37
  Requires-Dist: nh3
38
+ Requires-Dist: packaging
38
39
  Provides-Extra: otp
39
40
  Requires-Dist: pyotp>=2.10.0; extra == "otp"
40
41
  Provides-Extra: heic-converter
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-pfx
3
- Version: 1.7.6.dev3
3
+ Version: 1.7.6.dev7
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -35,6 +35,7 @@ Requires-Dist: pyyaml
35
35
  Requires-Dist: pytz
36
36
  Requires-Dist: dill
37
37
  Requires-Dist: nh3
38
+ Requires-Dist: packaging
38
39
  Provides-Extra: otp
39
40
  Requires-Dist: pyotp>=2.10.0; extra == "otp"
40
41
  Provides-Extra: heic-converter
@@ -68,6 +68,7 @@ pfx/pfxcore/settings.py
68
68
  pfx/pfxcore/shortcuts.py
69
69
  pfx/pfxcore/test.py
70
70
  pfx/pfxcore/urls.py
71
+ pfx/pfxcore/version_check.py
71
72
  pfx/pfxcore/apidoc/__init__.py
72
73
  pfx/pfxcore/apidoc/parameters.py
73
74
  pfx/pfxcore/apidoc/schema.py
@@ -244,6 +245,7 @@ tests/tests/test_user_job_messages.py
244
245
  tests/tests/test_user_job_model.py
245
246
  tests/tests/test_user_queryset.py
246
247
  tests/tests/test_user_websocket.py
248
+ tests/tests/test_version_check.py
247
249
  tests/tests/test_view_decorators.py
248
250
  tests/tests/test_view_fields.py
249
251
  tests_base_user/__init__.py
@@ -8,6 +8,7 @@ pyyaml
8
8
  pytz
9
9
  dill
10
10
  nh3
11
+ packaging
11
12
 
12
13
  [heic-converter]
13
14
  python-magic
@@ -126,6 +126,7 @@
126
126
  "pfx/pfxcore/templates/registration/welcome_subject.txt",
127
127
  "pfx/pfxcore/test.py",
128
128
  "pfx/pfxcore/urls.py",
129
+ "pfx/pfxcore/version_check.py",
129
130
  "pfx/pfxcore/views/__init__.py",
130
131
  "pfx/pfxcore/views/authentication_views.py",
131
132
  "pfx/pfxcore/views/fields.py",
@@ -235,6 +236,7 @@
235
236
  "tests/tests/test_user_job_model.py",
236
237
  "tests/tests/test_user_queryset.py",
237
238
  "tests/tests/test_user_websocket.py",
239
+ "tests/tests/test_version_check.py",
238
240
  "tests/tests/test_view_decorators.py",
239
241
  "tests/tests/test_view_fields.py",
240
242
  "tests/urls.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.7.5",
3
+ "distance": 7,
4
+ "node": "g826b6a00c2d1527733a0e70ce4eda6b122b24cc9",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-08-31"
8
+ }
@@ -34,6 +34,36 @@ if len(sys.argv) > 1 and sys.argv[1] == 'test':
34
34
  PFX_TEST_MODE=True
35
35
  ```
36
36
 
37
+ ### PFX_CHECK_LATEST_VERSION
38
+
39
+ Boolean (optional, default `False`). When enabled, print a warning at
40
+ startup if a newer django-pfx is published on PyPI. The message is
41
+ printed rather than logged, so that a project log configuration cannot
42
+ silently filter it out.
43
+
44
+ Only versions of the same kind as the installed one are reported. A
45
+ project running on a dev build follows the dev builds, a project running
46
+ on a final release is only told about newer final releases. A project
47
+ that chose a final release will not move to a dev build, and since one
48
+ is published on every commit pushed to master, reporting them would turn
49
+ the message into permanent noise.
50
+
51
+ `pip install -r requirements.txt` does not upgrade a package that is
52
+ already installed and still satisfies the requirement, so a project can
53
+ keep running against an outdated django-pfx without anyone noticing.
54
+ This setting turns that into a visible message instead of something to
55
+ remember.
56
+
57
+ The check only runs under `runserver` (it relies on the same
58
+ `RUN_MAIN` guard as the other startup hooks), never during tests or in
59
+ production. It runs in a background thread, its result is cached for
60
+ 4 hours, and any failure (offline, proxy, PyPI outage) is silent.
61
+
62
+ ```python
63
+ DEBUG = True
64
+ PFX_CHECK_LATEST_VERSION = DEBUG
65
+ ```
66
+
37
67
 
38
68
  ## Authentication
39
69
 
@@ -108,4 +108,7 @@ class PfxCoreConfig(AppConfig):
108
108
  "PFX HEIC opener not registered "
109
109
  "(use `pip install django-pfx[heic-converter]` "
110
110
  "to use it.)")
111
+ if settings.PFX_CHECK_LATEST_VERSION:
112
+ from pfx.pfxcore.version_check import check_latest_version
113
+ check_latest_version()
111
114
  super().ready()
@@ -38,6 +38,8 @@ STORAGE_LOCAL_X_ACCEL_REDIRECT = False
38
38
 
39
39
  PFX_TEST_MODE = False
40
40
 
41
+ PFX_CHECK_LATEST_VERSION = False
42
+
41
43
  PFX_AUTH_GROUPS_CREATE_ONLY = False
42
44
 
43
45
  PFX_MFA_BACKENDS = [
@@ -7,7 +7,7 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: \n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2026-08-18 10:56+0200\n"
10
+ "POT-Creation-Date: 2026-08-12 16:15+0200\n"
11
11
  "PO-Revision-Date: 2021-06-22 23:31+0200\n"
12
12
  "Last-Translator: \n"
13
13
  "Language-Team: \n"
@@ -22,7 +22,7 @@ msgstr ""
22
22
  msgid "An internal server error occured."
23
23
  msgstr "Une erreur interne du serveur est survenue."
24
24
 
25
- #: default_settings.py:54
25
+ #: default_settings.py:56
26
26
  msgid "A system error occurred. Our support team has been notified."
27
27
  msgstr ""
28
28
  "Une erreur système s’est produite. Notre équipe d’assistance en a été "
@@ -206,12 +206,12 @@ msgid ""
206
206
  "You’re receiving this email because you requested an authentication code at "
207
207
  "%(site_name)s."
208
208
  msgstr ""
209
- "Vous recevez cet e-mail parce que vous avez demandé un code d’authentication "
209
+ "Vous recevez cet e-mail parce que vous avez demandé un code d’authentification "
210
210
  "sur %(site_name)s."
211
211
 
212
212
  #: templates/registration/otp_code_email.txt:4
213
213
  msgid "Authentication code:"
214
- msgstr "Code d’authentication :"
214
+ msgstr "Code d’authentification :"
215
215
 
216
216
  #: templates/registration/otp_code_email.txt:6
217
217
  #, python-format
@@ -234,7 +234,7 @@ msgstr "L’équipe de %(site_name)s"
234
234
  #: templates/registration/otp_code_subject.txt:2
235
235
  #, python-format
236
236
  msgid "New authentication code for %(site_name)s"
237
- msgstr "Nouveau code d’authentication pour %(site_name)s"
237
+ msgstr "Nouveau code d’authentification pour %(site_name)s"
238
238
 
239
239
  #: templates/registration/password_reset_email.txt:2
240
240
  #, python-format
@@ -0,0 +1,121 @@
1
+ """Development helper warning when a newer django-pfx is published.
2
+
3
+ `pip install -r requirements.txt` does not upgrade an already installed
4
+ package, so a project can keep running against an outdated django-pfx
5
+ without anyone noticing. Enable it with the PFX_CHECK_LATEST_VERSION
6
+ setting, it only runs under `runserver`.
7
+ """
8
+ import json
9
+ import logging
10
+ import tempfile
11
+ import threading
12
+ import time
13
+ import urllib.request
14
+ from importlib.metadata import PackageNotFoundError, version
15
+ from pathlib import Path
16
+
17
+ from django.core.management.color import color_style
18
+
19
+ from packaging.version import InvalidVersion, Version
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ PACKAGE = 'django-pfx'
24
+ PYPI_URL = f'https://pypi.org/pypi/{PACKAGE}/json'
25
+ CACHE_FILE = Path(tempfile.gettempdir()) / f'{PACKAGE}-latest-version.json'
26
+ CACHE_TTL = 4 * 60 * 60
27
+ TIMEOUT = 3
28
+
29
+
30
+ def parse_versions(raw_versions):
31
+ """Parse version strings, skipping the ones PyPI cannot guarantee."""
32
+ for raw in raw_versions:
33
+ try:
34
+ yield Version(raw)
35
+ except InvalidVersion:
36
+ continue
37
+
38
+
39
+ def fetch_latest():
40
+ """Newest versions published on PyPI, as (any, final).
41
+
42
+ The `info.version` field only reports final releases, so it never
43
+ sees the dev builds published from master, and the final releases
44
+ have to be sorted out here.
45
+ """
46
+ with urllib.request.urlopen(PYPI_URL, timeout=TIMEOUT) as response:
47
+ releases = json.load(response)['releases']
48
+ versions = list(parse_versions(releases))
49
+ final = max((v for v in versions if not v.is_prerelease), default=None)
50
+ return str(max(versions)), final and str(final)
51
+
52
+
53
+ def cached_latest():
54
+ """Newest published versions, fetched at most once every CACHE_TTL.
55
+
56
+ `runserver` reloads on every file change, an uncached check would
57
+ mean one PyPI request per save.
58
+ """
59
+ try:
60
+ age = time.time() - CACHE_FILE.stat().st_mtime
61
+ if age < CACHE_TTL:
62
+ cache = json.loads(CACHE_FILE.read_text())
63
+ return cache['latest'], cache['final']
64
+ except (OSError, ValueError, KeyError):
65
+ pass
66
+
67
+ latest, final = fetch_latest()
68
+ try:
69
+ CACHE_FILE.write_text(json.dumps(dict(latest=latest, final=final)))
70
+ except OSError:
71
+ pass # a missing cache only costs an extra request
72
+ return latest, final
73
+
74
+
75
+ def write(message):
76
+ """Print rather than log.
77
+
78
+ The message is only useful if the developer sees it, and a project
79
+ log configuration commonly filters out third party warnings.
80
+ Flushed because it is printed from a background thread, possibly
81
+ after the startup output.
82
+ """
83
+ print(color_style().WARNING(f"\n{message}\n"), flush=True)
84
+
85
+
86
+ def check():
87
+ try:
88
+ current = Version(version(PACKAGE))
89
+ except PackageNotFoundError:
90
+ return # running from a source checkout
91
+ latest, final = cached_latest()
92
+ # Report versions of the same kind as the installed one. A project
93
+ # running on a final release will not move to a dev build, reporting
94
+ # them would turn the message into permanent noise.
95
+ target = latest if current.is_prerelease else final
96
+ if not target:
97
+ return
98
+ target = Version(target)
99
+ if target <= current:
100
+ return
101
+ # No advice on how to update: the way the version is declared and
102
+ # installed is up to the project, any command suggested here would
103
+ # be wrong for someone.
104
+ kind = 'dev build' if target.is_prerelease else 'release'
105
+ write(
106
+ f"A newer {PACKAGE} {kind} is published: {target} "
107
+ f"(installed: {current}).")
108
+
109
+
110
+ def check_safely():
111
+ try:
112
+ check()
113
+ except Exception as e:
114
+ # A development convenience must never break the startup
115
+ # (offline, proxy, PyPI outage, ...).
116
+ logger.debug("Unable to check the latest %s version: %s", PACKAGE, e)
117
+
118
+
119
+ def check_latest_version():
120
+ """Check in the background, so startup never waits on PyPI."""
121
+ threading.Thread(target=check_safely, daemon=True).start()
@@ -41,6 +41,7 @@ install_requires =
41
41
  pytz
42
42
  dill
43
43
  nh3
44
+ packaging
44
45
 
45
46
  [options.extras_require]
46
47
  otp = pyotp>=2.10.0
@@ -0,0 +1,96 @@
1
+ import io
2
+ import json
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ from django.test import TestCase
6
+
7
+ from pfx.pfxcore.version_check import (
8
+ check,
9
+ check_safely,
10
+ fetch_latest,
11
+ parse_versions,
12
+ )
13
+
14
+ MODULE = 'pfx.pfxcore.version_check'
15
+
16
+
17
+ def pypi_response(payload):
18
+ response = MagicMock()
19
+ response.__enter__.return_value = io.StringIO(json.dumps(payload))
20
+ return response
21
+
22
+
23
+ class VersionCheckTest(TestCase):
24
+
25
+ def test_parse_versions_skips_invalid(self):
26
+ versions = parse_versions(['1.7.4', 'not-a-version', '1.7.5.dev7'])
27
+ self.assertEqual(
28
+ [str(v) for v in versions], ['1.7.4', '1.7.5.dev7'])
29
+
30
+ def test_fetch_latest_separates_dev_and_final(self):
31
+ # `info.version` reports 1.7.4, the latest final release, while
32
+ # the newest published version is a dev build.
33
+ payload = dict(
34
+ info=dict(version='1.7.4'),
35
+ releases={'1.7.3': [], '1.7.4': [], '1.7.5.dev7': []})
36
+ with patch(
37
+ f'{MODULE}.urllib.request.urlopen',
38
+ return_value=pypi_response(payload)):
39
+ self.assertEqual(fetch_latest(), ('1.7.5.dev7', '1.7.4'))
40
+
41
+ @patch(f'{MODULE}.cached_latest', return_value=('1.7.5.dev7', '1.7.4'))
42
+ @patch(f'{MODULE}.version', return_value='1.7.3.dev2')
43
+ @patch(f'{MODULE}.write')
44
+ def test_dev_install_is_told_about_dev_builds(
45
+ self, write, version, cached_latest):
46
+ check()
47
+ message = write.call_args.args[0]
48
+ self.assertIn('dev build', message)
49
+ self.assertIn('1.7.5.dev7', message)
50
+ self.assertIn('1.7.3.dev2', message)
51
+
52
+ @patch(f'{MODULE}.cached_latest', return_value=('1.7.5.dev7', '1.7.4'))
53
+ @patch(f'{MODULE}.version', return_value='1.7.3')
54
+ @patch(f'{MODULE}.write')
55
+ def test_final_install_is_told_about_final_releases(
56
+ self, write, version, cached_latest):
57
+ check()
58
+ message = write.call_args.args[0]
59
+ self.assertIn('release', message)
60
+ self.assertIn('1.7.4', message)
61
+ self.assertNotIn('dev', message)
62
+
63
+ @patch(f'{MODULE}.cached_latest', return_value=('1.7.5.dev7', '1.7.4'))
64
+ @patch(f'{MODULE}.version', return_value='1.7.4')
65
+ @patch(f'{MODULE}.write')
66
+ def test_final_install_ignores_newer_dev_builds(
67
+ self, write, version, cached_latest):
68
+ # The whole point: a project on a final release would otherwise
69
+ # be warned on every commit pushed to master.
70
+ check()
71
+ write.assert_not_called()
72
+
73
+ @patch(f'{MODULE}.cached_latest', return_value=('1.7.5.dev7', '1.7.4'))
74
+ @patch(f'{MODULE}.version', return_value='1.7.5.dev7')
75
+ @patch(f'{MODULE}.write')
76
+ def test_check_silent_when_up_to_date(
77
+ self, write, version, cached_latest):
78
+ check()
79
+ write.assert_not_called()
80
+
81
+ @patch(f'{MODULE}.cached_latest', return_value=('1.7.5.dev7', '1.7.4'))
82
+ @patch(f'{MODULE}.version', return_value='1.7.6.dev1')
83
+ @patch(f'{MODULE}.write')
84
+ def test_check_silent_when_ahead_of_pypi(
85
+ self, write, version, cached_latest):
86
+ # A local build ahead of PyPI must not be reported.
87
+ check()
88
+ write.assert_not_called()
89
+
90
+ @patch(f'{MODULE}.check', side_effect=OSError('no network'))
91
+ @patch(f'{MODULE}.write')
92
+ @patch(f'{MODULE}.logger')
93
+ def test_check_safely_never_raises(self, logger, write, check_mock):
94
+ check_safely()
95
+ logger.debug.assert_called_once()
96
+ write.assert_not_called()
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "1.7.5",
3
- "distance": 3,
4
- "node": "g42bba3cdfc22d2fb5c6432d8a822d8b9a8875163",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-08-18"
8
- }
File without changes