amsdal 0.4.13__cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
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.
- amsdal/Third-Party Materials - AMSDAL Dependencies - License Notices.md +1334 -0
- amsdal/__about__.py +4 -0
- amsdal/__about__.pyi +1 -0
- amsdal/__init__.py +23 -0
- amsdal/__init__.pyi +9 -0
- amsdal/__migrations__/0000_initial.py +217 -0
- amsdal/__migrations__/0001_datetime_type.py +18 -0
- amsdal/__migrations__/0002_fixture_order.py +44 -0
- amsdal/__migrations__/0003_schema_type_in_class_meta.py +44 -0
- amsdal/cloud/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/__init__.pyi +0 -0
- amsdal/cloud/client.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/client.pyi +57 -0
- amsdal/cloud/constants.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/constants.pyi +13 -0
- amsdal/cloud/enums.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/enums.pyi +68 -0
- amsdal/cloud/models/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/models/__init__.pyi +0 -0
- amsdal/cloud/models/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/models/base.pyi +247 -0
- amsdal/cloud/services/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/__init__.pyi +0 -0
- amsdal/cloud/services/actions/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/__init__.pyi +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.pyi +19 -0
- amsdal/cloud/services/actions/add_basic_auth.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_basic_auth.pyi +21 -0
- amsdal/cloud/services/actions/add_dependency.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_dependency.pyi +19 -0
- amsdal/cloud/services/actions/add_secret.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_secret.pyi +20 -0
- amsdal/cloud/services/actions/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/base.pyi +122 -0
- amsdal/cloud/services/actions/create_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_deploy.pyi +41 -0
- amsdal/cloud/services/actions/create_env.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_env.pyi +19 -0
- amsdal/cloud/services/actions/create_session.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_session.pyi +17 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.pyi +19 -0
- amsdal/cloud/services/actions/delete_basic_auth.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_basic_auth.pyi +20 -0
- amsdal/cloud/services/actions/delete_dependency.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_dependency.pyi +21 -0
- amsdal/cloud/services/actions/delete_env.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_env.pyi +21 -0
- amsdal/cloud/services/actions/delete_secret.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_secret.pyi +21 -0
- amsdal/cloud/services/actions/destroy_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/destroy_deploy.pyi +18 -0
- amsdal/cloud/services/actions/expose_db.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/expose_db.pyi +22 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.pyi +21 -0
- amsdal/cloud/services/actions/get_monitoring_info.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/get_monitoring_info.pyi +21 -0
- amsdal/cloud/services/actions/list_dependencies.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_dependencies.pyi +21 -0
- amsdal/cloud/services/actions/list_deploys.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_deploys.pyi +19 -0
- amsdal/cloud/services/actions/list_envs.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_envs.pyi +20 -0
- amsdal/cloud/services/actions/list_secrets.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_secrets.pyi +22 -0
- amsdal/cloud/services/actions/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/manager.pyi +278 -0
- amsdal/cloud/services/actions/signup_action.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/signup_action.pyi +20 -0
- amsdal/cloud/services/actions/update_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/update_deploy.pyi +19 -0
- amsdal/cloud/services/auth/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/__init__.pyi +0 -0
- amsdal/cloud/services/auth/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/base.pyi +6 -0
- amsdal/cloud/services/auth/credentials.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/credentials.pyi +30 -0
- amsdal/cloud/services/auth/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/manager.pyi +26 -0
- amsdal/cloud/services/auth/signup_service.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/signup_service.pyi +32 -0
- amsdal/cloud/services/auth/token.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/token.pyi +27 -0
- amsdal/configs/__init__.py +0 -0
- amsdal/configs/__init__.pyi +0 -0
- amsdal/configs/constants.py +33 -0
- amsdal/configs/constants.pyi +22 -0
- amsdal/configs/main.py +258 -0
- amsdal/configs/main.pyi +173 -0
- amsdal/context/__init__.py +0 -0
- amsdal/context/__init__.pyi +0 -0
- amsdal/context/manager.py +69 -0
- amsdal/context/manager.pyi +50 -0
- amsdal/contrib/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/contrib/__init__.pyi +0 -0
- amsdal/contrib/app_config.py +7 -0
- amsdal/contrib/app_config.pyi +6 -0
- amsdal/contrib/auth/__init__.py +0 -0
- amsdal/contrib/auth/__init__.pyi +0 -0
- amsdal/contrib/auth/app.py +27 -0
- amsdal/contrib/auth/app.pyi +15 -0
- amsdal/contrib/auth/decorators/__init__.py +35 -0
- amsdal/contrib/auth/decorators/__init__.pyi +6 -0
- amsdal/contrib/auth/errors.py +7 -0
- amsdal/contrib/auth/errors.pyi +4 -0
- amsdal/contrib/auth/fixtures/basic_permissions.json +64 -0
- amsdal/contrib/auth/lifecycle/__init__.py +0 -0
- amsdal/contrib/auth/lifecycle/__init__.pyi +0 -0
- amsdal/contrib/auth/lifecycle/consumer.py +394 -0
- amsdal/contrib/auth/lifecycle/consumer.pyi +108 -0
- amsdal/contrib/auth/migrations/0000_initial.py +84 -0
- amsdal/contrib/auth/models/__init__.py +0 -0
- amsdal/contrib/auth/models/__init__.pyi +0 -0
- amsdal/contrib/auth/models/login_session.py +118 -0
- amsdal/contrib/auth/models/login_session.pyi +37 -0
- amsdal/contrib/auth/models/permission.py +23 -0
- amsdal/contrib/auth/models/permission.pyi +18 -0
- amsdal/contrib/auth/models/user.py +106 -0
- amsdal/contrib/auth/models/user.pyi +46 -0
- amsdal/contrib/auth/settings.py +36 -0
- amsdal/contrib/auth/settings.pyi +26 -0
- amsdal/contrib/frontend_configs/__init__.py +0 -0
- amsdal/contrib/frontend_configs/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/app.py +24 -0
- amsdal/contrib/frontend_configs/app.pyi +19 -0
- amsdal/contrib/frontend_configs/constants.py +1 -0
- amsdal/contrib/frontend_configs/constants.pyi +1 -0
- amsdal/contrib/frontend_configs/conversion/__init__.py +5 -0
- amsdal/contrib/frontend_configs/conversion/__init__.pyi +3 -0
- amsdal/contrib/frontend_configs/conversion/convert.py +286 -0
- amsdal/contrib/frontend_configs/conversion/convert.pyi +22 -0
- amsdal/contrib/frontend_configs/lifecycle/__init__.py +0 -0
- amsdal/contrib/frontend_configs/lifecycle/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/lifecycle/consumer.py +306 -0
- amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +98 -0
- amsdal/contrib/frontend_configs/migrations/0000_initial.py +256 -0
- amsdal/contrib/frontend_configs/models/__init__.py +0 -0
- amsdal/contrib/frontend_configs/models/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/models/frontend_activator_config.py +22 -0
- amsdal/contrib/frontend_configs/models/frontend_activator_config.pyi +12 -0
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator.py +11 -0
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator.pyi +7 -0
- amsdal/contrib/frontend_configs/models/frontend_config_control_action.py +54 -0
- amsdal/contrib/frontend_configs/models/frontend_config_control_action.pyi +32 -0
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator.py +21 -0
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator.pyi +11 -0
- amsdal/contrib/frontend_configs/models/frontend_config_option.py +12 -0
- amsdal/contrib/frontend_configs/models/frontend_config_option.pyi +8 -0
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.py +17 -0
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.pyi +8 -0
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option.py +13 -0
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option.pyi +9 -0
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask.py +14 -0
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask.pyi +10 -0
- amsdal/contrib/frontend_configs/models/frontend_config_validator.py +28 -0
- amsdal/contrib/frontend_configs/models/frontend_config_validator.pyi +15 -0
- amsdal/contrib/frontend_configs/models/frontend_control_config.py +87 -0
- amsdal/contrib/frontend_configs/models/frontend_control_config.pyi +35 -0
- amsdal/contrib/frontend_configs/models/frontend_model_config.py +14 -0
- amsdal/contrib/frontend_configs/models/frontend_model_config.pyi +9 -0
- amsdal/contrib/frontend_configs/utils.py +29 -0
- amsdal/contrib/frontend_configs/utils.pyi +17 -0
- amsdal/errors.py +31 -0
- amsdal/errors.pyi +12 -0
- amsdal/fixtures/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/__init__.pyi +0 -0
- amsdal/fixtures/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/manager.pyi +170 -0
- amsdal/fixtures/utils.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/utils.pyi +9 -0
- amsdal/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/manager.pyi +265 -0
- amsdal/mixins/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/mixins/__init__.pyi +0 -0
- amsdal/mixins/class_versions_mixin.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/mixins/class_versions_mixin.pyi +12 -0
- amsdal/models/__init__.py +19 -0
- amsdal/models/__init__.pyi +9 -0
- amsdal/models/core/__init__.py +0 -0
- amsdal/models/core/__init__.pyi +0 -0
- amsdal/models/core/class_object.py +37 -0
- amsdal/models/core/class_object.pyi +24 -0
- amsdal/models/core/class_object_meta.py +26 -0
- amsdal/models/core/class_object_meta.pyi +15 -0
- amsdal/models/core/class_property.py +20 -0
- amsdal/models/core/class_property.pyi +11 -0
- amsdal/models/core/class_property_meta.py +15 -0
- amsdal/models/core/class_property_meta.pyi +10 -0
- amsdal/models/core/file.py +156 -0
- amsdal/models/core/file.pyi +104 -0
- amsdal/models/core/fixture.py +25 -0
- amsdal/models/core/fixture.pyi +14 -0
- amsdal/models/core/option.py +11 -0
- amsdal/models/core/option.pyi +8 -0
- amsdal/models/core/validator.py +12 -0
- amsdal/models/core/validator.pyi +8 -0
- amsdal/models/types/__init__.py +0 -0
- amsdal/models/types/__init__.pyi +0 -0
- amsdal/models/types/object.py +26 -0
- amsdal/models/types/object.pyi +16 -0
- amsdal/py.typed +0 -0
- amsdal/queryset/__init__.py +21 -0
- amsdal/queryset/__init__.pyi +6 -0
- amsdal/schemas/__init__.py +0 -0
- amsdal/schemas/__init__.pyi +0 -0
- amsdal/schemas/core/class_object/model.json +31 -0
- amsdal/schemas/core/class_object/properties/display_name.py +9 -0
- amsdal/schemas/core/class_object_meta/model.json +59 -0
- amsdal/schemas/core/class_property/model.json +22 -0
- amsdal/schemas/core/class_property_meta/model.json +23 -0
- amsdal/schemas/core/file/hooks/pre_create.py +24 -0
- amsdal/schemas/core/file/hooks/pre_update.py +24 -0
- amsdal/schemas/core/file/model.json +23 -0
- amsdal/schemas/core/file/properties/from_file.py +34 -0
- amsdal/schemas/core/file/properties/mimetype.py +13 -0
- amsdal/schemas/core/file/properties/str.py +6 -0
- amsdal/schemas/core/file/properties/to_file.py +24 -0
- amsdal/schemas/core/file/properties/validate_data.py +32 -0
- amsdal/schemas/core/fixture/model.json +35 -0
- amsdal/schemas/core/option/model.json +19 -0
- amsdal/schemas/core/validator/model.json +19 -0
- amsdal/schemas/interfaces.py +25 -0
- amsdal/schemas/interfaces.pyi +20 -0
- amsdal/schemas/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/schemas/manager.py +0 -0
- amsdal/schemas/manager.pyi +0 -0
- amsdal/schemas/mixins/__init__.py +0 -0
- amsdal/schemas/mixins/__init__.pyi +0 -0
- amsdal/schemas/mixins/check_dependencies_mixin.py +125 -0
- amsdal/schemas/mixins/check_dependencies_mixin.pyi +45 -0
- amsdal/schemas/mixins/verify_schemas_mixin.py +96 -0
- amsdal/schemas/mixins/verify_schemas_mixin.pyi +33 -0
- amsdal/schemas/repository.py +84 -0
- amsdal/schemas/repository.pyi +22 -0
- amsdal/schemas/types/anything/model.json +7 -0
- amsdal/schemas/types/array/model.json +7 -0
- amsdal/schemas/types/binary/model.json +7 -0
- amsdal/schemas/types/boolean/model.json +17 -0
- amsdal/schemas/types/date/model.json +7 -0
- amsdal/schemas/types/datetime/model.json +7 -0
- amsdal/schemas/types/dictionary/model.json +8 -0
- amsdal/schemas/types/number/model.json +8 -0
- amsdal/schemas/types/object/model.json +53 -0
- amsdal/schemas/types/string/model.json +8 -0
- amsdal/schemas/utils.py +16 -0
- amsdal/schemas/utils.pyi +10 -0
- amsdal/services/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/services/__init__.pyi +0 -0
- amsdal/services/transaction_execution.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/services/transaction_execution.pyi +93 -0
- amsdal/transactions/__init__.py +13 -0
- amsdal/transactions/__init__.pyi +4 -0
- amsdal/utils/__init__.py +0 -0
- amsdal/utils/__init__.pyi +0 -0
- amsdal/utils/contrib_paths.py +23 -0
- amsdal/utils/contrib_paths.pyi +14 -0
- amsdal/utils/rollback/__init__.py +440 -0
- amsdal/utils/rollback/__init__.pyi +38 -0
- amsdal/utils/tests/__init__.py +0 -0
- amsdal/utils/tests/enums.py +18 -0
- amsdal/utils/tests/factories.py +49 -0
- amsdal/utils/tests/helpers.py +499 -0
- amsdal-0.4.13.dist-info/METADATA +369 -0
- amsdal-0.4.13.dist-info/RECORD +269 -0
- amsdal-0.4.13.dist-info/WHEEL +6 -0
- amsdal-0.4.13.dist-info/licenses/LICENSE.txt +107 -0
- amsdal-0.4.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
import contextlib
|
2
|
+
from typing import Any
|
3
|
+
from typing import Generic
|
4
|
+
from typing import TypeVar
|
5
|
+
from typing import Union
|
6
|
+
from typing import get_origin
|
7
|
+
|
8
|
+
from amsdal_models.classes.model import LegacyModel
|
9
|
+
from amsdal_models.classes.model import Model
|
10
|
+
from amsdal_utils.models.data_models.reference import Reference
|
11
|
+
from polyfactory.factories.base import BuildContext
|
12
|
+
from polyfactory.field_meta import FieldMeta
|
13
|
+
from polyfactory.field_meta import Null
|
14
|
+
|
15
|
+
try:
|
16
|
+
import polyfactory.factories.pydantic_factory as factories
|
17
|
+
except ImportError:
|
18
|
+
_msg = '"polyfactory" package is required for using this module. Use "pip install amsdal[factory]" to install it.'
|
19
|
+
raise ImportError(_msg) from None
|
20
|
+
|
21
|
+
|
22
|
+
T = TypeVar('T', bound=Model)
|
23
|
+
|
24
|
+
|
25
|
+
class AmsdalFactory(Generic[T], factories.ModelFactory[T]):
|
26
|
+
__is_base_factory__ = True
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
def get_field_value(
|
30
|
+
cls,
|
31
|
+
field_meta: FieldMeta,
|
32
|
+
field_build_parameters: Any | None = None,
|
33
|
+
build_context: BuildContext | None = None,
|
34
|
+
) -> Any:
|
35
|
+
if get_origin(field_meta.annotation) is Union and field_meta.default is Null:
|
36
|
+
with contextlib.suppress(TypeError):
|
37
|
+
is_class_definition = any(
|
38
|
+
issubclass(arg, LegacyModel) for arg in field_meta.annotation.__args__
|
39
|
+
) and any(issubclass(arg, Reference) for arg in field_meta.annotation.__args__)
|
40
|
+
is_optional = any(issubclass(arg, type(None)) for arg in field_meta.annotation.__args__)
|
41
|
+
|
42
|
+
if is_optional:
|
43
|
+
field_meta.annotation = type(None)
|
44
|
+
elif is_class_definition:
|
45
|
+
for _type in field_meta.annotation.__args__:
|
46
|
+
if not issubclass(_type, LegacyModel | type(None) | Reference):
|
47
|
+
field_meta.annotation = _type
|
48
|
+
|
49
|
+
return super().get_field_value(field_meta, field_build_parameters, build_context)
|
@@ -0,0 +1,499 @@
|
|
1
|
+
import contextlib
|
2
|
+
import os
|
3
|
+
import random
|
4
|
+
import string
|
5
|
+
import tempfile
|
6
|
+
from collections.abc import AsyncGenerator
|
7
|
+
from collections.abc import AsyncIterator
|
8
|
+
from collections.abc import Generator
|
9
|
+
from collections.abc import Iterator
|
10
|
+
from contextlib import asynccontextmanager
|
11
|
+
from contextlib import contextmanager
|
12
|
+
from contextlib import suppress
|
13
|
+
from pathlib import Path
|
14
|
+
from typing import Any
|
15
|
+
|
16
|
+
import yaml
|
17
|
+
from amsdal_data.application import AsyncDataApplication
|
18
|
+
from amsdal_data.connections.historical.schema_version_manager import AsyncHistoricalSchemaVersionManager
|
19
|
+
from amsdal_data.connections.historical.schema_version_manager import HistoricalSchemaVersionManager
|
20
|
+
from amsdal_models.migration import migrations
|
21
|
+
from amsdal_models.migration.executors.default_executor import DefaultAsyncMigrationExecutor
|
22
|
+
from amsdal_models.migration.executors.default_executor import DefaultMigrationExecutor
|
23
|
+
from amsdal_models.migration.file_migration_executor import FileMigrationExecutorManager
|
24
|
+
from amsdal_models.migration.file_migration_generator import FileMigrationGenerator
|
25
|
+
from amsdal_models.migration.file_migration_writer import FileMigrationWriter
|
26
|
+
from amsdal_models.migration.migrations import MigrateData
|
27
|
+
from amsdal_models.migration.migrations import MigrationSchemas
|
28
|
+
from amsdal_models.migration.migrations_loader import MigrationsLoader
|
29
|
+
from amsdal_models.migration.utils import contrib_to_module_root_path
|
30
|
+
from amsdal_models.schemas.class_schema_loader import ClassSchemaLoader
|
31
|
+
from amsdal_utils.config.manager import AmsdalConfigManager
|
32
|
+
from amsdal_utils.models.enums import ModuleType
|
33
|
+
|
34
|
+
from amsdal.configs.constants import CORE_MIGRATIONS_PATH
|
35
|
+
from amsdal.configs.main import settings
|
36
|
+
from amsdal.manager import AmsdalManager
|
37
|
+
from amsdal.manager import AsyncAmsdalManager
|
38
|
+
from amsdal.utils.tests.enums import DbExecutionType
|
39
|
+
from amsdal.utils.tests.enums import LakehouseOption
|
40
|
+
from amsdal.utils.tests.enums import StateOption
|
41
|
+
|
42
|
+
TESTS_DIR = Path(os.getcwd())
|
43
|
+
|
44
|
+
|
45
|
+
def create_postgres_database(database: str) -> tuple[str, str, str, str]:
|
46
|
+
import psycopg
|
47
|
+
|
48
|
+
db_host = os.getenv('POSTGRES_HOST', 'localhost')
|
49
|
+
db_port = os.getenv('POSTGRES_PORT', '5432')
|
50
|
+
db_user = os.getenv('POSTGRES_USER', 'postgres')
|
51
|
+
db_password = os.getenv('POSTGRES_PASSWORD', 'example')
|
52
|
+
|
53
|
+
conn = psycopg.connect(
|
54
|
+
host=db_host,
|
55
|
+
port=db_port,
|
56
|
+
user=db_user,
|
57
|
+
password=db_password,
|
58
|
+
autocommit=True,
|
59
|
+
)
|
60
|
+
cur = conn.cursor()
|
61
|
+
|
62
|
+
with suppress(psycopg.errors.DuplicateDatabase):
|
63
|
+
cur.execute(f'CREATE DATABASE "{database}"')
|
64
|
+
|
65
|
+
cur.close()
|
66
|
+
conn.close()
|
67
|
+
|
68
|
+
return (
|
69
|
+
db_host,
|
70
|
+
db_port,
|
71
|
+
db_user,
|
72
|
+
db_password,
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
@contextmanager
|
77
|
+
def override_settings(**kwargs: Any) -> Iterator[None]:
|
78
|
+
"""
|
79
|
+
A context manager that temporarily overrides settings.
|
80
|
+
|
81
|
+
This is a copy of django.test.utils.override_settings, but with the
|
82
|
+
ability to override settings with None.
|
83
|
+
"""
|
84
|
+
from amsdal.configs.main import settings
|
85
|
+
|
86
|
+
original_settings = settings.model_dump()
|
87
|
+
|
88
|
+
settings.override(**kwargs)
|
89
|
+
|
90
|
+
try:
|
91
|
+
yield
|
92
|
+
finally:
|
93
|
+
settings.override(**original_settings)
|
94
|
+
|
95
|
+
|
96
|
+
def _get_config_template(
|
97
|
+
db_execution_type: DbExecutionType,
|
98
|
+
lakehouse_option: LakehouseOption,
|
99
|
+
state_option: StateOption,
|
100
|
+
*,
|
101
|
+
is_async: bool = False,
|
102
|
+
) -> str:
|
103
|
+
config_object: dict[str, Any] = {
|
104
|
+
'application_name': 'test_client_app',
|
105
|
+
'async_mode': is_async,
|
106
|
+
'connections': [
|
107
|
+
{
|
108
|
+
'name': 'lock',
|
109
|
+
'backend': 'amsdal_data.lock.implementations.thread_lock.ThreadLock',
|
110
|
+
},
|
111
|
+
],
|
112
|
+
'resources_config': {
|
113
|
+
'lakehouse': 'lakehouse',
|
114
|
+
'lock': 'lock',
|
115
|
+
'repository': {'default': 'state'},
|
116
|
+
},
|
117
|
+
}
|
118
|
+
if lakehouse_option in [
|
119
|
+
LakehouseOption.postgres,
|
120
|
+
LakehouseOption.postgres_immutable,
|
121
|
+
]:
|
122
|
+
config_object['connections'].append(
|
123
|
+
{
|
124
|
+
'name': 'lakehouse',
|
125
|
+
'backend': 'postgres-historical-async' if is_async else 'postgres-historical',
|
126
|
+
'credentials': [
|
127
|
+
{
|
128
|
+
'db_host': '{{ db_host }}',
|
129
|
+
'db_port': '{{ db_port }}',
|
130
|
+
'db_user': '{{ db_user }}',
|
131
|
+
'db_password': '{{ db_password }}',
|
132
|
+
'db_name': '{{ lakehouse_postgres_db }}',
|
133
|
+
}
|
134
|
+
],
|
135
|
+
}
|
136
|
+
)
|
137
|
+
elif lakehouse_option in [LakehouseOption.sqlite, LakehouseOption.sqlite_immutable]:
|
138
|
+
config_object['connections'].append(
|
139
|
+
{
|
140
|
+
'name': 'lakehouse',
|
141
|
+
'backend': 'sqlite-historical-async' if is_async else 'sqlite-historical',
|
142
|
+
'credentials': [{'db_path': '{{ db_dir }}/sqlite_lakehouse.sqlite3'}],
|
143
|
+
}
|
144
|
+
)
|
145
|
+
|
146
|
+
if db_execution_type == DbExecutionType.lakehouse_only:
|
147
|
+
config_object['resources_config']['repository']['default'] = 'lakehouse'
|
148
|
+
|
149
|
+
return yaml.dump(config_object)
|
150
|
+
|
151
|
+
if state_option == StateOption.postgres:
|
152
|
+
config_object['connections'].append(
|
153
|
+
{
|
154
|
+
'name': 'state',
|
155
|
+
'backend': 'postgres-async' if is_async else 'postgres',
|
156
|
+
'credentials': [
|
157
|
+
{
|
158
|
+
'db_host': '{{ db_host }}',
|
159
|
+
'db_port': '{{ db_port }}',
|
160
|
+
'db_user': '{{ db_user }}',
|
161
|
+
'db_password': '{{ db_password }}',
|
162
|
+
'db_name': '{{ state_postgres_db }}',
|
163
|
+
}
|
164
|
+
],
|
165
|
+
}
|
166
|
+
)
|
167
|
+
|
168
|
+
elif state_option == StateOption.sqlite:
|
169
|
+
config_object['connections'].append(
|
170
|
+
{
|
171
|
+
'name': 'state',
|
172
|
+
'backend': 'sqlite-async' if is_async else 'sqlite',
|
173
|
+
'credentials': [{'db_path': '{{ db_dir }}/sqlite_state.sqlite3'}],
|
174
|
+
}
|
175
|
+
)
|
176
|
+
|
177
|
+
return yaml.dump(config_object)
|
178
|
+
|
179
|
+
|
180
|
+
@contextmanager
|
181
|
+
def _init_manager(
|
182
|
+
db_execution_type: DbExecutionType,
|
183
|
+
lakehouse_option: LakehouseOption,
|
184
|
+
state_option: StateOption,
|
185
|
+
*,
|
186
|
+
is_async: bool = False,
|
187
|
+
) -> Generator[tuple[Path, Path], Any, None]:
|
188
|
+
Path('.tmp').mkdir(exist_ok=True)
|
189
|
+
with tempfile.TemporaryDirectory(dir='.tmp') as temp_dir:
|
190
|
+
db_dir = Path(temp_dir) / 'db_dir'
|
191
|
+
(db_dir / 'warehouse').mkdir(exist_ok=True, parents=True)
|
192
|
+
|
193
|
+
lakehouse_database = ''.join(random.sample(string.ascii_letters, 16))
|
194
|
+
state_database = ''.join(random.sample(string.ascii_letters, 16))
|
195
|
+
config_text = _get_config_template(db_execution_type, lakehouse_option, state_option, is_async=is_async)
|
196
|
+
|
197
|
+
if lakehouse_option in [
|
198
|
+
LakehouseOption.postgres,
|
199
|
+
LakehouseOption.postgres_immutable,
|
200
|
+
]:
|
201
|
+
(
|
202
|
+
db_host,
|
203
|
+
db_port,
|
204
|
+
db_user,
|
205
|
+
db_password,
|
206
|
+
) = create_postgres_database(lakehouse_database)
|
207
|
+
|
208
|
+
config_text = (
|
209
|
+
config_text.replace('{{ db_host }}', db_host)
|
210
|
+
.replace('{{ db_port }}', db_port)
|
211
|
+
.replace('{{ db_user }}', db_user)
|
212
|
+
.replace('{{ db_password }}', db_password)
|
213
|
+
.replace('{{ lakehouse_postgres_db }}', lakehouse_database)
|
214
|
+
)
|
215
|
+
elif lakehouse_option in [
|
216
|
+
LakehouseOption.sqlite,
|
217
|
+
LakehouseOption.sqlite_immutable,
|
218
|
+
]:
|
219
|
+
config_text = config_text.replace('{{ db_dir }}', db_dir.absolute().as_posix())
|
220
|
+
|
221
|
+
if state_option == StateOption.postgres:
|
222
|
+
create_postgres_database(state_database)
|
223
|
+
config_text = (
|
224
|
+
config_text.replace('{{ db_host }}', db_host)
|
225
|
+
.replace('{{ db_port }}', db_port)
|
226
|
+
.replace('{{ db_user }}', db_user)
|
227
|
+
.replace('{{ db_password }}', db_password)
|
228
|
+
.replace('{{ state_postgres_db }}', state_database)
|
229
|
+
)
|
230
|
+
elif state_option == StateOption.sqlite:
|
231
|
+
config_text = config_text.replace('{{ db_dir }}', db_dir.absolute().as_posix())
|
232
|
+
|
233
|
+
config_path = Path(temp_dir) / 'config.yml'
|
234
|
+
config_path.write_text(config_text)
|
235
|
+
|
236
|
+
yield db_dir, config_path
|
237
|
+
|
238
|
+
|
239
|
+
@contextmanager
|
240
|
+
def init_manager(
|
241
|
+
src_dir_path: Path,
|
242
|
+
db_execution_type: DbExecutionType,
|
243
|
+
lakehouse_option: LakehouseOption,
|
244
|
+
state_option: StateOption,
|
245
|
+
) -> Generator[AmsdalManager, Any, None]:
|
246
|
+
with _init_manager(db_execution_type, lakehouse_option, state_option) as (
|
247
|
+
db_dir,
|
248
|
+
config_path,
|
249
|
+
):
|
250
|
+
with override_settings(
|
251
|
+
APP_PATH=db_dir,
|
252
|
+
CONFIG_PATH=config_path,
|
253
|
+
USER_MODELS_MODULE_PATH=src_dir_path / 'models',
|
254
|
+
TRANSACTIONS_MODULE_PATH=src_dir_path / 'transactions',
|
255
|
+
FIXTURES_MODULE_PATH=src_dir_path / 'fixtures',
|
256
|
+
):
|
257
|
+
config_manager = AmsdalConfigManager()
|
258
|
+
config_manager.load_config(config_path)
|
259
|
+
manager = AmsdalManager()
|
260
|
+
manager.setup()
|
261
|
+
manager.post_setup() # type: ignore[call-arg]
|
262
|
+
|
263
|
+
try:
|
264
|
+
yield manager
|
265
|
+
finally:
|
266
|
+
manager.teardown()
|
267
|
+
AmsdalManager.invalidate()
|
268
|
+
AmsdalConfigManager.invalidate()
|
269
|
+
|
270
|
+
|
271
|
+
@asynccontextmanager
|
272
|
+
async def async_init_manager(
|
273
|
+
src_dir_path: Path,
|
274
|
+
db_execution_type: DbExecutionType,
|
275
|
+
lakehouse_option: LakehouseOption,
|
276
|
+
state_option: StateOption,
|
277
|
+
) -> AsyncIterator[AsyncAmsdalManager]:
|
278
|
+
with _init_manager(db_execution_type, lakehouse_option, state_option, is_async=True) as (db_dir, config_path):
|
279
|
+
with override_settings(
|
280
|
+
APP_PATH=db_dir,
|
281
|
+
CONFIG_PATH=config_path,
|
282
|
+
USER_MODELS_MODULE_PATH=src_dir_path / 'models',
|
283
|
+
TRANSACTIONS_MODULE_PATH=src_dir_path / 'transactions',
|
284
|
+
FIXTURES_MODULE_PATH=src_dir_path / 'fixtures',
|
285
|
+
):
|
286
|
+
config_manager = AmsdalConfigManager()
|
287
|
+
config_manager.load_config(config_path)
|
288
|
+
manager = AsyncAmsdalManager()
|
289
|
+
await manager.setup()
|
290
|
+
await manager.post_setup() # type: ignore[call-arg,misc]
|
291
|
+
|
292
|
+
try:
|
293
|
+
yield manager
|
294
|
+
finally:
|
295
|
+
await manager.teardown()
|
296
|
+
await AsyncDataApplication().teardown()
|
297
|
+
AsyncAmsdalManager.invalidate()
|
298
|
+
AmsdalConfigManager.invalidate()
|
299
|
+
AsyncDataApplication.invalidate()
|
300
|
+
|
301
|
+
|
302
|
+
def migrate() -> None:
|
303
|
+
schemas = MigrationSchemas()
|
304
|
+
executor = DefaultMigrationExecutor(schemas, use_foreign_keys=True)
|
305
|
+
|
306
|
+
with contextlib.suppress(Exception):
|
307
|
+
HistoricalSchemaVersionManager().object_classes # noqa: B018
|
308
|
+
|
309
|
+
_migrate_per_loader(
|
310
|
+
executor,
|
311
|
+
MigrationsLoader(
|
312
|
+
migrations_dir=CORE_MIGRATIONS_PATH,
|
313
|
+
module_type=ModuleType.CORE,
|
314
|
+
),
|
315
|
+
)
|
316
|
+
|
317
|
+
for contrib in settings.CONTRIBS:
|
318
|
+
contrib_root_path = contrib_to_module_root_path(contrib)
|
319
|
+
_migrate_per_loader(
|
320
|
+
executor,
|
321
|
+
MigrationsLoader(
|
322
|
+
migrations_dir=contrib_root_path / settings.MIGRATIONS_DIRECTORY_NAME,
|
323
|
+
module_type=ModuleType.CONTRIB,
|
324
|
+
module_name=contrib,
|
325
|
+
),
|
326
|
+
)
|
327
|
+
|
328
|
+
user_schema_loader = ClassSchemaLoader(
|
329
|
+
settings.USER_MODELS_MODULE,
|
330
|
+
class_filter=lambda cls: cls.__module_type__ == ModuleType.USER,
|
331
|
+
)
|
332
|
+
_schemas, _cycle_schemas = user_schema_loader.load_sorted()
|
333
|
+
_schemas_map = {_schema.title: _schema for _schema in _schemas}
|
334
|
+
|
335
|
+
for object_schema in _schemas:
|
336
|
+
for _operation_data in FileMigrationGenerator.build_operations(
|
337
|
+
ModuleType.USER,
|
338
|
+
object_schema,
|
339
|
+
None,
|
340
|
+
):
|
341
|
+
_operation_name = FileMigrationWriter.operation_name_map[_operation_data.type]
|
342
|
+
_operation = getattr(migrations, _operation_name)(
|
343
|
+
module_type=ModuleType.USER,
|
344
|
+
class_name=_operation_data.class_name,
|
345
|
+
new_schema=_operation_data.new_schema.model_dump(),
|
346
|
+
)
|
347
|
+
|
348
|
+
_operation.forward(executor)
|
349
|
+
|
350
|
+
for object_schema in _cycle_schemas:
|
351
|
+
for _operation_data in FileMigrationGenerator.build_operations(
|
352
|
+
ModuleType.USER,
|
353
|
+
object_schema,
|
354
|
+
_schemas_map[object_schema.title],
|
355
|
+
):
|
356
|
+
_operation_name = FileMigrationWriter.operation_name_map[_operation_data.type]
|
357
|
+
_operation = getattr(migrations, _operation_name)(
|
358
|
+
module_type=ModuleType.USER,
|
359
|
+
class_name=_operation_data.class_name,
|
360
|
+
new_schema=_operation_data.new_schema.model_dump(),
|
361
|
+
)
|
362
|
+
|
363
|
+
_operation.forward(executor)
|
364
|
+
|
365
|
+
executor.flush_buffer()
|
366
|
+
|
367
|
+
|
368
|
+
def _migrate_per_loader(executor: DefaultMigrationExecutor, loader: MigrationsLoader) -> None:
|
369
|
+
for _migration in loader:
|
370
|
+
migration_class = FileMigrationExecutorManager.get_migration_class(_migration)
|
371
|
+
migration_class_instance = migration_class()
|
372
|
+
|
373
|
+
for _operation in migration_class_instance.operations:
|
374
|
+
if isinstance(_operation, MigrateData):
|
375
|
+
executor.flush_buffer()
|
376
|
+
|
377
|
+
_operation.forward(executor)
|
378
|
+
|
379
|
+
executor.flush_buffer()
|
380
|
+
|
381
|
+
|
382
|
+
async def async_migrate() -> None:
|
383
|
+
schemas = MigrationSchemas()
|
384
|
+
executor = DefaultAsyncMigrationExecutor(schemas)
|
385
|
+
|
386
|
+
with contextlib.suppress(Exception):
|
387
|
+
await AsyncHistoricalSchemaVersionManager().object_classes
|
388
|
+
|
389
|
+
await _async_migrate_per_loader(
|
390
|
+
executor,
|
391
|
+
MigrationsLoader(
|
392
|
+
migrations_dir=CORE_MIGRATIONS_PATH,
|
393
|
+
module_type=ModuleType.CORE,
|
394
|
+
),
|
395
|
+
)
|
396
|
+
|
397
|
+
for contrib in settings.CONTRIBS:
|
398
|
+
contrib_root_path = contrib_to_module_root_path(contrib)
|
399
|
+
await _async_migrate_per_loader(
|
400
|
+
executor,
|
401
|
+
MigrationsLoader(
|
402
|
+
migrations_dir=contrib_root_path / settings.MIGRATIONS_DIRECTORY_NAME,
|
403
|
+
module_type=ModuleType.CONTRIB,
|
404
|
+
module_name=contrib,
|
405
|
+
),
|
406
|
+
)
|
407
|
+
|
408
|
+
user_schema_loader = ClassSchemaLoader(
|
409
|
+
settings.USER_MODELS_MODULE,
|
410
|
+
class_filter=lambda cls: cls.__module_type__ == ModuleType.USER,
|
411
|
+
)
|
412
|
+
_schemas, _cycle_schemas = user_schema_loader.load_sorted()
|
413
|
+
_schemas_map = {_schema.title: _schema for _schema in _schemas}
|
414
|
+
|
415
|
+
for object_schema in _schemas:
|
416
|
+
for _operation_data in FileMigrationGenerator.build_operations(
|
417
|
+
ModuleType.USER,
|
418
|
+
object_schema,
|
419
|
+
None,
|
420
|
+
):
|
421
|
+
_operation_name = FileMigrationWriter.operation_name_map[_operation_data.type]
|
422
|
+
_operation = getattr(migrations, _operation_name)(
|
423
|
+
module_type=ModuleType.USER,
|
424
|
+
class_name=_operation_data.class_name,
|
425
|
+
new_schema=_operation_data.new_schema.model_dump(),
|
426
|
+
)
|
427
|
+
|
428
|
+
_operation.forward(executor)
|
429
|
+
|
430
|
+
for object_schema in _cycle_schemas:
|
431
|
+
for _operation_data in FileMigrationGenerator.build_operations(
|
432
|
+
ModuleType.USER,
|
433
|
+
object_schema,
|
434
|
+
_schemas_map[object_schema.title],
|
435
|
+
):
|
436
|
+
_operation_name = FileMigrationWriter.operation_name_map[_operation_data.type]
|
437
|
+
_operation = getattr(migrations, _operation_name)(
|
438
|
+
module_type=ModuleType.USER,
|
439
|
+
class_name=_operation_data.class_name,
|
440
|
+
new_schema=_operation_data.new_schema.model_dump(),
|
441
|
+
)
|
442
|
+
|
443
|
+
_operation.forward(executor)
|
444
|
+
|
445
|
+
await executor.flush_buffer()
|
446
|
+
|
447
|
+
|
448
|
+
async def _async_migrate_per_loader(executor: DefaultAsyncMigrationExecutor, loader: MigrationsLoader) -> None:
|
449
|
+
for _migration in loader:
|
450
|
+
migration_class = FileMigrationExecutorManager.get_migration_class(_migration)
|
451
|
+
migration_class_instance = migration_class()
|
452
|
+
|
453
|
+
for _operation in migration_class_instance.operations:
|
454
|
+
if isinstance(_operation, MigrateData):
|
455
|
+
await executor.flush_buffer()
|
456
|
+
|
457
|
+
_operation.forward(executor)
|
458
|
+
|
459
|
+
await executor.flush_buffer()
|
460
|
+
|
461
|
+
|
462
|
+
@contextmanager
|
463
|
+
def init_manager_and_migrate(
|
464
|
+
src_dir_path: Path,
|
465
|
+
db_execution_type: DbExecutionType,
|
466
|
+
lakehouse_option: LakehouseOption,
|
467
|
+
state_option: StateOption,
|
468
|
+
) -> Generator[AmsdalManager, Any, None]:
|
469
|
+
with init_manager(
|
470
|
+
src_dir_path=src_dir_path,
|
471
|
+
db_execution_type=db_execution_type,
|
472
|
+
lakehouse_option=lakehouse_option,
|
473
|
+
state_option=state_option,
|
474
|
+
) as manager:
|
475
|
+
migrate()
|
476
|
+
manager.authenticate()
|
477
|
+
manager.init_classes()
|
478
|
+
|
479
|
+
yield manager
|
480
|
+
|
481
|
+
|
482
|
+
@asynccontextmanager
|
483
|
+
async def async_init_manager_and_migrate(
|
484
|
+
src_dir_path: Path,
|
485
|
+
db_execution_type: DbExecutionType,
|
486
|
+
lakehouse_option: LakehouseOption,
|
487
|
+
state_option: StateOption,
|
488
|
+
) -> AsyncGenerator[AsyncAmsdalManager, Any]:
|
489
|
+
async with async_init_manager(
|
490
|
+
src_dir_path=src_dir_path,
|
491
|
+
db_execution_type=db_execution_type,
|
492
|
+
lakehouse_option=lakehouse_option,
|
493
|
+
state_option=state_option,
|
494
|
+
) as manager:
|
495
|
+
await async_migrate()
|
496
|
+
manager.authenticate()
|
497
|
+
manager.init_classes()
|
498
|
+
|
499
|
+
yield manager
|