fal 0.9.2__py3-none-any.whl → 0.9.4__py3-none-any.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.
Potentially problematic release.
This version of fal might be problematic. Click here for more details.
- _fal_testing/utils.py +2 -2
- dbt/adapters/fal/__init__.py +21 -0
- dbt/adapters/fal/__version__.py +1 -0
- dbt/adapters/fal/connections.py +18 -0
- dbt/adapters/fal/impl.py +93 -0
- dbt/adapters/fal/load_db_profile.py +80 -0
- dbt/adapters/fal/wrappers.py +113 -0
- dbt/adapters/fal_experimental/__init__.py +11 -0
- dbt/adapters/fal_experimental/__version__.py +1 -0
- dbt/adapters/fal_experimental/adapter.py +149 -0
- dbt/adapters/fal_experimental/adapter_support.py +234 -0
- dbt/adapters/fal_experimental/connections.py +72 -0
- dbt/adapters/fal_experimental/impl.py +240 -0
- dbt/adapters/fal_experimental/support/athena.py +92 -0
- dbt/adapters/fal_experimental/support/bigquery.py +74 -0
- dbt/adapters/fal_experimental/support/duckdb.py +28 -0
- dbt/adapters/fal_experimental/support/postgres.py +88 -0
- dbt/adapters/fal_experimental/support/redshift.py +56 -0
- dbt/adapters/fal_experimental/support/snowflake.py +76 -0
- dbt/adapters/fal_experimental/support/trino.py +26 -0
- dbt/adapters/fal_experimental/telemetry/__init__.py +1 -0
- dbt/adapters/fal_experimental/telemetry/telemetry.py +411 -0
- dbt/adapters/fal_experimental/teleport.py +192 -0
- dbt/adapters/fal_experimental/teleport_adapter_support.py +23 -0
- dbt/adapters/fal_experimental/teleport_support/duckdb.py +122 -0
- dbt/adapters/fal_experimental/teleport_support/snowflake.py +72 -0
- dbt/adapters/fal_experimental/utils/__init__.py +50 -0
- dbt/adapters/fal_experimental/utils/environments.py +302 -0
- dbt/fal/adapters/python/__init__.py +3 -0
- dbt/fal/adapters/python/connections.py +319 -0
- dbt/fal/adapters/python/impl.py +291 -0
- dbt/fal/adapters/teleport/__init__.py +3 -0
- dbt/fal/adapters/teleport/impl.py +103 -0
- dbt/fal/adapters/teleport/info.py +73 -0
- dbt/include/fal/__init__.py +3 -0
- dbt/include/fal/dbt_project.yml +5 -0
- dbt/include/fal/macros/materializations/table.sql +46 -0
- dbt/include/fal/macros/teleport_duckdb.sql +8 -0
- dbt/include/fal/macros/teleport_snowflake.sql +31 -0
- dbt/include/fal_experimental/__init__.py +3 -0
- dbt/include/fal_experimental/dbt_project.yml +5 -0
- dbt/include/fal_experimental/macros/materializations/table.sql +36 -0
- fal/__init__.py +61 -11
- fal/dbt/__init__.py +11 -0
- fal/dbt/cli/__init__.py +1 -0
- fal/{cli → dbt/cli}/args.py +7 -2
- fal/{cli → dbt/cli}/cli.py +18 -3
- fal/{cli → dbt/cli}/dbt_runner.py +1 -1
- fal/{cli → dbt/cli}/fal_runner.py +6 -6
- fal/{cli → dbt/cli}/flow_runner.py +9 -9
- fal/{cli → dbt/cli}/model_generator/model_generator.py +5 -5
- fal/{cli → dbt/cli}/selectors.py +2 -2
- fal/{fal_script.py → dbt/fal_script.py} +4 -4
- {faldbt → fal/dbt/integration}/lib.py +2 -2
- {faldbt → fal/dbt/integration}/magics.py +2 -2
- {faldbt → fal/dbt/integration}/parse.py +7 -7
- {faldbt → fal/dbt/integration}/project.py +7 -7
- fal/dbt/integration/utils/yaml_helper.py +80 -0
- fal/dbt/new/project.py +43 -0
- fal/{node_graph.py → dbt/node_graph.py} +2 -2
- fal/{packages → dbt/packages}/dependency_analysis.py +32 -38
- fal/{packages → dbt/packages}/environments/__init__.py +3 -3
- fal/{packages → dbt/packages}/environments/base.py +2 -2
- fal/{packages → dbt/packages}/environments/conda.py +3 -3
- fal/{packages → dbt/packages}/environments/virtual_env.py +3 -3
- fal/{packages → dbt/packages}/isolated_runner.py +5 -5
- fal/{planner → dbt/planner}/executor.py +4 -4
- fal/{planner → dbt/planner}/plan.py +3 -3
- fal/{planner → dbt/planner}/schedule.py +5 -5
- fal/{planner → dbt/planner}/tasks.py +5 -5
- fal/{telemetry → dbt/telemetry}/telemetry.py +4 -4
- fal/{typing.py → dbt/typing.py} +2 -2
- fal/{utils.py → dbt/utils.py} +2 -2
- {fal-0.9.2.dist-info → fal-0.9.4.dist-info}/METADATA +98 -117
- fal-0.9.4.dist-info/RECORD +91 -0
- fal-0.9.4.dist-info/entry_points.txt +4 -0
- fal/cli/__init__.py +0 -1
- fal-0.9.2.dist-info/RECORD +0 -47
- fal-0.9.2.dist-info/entry_points.txt +0 -3
- {faldbt → dbt/adapters/fal_experimental}/utils/yaml_helper.py +0 -0
- /fal/{cli → dbt/cli}/model_generator/__init__.py +0 -0
- /fal/{cli → dbt/cli}/model_generator/module_check.py +0 -0
- /fal/{feature_store → dbt/feature_store}/__init__.py +0 -0
- /fal/{feature_store → dbt/feature_store}/feature.py +0 -0
- /fal/{packages → dbt/integration}/__init__.py +0 -0
- {faldbt → fal/dbt/integration}/logger.py +0 -0
- /fal/{planner → dbt/integration/utils}/__init__.py +0 -0
- {faldbt → fal/dbt/integration}/version.py +0 -0
- /fal/{telemetry → dbt/packages}/__init__.py +0 -0
- /fal/{packages → dbt/packages}/bridge.py +0 -0
- {faldbt → fal/dbt/planner}/__init__.py +0 -0
- {faldbt/utils → fal/dbt/telemetry}/__init__.py +0 -0
- {fal-0.9.2.dist-info → fal-0.9.4.dist-info}/WHEEL +0 -0
_fal_testing/utils.py
CHANGED
|
@@ -43,8 +43,8 @@ def get_environment_type():
|
|
|
43
43
|
# To determine whether this is a fal-created environment or not
|
|
44
44
|
# we'll check whether the executable that is running this script
|
|
45
45
|
# is located under any of the designated fal environment directories.
|
|
46
|
-
from fal.packages.environments.virtual_env import _BASE_VENV_DIR
|
|
47
|
-
from fal.packages.environments.conda import _BASE_CONDA_DIR
|
|
46
|
+
from fal.dbt.packages.environments.virtual_env import _BASE_VENV_DIR
|
|
47
|
+
from fal.dbt.packages.environments.conda import _BASE_CONDA_DIR
|
|
48
48
|
|
|
49
49
|
executable_path = Path(sys.executable)
|
|
50
50
|
for environment_type, prefix in [
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dbt.adapters.base import AdapterPlugin
|
|
2
|
+
|
|
3
|
+
from dbt.adapters.fal.connections import FalEncCredentials
|
|
4
|
+
from dbt.include import fal
|
|
5
|
+
|
|
6
|
+
# Avoid loading the plugin code for any import
|
|
7
|
+
def __getattr__(name):
|
|
8
|
+
if name == "FalEncAdapter":
|
|
9
|
+
from dbt.adapters.fal.impl import FalEncAdapter
|
|
10
|
+
|
|
11
|
+
return FalEncAdapter
|
|
12
|
+
if name == "Plugin":
|
|
13
|
+
from dbt.adapters.fal.impl import FalEncAdapter
|
|
14
|
+
|
|
15
|
+
return AdapterPlugin(
|
|
16
|
+
adapter=FalEncAdapter,
|
|
17
|
+
credentials=FalEncCredentials,
|
|
18
|
+
include_path=fal.PACKAGE_PATH
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = '0.9.4'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dbt.adapters.fal_experimental.connections import FalCredentials
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class FalEncCredentials(FalCredentials):
|
|
7
|
+
db_profile: str = ""
|
|
8
|
+
|
|
9
|
+
def _connection_keys(self):
|
|
10
|
+
return () + super()._connection_keys()
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def type(self):
|
|
14
|
+
return "fal"
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def unique_field(self):
|
|
18
|
+
return self.db_profile
|
dbt/adapters/fal/impl.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from contextlib import contextmanager
|
|
5
|
+
from dbt.adapters.base.impl import BaseAdapter
|
|
6
|
+
from dbt.adapters.base.relation import BaseRelation
|
|
7
|
+
from dbt.adapters.protocol import AdapterConfig
|
|
8
|
+
from dbt.adapters.factory import FACTORY
|
|
9
|
+
|
|
10
|
+
# TODO: offer in `from isolate import is_agent`
|
|
11
|
+
from isolate.connections.common import is_agent
|
|
12
|
+
|
|
13
|
+
from .connections import FalEncCredentials
|
|
14
|
+
from .wrappers import FalEncAdapterWrapper, FalCredentialsWrapper
|
|
15
|
+
|
|
16
|
+
from .load_db_profile import load_profiles_info_1_5
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FalConfigs(AdapterConfig):
|
|
20
|
+
fal_environment: Optional[str]
|
|
21
|
+
fal_machine: Optional[str]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@contextmanager
|
|
25
|
+
def _release_plugin_lock():
|
|
26
|
+
FACTORY.lock.release()
|
|
27
|
+
try:
|
|
28
|
+
yield
|
|
29
|
+
finally:
|
|
30
|
+
FACTORY.lock.acquire()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
DB_PROFILE = None
|
|
34
|
+
DB_RELATION = BaseRelation
|
|
35
|
+
OVERRIDE_PROPERTIES = {}
|
|
36
|
+
|
|
37
|
+
# NOTE: Should this file run on isolate agents? Could we skip it entirely and build a FalEncAdapterWrapper directly?
|
|
38
|
+
if not is_agent():
|
|
39
|
+
DB_PROFILE, OVERRIDE_PROPERTIES = load_profiles_info_1_5()
|
|
40
|
+
DB_RELATION = FACTORY.get_relation_class_by_name(DB_PROFILE.credentials.type)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class FalEncAdapter(BaseAdapter):
|
|
44
|
+
Relation = DB_RELATION # type: ignore
|
|
45
|
+
|
|
46
|
+
# TODO: how do we actually use this?
|
|
47
|
+
AdapterSpecificConfigs = FalConfigs
|
|
48
|
+
|
|
49
|
+
def __new__(cls, config):
|
|
50
|
+
# There are two different credentials types which can be passed to FalEncAdapter
|
|
51
|
+
# 1. FalEncCredentials
|
|
52
|
+
# 2. FalCredentialsWrapper
|
|
53
|
+
#
|
|
54
|
+
# For `FalEncCredentials`, we have to go through parsing the profiles.yml (so that we
|
|
55
|
+
# can obtain the real 'db' credentials). But for the other one, we can just use
|
|
56
|
+
# the bound credentials directly (e.g. in isolated mode where we don't actually
|
|
57
|
+
# have access to the profiles.yml file).
|
|
58
|
+
|
|
59
|
+
fal_credentials = config.credentials
|
|
60
|
+
if isinstance(fal_credentials, FalEncCredentials):
|
|
61
|
+
db_credentials = DB_PROFILE.credentials
|
|
62
|
+
else:
|
|
63
|
+
# Since profile construction (in the case above) already registers the
|
|
64
|
+
# adapter plugin for the db type, we need to also mimic that here.
|
|
65
|
+
assert isinstance(fal_credentials, FalCredentialsWrapper)
|
|
66
|
+
db_credentials = fal_credentials._db_creds
|
|
67
|
+
with _release_plugin_lock():
|
|
68
|
+
FACTORY.load_plugin(db_credentials.type)
|
|
69
|
+
|
|
70
|
+
# TODO: maybe we can do this better?
|
|
71
|
+
with _release_plugin_lock():
|
|
72
|
+
db_adapter_class = FACTORY.get_adapter_class_by_name(db_credentials.type)
|
|
73
|
+
original_plugin = FACTORY.get_plugin_by_name(fal_credentials.type)
|
|
74
|
+
original_plugin.dependencies = [db_credentials.type]
|
|
75
|
+
|
|
76
|
+
config.python_adapter_credentials = fal_credentials
|
|
77
|
+
config.sql_adapter_credentials = db_credentials
|
|
78
|
+
|
|
79
|
+
for key in OVERRIDE_PROPERTIES:
|
|
80
|
+
if OVERRIDE_PROPERTIES[key] is not None:
|
|
81
|
+
setattr(config, key, OVERRIDE_PROPERTIES[key])
|
|
82
|
+
|
|
83
|
+
with _release_plugin_lock():
|
|
84
|
+
# Temporary credentials for register
|
|
85
|
+
config.credentials = config.sql_adapter_credentials
|
|
86
|
+
FACTORY.register_adapter(config)
|
|
87
|
+
config.credentials = FalCredentialsWrapper(config.sql_adapter_credentials)
|
|
88
|
+
|
|
89
|
+
return FalEncAdapterWrapper(db_adapter_class, config)
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def type(cls):
|
|
93
|
+
return "fal"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import Dict, Any, Tuple, Optional
|
|
2
|
+
|
|
3
|
+
from dbt.flags import get_flags, Namespace
|
|
4
|
+
from dbt.config.project import load_raw_project
|
|
5
|
+
from dbt.config.profile import read_profile, Profile
|
|
6
|
+
from dbt.config.renderer import ProfileRenderer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# NOTE: inspired in https://github.com/dbt-labs/dbt-core/blob/828d723512fced809c63e369a82c7eb570a74986/core/dbt/config/runtime.py#L58-L74
|
|
10
|
+
def find_profile_name(
|
|
11
|
+
profile_override: Optional[str],
|
|
12
|
+
project_root: str,
|
|
13
|
+
profile_renderer: ProfileRenderer,
|
|
14
|
+
):
|
|
15
|
+
if profile_override is not None:
|
|
16
|
+
profile_name = profile_override
|
|
17
|
+
else:
|
|
18
|
+
raw_project = load_raw_project(project_root)
|
|
19
|
+
raw_profile_name = raw_project.get("profile")
|
|
20
|
+
profile_name = profile_renderer.render_value(raw_profile_name)
|
|
21
|
+
|
|
22
|
+
return profile_name
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# NOTE: inspired in https://github.com/dbt-labs/dbt-core/blob/828d723512fced809c63e369a82c7eb570a74986/core/dbt/config/profile.py#L279-L311
|
|
26
|
+
def find_target_name(
|
|
27
|
+
target_override: Optional[str], raw_profile: dict, profile_renderer: ProfileRenderer
|
|
28
|
+
):
|
|
29
|
+
if target_override is not None:
|
|
30
|
+
target_name = target_override
|
|
31
|
+
elif "target" in raw_profile:
|
|
32
|
+
# render the target if it was parsed from yaml
|
|
33
|
+
target_name = profile_renderer.render_value(raw_profile["target"])
|
|
34
|
+
else:
|
|
35
|
+
target_name = "default"
|
|
36
|
+
|
|
37
|
+
return target_name
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_profiles_info_1_5() -> Tuple[Profile, Dict[str, Any]]:
|
|
41
|
+
flags: Namespace = get_flags()
|
|
42
|
+
|
|
43
|
+
profile_renderer = ProfileRenderer(getattr(flags, "VARS", {}))
|
|
44
|
+
|
|
45
|
+
profile_name = find_profile_name(flags.PROFILE, flags.PROJECT_DIR, profile_renderer)
|
|
46
|
+
|
|
47
|
+
raw_profiles = read_profile(flags.PROFILES_DIR)
|
|
48
|
+
raw_profile = raw_profiles[profile_name]
|
|
49
|
+
|
|
50
|
+
target_name = find_target_name(flags.TARGET, raw_profile, profile_renderer)
|
|
51
|
+
|
|
52
|
+
fal_dict = Profile._get_profile_data(
|
|
53
|
+
profile=raw_profile,
|
|
54
|
+
profile_name=profile_name,
|
|
55
|
+
target_name=target_name,
|
|
56
|
+
)
|
|
57
|
+
db_profile_target_name = fal_dict.get("db_profile")
|
|
58
|
+
assert (
|
|
59
|
+
db_profile_target_name
|
|
60
|
+
), "fal credentials must have a `db_profile` property set"
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
db_profile = Profile.from_raw_profile_info(
|
|
64
|
+
raw_profile=raw_profile,
|
|
65
|
+
profile_name=profile_name,
|
|
66
|
+
renderer=profile_renderer,
|
|
67
|
+
# TODO: should we load the user_config?
|
|
68
|
+
user_config={},
|
|
69
|
+
target_override=db_profile_target_name,
|
|
70
|
+
)
|
|
71
|
+
except RecursionError as error:
|
|
72
|
+
raise AttributeError(
|
|
73
|
+
"Did you wrap a type 'fal' profile with another type 'fal' profile?"
|
|
74
|
+
) from error
|
|
75
|
+
|
|
76
|
+
override_properties = {
|
|
77
|
+
"threads": getattr(flags, "THREADS", None) or fal_dict.get("threads") or db_profile.threads,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return db_profile, override_properties
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from typing import Any, Optional, Type, Set
|
|
2
|
+
|
|
3
|
+
from dbt.adapters.factory import get_adapter_by_type
|
|
4
|
+
from dbt.adapters.base.meta import available
|
|
5
|
+
from dbt.adapters.base.impl import BaseAdapter
|
|
6
|
+
from dbt.contracts.connection import Credentials
|
|
7
|
+
from dbt.parser.manifest import ManifestLoader
|
|
8
|
+
from dbt.clients.jinja import MacroGenerator
|
|
9
|
+
|
|
10
|
+
from ..fal_experimental.utils import cache_static
|
|
11
|
+
from ..fal_experimental.impl import FalAdapterMixin
|
|
12
|
+
from ..fal_experimental import telemetry
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FalCredentialsWrapper:
|
|
16
|
+
_db_creds: Optional[Credentials] = None
|
|
17
|
+
|
|
18
|
+
def __init__(self, db_creds: Credentials):
|
|
19
|
+
self._db_creds = db_creds
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def type(self):
|
|
23
|
+
if find_funcs_in_stack({"to_target_dict", "db_materialization"}):
|
|
24
|
+
# This makes sense for both SQL and Python because the target is always the db
|
|
25
|
+
return self._db_creds.type
|
|
26
|
+
|
|
27
|
+
return "fal"
|
|
28
|
+
|
|
29
|
+
def __getattr__(self, name: str) -> Any:
|
|
30
|
+
"""
|
|
31
|
+
Directly proxy to the DB adapter, just shadowing the type
|
|
32
|
+
"""
|
|
33
|
+
return getattr(self._db_creds, name)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FalEncAdapterWrapper(FalAdapterMixin):
|
|
37
|
+
def __init__(self, db_adapter_type: Type[BaseAdapter], config):
|
|
38
|
+
# Use the db_adapter_type connection manager
|
|
39
|
+
self.ConnectionManager = db_adapter_type.ConnectionManager
|
|
40
|
+
|
|
41
|
+
db_adapter = get_adapter_by_type(db_adapter_type.type())
|
|
42
|
+
super().__init__(config, db_adapter)
|
|
43
|
+
|
|
44
|
+
# HACK: A Python adapter does not have self._available_ all the attributes a DB adapter does.
|
|
45
|
+
# Since we use the DB adapter as the storage for the Python adapter, we must proxy to it
|
|
46
|
+
# all the unhandled calls.
|
|
47
|
+
|
|
48
|
+
# self._available_ is set by metaclass=AdapterMeta
|
|
49
|
+
self._available_ = self._db_adapter._available_.union(self._available_)
|
|
50
|
+
# self._parse_replacements_ is set by metaclass=AdapterMeta
|
|
51
|
+
self._parse_replacements_.update(self._db_adapter._parse_replacements_)
|
|
52
|
+
|
|
53
|
+
telemetry.log_api(
|
|
54
|
+
"encapsulate_init",
|
|
55
|
+
config=config,
|
|
56
|
+
additional_props={
|
|
57
|
+
"is_teleport": self.is_teleport(),
|
|
58
|
+
"fal_host": self.credentials.host,
|
|
59
|
+
},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def submit_python_job(self, *args, **kwargs):
|
|
63
|
+
return super().submit_python_job(*args, **kwargs)
|
|
64
|
+
|
|
65
|
+
@available
|
|
66
|
+
@telemetry.log_call(
|
|
67
|
+
"encapsulate_db_materialization", log_args=["materialization"], config=True
|
|
68
|
+
)
|
|
69
|
+
def db_materialization(self, context: dict, materialization: str):
|
|
70
|
+
# NOTE: inspired by https://github.com/dbt-labs/dbt-core/blob/be4a91a0fe35a619587b7a0145e190690e3771c6/core/dbt/task/run.py#L254-L290
|
|
71
|
+
materialization_macro = self.manifest.find_materialization_macro_by_name(
|
|
72
|
+
self.config.project_name, materialization, self._db_adapter.type()
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# HACK: run the entire SQL materialization and return the resulting dict with relations created
|
|
76
|
+
return MacroGenerator(
|
|
77
|
+
materialization_macro, context, stack=context["context_macro_stack"]
|
|
78
|
+
)()
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
@cache_static
|
|
82
|
+
def manifest(self):
|
|
83
|
+
return ManifestLoader.get_full_manifest(self.config)
|
|
84
|
+
|
|
85
|
+
def type(self):
|
|
86
|
+
# NOTE: This does not let `fal__` macros to be used
|
|
87
|
+
# Maybe for 1.5 we will get a more reliable way to detect if we are in a SQL or Python context
|
|
88
|
+
if find_funcs_in_stack({"render", "db_materialization"}):
|
|
89
|
+
return self._db_adapter.type()
|
|
90
|
+
|
|
91
|
+
return "fal"
|
|
92
|
+
|
|
93
|
+
def __getattr__(self, name):
|
|
94
|
+
"""
|
|
95
|
+
Directly proxy to the DB adapter, Python adapter in this case does what we explicitly define in this class.
|
|
96
|
+
"""
|
|
97
|
+
if hasattr(self._db_adapter, name):
|
|
98
|
+
return getattr(self._db_adapter, name)
|
|
99
|
+
else:
|
|
100
|
+
getattr(super(), name)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def find_funcs_in_stack(funcs: Set[str]) -> bool:
|
|
104
|
+
import inspect
|
|
105
|
+
|
|
106
|
+
# NOTE: from https://stackoverflow.com/a/42636264/1276441
|
|
107
|
+
frame = inspect.currentframe()
|
|
108
|
+
while frame:
|
|
109
|
+
if frame.f_code.co_name in funcs:
|
|
110
|
+
return True
|
|
111
|
+
frame = frame.f_back
|
|
112
|
+
|
|
113
|
+
return False
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from dbt.adapters.base import AdapterPlugin
|
|
2
|
+
|
|
3
|
+
from dbt.adapters.fal_experimental.connections import FalCredentials
|
|
4
|
+
from dbt.adapters.fal_experimental.impl import FalAdapter
|
|
5
|
+
from dbt.include import fal_experimental
|
|
6
|
+
|
|
7
|
+
Plugin = AdapterPlugin(
|
|
8
|
+
adapter=FalAdapter,
|
|
9
|
+
credentials=FalCredentials,
|
|
10
|
+
include_path=fal_experimental.PACKAGE_PATH,
|
|
11
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = '0.9.4'
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import zipfile
|
|
4
|
+
import io
|
|
5
|
+
from functools import partial
|
|
6
|
+
from tempfile import NamedTemporaryFile
|
|
7
|
+
from typing import Any, Optional
|
|
8
|
+
|
|
9
|
+
from dbt.adapters.base.impl import BaseAdapter
|
|
10
|
+
from dbt.config.runtime import RuntimeConfig
|
|
11
|
+
from dbt.contracts.connection import AdapterResponse
|
|
12
|
+
from dbt.flags import get_flags, Namespace
|
|
13
|
+
|
|
14
|
+
from fal_serverless import FalServerlessHost, isolated
|
|
15
|
+
from dbt.adapters.fal_experimental.utils.environments import (
|
|
16
|
+
EnvironmentDefinition,
|
|
17
|
+
get_default_pip_dependencies
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from dbt.parser.manifest import MacroManifest, Manifest
|
|
21
|
+
|
|
22
|
+
from .adapter_support import (
|
|
23
|
+
prepare_for_adapter,
|
|
24
|
+
read_relation_as_df,
|
|
25
|
+
reconstruct_adapter,
|
|
26
|
+
write_df_to_relation,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from .utils import extra_path, get_fal_scripts_path, retrieve_symbol
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def run_with_adapter(code: str, adapter: BaseAdapter, config: RuntimeConfig) -> Any:
|
|
33
|
+
# main symbol is defined during dbt-fal's compilation
|
|
34
|
+
# and acts as an entrypoint for us to run the model.
|
|
35
|
+
fal_scripts_path = str(get_fal_scripts_path(config))
|
|
36
|
+
with extra_path(fal_scripts_path):
|
|
37
|
+
main = retrieve_symbol(code, "main")
|
|
38
|
+
return main(
|
|
39
|
+
read_df=prepare_for_adapter(adapter, read_relation_as_df),
|
|
40
|
+
write_df=prepare_for_adapter(adapter, write_df_to_relation),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _isolated_runner(
|
|
45
|
+
code: str,
|
|
46
|
+
flags: Namespace,
|
|
47
|
+
config: RuntimeConfig,
|
|
48
|
+
manifest: Manifest,
|
|
49
|
+
macro_manifest: MacroManifest,
|
|
50
|
+
local_packages: Optional[bytes] = None,
|
|
51
|
+
) -> Any:
|
|
52
|
+
# This function can be run in an entirely separate
|
|
53
|
+
# process or an environment, so we need to reconstruct
|
|
54
|
+
# the DB adapter solely from the config.
|
|
55
|
+
adapter = reconstruct_adapter(flags, config, manifest, macro_manifest)
|
|
56
|
+
fal_scripts_path = get_fal_scripts_path(config)
|
|
57
|
+
if local_packages is not None:
|
|
58
|
+
if fal_scripts_path.exists():
|
|
59
|
+
import shutil
|
|
60
|
+
shutil.rmtree(fal_scripts_path)
|
|
61
|
+
fal_scripts_path.parent.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
zip_file = zipfile.ZipFile(io.BytesIO(local_packages))
|
|
63
|
+
zip_file.extractall(fal_scripts_path)
|
|
64
|
+
|
|
65
|
+
return run_with_adapter(code, adapter, config)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def run_in_environment_with_adapter(
|
|
69
|
+
environment: EnvironmentDefinition,
|
|
70
|
+
code: str,
|
|
71
|
+
config: RuntimeConfig,
|
|
72
|
+
manifest: Manifest,
|
|
73
|
+
macro_manifest: MacroManifest,
|
|
74
|
+
adapter_type: str
|
|
75
|
+
) -> AdapterResponse:
|
|
76
|
+
"""Run the 'main' function inside the given code on the
|
|
77
|
+
specified environment.
|
|
78
|
+
|
|
79
|
+
The environment_name must be defined inside fal_project.yml file
|
|
80
|
+
in your project's root directory."""
|
|
81
|
+
compressed_local_packages = None
|
|
82
|
+
|
|
83
|
+
is_remote = type(environment.host) is FalServerlessHost
|
|
84
|
+
|
|
85
|
+
deps = get_default_pip_dependencies(
|
|
86
|
+
is_remote=is_remote,
|
|
87
|
+
adapter_type=adapter_type)
|
|
88
|
+
|
|
89
|
+
fal_scripts_path = get_fal_scripts_path(config)
|
|
90
|
+
|
|
91
|
+
if is_remote and fal_scripts_path.exists():
|
|
92
|
+
with NamedTemporaryFile() as temp_file:
|
|
93
|
+
with zipfile.ZipFile(
|
|
94
|
+
temp_file.name, "w", zipfile.ZIP_DEFLATED
|
|
95
|
+
) as zip_file:
|
|
96
|
+
for entry in fal_scripts_path.rglob("*"):
|
|
97
|
+
zip_file.write(entry, entry.relative_to(fal_scripts_path))
|
|
98
|
+
|
|
99
|
+
compressed_local_packages = temp_file.read()
|
|
100
|
+
|
|
101
|
+
execute_model = partial(
|
|
102
|
+
_isolated_runner,
|
|
103
|
+
code=code,
|
|
104
|
+
flags=get_flags(),
|
|
105
|
+
config=config,
|
|
106
|
+
manifest=manifest,
|
|
107
|
+
macro_manifest=macro_manifest,
|
|
108
|
+
local_packages=compressed_local_packages
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if environment.kind == "local":
|
|
112
|
+
result = execute_model()
|
|
113
|
+
return result
|
|
114
|
+
|
|
115
|
+
extra = {}
|
|
116
|
+
if is_remote:
|
|
117
|
+
# Machine type is only applicable in FalServerlesshost
|
|
118
|
+
extra = {"machine_type": environment.machine_type}
|
|
119
|
+
|
|
120
|
+
if environment.kind == "virtualenv":
|
|
121
|
+
requirements = environment.config.get("requirements", [])
|
|
122
|
+
requirements += deps
|
|
123
|
+
isolated_function = isolated(
|
|
124
|
+
kind="virtualenv",
|
|
125
|
+
host=environment.host,
|
|
126
|
+
requirements=requirements,
|
|
127
|
+
**extra
|
|
128
|
+
)(execute_model)
|
|
129
|
+
elif environment.kind == "conda":
|
|
130
|
+
dependencies = environment.config.pop("packages", [])
|
|
131
|
+
dependencies.append({"pip": deps})
|
|
132
|
+
env_dict = {
|
|
133
|
+
"name": "dbt_fal_env",
|
|
134
|
+
"channels": ["conda-forge", "defaults"],
|
|
135
|
+
"dependencies": dependencies
|
|
136
|
+
}
|
|
137
|
+
isolated_function = isolated(
|
|
138
|
+
kind="conda",
|
|
139
|
+
host=environment.host,
|
|
140
|
+
env_dict=env_dict,
|
|
141
|
+
**extra
|
|
142
|
+
)(execute_model)
|
|
143
|
+
else:
|
|
144
|
+
# We should not reach this point, because environment types are validated when the
|
|
145
|
+
# environment objects are created (in utils/environments.py).
|
|
146
|
+
raise Exception(f"Environment type not supported: {environment.kind}")
|
|
147
|
+
|
|
148
|
+
result = isolated_function()
|
|
149
|
+
return result
|