dvt-core 1.11.0b4__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 dvt-core might be problematic. Click here for more details.
- dvt/__init__.py +7 -0
- dvt/_pydantic_shim.py +26 -0
- dvt/adapters/__init__.py +16 -0
- dvt/adapters/multi_adapter_manager.py +268 -0
- dvt/artifacts/__init__.py +0 -0
- dvt/artifacts/exceptions/__init__.py +1 -0
- dvt/artifacts/exceptions/schemas.py +31 -0
- dvt/artifacts/resources/__init__.py +116 -0
- dvt/artifacts/resources/base.py +68 -0
- dvt/artifacts/resources/types.py +93 -0
- dvt/artifacts/resources/v1/analysis.py +10 -0
- dvt/artifacts/resources/v1/catalog.py +23 -0
- dvt/artifacts/resources/v1/components.py +275 -0
- dvt/artifacts/resources/v1/config.py +282 -0
- dvt/artifacts/resources/v1/documentation.py +11 -0
- dvt/artifacts/resources/v1/exposure.py +52 -0
- dvt/artifacts/resources/v1/function.py +53 -0
- dvt/artifacts/resources/v1/generic_test.py +32 -0
- dvt/artifacts/resources/v1/group.py +22 -0
- dvt/artifacts/resources/v1/hook.py +11 -0
- dvt/artifacts/resources/v1/macro.py +30 -0
- dvt/artifacts/resources/v1/metric.py +173 -0
- dvt/artifacts/resources/v1/model.py +146 -0
- dvt/artifacts/resources/v1/owner.py +10 -0
- dvt/artifacts/resources/v1/saved_query.py +112 -0
- dvt/artifacts/resources/v1/seed.py +42 -0
- dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dvt/artifacts/resources/v1/semantic_model.py +315 -0
- dvt/artifacts/resources/v1/singular_test.py +14 -0
- dvt/artifacts/resources/v1/snapshot.py +92 -0
- dvt/artifacts/resources/v1/source_definition.py +85 -0
- dvt/artifacts/resources/v1/sql_operation.py +10 -0
- dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
- dvt/artifacts/schemas/__init__.py +0 -0
- dvt/artifacts/schemas/base.py +191 -0
- dvt/artifacts/schemas/batch_results.py +24 -0
- dvt/artifacts/schemas/catalog/__init__.py +12 -0
- dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
- dvt/artifacts/schemas/freshness/__init__.py +1 -0
- dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
- dvt/artifacts/schemas/manifest/__init__.py +2 -0
- dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
- dvt/artifacts/schemas/results.py +148 -0
- dvt/artifacts/schemas/run/__init__.py +2 -0
- dvt/artifacts/schemas/run/v5/__init__.py +0 -0
- dvt/artifacts/schemas/run/v5/run.py +184 -0
- dvt/artifacts/schemas/upgrades/__init__.py +4 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dvt/artifacts/utils/validation.py +153 -0
- dvt/cli/__init__.py +1 -0
- dvt/cli/context.py +16 -0
- dvt/cli/exceptions.py +56 -0
- dvt/cli/flags.py +558 -0
- dvt/cli/main.py +971 -0
- dvt/cli/option_types.py +121 -0
- dvt/cli/options.py +79 -0
- dvt/cli/params.py +803 -0
- dvt/cli/requires.py +478 -0
- dvt/cli/resolvers.py +32 -0
- dvt/cli/types.py +40 -0
- dvt/clients/__init__.py +0 -0
- dvt/clients/checked_load.py +82 -0
- dvt/clients/git.py +164 -0
- dvt/clients/jinja.py +206 -0
- dvt/clients/jinja_static.py +245 -0
- dvt/clients/registry.py +192 -0
- dvt/clients/yaml_helper.py +68 -0
- dvt/compilation.py +833 -0
- dvt/compute/__init__.py +26 -0
- dvt/compute/base.py +288 -0
- dvt/compute/engines/__init__.py +13 -0
- dvt/compute/engines/duckdb_engine.py +368 -0
- dvt/compute/engines/spark_engine.py +273 -0
- dvt/compute/query_analyzer.py +212 -0
- dvt/compute/router.py +483 -0
- dvt/config/__init__.py +4 -0
- dvt/config/catalogs.py +95 -0
- dvt/config/compute_config.py +406 -0
- dvt/config/profile.py +411 -0
- dvt/config/profiles_v2.py +464 -0
- dvt/config/project.py +893 -0
- dvt/config/renderer.py +232 -0
- dvt/config/runtime.py +491 -0
- dvt/config/selectors.py +209 -0
- dvt/config/utils.py +78 -0
- dvt/connectors/.gitignore +6 -0
- dvt/connectors/README.md +306 -0
- dvt/connectors/catalog.yml +217 -0
- dvt/connectors/download_connectors.py +300 -0
- dvt/constants.py +29 -0
- dvt/context/__init__.py +0 -0
- dvt/context/base.py +746 -0
- dvt/context/configured.py +136 -0
- dvt/context/context_config.py +350 -0
- dvt/context/docs.py +82 -0
- dvt/context/exceptions_jinja.py +179 -0
- dvt/context/macro_resolver.py +195 -0
- dvt/context/macros.py +171 -0
- dvt/context/manifest.py +73 -0
- dvt/context/providers.py +2198 -0
- dvt/context/query_header.py +14 -0
- dvt/context/secret.py +59 -0
- dvt/context/target.py +74 -0
- dvt/contracts/__init__.py +0 -0
- dvt/contracts/files.py +413 -0
- dvt/contracts/graph/__init__.py +0 -0
- dvt/contracts/graph/manifest.py +1904 -0
- dvt/contracts/graph/metrics.py +98 -0
- dvt/contracts/graph/model_config.py +71 -0
- dvt/contracts/graph/node_args.py +42 -0
- dvt/contracts/graph/nodes.py +1806 -0
- dvt/contracts/graph/semantic_manifest.py +233 -0
- dvt/contracts/graph/unparsed.py +812 -0
- dvt/contracts/project.py +417 -0
- dvt/contracts/results.py +53 -0
- dvt/contracts/selection.py +23 -0
- dvt/contracts/sql.py +86 -0
- dvt/contracts/state.py +69 -0
- dvt/contracts/util.py +46 -0
- dvt/deprecations.py +347 -0
- dvt/deps/__init__.py +0 -0
- dvt/deps/base.py +153 -0
- dvt/deps/git.py +196 -0
- dvt/deps/local.py +80 -0
- dvt/deps/registry.py +131 -0
- dvt/deps/resolver.py +149 -0
- dvt/deps/tarball.py +121 -0
- dvt/docs/source/_ext/dbt_click.py +118 -0
- dvt/docs/source/conf.py +32 -0
- dvt/env_vars.py +64 -0
- dvt/event_time/event_time.py +40 -0
- dvt/event_time/sample_window.py +60 -0
- dvt/events/__init__.py +16 -0
- dvt/events/base_types.py +37 -0
- dvt/events/core_types_pb2.py +2 -0
- dvt/events/logging.py +109 -0
- dvt/events/types.py +2534 -0
- dvt/exceptions.py +1487 -0
- dvt/flags.py +89 -0
- dvt/graph/__init__.py +11 -0
- dvt/graph/cli.py +248 -0
- dvt/graph/graph.py +172 -0
- dvt/graph/queue.py +213 -0
- dvt/graph/selector.py +375 -0
- dvt/graph/selector_methods.py +976 -0
- dvt/graph/selector_spec.py +223 -0
- dvt/graph/thread_pool.py +18 -0
- dvt/hooks.py +21 -0
- dvt/include/README.md +49 -0
- dvt/include/__init__.py +3 -0
- dvt/include/global_project.py +4 -0
- dvt/include/starter_project/.gitignore +4 -0
- dvt/include/starter_project/README.md +15 -0
- dvt/include/starter_project/__init__.py +3 -0
- dvt/include/starter_project/analyses/.gitkeep +0 -0
- dvt/include/starter_project/dvt_project.yml +36 -0
- dvt/include/starter_project/macros/.gitkeep +0 -0
- dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
- dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
- dvt/include/starter_project/models/example/schema.yml +21 -0
- dvt/include/starter_project/seeds/.gitkeep +0 -0
- dvt/include/starter_project/snapshots/.gitkeep +0 -0
- dvt/include/starter_project/tests/.gitkeep +0 -0
- dvt/internal_deprecations.py +27 -0
- dvt/jsonschemas/__init__.py +3 -0
- dvt/jsonschemas/jsonschemas.py +309 -0
- dvt/jsonschemas/project/0.0.110.json +4717 -0
- dvt/jsonschemas/project/0.0.85.json +2015 -0
- dvt/jsonschemas/resources/0.0.110.json +2636 -0
- dvt/jsonschemas/resources/0.0.85.json +2536 -0
- dvt/jsonschemas/resources/latest.json +6773 -0
- dvt/links.py +4 -0
- dvt/materializations/__init__.py +0 -0
- dvt/materializations/incremental/__init__.py +0 -0
- dvt/materializations/incremental/microbatch.py +235 -0
- dvt/mp_context.py +8 -0
- dvt/node_types.py +37 -0
- dvt/parser/__init__.py +23 -0
- dvt/parser/analysis.py +21 -0
- dvt/parser/base.py +549 -0
- dvt/parser/common.py +267 -0
- dvt/parser/docs.py +52 -0
- dvt/parser/fixtures.py +51 -0
- dvt/parser/functions.py +30 -0
- dvt/parser/generic_test.py +100 -0
- dvt/parser/generic_test_builders.py +334 -0
- dvt/parser/hooks.py +119 -0
- dvt/parser/macros.py +137 -0
- dvt/parser/manifest.py +2204 -0
- dvt/parser/models.py +574 -0
- dvt/parser/partial.py +1179 -0
- dvt/parser/read_files.py +445 -0
- dvt/parser/schema_generic_tests.py +423 -0
- dvt/parser/schema_renderer.py +111 -0
- dvt/parser/schema_yaml_readers.py +936 -0
- dvt/parser/schemas.py +1467 -0
- dvt/parser/search.py +149 -0
- dvt/parser/seeds.py +28 -0
- dvt/parser/singular_test.py +20 -0
- dvt/parser/snapshots.py +44 -0
- dvt/parser/sources.py +557 -0
- dvt/parser/sql.py +63 -0
- dvt/parser/unit_tests.py +622 -0
- dvt/plugins/__init__.py +20 -0
- dvt/plugins/contracts.py +10 -0
- dvt/plugins/exceptions.py +2 -0
- dvt/plugins/manager.py +164 -0
- dvt/plugins/manifest.py +21 -0
- dvt/profiler.py +20 -0
- dvt/py.typed +1 -0
- dvt/runners/__init__.py +2 -0
- dvt/runners/exposure_runner.py +7 -0
- dvt/runners/no_op_runner.py +46 -0
- dvt/runners/saved_query_runner.py +7 -0
- dvt/selected_resources.py +8 -0
- dvt/task/__init__.py +0 -0
- dvt/task/base.py +504 -0
- dvt/task/build.py +197 -0
- dvt/task/clean.py +57 -0
- dvt/task/clone.py +162 -0
- dvt/task/compile.py +151 -0
- dvt/task/compute.py +366 -0
- dvt/task/debug.py +650 -0
- dvt/task/deps.py +280 -0
- dvt/task/docs/__init__.py +3 -0
- dvt/task/docs/generate.py +408 -0
- dvt/task/docs/index.html +250 -0
- dvt/task/docs/serve.py +28 -0
- dvt/task/freshness.py +323 -0
- dvt/task/function.py +122 -0
- dvt/task/group_lookup.py +46 -0
- dvt/task/init.py +374 -0
- dvt/task/list.py +237 -0
- dvt/task/printer.py +176 -0
- dvt/task/profiles.py +256 -0
- dvt/task/retry.py +175 -0
- dvt/task/run.py +1146 -0
- dvt/task/run_operation.py +142 -0
- dvt/task/runnable.py +802 -0
- dvt/task/seed.py +104 -0
- dvt/task/show.py +150 -0
- dvt/task/snapshot.py +57 -0
- dvt/task/sql.py +111 -0
- dvt/task/test.py +464 -0
- dvt/tests/fixtures/__init__.py +1 -0
- dvt/tests/fixtures/project.py +620 -0
- dvt/tests/util.py +651 -0
- dvt/tracking.py +529 -0
- dvt/utils/__init__.py +3 -0
- dvt/utils/artifact_upload.py +151 -0
- dvt/utils/utils.py +408 -0
- dvt/version.py +249 -0
- dvt_core-1.11.0b4.dist-info/METADATA +252 -0
- dvt_core-1.11.0b4.dist-info/RECORD +261 -0
- dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
- dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
- dvt_core-1.11.0b4.dist-info/top_level.txt +1 -0
dvt/cli/option_types.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import pytz
|
|
4
|
+
from click import Choice, Context, Parameter, ParamType
|
|
5
|
+
from dvt.config.utils import normalize_warn_error_options, parse_cli_yaml_string
|
|
6
|
+
from dvt.event_time.sample_window import SampleWindow
|
|
7
|
+
from dvt.events import ALL_EVENT_NAMES
|
|
8
|
+
from dvt.exceptions import OptionNotYamlDictError, ValidationError
|
|
9
|
+
|
|
10
|
+
from dbt_common.exceptions import DbtValidationError
|
|
11
|
+
from dbt_common.helper_types import WarnErrorOptionsV2
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class YAML(ParamType):
|
|
15
|
+
"""The Click YAML type. Converts YAML strings into objects."""
|
|
16
|
+
|
|
17
|
+
name = "YAML"
|
|
18
|
+
|
|
19
|
+
def convert(self, value, param, ctx):
|
|
20
|
+
# assume non-string values are a problem
|
|
21
|
+
if not isinstance(value, str):
|
|
22
|
+
self.fail(f"Cannot load YAML from type {type(value)}", param, ctx)
|
|
23
|
+
try:
|
|
24
|
+
param_option_name = param.opts[0] if param.opts else param.name
|
|
25
|
+
return parse_cli_yaml_string(value, param_option_name.strip("-"))
|
|
26
|
+
except (ValidationError, DbtValidationError, OptionNotYamlDictError):
|
|
27
|
+
self.fail(f"String '{value}' is not valid YAML", param, ctx)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Package(ParamType):
|
|
31
|
+
"""The Click STRING type. Converts string into dict with package name and version.
|
|
32
|
+
Example package:
|
|
33
|
+
package-name@1.0.0
|
|
34
|
+
package-name
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
name = "NewPackage"
|
|
38
|
+
|
|
39
|
+
def convert(self, value, param, ctx):
|
|
40
|
+
# assume non-string values are a problem
|
|
41
|
+
if not isinstance(value, str):
|
|
42
|
+
self.fail(f"Cannot load Package from type {type(value)}", param, ctx)
|
|
43
|
+
try:
|
|
44
|
+
package_name, package_version = value.split("@")
|
|
45
|
+
return {"name": package_name, "version": package_version}
|
|
46
|
+
except ValueError:
|
|
47
|
+
return {"name": value, "version": None}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class WarnErrorOptionsType(YAML):
|
|
51
|
+
"""The Click WarnErrorOptions type. Converts YAML strings into objects."""
|
|
52
|
+
|
|
53
|
+
name = "WarnErrorOptionsType"
|
|
54
|
+
|
|
55
|
+
def convert(self, value, param, ctx):
|
|
56
|
+
# this function is being used by param in click
|
|
57
|
+
warn_error_options = super().convert(value, param, ctx)
|
|
58
|
+
normalize_warn_error_options(warn_error_options)
|
|
59
|
+
|
|
60
|
+
return WarnErrorOptionsV2(
|
|
61
|
+
error=warn_error_options.get("error", []),
|
|
62
|
+
warn=warn_error_options.get("warn", []),
|
|
63
|
+
silence=warn_error_options.get("silence", []),
|
|
64
|
+
valid_error_names=ALL_EVENT_NAMES,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Truthy(ParamType):
|
|
69
|
+
"""The Click Truthy type. Converts strings into a "truthy" type"""
|
|
70
|
+
|
|
71
|
+
name = "TRUTHY"
|
|
72
|
+
|
|
73
|
+
def convert(self, value, param, ctx):
|
|
74
|
+
# assume non-string / non-None values are a problem
|
|
75
|
+
if not isinstance(value, (str, None)):
|
|
76
|
+
self.fail(f"Cannot load TRUTHY from type {type(value)}", param, ctx)
|
|
77
|
+
|
|
78
|
+
if value is None or value.lower() in ("0", "false", "f"):
|
|
79
|
+
return None
|
|
80
|
+
else:
|
|
81
|
+
return value
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ChoiceTuple(Choice):
|
|
85
|
+
name = "CHOICE_TUPLE"
|
|
86
|
+
|
|
87
|
+
def convert(self, value, param, ctx):
|
|
88
|
+
if not isinstance(value, str):
|
|
89
|
+
for value_item in value:
|
|
90
|
+
super().convert(value_item, param, ctx)
|
|
91
|
+
else:
|
|
92
|
+
super().convert(value, param, ctx)
|
|
93
|
+
|
|
94
|
+
return value
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class SampleType(ParamType):
|
|
98
|
+
name = "SAMPLE"
|
|
99
|
+
|
|
100
|
+
def convert(
|
|
101
|
+
self, value, param: Optional[Parameter], ctx: Optional[Context]
|
|
102
|
+
) -> Optional[SampleWindow]:
|
|
103
|
+
if value is None:
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
if isinstance(value, str):
|
|
107
|
+
try:
|
|
108
|
+
# Try and identify if it's a "dict" or a "str"
|
|
109
|
+
if value.lstrip()[0] == "{":
|
|
110
|
+
param_option_name: str = param.opts[0] if param.opts else param.name # type: ignore
|
|
111
|
+
parsed_dict = parse_cli_yaml_string(value, param_option_name.strip("-"))
|
|
112
|
+
sample_window = SampleWindow.from_dict(parsed_dict)
|
|
113
|
+
sample_window.start = sample_window.start.replace(tzinfo=pytz.UTC)
|
|
114
|
+
sample_window.end = sample_window.end.replace(tzinfo=pytz.UTC)
|
|
115
|
+
return sample_window
|
|
116
|
+
else:
|
|
117
|
+
return SampleWindow.from_relative_string(value)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
self.fail(e.__str__(), param, ctx)
|
|
120
|
+
else:
|
|
121
|
+
self.fail(f"Cannot load SAMPLE_WINDOW from type {type(value)}", param, ctx)
|
dvt/cli/options.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import typing as t
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
from click import Context
|
|
6
|
+
from click.parser import _OptionParser, _ParsingState
|
|
7
|
+
from dvt.cli.option_types import ChoiceTuple
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Implementation from: https://stackoverflow.com/a/48394004
|
|
11
|
+
# Note MultiOption options must be specified with type=tuple or type=ChoiceTuple (https://github.com/pallets/click/issues/2012)
|
|
12
|
+
class MultiOption(click.Option):
|
|
13
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
14
|
+
self.save_other_options = kwargs.pop("save_other_options", True)
|
|
15
|
+
nargs = kwargs.pop("nargs", -1)
|
|
16
|
+
assert nargs == -1, "nargs, if set, must be -1 not {}".format(nargs)
|
|
17
|
+
super(MultiOption, self).__init__(*args, **kwargs)
|
|
18
|
+
# this makes mypy happy, setting these to None causes mypy failures
|
|
19
|
+
self._previous_parser_process = lambda *args, **kwargs: None
|
|
20
|
+
self._eat_all_parser = lambda *args, **kwargs: None
|
|
21
|
+
|
|
22
|
+
# validate that multiple=True
|
|
23
|
+
multiple = kwargs.pop("multiple", None)
|
|
24
|
+
msg = f"MultiOption named `{self.name}` must have multiple=True (rather than {multiple})"
|
|
25
|
+
assert multiple, msg
|
|
26
|
+
|
|
27
|
+
# validate that type=tuple or type=ChoiceTuple
|
|
28
|
+
option_type = kwargs.pop("type", None)
|
|
29
|
+
msg = f"MultiOption named `{self.name}` must be tuple or ChoiceTuple (rather than {option_type})"
|
|
30
|
+
if inspect.isclass(option_type):
|
|
31
|
+
assert issubclass(option_type, tuple), msg
|
|
32
|
+
else:
|
|
33
|
+
assert isinstance(option_type, ChoiceTuple), msg
|
|
34
|
+
|
|
35
|
+
def add_to_parser(self, parser: _OptionParser, ctx: Context):
|
|
36
|
+
def parser_process(value: str, state: _ParsingState):
|
|
37
|
+
# method to hook to the parser.process
|
|
38
|
+
done = False
|
|
39
|
+
value_list = str.split(value, " ")
|
|
40
|
+
if self.save_other_options:
|
|
41
|
+
# grab everything up to the next option
|
|
42
|
+
while state.rargs and not done:
|
|
43
|
+
for prefix in self._eat_all_parser.prefixes: # type: ignore[attr-defined]
|
|
44
|
+
if state.rargs[0].startswith(prefix):
|
|
45
|
+
done = True
|
|
46
|
+
if not done:
|
|
47
|
+
value_list.append(state.rargs.pop(0))
|
|
48
|
+
else:
|
|
49
|
+
# grab everything remaining
|
|
50
|
+
value_list += state.rargs
|
|
51
|
+
state.rargs[:] = []
|
|
52
|
+
value_tuple = tuple(value_list)
|
|
53
|
+
# call the actual process
|
|
54
|
+
self._previous_parser_process(value_tuple, state)
|
|
55
|
+
|
|
56
|
+
retval = super(MultiOption, self).add_to_parser(parser, ctx)
|
|
57
|
+
for name in self.opts:
|
|
58
|
+
our_parser = parser._long_opt.get(name) or parser._short_opt.get(name)
|
|
59
|
+
if our_parser:
|
|
60
|
+
self._eat_all_parser = our_parser # type: ignore[assignment]
|
|
61
|
+
self._previous_parser_process = our_parser.process
|
|
62
|
+
# mypy doesnt like assingment to a method see https://github.com/python/mypy/issues/708
|
|
63
|
+
our_parser.process = parser_process # type: ignore[method-assign]
|
|
64
|
+
break
|
|
65
|
+
return retval
|
|
66
|
+
|
|
67
|
+
def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any:
|
|
68
|
+
def flatten(data):
|
|
69
|
+
if isinstance(data, tuple):
|
|
70
|
+
for x in data:
|
|
71
|
+
yield from flatten(x)
|
|
72
|
+
else:
|
|
73
|
+
yield data
|
|
74
|
+
|
|
75
|
+
# there will be nested tuples to flatten when multiple=True
|
|
76
|
+
value = super(MultiOption, self).type_cast_value(ctx, value)
|
|
77
|
+
if value:
|
|
78
|
+
value = tuple(flatten(value))
|
|
79
|
+
return value
|