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.
- {openmodule-13.4.0 → openmodule-13.6.0}/AUTHORS +1 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/ChangeLog +12 -19
- {openmodule-13.4.0/openmodule.egg-info → openmodule-13.6.0}/PKG-INFO +1 -1
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/migrations.md +13 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/core.py +5 -4
- openmodule-13.6.0/openmodule/database/database.py +169 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/database/env.py +0 -1
- openmodule-13.4.0/openmodule/database/database.py → openmodule-13.6.0/openmodule/database/migration.py +20 -120
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/dispatcher.py +21 -10
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/logging.py +3 -2
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/messaging.py +16 -5
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/base.py +19 -10
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/server.py +3 -11
- {openmodule-13.4.0 → openmodule-13.6.0/openmodule.egg-info}/PKG-INFO +1 -1
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/SOURCES.txt +16 -1
- openmodule-13.6.0/openmodule.egg-info/pbr.json +1 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/database.py +16 -1
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/env.py +1 -1
- {openmodule-13.4.0/tests/test_access_service_database → openmodule-13.6.0/tests/migration_double_column_delete_error}/alembic/env.py +1 -1
- openmodule-13.6.0/tests/migration_double_column_delete_error/alembic/versions/812a3e5b8517_initial.py +62 -0
- openmodule-13.6.0/tests/migration_double_column_delete_error/alembic/versions/a7ea100a784f_key_error.py +40 -0
- openmodule-13.6.0/tests/migration_double_column_delete_error/alembic.ini +85 -0
- openmodule-13.6.0/tests/migration_double_column_delete_error/makemigration.sh +21 -0
- openmodule-13.6.0/tests/migration_no_such_table_error/alembic/env.py +4 -0
- openmodule-13.6.0/tests/migration_no_such_table_error/alembic/versions/812a3e5b8517_initial.py +62 -0
- openmodule-13.6.0/tests/migration_no_such_table_error/alembic/versions/a7ea100a784f_no_such_table_error.py +39 -0
- openmodule-13.6.0/tests/migration_no_such_table_error/alembic.ini +85 -0
- openmodule-13.6.0/tests/migration_no_such_table_error/makemigration.sh +21 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic/env.py +1 -1
- openmodule-13.6.0/tests/test_access_service_database/alembic/env.py +4 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_alembic_migrations.py +3 -1
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_checks.py +2 -2
- openmodule-13.6.0/tests/test_core.py +104 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/alembic/env.py +1 -1
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database.py +54 -3
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_dispatcher.py +14 -4
- openmodule-13.6.0/tests/test_kv_store_database/alembic/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic/env.py +1 -1
- openmodule-13.6.0/tests/test_kv_store_database/alembic/script.py.mako +24 -0
- openmodule-13.6.0/tests/test_kv_store_multiple_database/alembic/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic/env.py +1 -1
- openmodule-13.6.0/tests/test_kv_store_multiple_database/alembic/script.py.mako +24 -0
- openmodule-13.6.0/tests/test_logging.py +29 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_messaging.py +10 -4
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_rpc.py +2 -4
- openmodule-13.4.0/openmodule/checks.py +0 -28
- openmodule-13.4.0/openmodule.egg-info/pbr.json +0 -1
- openmodule-13.4.0/tests/test_core.py +0 -48
- {openmodule-13.4.0 → openmodule-13.6.0}/.gitlab-ci.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/LICENSE +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/README.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/access_service.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/anonymization.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/cleanup.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/coding_standard.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/commands.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/connection_status_listener.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/csv_export.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/database.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/README.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/openmodule/models/access_service.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/openmodule/utils/access_service.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/openmodule_test/access_service.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/access_service/tests/test_utils_access_service.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/api/openmodule/utils/api.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/api/openmodule_test/api.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/api/tests/test_utils_api.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/package_reader/openmodule/utils/package_reader.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/package_reader/openmodule_test/fake_package_creator.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/deprecated_code/package_reader/tests/test_package_reader.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/event_sending.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/getting_started.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/health.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/images/broker.drawio.png +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/known_issues.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/package_reader.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/rpc.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/settings.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/settings_provider.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/testing.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/translation.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/docs/utils.md +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/alert.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/config.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/connection_status.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/database/custom_types.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/health.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/access_service.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/alert.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/io.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/kv_store.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/presence.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/privacy.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/rpc.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/settings.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/validation.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/models/vehicle.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/client.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/rpc/common.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/sentry.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/threading.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/access_service.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/charset.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/cleanup.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/csv_export.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/databox.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/db_helper.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/eventlog.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/io.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/kv_store.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/matching.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/misc_functions.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/package_reader.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/presence.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/schema.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/settings.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/translation.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule/utils/validation.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/dependency_links.txt +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/not-zip-safe +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/requires.txt +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule.egg-info/top_level.txt +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/setup.cfg +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/setup.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_commands/translate.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/alert.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/connection_status.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/core.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/eventlistener.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/files.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/gate.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/health.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/interrupt.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/io_simulator.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/package_reader.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/presence.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/requirements.txt +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/rpc.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/settings.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/setup.cfg +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/setup.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/utils.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/openmodule_test/zeromq.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/requirements.txt +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/setup.cfg +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/setup.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/test-requirements.txt +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/config.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/database_models_migration.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/database_models_test.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/README +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/script.py.mako +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic/versions/ff26e54332f9_datetime_models.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/alembic.ini +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/invalid_database/makemigration.sh +0 -0
- {openmodule-13.4.0/tests/migration_test_database → openmodule-13.6.0/tests/migration_double_column_delete_error/alembic}/__init__.py +0 -0
- {openmodule-13.4.0/tests/migration_test_database → openmodule-13.6.0/tests/migration_double_column_delete_error}/alembic/script.py.mako +0 -0
- {openmodule-13.4.0/tests/migration_test_database → openmodule-13.6.0/tests/migration_no_such_table_error}/alembic/__init__.py +0 -0
- {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
- {openmodule-13.4.0/tests/migration_test_database/alembic/versions → openmodule-13.6.0/tests/migration_test_database}/__init__.py +0 -0
- {openmodule-13.4.0/tests/test_access_service_database → openmodule-13.6.0/tests/migration_test_database}/alembic/__init__.py +0 -0
- {openmodule-13.4.0/tests/test_database → openmodule-13.6.0/tests/migration_test_database}/alembic/script.py.mako +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic/versions/19789aa5361c_initial.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic/versions/19d887929ae7_alter.py +0 -0
- {openmodule-13.4.0/tests/test_database/alembic → openmodule-13.6.0/tests/migration_test_database/alembic/versions}/__init__.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic.ini +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/alembic_migration_test_database.sqlite3 +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/migration_test_database/makemigration.sh +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/configs/config.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/configs/test_config.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/configs/test_config_1.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/standard_schemes/DEFAULT-10.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/standard_schemes/DEFAULT-20.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/standard_schemes/LEGACY-0.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/de/LC_MESSAGES/translation.mo +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/de/LC_MESSAGES/translation.po +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/en/LC_MESSAGES/translation.mo +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/en/LC_MESSAGES/translation.po +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/locale/translation.pot +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/translation/translate.sh +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/A-10.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/A-20.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/DEFAULT-10.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/DEFAULT-20.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/DEFAULT-30.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/LEGACY-0.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-10.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-20.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-30.yml +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/resources/utils_matching/TEST-40.yml +0 -0
- {openmodule-13.4.0/tests/test_kv_store_database → openmodule-13.6.0/tests/test_access_service_database}/alembic/__init__.py +0 -0
- {openmodule-13.4.0/tests/test_kv_store_database → openmodule-13.6.0/tests/test_access_service_database}/alembic/script.py.mako +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic/versions/7bd4fcd38fde_removed_nfc_and_pin.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic/versions/9ca98a2e5674_added_parksettings_id.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic/versions/c821971f9230_initial.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/alembic.ini +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_access_service_database/makemigration.sh +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_alert.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_config.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_connection_status.py +0 -0
- {openmodule-13.4.0/tests/test_kv_store_multiple_database → openmodule-13.6.0/tests/test_database}/alembic/__init__.py +0 -0
- {openmodule-13.4.0/tests/test_kv_store_multiple_database → openmodule-13.6.0/tests/test_database}/alembic/script.py.mako +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/alembic/versions/32b8c728abbf_initial.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/alembic.ini +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_database/makemigration.sh +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_health.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_interrupt.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_io_listen.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic/versions/9c5c944221f4_deprecated_kv_entry_example.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic/versions/c55a69026a25_initial.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/alembic.ini +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_database/makemigration.sh +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic/README +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic/versions/cdb3214131a9_initial.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/alembic.ini +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_kv_store_multiple_database/makemigration.sh +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_mockrpcclient.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_model.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_schema.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_sentry.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_test_alert.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_test_gate.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_test_zeromq.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_access_service.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_charset.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_cleanup.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_csv_export.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_databox.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_eventlog.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_kv_store.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_kv_store_multiple.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_matching.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_misc_functions.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_package_reader.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_presence.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_settings.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_validation.py +0 -0
- {openmodule-13.4.0 → openmodule-13.6.0}/tests/test_utils_vehicle.py +0 -0
- {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,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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
197
|
-
|
|
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.
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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):
|