openmodule 13.3.0__tar.gz → 13.5.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.3.0 → openmodule-13.5.0}/AUTHORS +1 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/ChangeLog +12 -15
- {openmodule-13.3.0/openmodule.egg-info → openmodule-13.5.0}/PKG-INFO +1 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/migrations.md +6 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/core.py +2 -2
- openmodule-13.5.0/openmodule/database/database.py +169 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/database/env.py +0 -1
- openmodule-13.3.0/openmodule/database/database.py → openmodule-13.5.0/openmodule/database/migration.py +20 -120
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/base.py +18 -3
- {openmodule-13.3.0 → openmodule-13.5.0/openmodule.egg-info}/PKG-INFO +1 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule.egg-info/SOURCES.txt +15 -1
- openmodule-13.5.0/openmodule.egg-info/pbr.json +1 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule.egg-info/requires.txt +1 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/database.py +16 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/requirements.txt +1 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/invalid_database/alembic/env.py +1 -1
- {openmodule-13.3.0/tests/test_access_service_database → openmodule-13.5.0/tests/migration_double_column_delete_error}/alembic/env.py +1 -1
- openmodule-13.5.0/tests/migration_double_column_delete_error/alembic/versions/812a3e5b8517_initial.py +62 -0
- openmodule-13.5.0/tests/migration_double_column_delete_error/alembic/versions/a7ea100a784f_key_error.py +40 -0
- openmodule-13.5.0/tests/migration_double_column_delete_error/alembic.ini +85 -0
- openmodule-13.5.0/tests/migration_double_column_delete_error/makemigration.sh +21 -0
- openmodule-13.5.0/tests/migration_no_such_table_error/alembic/env.py +4 -0
- openmodule-13.5.0/tests/migration_no_such_table_error/alembic/versions/812a3e5b8517_initial.py +62 -0
- openmodule-13.5.0/tests/migration_no_such_table_error/alembic/versions/a7ea100a784f_no_such_table_error.py +39 -0
- openmodule-13.5.0/tests/migration_no_such_table_error/alembic.ini +85 -0
- openmodule-13.5.0/tests/migration_no_such_table_error/makemigration.sh +21 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/migration_test_database/alembic/env.py +1 -1
- openmodule-13.5.0/tests/test_access_service_database/alembic/env.py +4 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_alembic_migrations.py +3 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_checks.py +2 -2
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_database/alembic/env.py +1 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_database.py +31 -3
- openmodule-13.5.0/tests/test_kv_store_database/alembic/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_database/alembic/env.py +1 -1
- openmodule-13.5.0/tests/test_kv_store_database/alembic/script.py.mako +24 -0
- openmodule-13.5.0/tests/test_kv_store_multiple_database/alembic/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_multiple_database/alembic/env.py +1 -1
- openmodule-13.5.0/tests/test_kv_store_multiple_database/alembic/script.py.mako +24 -0
- openmodule-13.3.0/openmodule/checks.py +0 -28
- openmodule-13.3.0/openmodule.egg-info/pbr.json +0 -1
- {openmodule-13.3.0 → openmodule-13.5.0}/.gitlab-ci.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/LICENSE +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/README.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/access_service.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/anonymization.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/cleanup.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/coding_standard.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/commands.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/connection_status_listener.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/csv_export.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/database.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/README.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/access_service/openmodule/models/access_service.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/access_service/openmodule/utils/access_service.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/access_service/openmodule_test/access_service.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/access_service/tests/test_utils_access_service.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/api/openmodule/utils/api.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/api/openmodule_test/api.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/api/tests/test_utils_api.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/package_reader/openmodule/utils/package_reader.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/package_reader/openmodule_test/fake_package_creator.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/deprecated_code/package_reader/tests/test_package_reader.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/event_sending.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/getting_started.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/health.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/images/broker.drawio.png +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/known_issues.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/package_reader.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/rpc.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/settings.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/settings_provider.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/testing.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/translation.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/docs/utils.md +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/alert.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/config.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/connection_status.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/database/custom_types.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/dispatcher.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/health.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/logging.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/messaging.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/access_service.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/alert.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/io.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/kv_store.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/presence.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/privacy.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/rpc.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/settings.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/validation.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/models/vehicle.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/rpc/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/rpc/client.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/rpc/common.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/rpc/server.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/sentry.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/threading.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/access_service.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/charset.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/cleanup.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/csv_export.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/databox.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/db_helper.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/eventlog.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/io.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/kv_store.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/matching.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/misc_functions.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/package_reader.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/presence.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/schema.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/settings.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/translation.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule/utils/validation.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule.egg-info/dependency_links.txt +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule.egg-info/not-zip-safe +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule.egg-info/top_level.txt +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_commands/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_commands/setup.cfg +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_commands/setup.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_commands/translate.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/alert.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/connection_status.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/core.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/eventlistener.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/files.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/gate.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/health.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/interrupt.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/io_simulator.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/package_reader.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/presence.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/requirements.txt +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/rpc.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/settings.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/setup.cfg +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/setup.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/utils.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/openmodule_test/zeromq.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/setup.cfg +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/setup.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/test-requirements.txt +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/config.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/database_models_migration.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/database_models_test.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/invalid_database/alembic/README +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/invalid_database/alembic/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/invalid_database/alembic/script.py.mako +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/invalid_database/alembic/versions/ff26e54332f9_datetime_models.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/invalid_database/alembic.ini +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/invalid_database/makemigration.sh +0 -0
- {openmodule-13.3.0/tests/migration_test_database → openmodule-13.5.0/tests/migration_double_column_delete_error/alembic}/__init__.py +0 -0
- {openmodule-13.3.0/tests/migration_test_database → openmodule-13.5.0/tests/migration_double_column_delete_error}/alembic/script.py.mako +0 -0
- {openmodule-13.3.0/tests/migration_test_database → openmodule-13.5.0/tests/migration_no_such_table_error}/alembic/__init__.py +0 -0
- {openmodule-13.3.0/tests/test_access_service_database → openmodule-13.5.0/tests/migration_no_such_table_error}/alembic/script.py.mako +0 -0
- {openmodule-13.3.0/tests/migration_test_database/alembic/versions → openmodule-13.5.0/tests/migration_test_database}/__init__.py +0 -0
- {openmodule-13.3.0/tests/test_access_service_database → openmodule-13.5.0/tests/migration_test_database}/alembic/__init__.py +0 -0
- {openmodule-13.3.0/tests/test_database → openmodule-13.5.0/tests/migration_test_database}/alembic/script.py.mako +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/migration_test_database/alembic/versions/19789aa5361c_initial.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/migration_test_database/alembic/versions/19d887929ae7_alter.py +0 -0
- {openmodule-13.3.0/tests/test_database/alembic → openmodule-13.5.0/tests/migration_test_database/alembic/versions}/__init__.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/migration_test_database/alembic.ini +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/migration_test_database/alembic_migration_test_database.sqlite3 +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/migration_test_database/makemigration.sh +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/configs/config.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/configs/test_config.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/configs/test_config_1.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/standard_schemes/DEFAULT-10.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/standard_schemes/DEFAULT-20.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/standard_schemes/LEGACY-0.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/translation/locale/de/LC_MESSAGES/translation.mo +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/translation/locale/de/LC_MESSAGES/translation.po +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/translation/locale/en/LC_MESSAGES/translation.mo +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/translation/locale/en/LC_MESSAGES/translation.po +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/translation/locale/translation.pot +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/translation/translate.sh +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/A-10.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/A-20.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/DEFAULT-10.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/DEFAULT-20.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/DEFAULT-30.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/LEGACY-0.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/TEST-10.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/TEST-20.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/TEST-30.yml +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/resources/utils_matching/TEST-40.yml +0 -0
- {openmodule-13.3.0/tests/test_kv_store_database → openmodule-13.5.0/tests/test_access_service_database}/alembic/__init__.py +0 -0
- {openmodule-13.3.0/tests/test_kv_store_database → openmodule-13.5.0/tests/test_access_service_database}/alembic/script.py.mako +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_access_service_database/alembic/versions/7bd4fcd38fde_removed_nfc_and_pin.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_access_service_database/alembic/versions/9ca98a2e5674_added_parksettings_id.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_access_service_database/alembic/versions/c821971f9230_initial.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_access_service_database/alembic.ini +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_access_service_database/makemigration.sh +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_alert.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_config.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_connection_status.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_core.py +0 -0
- {openmodule-13.3.0/tests/test_kv_store_multiple_database → openmodule-13.5.0/tests/test_database}/alembic/__init__.py +0 -0
- {openmodule-13.3.0/tests/test_kv_store_multiple_database → openmodule-13.5.0/tests/test_database}/alembic/script.py.mako +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_database/alembic/versions/32b8c728abbf_initial.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_database/alembic.ini +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_database/makemigration.sh +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_dispatcher.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_health.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_interrupt.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_io_listen.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_database/alembic/versions/9c5c944221f4_deprecated_kv_entry_example.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_database/alembic/versions/c55a69026a25_initial.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_database/alembic.ini +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_database/makemigration.sh +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_multiple_database/alembic/README +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_multiple_database/alembic/versions/cdb3214131a9_initial.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_multiple_database/alembic.ini +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_kv_store_multiple_database/makemigration.sh +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_messaging.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_mockrpcclient.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_model.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_rpc.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_schema.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_sentry.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_test_alert.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_test_gate.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_test_zeromq.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_access_service.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_charset.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_cleanup.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_csv_export.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_databox.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_eventlog.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_kv_store.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_kv_store_multiple.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_matching.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_misc_functions.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_package_reader.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_presence.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_settings.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_validation.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.0}/tests/test_utils_vehicle.py +0 -0
- {openmodule-13.3.0 → openmodule-13.5.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.5.0
|
|
5
|
+
-------
|
|
6
|
+
|
|
7
|
+
* document breaking changes
|
|
8
|
+
* OM-405: remove database imports from non-database services + isolate migration process to exclude alembic import in the main process
|
|
9
|
+
|
|
10
|
+
v13.4.0
|
|
11
|
+
-------
|
|
12
|
+
|
|
13
|
+
* Om 164: settings-models update for enforcment settings changes
|
|
14
|
+
|
|
4
15
|
v13.3.0
|
|
5
16
|
-------
|
|
6
17
|
|
|
@@ -11,6 +22,7 @@ v13.2.0
|
|
|
11
22
|
|
|
12
23
|
* OM 187 Alembic migration test mixin
|
|
13
24
|
* add a testcase for expiring session data while adding new models (noticed in OM-327)
|
|
25
|
+
* OM-184 Openmodule Database: Migration Backups and Fix in Operations "pre\_downgrade()" and "post\_downgrade()"
|
|
14
26
|
* Aktualisierung der Dokumentation
|
|
15
27
|
* OM-255
|
|
16
28
|
|
|
@@ -152,12 +164,6 @@ v11.1.0.rc5
|
|
|
152
164
|
-----------
|
|
153
165
|
|
|
154
166
|
* again no language set in testing
|
|
155
|
-
|
|
156
|
-
v11.1.0.rc4
|
|
157
|
-
-----------
|
|
158
|
-
|
|
159
|
-
* arivo-schedule in public pip
|
|
160
|
-
* use temporary arivo-schedule package
|
|
161
167
|
* removed mock rpcs from schema
|
|
162
168
|
|
|
163
169
|
v11.0.3
|
|
@@ -209,12 +215,3 @@ v10.0.2.rc1
|
|
|
209
215
|
-----------
|
|
210
216
|
|
|
211
217
|
* Refactor for backend class for controller v2
|
|
212
|
-
|
|
213
|
-
v10.0.1
|
|
214
|
-
-------
|
|
215
|
-
|
|
216
|
-
* fix for ondelete=CASCADE database migration problem
|
|
217
|
-
|
|
218
|
-
v10.0.0
|
|
219
|
-
-------
|
|
220
|
-
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Breaking Version Changes
|
|
2
2
|
|
|
3
|
+
## 13.5.0:
|
|
4
|
+
* changes in env.py of alembic: <br>
|
|
5
|
+
`from openmodule.database.database import run_env_py` -> <br>
|
|
6
|
+
`from openmodule.database.migration import run_env_py`
|
|
7
|
+
|
|
8
|
+
|
|
3
9
|
## 13.0.0
|
|
4
10
|
* access_service:
|
|
5
11
|
* changes in AccessCheckAccess (removed media field) and AccessCheckResponse (removed error field)
|
|
@@ -11,7 +11,6 @@ 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
|
|
@@ -23,7 +22,7 @@ from openmodule.connection_status import ConnectionStatusListener
|
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
class OpenModuleCore(threading.Thread):
|
|
26
|
-
database
|
|
25
|
+
database = None
|
|
27
26
|
|
|
28
27
|
def __init__(self, context, config, messages_executor=None):
|
|
29
28
|
super().__init__(target=get_thread_wrapper(self._run))
|
|
@@ -54,6 +53,7 @@ class OpenModuleCore(threading.Thread):
|
|
|
54
53
|
match_type=True, register_schema=False)
|
|
55
54
|
|
|
56
55
|
def init_database(self):
|
|
56
|
+
from openmodule.database.database import Database
|
|
57
57
|
if self.config.TESTING:
|
|
58
58
|
self.database = Database(self.config.DATABASE_FOLDER, self.config.NAME, alembic_path="../src/database")
|
|
59
59
|
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"
|
|
@@ -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
|
|
|
@@ -43,7 +43,6 @@ docs/deprecated_code/package_reader/tests/test_package_reader.py
|
|
|
43
43
|
docs/images/broker.drawio.png
|
|
44
44
|
openmodule/__init__.py
|
|
45
45
|
openmodule/alert.py
|
|
46
|
-
openmodule/checks.py
|
|
47
46
|
openmodule/config.py
|
|
48
47
|
openmodule/connection_status.py
|
|
49
48
|
openmodule/core.py
|
|
@@ -63,6 +62,7 @@ openmodule.egg-info/top_level.txt
|
|
|
63
62
|
openmodule/database/custom_types.py
|
|
64
63
|
openmodule/database/database.py
|
|
65
64
|
openmodule/database/env.py
|
|
65
|
+
openmodule/database/migration.py
|
|
66
66
|
openmodule/models/__init__.py
|
|
67
67
|
openmodule/models/access_service.py
|
|
68
68
|
openmodule/models/alert.py
|
|
@@ -167,6 +167,20 @@ tests/invalid_database/alembic/__init__.py
|
|
|
167
167
|
tests/invalid_database/alembic/env.py
|
|
168
168
|
tests/invalid_database/alembic/script.py.mako
|
|
169
169
|
tests/invalid_database/alembic/versions/ff26e54332f9_datetime_models.py
|
|
170
|
+
tests/migration_double_column_delete_error/alembic.ini
|
|
171
|
+
tests/migration_double_column_delete_error/makemigration.sh
|
|
172
|
+
tests/migration_double_column_delete_error/alembic/__init__.py
|
|
173
|
+
tests/migration_double_column_delete_error/alembic/env.py
|
|
174
|
+
tests/migration_double_column_delete_error/alembic/script.py.mako
|
|
175
|
+
tests/migration_double_column_delete_error/alembic/versions/812a3e5b8517_initial.py
|
|
176
|
+
tests/migration_double_column_delete_error/alembic/versions/a7ea100a784f_key_error.py
|
|
177
|
+
tests/migration_no_such_table_error/alembic.ini
|
|
178
|
+
tests/migration_no_such_table_error/makemigration.sh
|
|
179
|
+
tests/migration_no_such_table_error/alembic/__init__.py
|
|
180
|
+
tests/migration_no_such_table_error/alembic/env.py
|
|
181
|
+
tests/migration_no_such_table_error/alembic/script.py.mako
|
|
182
|
+
tests/migration_no_such_table_error/alembic/versions/812a3e5b8517_initial.py
|
|
183
|
+
tests/migration_no_such_table_error/alembic/versions/a7ea100a784f_no_such_table_error.py
|
|
170
184
|
tests/migration_test_database/__init__.py
|
|
171
185
|
tests/migration_test_database/alembic.ini
|
|
172
186
|
tests/migration_test_database/alembic_migration_test_database.sqlite3
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"git_version": "84be0cd", "is_release": true}
|
|
@@ -9,7 +9,10 @@ from sqlalchemy import MetaData
|
|
|
9
9
|
from sqlalchemy.ext.automap import automap_base
|
|
10
10
|
|
|
11
11
|
from openmodule.config import settings
|
|
12
|
-
from openmodule.database.database import Database, database_path
|
|
12
|
+
from openmodule.database.database import Database, database_path
|
|
13
|
+
from openmodule.database.migration import alembic_config
|
|
14
|
+
|
|
15
|
+
from unittest.mock import patch
|
|
13
16
|
|
|
14
17
|
_first_start = True
|
|
15
18
|
|
|
@@ -37,6 +40,7 @@ class SQLiteTestMixin(TestCase):
|
|
|
37
40
|
database_folder: str = None # defaults to settings.DATABASE_FOLDER
|
|
38
41
|
alembic_path = "../src/database"
|
|
39
42
|
database_name = "database"
|
|
43
|
+
main_process_migration = True # change if migration should be performed in a separate process
|
|
40
44
|
|
|
41
45
|
@classmethod
|
|
42
46
|
def get_database_folder(cls):
|
|
@@ -44,6 +48,15 @@ class SQLiteTestMixin(TestCase):
|
|
|
44
48
|
|
|
45
49
|
@classmethod
|
|
46
50
|
def setUpClass(cls) -> None:
|
|
51
|
+
if cls.main_process_migration is True:
|
|
52
|
+
from openmodule.database.migration import migrate_database
|
|
53
|
+
# instead of migrating db in a child process we 'mock' it in the main process to prevent some errors
|
|
54
|
+
cls.patcher = patch(
|
|
55
|
+
'openmodule.database.database.execute_migration',
|
|
56
|
+
new=lambda x, y: migrate_database(x, y)
|
|
57
|
+
)
|
|
58
|
+
cls.patcher.start()
|
|
59
|
+
|
|
47
60
|
# we only know which databases are in use on tear down, so truncating only works in teardown
|
|
48
61
|
# but in order to not be annoyed by failed tests which left broken databases, we delete all databases
|
|
49
62
|
# once initially
|
|
@@ -75,6 +88,8 @@ class SQLiteTestMixin(TestCase):
|
|
|
75
88
|
cls.database.shutdown()
|
|
76
89
|
os.unlink(database_path(cls.get_database_folder(), cls.database_name))
|
|
77
90
|
super().tearDownClass()
|
|
91
|
+
if cls.main_process_migration is True:
|
|
92
|
+
cls.patcher.stop()
|
|
78
93
|
|
|
79
94
|
|
|
80
95
|
class AlembicMigrationTestMixin(SQLiteTestMixin):
|