openmodule 13.4.0__tar.gz → 13.6.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 (248) hide show
  1. {openmodule-13.4.0 → openmodule-13.6.0}/AUTHORS +1 -0
  2. {openmodule-13.4.0 → openmodule-13.6.0}/ChangeLog +12 -19
  3. {openmodule-13.4.0/openmodule.egg-info → openmodule-13.6.0}/PKG-INFO +1 -1
  4. {openmodule-13.4.0 → openmodule-13.6.0}/docs/migrations.md +13 -0
  5. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/core.py +5 -4
  6. openmodule-13.6.0/openmodule/database/database.py +169 -0
  7. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/database/env.py +0 -1
  8. openmodule-13.4.0/openmodule/database/database.py → openmodule-13.6.0/openmodule/database/migration.py +20 -120
  9. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/dispatcher.py +21 -10
  10. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/logging.py +3 -2
  11. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/messaging.py +16 -5
  12. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/base.py +19 -10
  13. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/server.py +3 -11
  14. {openmodule-13.4.0 → openmodule-13.6.0/openmodule.egg-info}/PKG-INFO +1 -1
  15. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/SOURCES.txt +16 -1
  16. openmodule-13.6.0/openmodule.egg-info/pbr.json +1 -0
  17. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/database.py +16 -1
  18. {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/env.py +1 -1
  19. {openmodule-13.4.0/tests/test_access_service_database → openmodule-13.6.0/tests/migration_double_column_delete_error}/alembic/env.py +1 -1
  20. openmodule-13.6.0/tests/migration_double_column_delete_error/alembic/versions/812a3e5b8517_initial.py +62 -0
  21. openmodule-13.6.0/tests/migration_double_column_delete_error/alembic/versions/a7ea100a784f_key_error.py +40 -0
  22. openmodule-13.6.0/tests/migration_double_column_delete_error/alembic.ini +85 -0
  23. openmodule-13.6.0/tests/migration_double_column_delete_error/makemigration.sh +21 -0
  24. openmodule-13.6.0/tests/migration_no_such_table_error/alembic/env.py +4 -0
  25. openmodule-13.6.0/tests/migration_no_such_table_error/alembic/versions/812a3e5b8517_initial.py +62 -0
  26. openmodule-13.6.0/tests/migration_no_such_table_error/alembic/versions/a7ea100a784f_no_such_table_error.py +39 -0
  27. openmodule-13.6.0/tests/migration_no_such_table_error/alembic.ini +85 -0
  28. openmodule-13.6.0/tests/migration_no_such_table_error/makemigration.sh +21 -0
  29. {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic/env.py +1 -1
  30. openmodule-13.6.0/tests/test_access_service_database/alembic/env.py +4 -0
  31. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_alembic_migrations.py +3 -1
  32. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_checks.py +2 -2
  33. openmodule-13.6.0/tests/test_core.py +104 -0
  34. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/alembic/env.py +1 -1
  35. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database.py +54 -3
  36. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_dispatcher.py +14 -4
  37. openmodule-13.6.0/tests/test_kv_store_database/alembic/__init__.py +0 -0
  38. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic/env.py +1 -1
  39. openmodule-13.6.0/tests/test_kv_store_database/alembic/script.py.mako +24 -0
  40. openmodule-13.6.0/tests/test_kv_store_multiple_database/alembic/__init__.py +0 -0
  41. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic/env.py +1 -1
  42. openmodule-13.6.0/tests/test_kv_store_multiple_database/alembic/script.py.mako +24 -0
  43. openmodule-13.6.0/tests/test_logging.py +29 -0
  44. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_messaging.py +10 -4
  45. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_rpc.py +2 -4
  46. openmodule-13.4.0/openmodule/checks.py +0 -28
  47. openmodule-13.4.0/openmodule.egg-info/pbr.json +0 -1
  48. openmodule-13.4.0/tests/test_core.py +0 -48
  49. {openmodule-13.4.0 → openmodule-13.6.0}/.gitlab-ci.yml +0 -0
  50. {openmodule-13.4.0 → openmodule-13.6.0}/LICENSE +0 -0
  51. {openmodule-13.4.0 → openmodule-13.6.0}/README.md +0 -0
  52. {openmodule-13.4.0 → openmodule-13.6.0}/docs/access_service.md +0 -0
  53. {openmodule-13.4.0 → openmodule-13.6.0}/docs/anonymization.md +0 -0
  54. {openmodule-13.4.0 → openmodule-13.6.0}/docs/cleanup.md +0 -0
  55. {openmodule-13.4.0 → openmodule-13.6.0}/docs/coding_standard.md +0 -0
  56. {openmodule-13.4.0 → openmodule-13.6.0}/docs/commands.md +0 -0
  57. {openmodule-13.4.0 → openmodule-13.6.0}/docs/connection_status_listener.md +0 -0
  58. {openmodule-13.4.0 → openmodule-13.6.0}/docs/csv_export.md +0 -0
  59. {openmodule-13.4.0 → openmodule-13.6.0}/docs/database.md +0 -0
  60. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated.md +0 -0
  61. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/README.md +0 -0
  62. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/openmodule/models/access_service.py +0 -0
  63. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/openmodule/utils/access_service.py +0 -0
  64. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/openmodule_test/access_service.py +0 -0
  65. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/tests/test_utils_access_service.py +0 -0
  66. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/api/openmodule/utils/api.py +0 -0
  67. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/api/openmodule_test/api.py +0 -0
  68. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/api/tests/test_utils_api.py +0 -0
  69. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/package_reader/openmodule/utils/package_reader.py +0 -0
  70. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/package_reader/openmodule_test/fake_package_creator.py +0 -0
  71. {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/package_reader/tests/test_package_reader.py +0 -0
  72. {openmodule-13.4.0 → openmodule-13.6.0}/docs/event_sending.md +0 -0
  73. {openmodule-13.4.0 → openmodule-13.6.0}/docs/getting_started.md +0 -0
  74. {openmodule-13.4.0 → openmodule-13.6.0}/docs/health.md +0 -0
  75. {openmodule-13.4.0 → openmodule-13.6.0}/docs/images/broker.drawio.png +0 -0
  76. {openmodule-13.4.0 → openmodule-13.6.0}/docs/known_issues.md +0 -0
  77. {openmodule-13.4.0 → openmodule-13.6.0}/docs/package_reader.md +0 -0
  78. {openmodule-13.4.0 → openmodule-13.6.0}/docs/rpc.md +0 -0
  79. {openmodule-13.4.0 → openmodule-13.6.0}/docs/settings.md +0 -0
  80. {openmodule-13.4.0 → openmodule-13.6.0}/docs/settings_provider.md +0 -0
  81. {openmodule-13.4.0 → openmodule-13.6.0}/docs/testing.md +0 -0
  82. {openmodule-13.4.0 → openmodule-13.6.0}/docs/translation.md +0 -0
  83. {openmodule-13.4.0 → openmodule-13.6.0}/docs/utils.md +0 -0
  84. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/__init__.py +0 -0
  85. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/alert.py +0 -0
  86. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/config.py +0 -0
  87. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/connection_status.py +0 -0
  88. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/database/custom_types.py +0 -0
  89. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/health.py +0 -0
  90. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/__init__.py +0 -0
  91. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/access_service.py +0 -0
  92. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/alert.py +0 -0
  93. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/io.py +0 -0
  94. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/kv_store.py +0 -0
  95. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/presence.py +0 -0
  96. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/privacy.py +0 -0
  97. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/rpc.py +0 -0
  98. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/settings.py +0 -0
  99. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/validation.py +0 -0
  100. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/vehicle.py +0 -0
  101. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/__init__.py +0 -0
  102. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/client.py +0 -0
  103. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/common.py +0 -0
  104. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/sentry.py +0 -0
  105. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/threading.py +0 -0
  106. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/__init__.py +0 -0
  107. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/access_service.py +0 -0
  108. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/charset.py +0 -0
  109. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/cleanup.py +0 -0
  110. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/csv_export.py +0 -0
  111. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/databox.py +0 -0
  112. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/db_helper.py +0 -0
  113. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/eventlog.py +0 -0
  114. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/io.py +0 -0
  115. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/kv_store.py +0 -0
  116. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/matching.py +0 -0
  117. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/misc_functions.py +0 -0
  118. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/package_reader.py +0 -0
  119. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/presence.py +0 -0
  120. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/schema.py +0 -0
  121. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/settings.py +0 -0
  122. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/translation.py +0 -0
  123. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/validation.py +0 -0
  124. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/dependency_links.txt +0 -0
  125. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/not-zip-safe +0 -0
  126. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/requires.txt +0 -0
  127. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/top_level.txt +0 -0
  128. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/__init__.py +0 -0
  129. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/setup.cfg +0 -0
  130. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/setup.py +0 -0
  131. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/translate.py +0 -0
  132. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/__init__.py +0 -0
  133. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/alert.py +0 -0
  134. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/connection_status.py +0 -0
  135. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/core.py +0 -0
  136. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/eventlistener.py +0 -0
  137. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/files.py +0 -0
  138. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/gate.py +0 -0
  139. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/health.py +0 -0
  140. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/interrupt.py +0 -0
  141. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/io_simulator.py +0 -0
  142. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/package_reader.py +0 -0
  143. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/presence.py +0 -0
  144. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/requirements.txt +0 -0
  145. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/rpc.py +0 -0
  146. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/settings.py +0 -0
  147. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/setup.cfg +0 -0
  148. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/setup.py +0 -0
  149. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/utils.py +0 -0
  150. {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/zeromq.py +0 -0
  151. {openmodule-13.4.0 → openmodule-13.6.0}/requirements.txt +0 -0
  152. {openmodule-13.4.0 → openmodule-13.6.0}/setup.cfg +0 -0
  153. {openmodule-13.4.0 → openmodule-13.6.0}/setup.py +0 -0
  154. {openmodule-13.4.0 → openmodule-13.6.0}/test-requirements.txt +0 -0
  155. {openmodule-13.4.0 → openmodule-13.6.0}/tests/__init__.py +0 -0
  156. {openmodule-13.4.0 → openmodule-13.6.0}/tests/config.py +0 -0
  157. {openmodule-13.4.0 → openmodule-13.6.0}/tests/database_models_migration.py +0 -0
  158. {openmodule-13.4.0 → openmodule-13.6.0}/tests/database_models_test.py +0 -0
  159. {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/README +0 -0
  160. {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/__init__.py +0 -0
  161. {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/script.py.mako +0 -0
  162. {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/versions/ff26e54332f9_datetime_models.py +0 -0
  163. {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic.ini +0 -0
  164. {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/makemigration.sh +0 -0
  165. {openmodule-13.4.0/tests/migration_test_database → openmodule-13.6.0/tests/migration_double_column_delete_error/alembic}/__init__.py +0 -0
  166. {openmodule-13.4.0/tests/migration_test_database → openmodule-13.6.0/tests/migration_double_column_delete_error}/alembic/script.py.mako +0 -0
  167. {openmodule-13.4.0/tests/migration_test_database → openmodule-13.6.0/tests/migration_no_such_table_error}/alembic/__init__.py +0 -0
  168. {openmodule-13.4.0/tests/test_access_service_database → openmodule-13.6.0/tests/migration_no_such_table_error}/alembic/script.py.mako +0 -0
  169. {openmodule-13.4.0/tests/migration_test_database/alembic/versions → openmodule-13.6.0/tests/migration_test_database}/__init__.py +0 -0
  170. {openmodule-13.4.0/tests/test_access_service_database → openmodule-13.6.0/tests/migration_test_database}/alembic/__init__.py +0 -0
  171. {openmodule-13.4.0/tests/test_database → openmodule-13.6.0/tests/migration_test_database}/alembic/script.py.mako +0 -0
  172. {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic/versions/19789aa5361c_initial.py +0 -0
  173. {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic/versions/19d887929ae7_alter.py +0 -0
  174. {openmodule-13.4.0/tests/test_database/alembic → openmodule-13.6.0/tests/migration_test_database/alembic/versions}/__init__.py +0 -0
  175. {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic.ini +0 -0
  176. {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic_migration_test_database.sqlite3 +0 -0
  177. {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/makemigration.sh +0 -0
  178. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/configs/config.py +0 -0
  179. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/configs/test_config.py +0 -0
  180. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/configs/test_config_1.py +0 -0
  181. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/standard_schemes/DEFAULT-10.yml +0 -0
  182. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/standard_schemes/DEFAULT-20.yml +0 -0
  183. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/standard_schemes/LEGACY-0.yml +0 -0
  184. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/de/LC_MESSAGES/translation.mo +0 -0
  185. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/de/LC_MESSAGES/translation.po +0 -0
  186. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/en/LC_MESSAGES/translation.mo +0 -0
  187. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/en/LC_MESSAGES/translation.po +0 -0
  188. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/translation.pot +0 -0
  189. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/translate.sh +0 -0
  190. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/A-10.yml +0 -0
  191. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/A-20.yml +0 -0
  192. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/DEFAULT-10.yml +0 -0
  193. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/DEFAULT-20.yml +0 -0
  194. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/DEFAULT-30.yml +0 -0
  195. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/LEGACY-0.yml +0 -0
  196. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-10.yml +0 -0
  197. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-20.yml +0 -0
  198. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-30.yml +0 -0
  199. {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-40.yml +0 -0
  200. {openmodule-13.4.0/tests/test_kv_store_database → openmodule-13.6.0/tests/test_access_service_database}/alembic/__init__.py +0 -0
  201. {openmodule-13.4.0/tests/test_kv_store_database → openmodule-13.6.0/tests/test_access_service_database}/alembic/script.py.mako +0 -0
  202. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic/versions/7bd4fcd38fde_removed_nfc_and_pin.py +0 -0
  203. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic/versions/9ca98a2e5674_added_parksettings_id.py +0 -0
  204. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic/versions/c821971f9230_initial.py +0 -0
  205. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic.ini +0 -0
  206. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/makemigration.sh +0 -0
  207. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_alert.py +0 -0
  208. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_config.py +0 -0
  209. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_connection_status.py +0 -0
  210. {openmodule-13.4.0/tests/test_kv_store_multiple_database → openmodule-13.6.0/tests/test_database}/alembic/__init__.py +0 -0
  211. {openmodule-13.4.0/tests/test_kv_store_multiple_database → openmodule-13.6.0/tests/test_database}/alembic/script.py.mako +0 -0
  212. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/alembic/versions/32b8c728abbf_initial.py +0 -0
  213. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/alembic.ini +0 -0
  214. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/makemigration.sh +0 -0
  215. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_health.py +0 -0
  216. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_interrupt.py +0 -0
  217. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_io_listen.py +0 -0
  218. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic/versions/9c5c944221f4_deprecated_kv_entry_example.py +0 -0
  219. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic/versions/c55a69026a25_initial.py +0 -0
  220. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic.ini +0 -0
  221. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/makemigration.sh +0 -0
  222. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic/README +0 -0
  223. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic/versions/cdb3214131a9_initial.py +0 -0
  224. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic.ini +0 -0
  225. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/makemigration.sh +0 -0
  226. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_mockrpcclient.py +0 -0
  227. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_model.py +0 -0
  228. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_schema.py +0 -0
  229. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_sentry.py +0 -0
  230. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_test_alert.py +0 -0
  231. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_test_gate.py +0 -0
  232. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_test_zeromq.py +0 -0
  233. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_access_service.py +0 -0
  234. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_charset.py +0 -0
  235. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_cleanup.py +0 -0
  236. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_csv_export.py +0 -0
  237. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_databox.py +0 -0
  238. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_eventlog.py +0 -0
  239. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_kv_store.py +0 -0
  240. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_kv_store_multiple.py +0 -0
  241. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_matching.py +0 -0
  242. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_misc_functions.py +0 -0
  243. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_package_reader.py +0 -0
  244. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_presence.py +0 -0
  245. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_settings.py +0 -0
  246. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_validation.py +0 -0
  247. {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_vehicle.py +0 -0
  248. {openmodule-13.4.0 → openmodule-13.6.0}/tox.ini +0 -0
@@ -1,6 +1,7 @@
1
1
  Kevin Koller <k.koller@accessio.at>
2
2
  Kevin Koller <k.koller@arivo.co>
3
3
  Matthias Mietschnig <m.mietschnig@arivo.co>
4
+ Maximilian Bialek <m.bialek@arivo.co>
4
5
  Philipp Reitter <i@philipp.ninja>
5
6
  Philipp Reitter <p.reitter@accessio.at>
6
7
  Philipp Reitter <p.reitter@gmail.com>
@@ -1,6 +1,17 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ v13.6.0
5
+ -------
6
+
7
+ * OM-697 OpenModule Verbesserungen
8
+
9
+ v13.5.0
10
+ -------
11
+
12
+ * document breaking changes
13
+ * OM-405: remove database imports from non-database services + isolate migration process to exclude alembic import in the main process
14
+
4
15
  v13.4.0
5
16
  -------
6
17
 
@@ -16,6 +27,7 @@ v13.2.0
16
27
 
17
28
  * OM 187 Alembic migration test mixin
18
29
  * add a testcase for expiring session data while adding new models (noticed in OM-327)
30
+ * OM-184 Openmodule Database: Migration Backups and Fix in Operations "pre\_downgrade()" and "post\_downgrade()"
19
31
  * Aktualisierung der Dokumentation
20
32
  * OM-255
21
33
 
@@ -152,16 +164,6 @@ v11.1.0
152
164
  -------
153
165
 
154
166
  * csv export library, databox upload and schedule library tips
155
-
156
- v11.1.0.rc5
157
- -----------
158
-
159
- * again no language set in testing
160
-
161
- v11.1.0.rc4
162
- -----------
163
-
164
- * arivo-schedule in public pip
165
167
  * removed mock rpcs from schema
166
168
 
167
169
  v11.0.3
@@ -208,12 +210,3 @@ v10.0.2.rc2
208
210
 
209
211
  * added more documentation [skip ci]
210
212
  * fixes an issue in the multiprocessing\_logging package in testcases
211
-
212
- v10.0.2.rc1
213
- -----------
214
-
215
- * Refactor for backend class for controller v2
216
-
217
- v10.0.1
218
- -------
219
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: openmodule
3
- Version: 13.4.0
3
+ Version: 13.6.0
4
4
  Summary: Libraries for developing the arivo openmodule
5
5
  Home-page: https://gitlab.com/arivo-public/device-python/openmodule.git
6
6
  Author: ARIVO
@@ -1,5 +1,18 @@
1
1
  # Breaking Version Changes
2
2
 
3
+ ## 13.6.0
4
+ * messaging:
5
+ * the `get_pub_socket` now raises an AssertionError because we no longer support multiple publishers.
6
+ This was done because pub sockets receive all subscriptions and queue them until the next message is sent.
7
+ This can lead to a memory leak.
8
+ * logging:
9
+ * logs will now be written to stdout instead of stderr
10
+
11
+ ## 13.5.0:
12
+ * changes in env.py of alembic: <br>
13
+ `from openmodule.database.database import run_env_py` -> <br>
14
+ `from openmodule.database.migration import run_env_py`
15
+
3
16
  ## 13.0.0
4
17
  * access_service:
5
18
  * changes in AccessCheckAccess (removed media field) and AccessCheckResponse (removed error field)
@@ -11,11 +11,11 @@ from typing import Optional
11
11
 
12
12
  from openmodule.alert import AlertHandler
13
13
  from openmodule.config import validate_config_module
14
- from openmodule.database.database import Database
15
14
  from openmodule.dispatcher import ZMQMessageDispatcher
16
15
  from openmodule.health import HealthHandlerType, Healthz, HealthPingMessage
17
16
  from openmodule.logging import init_logging
18
- from openmodule.messaging import get_pub_socket, get_sub_socket, receive_message_from_socket, wait_for_connection
17
+ from openmodule.messaging import _internal_get_pub_socket, get_sub_socket, receive_message_from_socket, \
18
+ wait_for_connection
19
19
  from openmodule.models.base import ZMQMessage
20
20
  from openmodule.sentry import init_sentry, should_activate_sentry, deinit_sentry
21
21
  from openmodule.threading import get_thread_wrapper
@@ -23,7 +23,7 @@ from openmodule.connection_status import ConnectionStatusListener
23
23
 
24
24
 
25
25
  class OpenModuleCore(threading.Thread):
26
- database: Optional[Database] = None
26
+ database = None
27
27
 
28
28
  def __init__(self, context, config, messages_executor=None):
29
29
  super().__init__(target=get_thread_wrapper(self._run))
@@ -36,7 +36,7 @@ class OpenModuleCore(threading.Thread):
36
36
  self.pub_lock = threading.Lock()
37
37
  self.sub_lock = threading.Lock()
38
38
 
39
- self.pub_socket = get_pub_socket(self.context, self.config, linger=1000)
39
+ self.pub_socket = _internal_get_pub_socket(self.context, self.config, linger=1000)
40
40
  self.sub_socket = get_sub_socket(self.context, self.config)
41
41
  self.sub_socket_internal = get_sub_socket(self.context, self.config)
42
42
 
@@ -54,6 +54,7 @@ class OpenModuleCore(threading.Thread):
54
54
  match_type=True, register_schema=False)
55
55
 
56
56
  def init_database(self):
57
+ from openmodule.database.database import Database
57
58
  if self.config.TESTING:
58
59
  self.database = Database(self.config.DATABASE_FOLDER, self.config.NAME, alembic_path="../src/database")
59
60
  else:
@@ -0,0 +1,169 @@
1
+ import os
2
+ import threading
3
+ import types
4
+ from typing import Optional, Tuple
5
+
6
+ from sqlalchemy import create_engine, event
7
+ from sqlalchemy.engine import Engine
8
+ from sqlalchemy.orm import sessionmaker, Session
9
+ from sqlalchemy.pool import StaticPool
10
+
11
+ from multiprocessing import Process, Pipe, ProcessError
12
+ import traceback
13
+
14
+
15
+ class MigrationError(BaseException):
16
+ # Exception wrapper to create exception with traceback from child process
17
+ pass
18
+
19
+
20
+ class MigrationProcess(Process):
21
+ def __init__(self, *args, **kwargs):
22
+ super().__init__(*args, **kwargs)
23
+ self._parent_conn, self._child_conn = Pipe()
24
+ self._exception: Optional[Tuple[Exception, str]] = None
25
+
26
+ def run(self):
27
+ try:
28
+ super().run()
29
+ self._child_conn.send(None)
30
+ except Exception as e:
31
+ tb = traceback.format_exc()
32
+ self._child_conn.send((e, tb))
33
+
34
+ @property
35
+ def exception(self) -> Optional[Tuple[Exception, str]]:
36
+ if self._parent_conn.poll():
37
+ self._exception = self._parent_conn.recv()
38
+ return self._exception
39
+
40
+
41
+ @event.listens_for(Engine, "connect")
42
+ def set_sqlite_pragma(dbapi_connection, connection_record):
43
+ cursor = dbapi_connection.cursor()
44
+ cursor.execute("PRAGMA foreign_keys=ON")
45
+ cursor.close()
46
+
47
+
48
+ active_databases = {}
49
+
50
+
51
+ def execute_migration(engine: Engine, alembic_path: Optional[str] = None):
52
+ def isolated_migration_process():
53
+ nonlocal engine, alembic_path
54
+ from openmodule.database.migration import migrate_database
55
+ migrate_database(engine, alembic_path)
56
+
57
+ p = MigrationProcess(target=isolated_migration_process)
58
+ try:
59
+ p.start()
60
+ p.join(timeout=5 * 60) # default 5 min timeout -> exception/sentry in service
61
+ except ProcessError:
62
+ raise
63
+ finally:
64
+ if p.is_alive():
65
+ p.kill() # send SIGKILL
66
+ p.join() # wait for death
67
+ p.close() # release all resources associated with the process
68
+
69
+ if p.exitcode is None:
70
+ raise TimeoutError("The duration of the migration exceeded the timeout")
71
+
72
+ if p.exception is not None:
73
+ # piped exception with full traceback from child process -> if migration failed e.g. bad migration file
74
+ e, tb = p.exception
75
+ raise MigrationError(tb) from e
76
+
77
+
78
+ def database_path(db_folder, db_name):
79
+ return os.path.join(db_folder, db_name) + ".sqlite3"
80
+
81
+
82
+ def get_database(db_folder: str, name: str, alembic_path: Optional[str] = None):
83
+ global active_databases
84
+ tmp = database_path(db_folder, name)
85
+ assert active_databases.get(tmp) is None, f"database {tmp} already exists," \
86
+ f" check if it was shutdown before a new one was created"
87
+ os.makedirs(db_folder, exist_ok=True)
88
+ path = f"sqlite:///{tmp}"
89
+ engine = create_engine(path, poolclass=StaticPool, connect_args={'check_same_thread': False})
90
+
91
+ # migration executed in a separate process -> no alembic import in main process
92
+ execute_migration(engine, alembic_path)
93
+
94
+ active_databases[tmp] = engine
95
+
96
+ return engine
97
+
98
+
99
+ class DatabaseContext:
100
+ def __init__(self, database: 'Database', expire_on_commit=True):
101
+ self.database = database
102
+ self.expire_on_commit = expire_on_commit
103
+
104
+ def __enter__(self) -> Session:
105
+ return self.database.__enter__(expire_on_commit=self.expire_on_commit)
106
+
107
+ def __exit__(self, exc_type, exc_val, exc_tb):
108
+ return self.database.__exit__(exc_type, exc_val, exc_tb)
109
+
110
+
111
+ class SessionWrapper(Session):
112
+ _closed = False
113
+
114
+ def __getattribute__(self, item):
115
+ attr = super().__getattribute__(item)
116
+ if isinstance(attr, types.MethodType) and self._closed:
117
+ raise AssertionError("Session is already closed")
118
+ return attr
119
+
120
+ def close(self):
121
+ super().close()
122
+ self._closed = True
123
+
124
+
125
+ class Database:
126
+ active_session: Optional[Session]
127
+
128
+ def __init__(self, database_folder, name="database", alembic_path=None):
129
+ self.db_folder = database_folder
130
+ self.name = name
131
+ self._engine = get_database(database_folder, name, alembic_path)
132
+ self._session = sessionmaker(bind=self._engine, class_=SessionWrapper)
133
+ self.active_session = None
134
+ self.lock = threading.RLock()
135
+
136
+ def is_open(self):
137
+ return bool(self._session)
138
+
139
+ def shutdown(self):
140
+ assert self.is_open(), "database is already closed, you called shutdown twice somewhere"
141
+
142
+ with self.lock:
143
+ self._session = None
144
+ global active_databases
145
+ active_databases.pop(database_path(self.db_folder, self.name), None)
146
+
147
+ def __call__(self, expire_on_commit=True) -> DatabaseContext:
148
+ return DatabaseContext(self, expire_on_commit=expire_on_commit)
149
+
150
+ def __enter__(self, expire_on_commit=True) -> Session:
151
+ assert self._session, "Session is already closed"
152
+ self.lock.acquire()
153
+ self.active_session = self._session(expire_on_commit=expire_on_commit)
154
+ return self.active_session
155
+
156
+ def flush(self, objects=None):
157
+ assert self._session and self.active_session, "Session is already closed"
158
+ self.active_session.flush(objects)
159
+
160
+ def __exit__(self, exc_type, exc_val, exc_tb):
161
+ try:
162
+ if exc_type is None:
163
+ self.active_session.commit()
164
+ else:
165
+ self.active_session.rollback()
166
+ finally:
167
+ self.active_session.close()
168
+ self.active_session = None
169
+ self.lock.release()
@@ -4,7 +4,6 @@ from alembic import context
4
4
  from sqlalchemy import engine_from_config
5
5
  from sqlalchemy import pool
6
6
 
7
- from openmodule.checks import check_invalid_database_column_type
8
7
  from openmodule.database.custom_types import CustomType
9
8
 
10
9
  # this is the Alembic Config object, which provides
@@ -2,39 +2,18 @@ import datetime
2
2
  import os
3
3
  import shutil
4
4
  import sys
5
- import threading
6
- import types
7
5
  import warnings
8
6
  from typing import Optional
9
-
7
+
10
8
  from alembic import command, context
11
9
  from alembic.autogenerate import comparators, renderers
12
10
  from alembic.config import Config
13
11
  from alembic.operations import Operations, MigrateOperation
14
12
  from alembic.runtime.migration import MigrationContext
15
- from sqlalchemy import create_engine, event, MetaData
13
+
14
+ from sqlalchemy import MetaData, DateTime
16
15
  from sqlalchemy.engine import Engine
17
16
  from sqlalchemy.engine.reflection import Inspector
18
- from sqlalchemy.orm import sessionmaker, Session
19
- from sqlalchemy.pool import StaticPool
20
-
21
- from openmodule.checks import check_invalid_database_column_type
22
-
23
-
24
- def drop_alembic_tmp_tables(op):
25
- conn = op.get_bind()
26
- inspector = Inspector.from_engine(conn)
27
- tables = inspector.get_table_names()
28
- for table in tables:
29
- if table.startswith("_alembic_tmp_"):
30
- op.drop_table(table)
31
-
32
-
33
- @event.listens_for(Engine, "connect")
34
- def set_sqlite_pragma(dbapi_connection, connection_record):
35
- cursor = dbapi_connection.cursor()
36
- cursor.execute("PRAGMA foreign_keys=ON")
37
- cursor.close()
38
17
 
39
18
 
40
19
  @Operations.register_operation("pre_upgrade")
@@ -43,7 +22,9 @@ class PreUpgradeOp(MigrateOperation):
43
22
  def pre_upgrade(cls, operations, **kw):
44
23
  migration_context: MigrationContext = operations.migration_context
45
24
  basename, _ = os.path.splitext(os.path.basename(migration_context.connection.engine.url.database))
46
- filename = f"{basename}_{datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')}.sqlite3.backup"
25
+ timestamp = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')
26
+ migration_revision = migration_context.get_current_revision()
27
+ filename = f"{basename}_{timestamp}_{migration_revision}.sqlite3.backup"
47
28
 
48
29
  shutil.copy(migration_context.connection.engine.url.database,
49
30
  os.path.join(os.path.dirname(migration_context.connection.engine.url.database), filename))
@@ -96,7 +77,12 @@ class PostDowngradeOp(MigrateOperation):
96
77
  def pre_upgrade(operations, operation):
97
78
  # NOTE: This is currently in sync with pre_downgrade, if you want to have
98
79
  # different behavior, you'll need to change th pre_downgrade function below
99
- drop_alembic_tmp_tables(operations)
80
+ conn = operations.get_bind()
81
+ inspector = Inspector.from_engine(conn)
82
+ tables = inspector.get_table_names()
83
+ for table in tables:
84
+ if table.startswith("_alembic_tmp_"):
85
+ operations.drop_table(table)
100
86
  operations.execute("PRAGMA foreign_keys = OFF")
101
87
 
102
88
 
@@ -143,7 +129,7 @@ def add_pre_upgrade_hooks(autogen_context, upgrade_ops, schemas):
143
129
  upgrade_ops.ops.append(PostUpgradeOp())
144
130
 
145
131
 
146
- def alembic_config(connection, alembic_path):
132
+ def alembic_config(connection: Engine, alembic_path: str):
147
133
  alembic_cfg = Config(os.path.join(alembic_path, "alembic.ini"),
148
134
  attributes={
149
135
  "configure_logging": False,
@@ -153,16 +139,15 @@ def alembic_config(connection, alembic_path):
153
139
  return alembic_cfg
154
140
 
155
141
 
156
- def migrate_database(connection, alembic_path=None):
142
+ def migrate_database(connection: Engine, alembic_path: Optional[str] = None):
157
143
  if alembic_path is None:
158
144
  alembic_path = os.path.join(os.getcwd(), "database")
159
145
  assert os.path.exists(os.path.abspath(alembic_path)), f"alembic path {os.path.abspath(alembic_path)} does not exist"
160
146
  config = alembic_config(connection, alembic_path)
161
147
  command.upgrade(config, "head")
162
- assert connection.execute("PRAGMA foreign_keys").fetchone()[0] == 1, "foreign keys are not enabled"
163
148
 
164
-
165
- active_databases = {}
149
+ check = connection.execute("PRAGMA foreign_keys").fetchone()
150
+ assert check is not None and check[0] == 1, "foreign keys are not enabled"
166
151
 
167
152
 
168
153
  def register_bases(bases, show_deprecation_warning=True):
@@ -193,92 +178,7 @@ def run_env_py(bases):
193
178
  del sys.modules["openmodule.database.env"] # unload the module, so we can re-run it (mostly testcases)
194
179
 
195
180
 
196
- def database_path(db_folder, db_name):
197
- return os.path.join(db_folder, db_name) + ".sqlite3"
198
-
199
-
200
- def get_database(db_folder: str, name: str, alembic_path=None):
201
- global active_databases
202
- tmp = database_path(db_folder, name)
203
- assert active_databases.get(tmp) is None, f"database {tmp} already exists," \
204
- f" check if it was shutdown before a new one was created"
205
- os.makedirs(db_folder, exist_ok=True)
206
- path = f"sqlite:///{tmp}"
207
- engine = create_engine(path, poolclass=StaticPool, connect_args={'check_same_thread': False})
208
- migrate_database(engine, alembic_path)
209
- active_databases[tmp] = engine
210
-
211
- return engine
212
-
213
-
214
- class DatabaseContext:
215
- def __init__(self, database: 'Database', expire_on_commit=True):
216
- self.database = database
217
- self.expire_on_commit = expire_on_commit
218
-
219
- def __enter__(self) -> Session:
220
- return self.database.__enter__(expire_on_commit=self.expire_on_commit)
221
-
222
- def __exit__(self, exc_type, exc_val, exc_tb):
223
- return self.database.__exit__(exc_type, exc_val, exc_tb)
224
-
225
-
226
- class SessionWrapper(Session):
227
- _closed = False
228
-
229
- def __getattribute__(self, item):
230
- attr = super().__getattribute__(item)
231
- if isinstance(attr, types.MethodType) and self._closed:
232
- raise AssertionError("Session is already closed")
233
- return attr
234
-
235
- def close(self):
236
- super().close()
237
- self._closed = True
238
-
239
-
240
- class Database:
241
- active_session: Optional[Session]
242
-
243
- def __init__(self, database_folder, name="database", alembic_path=None):
244
- self.db_folder = database_folder
245
- self.name = name
246
- self._engine = get_database(database_folder, name, alembic_path)
247
- self._session = sessionmaker(bind=self._engine, class_=SessionWrapper)
248
- self.active_session = None
249
- self.lock = threading.RLock()
250
-
251
- def is_open(self):
252
- return bool(self._session)
253
-
254
- def shutdown(self):
255
- assert self.is_open(), "database is already closed, you called shutdown twice somewhere"
256
-
257
- with self.lock:
258
- self._session = None
259
- global active_databases
260
- active_databases.pop(database_path(self.db_folder, self.name), None)
261
-
262
- def __call__(self, expire_on_commit=True) -> DatabaseContext:
263
- return DatabaseContext(self, expire_on_commit=expire_on_commit)
264
-
265
- def __enter__(self, expire_on_commit=True) -> Session:
266
- assert self._session, "Session is already closed"
267
- self.lock.acquire()
268
- self.active_session = self._session(expire_on_commit=expire_on_commit)
269
- return self.active_session
270
-
271
- def flush(self, objects=None):
272
- assert self._session and self.active_session, "Session is already closed"
273
- self.active_session.flush(objects)
274
-
275
- def __exit__(self, exc_type, exc_val, exc_tb):
276
- try:
277
- if exc_type is None:
278
- self.active_session.commit()
279
- else:
280
- self.active_session.rollback()
281
- finally:
282
- self.active_session.close()
283
- self.active_session = None
284
- self.lock.release()
181
+ def check_invalid_database_column_type(typ):
182
+ from openmodule import config
183
+ if config.run_checks():
184
+ assert not isinstance(typ, DateTime), "Do NOT use DateTime fields, use TZDateTime fields instead"
@@ -108,6 +108,8 @@ class MessageDispatcher:
108
108
  self.raise_validation_errors = raise_validation_errors
109
109
  self.raise_handler_errors = raise_handler_errors
110
110
  self.executor = executor or DummyExecutor()
111
+ self._shutdown = False
112
+ self._shutdown_lock = threading.Lock()
111
113
 
112
114
  @property
113
115
  def is_multi_threaded(self):
@@ -125,7 +127,9 @@ class MessageDispatcher:
125
127
  return True
126
128
 
127
129
  def shutdown(self, wait=True):
128
- self.executor.shutdown(wait=wait)
130
+ with self._shutdown_lock:
131
+ self._shutdown = True
132
+ self.executor.shutdown(wait=wait)
129
133
 
130
134
  def unregister_handler(self, listener: Listener):
131
135
  for topic, listeners in self.listeners.items():
@@ -180,15 +184,22 @@ class MessageDispatcher:
180
184
  return listener
181
185
 
182
186
  def dispatch(self, topic: str, message: Union[Dict, BaseModel]):
183
- assert isinstance(topic, str), "topic must be a string"
184
-
185
- if isinstance(message, BaseModel):
186
- message = message.dict()
187
-
188
- listeners = self.listeners.get(topic, [])
189
- for listener in listeners:
190
- if listener.matches(message):
191
- self.executor.submit(self.execute, listener, message)
187
+ with self._shutdown_lock:
188
+ if self._shutdown:
189
+ # We need to drop messages after shutdown somewhere.
190
+ # Cannot be done in the executor because it's a python builtin base class.
191
+ # Could also be done in core, but it's better here because messages and _messages_internal are
192
+ # both dispatched here.
193
+ return
194
+ assert isinstance(topic, str), "topic must be a string"
195
+
196
+ if isinstance(message, BaseModel):
197
+ message = message.dict()
198
+
199
+ listeners = self.listeners.get(topic, [])
200
+ for listener in listeners:
201
+ if listener.matches(message):
202
+ self.executor.submit(self.execute, listener, message)
192
203
 
193
204
  def execute(self, listener: Listener, message: Dict):
194
205
  try:
@@ -1,11 +1,12 @@
1
1
  import logging
2
+ import sys
2
3
 
3
4
 
4
5
  def init_logging(core):
5
6
  assert hasattr(core.config, "LOG_LEVEL"), (
6
- "LOG_LEVLE setting not found in your config. In order to use logging please add \n"
7
+ "LOG_LEVEL setting not found in your config. In order to use logging please add \n"
7
8
  "> LOG_LEVEL = config.log_level()\n"
8
9
  "to your config.py"
9
10
  )
10
- logging.basicConfig(level=core.config.LOG_LEVEL)
11
+ logging.basicConfig(level=core.config.LOG_LEVEL, stream=sys.stdout)
11
12
  logging.captureWarnings(True)
@@ -12,18 +12,29 @@ from openmodule.dispatcher import MessageDispatcher
12
12
  from openmodule.models.base import ZMQMessage
13
13
 
14
14
 
15
+ def _internal_get_pub_socket(context, config, linger=100) -> zmq.Socket:
16
+ socket: zmq.Socket = context.socket(zmq.PUB)
17
+ socket.setsockopt(zmq.LINGER, linger)
18
+ socket.setsockopt(zmq.TCP_KEEPALIVE, 1)
19
+ socket.setsockopt(zmq.TCP_KEEPALIVE_IDLE, 3600)
20
+ socket.connect(config.BROKER_SUB)
21
+ return socket
22
+
23
+
15
24
  def get_sub_socket(context, config, linger=0) -> zmq.Socket:
16
- socket = context.socket(zmq.SUB)
25
+ socket: zmq.Socket = context.socket(zmq.SUB)
17
26
  socket.setsockopt(zmq.LINGER, linger)
27
+ socket.setsockopt(zmq.TCP_KEEPALIVE, 1)
28
+ socket.setsockopt(zmq.TCP_KEEPALIVE_IDLE, 3600)
18
29
  socket.connect(config.BROKER_PUB)
19
30
  return socket
20
31
 
21
32
 
22
33
  def get_pub_socket(context, config, linger=100) -> zmq.Socket:
23
- socket = context.socket(zmq.PUB)
24
- socket.setsockopt(zmq.LINGER, linger)
25
- socket.connect(config.BROKER_SUB)
26
- return socket
34
+ raise AssertionError(
35
+ "This function must not be used. Use core().publish instead (OM-697, OM-700). This is because the pub_socket "
36
+ "receives all subscriptions which will result in a memory leak until a message is sent to the socket."
37
+ )
27
38
 
28
39
 
29
40
  def wait_for_connection(dispatcher: MessageDispatcher, pub_socket=None, pub_lock=None, timeout=100):
@@ -22,12 +22,27 @@ def _donotuse(v, *, default):
22
22
 
23
23
 
24
24
  if config.run_checks():
25
- from openmodule import checks
26
-
27
- meta_kwargs = {"metaclass": checks.CheckingOpenModuleModel}
25
+ class CheckingOpenModuleModel(type(BaseModel), type):
26
+ def __new__(mcs, name, bases, dct):
27
+ cls = super().__new__(mcs, name, bases, dct)
28
+
29
+ # noinspection PyUnresolvedReferences
30
+ for field_name, field in cls.__fields__.items():
31
+ if field.type_ == datetime:
32
+ assert "_timezone_validator" in field.class_validators, (
33
+ "datetime fields must use the timezone_validator to ensure all datetime values "
34
+ "are always naive. Otherwise runtime errors may occur depending on the isoformat used.\n"
35
+ f"In class {name}, please add:\n"
36
+ f' _tz_{field_name} = timezone_validator("{field_name}")'
37
+ )
38
+
39
+ return cls
40
+
41
+ meta_kwargs = {"metaclass": CheckingOpenModuleModel}
28
42
  else:
29
43
  meta_kwargs = {}
30
44
 
45
+
31
46
  ESCAPE_ASCII = re.compile(r'([^ -~])')
32
47
 
33
48
 
@@ -122,15 +137,9 @@ def datetime_to_timestamp(dt: datetime):
122
137
 
123
138
  class ZMQMessage(OpenModuleModel):
124
139
  timestamp: datetime = Field(default_factory=lambda: datetime.utcnow())
125
- name: str
140
+ name: str = Field(default_factory=lambda: settings.NAME)
126
141
  type: str
127
142
 
128
- def __init__(self, **kwargs):
129
- name = kwargs.pop("name", None)
130
- if name is None:
131
- name = settings.NAME
132
- super().__init__(name=name, **kwargs)
133
-
134
143
  _tz_timestamp = timezone_validator("timestamp")
135
144
 
136
145
  def publish_on_topic(self, pub_socket: zmq.Socket, topic: str):