relationalai 0.13.5__py3-none-any.whl → 1.0.0a1__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.
- relationalai/__init__.py +1 -256
- relationalai/config/__init__.py +56 -0
- relationalai/config/config.py +289 -0
- relationalai/config/config_fields.py +86 -0
- relationalai/config/connections/__init__.py +46 -0
- relationalai/config/connections/base.py +23 -0
- relationalai/config/connections/duckdb.py +29 -0
- relationalai/config/connections/snowflake.py +243 -0
- relationalai/config/external/__init__.py +17 -0
- relationalai/config/external/dbt_converter.py +101 -0
- relationalai/config/external/dbt_models.py +93 -0
- relationalai/config/external/snowflake_converter.py +41 -0
- relationalai/config/external/snowflake_models.py +85 -0
- relationalai/config/external/utils.py +19 -0
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +146 -22
- relationalai/semantics/backends/lqp/annotations.py +11 -0
- relationalai/semantics/backends/sql/sql_compiler.py +327 -0
- relationalai/semantics/frontend/base.py +1716 -0
- relationalai/semantics/frontend/core.py +179 -0
- relationalai/semantics/frontend/front_compiler.py +1313 -0
- relationalai/semantics/frontend/pprint.py +408 -0
- relationalai/semantics/metamodel/__init__.py +6 -40
- relationalai/semantics/metamodel/builtins.py +205 -772
- relationalai/semantics/metamodel/metamodel.py +437 -0
- relationalai/semantics/metamodel/metamodel_analyzer.py +519 -0
- relationalai/semantics/metamodel/pprint.py +412 -0
- relationalai/semantics/metamodel/rewriter.py +266 -0
- relationalai/semantics/metamodel/typer.py +1186 -0
- relationalai/semantics/std/__init__.py +60 -40
- relationalai/semantics/std/aggregates.py +149 -0
- relationalai/semantics/std/common.py +44 -0
- relationalai/semantics/std/constraints.py +37 -43
- relationalai/semantics/std/datetime.py +246 -135
- relationalai/semantics/std/decimals.py +45 -52
- relationalai/semantics/std/floats.py +13 -5
- relationalai/semantics/std/integers.py +26 -11
- relationalai/semantics/std/math.py +183 -112
- relationalai/semantics/std/numbers.py +86 -0
- relationalai/semantics/std/re.py +80 -62
- relationalai/semantics/std/strings.py +101 -46
- relationalai/shims/executor.py +161 -0
- relationalai/shims/helpers.py +126 -0
- relationalai/shims/hoister.py +221 -0
- relationalai/shims/mm2v0.py +1324 -0
- relationalai/tools/cli/__init__.py +6 -0
- relationalai/tools/cli/cli.py +90 -0
- relationalai/tools/cli/components/__init__.py +5 -0
- relationalai/tools/cli/components/progress_reader.py +1524 -0
- relationalai/tools/cli/components/utils.py +58 -0
- relationalai/tools/cli/config_template.py +45 -0
- relationalai/tools/cli/dev.py +19 -0
- relationalai/tools/debugger.py +289 -183
- relationalai/tools/typer_debugger.py +93 -0
- relationalai/util/dataclasses.py +43 -0
- relationalai/util/docutils.py +40 -0
- relationalai/util/error.py +199 -0
- relationalai/util/format.py +48 -109
- relationalai/util/naming.py +145 -0
- relationalai/util/python.py +35 -0
- relationalai/util/runtime.py +156 -0
- relationalai/util/schema.py +197 -0
- relationalai/util/source.py +185 -0
- relationalai/util/structures.py +163 -0
- relationalai/util/tracing.py +261 -0
- relationalai-1.0.0a1.dist-info/METADATA +44 -0
- relationalai-1.0.0a1.dist-info/RECORD +489 -0
- relationalai-1.0.0a1.dist-info/WHEEL +5 -0
- relationalai-1.0.0a1.dist-info/entry_points.txt +3 -0
- relationalai-1.0.0a1.dist-info/top_level.txt +2 -0
- v0/relationalai/__init__.py +216 -0
- v0/relationalai/clients/__init__.py +5 -0
- v0/relationalai/clients/azure.py +477 -0
- v0/relationalai/clients/client.py +912 -0
- v0/relationalai/clients/config.py +673 -0
- v0/relationalai/clients/direct_access_client.py +118 -0
- v0/relationalai/clients/hash_util.py +31 -0
- v0/relationalai/clients/local.py +571 -0
- v0/relationalai/clients/profile_polling.py +73 -0
- v0/relationalai/clients/result_helpers.py +420 -0
- v0/relationalai/clients/snowflake.py +3869 -0
- v0/relationalai/clients/types.py +113 -0
- v0/relationalai/clients/use_index_poller.py +980 -0
- v0/relationalai/clients/util.py +356 -0
- v0/relationalai/debugging.py +389 -0
- v0/relationalai/dsl.py +1749 -0
- v0/relationalai/early_access/builder/__init__.py +30 -0
- v0/relationalai/early_access/builder/builder/__init__.py +35 -0
- v0/relationalai/early_access/builder/snowflake/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/__init__.py +25 -0
- v0/relationalai/early_access/builder/std/decimals/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/integers/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/math/__init__.py +12 -0
- v0/relationalai/early_access/builder/std/strings/__init__.py +14 -0
- v0/relationalai/early_access/devtools/__init__.py +12 -0
- v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
- v0/relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
- v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
- v0/relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
- v0/relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
- v0/relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
- v0/relationalai/early_access/dsl/bindings/common.py +402 -0
- v0/relationalai/early_access/dsl/bindings/csv.py +170 -0
- v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
- v0/relationalai/early_access/dsl/bindings/snowflake.py +64 -0
- v0/relationalai/early_access/dsl/codegen/binder.py +411 -0
- v0/relationalai/early_access/dsl/codegen/common.py +79 -0
- v0/relationalai/early_access/dsl/codegen/helpers.py +23 -0
- v0/relationalai/early_access/dsl/codegen/relations.py +700 -0
- v0/relationalai/early_access/dsl/codegen/weaver.py +417 -0
- v0/relationalai/early_access/dsl/core/builders/__init__.py +47 -0
- v0/relationalai/early_access/dsl/core/builders/logic.py +19 -0
- v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
- v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
- v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
- v0/relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
- v0/relationalai/early_access/dsl/core/context.py +13 -0
- v0/relationalai/early_access/dsl/core/cset.py +132 -0
- v0/relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
- v0/relationalai/early_access/dsl/core/exprs/relational.py +18 -0
- v0/relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
- v0/relationalai/early_access/dsl/core/instances.py +44 -0
- v0/relationalai/early_access/dsl/core/logic/__init__.py +193 -0
- v0/relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
- v0/relationalai/early_access/dsl/core/logic/exists.py +223 -0
- v0/relationalai/early_access/dsl/core/logic/helper.py +163 -0
- v0/relationalai/early_access/dsl/core/namespaces.py +32 -0
- v0/relationalai/early_access/dsl/core/relations.py +276 -0
- v0/relationalai/early_access/dsl/core/rules.py +112 -0
- v0/relationalai/early_access/dsl/core/std/__init__.py +45 -0
- v0/relationalai/early_access/dsl/core/temporal/recall.py +6 -0
- v0/relationalai/early_access/dsl/core/types/__init__.py +270 -0
- v0/relationalai/early_access/dsl/core/types/concepts.py +128 -0
- v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
- v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
- v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
- v0/relationalai/early_access/dsl/core/types/standard.py +92 -0
- v0/relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
- v0/relationalai/early_access/dsl/core/types/variables.py +203 -0
- v0/relationalai/early_access/dsl/ir/compiler.py +318 -0
- v0/relationalai/early_access/dsl/ir/executor.py +260 -0
- v0/relationalai/early_access/dsl/ontologies/constraints.py +88 -0
- v0/relationalai/early_access/dsl/ontologies/export.py +30 -0
- v0/relationalai/early_access/dsl/ontologies/models.py +453 -0
- v0/relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
- v0/relationalai/early_access/dsl/ontologies/readings.py +60 -0
- v0/relationalai/early_access/dsl/ontologies/relationships.py +322 -0
- v0/relationalai/early_access/dsl/ontologies/roles.py +87 -0
- v0/relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
- v0/relationalai/early_access/dsl/orm/constraints.py +438 -0
- v0/relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
- v0/relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
- v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
- v0/relationalai/early_access/dsl/orm/measures/measures.py +299 -0
- v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
- v0/relationalai/early_access/dsl/orm/models.py +256 -0
- v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
- v0/relationalai/early_access/dsl/orm/printer.py +469 -0
- v0/relationalai/early_access/dsl/orm/reasoners.py +480 -0
- v0/relationalai/early_access/dsl/orm/relations.py +19 -0
- v0/relationalai/early_access/dsl/orm/relationships.py +251 -0
- v0/relationalai/early_access/dsl/orm/types.py +42 -0
- v0/relationalai/early_access/dsl/orm/utils.py +79 -0
- v0/relationalai/early_access/dsl/orm/verb.py +204 -0
- v0/relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
- v0/relationalai/early_access/dsl/relations.py +170 -0
- v0/relationalai/early_access/dsl/rulesets.py +69 -0
- v0/relationalai/early_access/dsl/schemas/__init__.py +450 -0
- v0/relationalai/early_access/dsl/schemas/builder.py +48 -0
- v0/relationalai/early_access/dsl/schemas/comp_names.py +51 -0
- v0/relationalai/early_access/dsl/schemas/components.py +203 -0
- v0/relationalai/early_access/dsl/schemas/contexts.py +156 -0
- v0/relationalai/early_access/dsl/schemas/exprs.py +89 -0
- v0/relationalai/early_access/dsl/schemas/fragments.py +464 -0
- v0/relationalai/early_access/dsl/serialization.py +79 -0
- v0/relationalai/early_access/dsl/serialize/exporter.py +163 -0
- v0/relationalai/early_access/dsl/snow/api.py +104 -0
- v0/relationalai/early_access/dsl/snow/common.py +76 -0
- v0/relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
- v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
- v0/relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
- v0/relationalai/early_access/dsl/types/__init__.py +40 -0
- v0/relationalai/early_access/dsl/types/concepts.py +12 -0
- v0/relationalai/early_access/dsl/types/entities.py +135 -0
- v0/relationalai/early_access/dsl/types/values.py +17 -0
- v0/relationalai/early_access/dsl/utils.py +102 -0
- v0/relationalai/early_access/graphs/__init__.py +13 -0
- v0/relationalai/early_access/lqp/__init__.py +12 -0
- v0/relationalai/early_access/lqp/compiler/__init__.py +12 -0
- v0/relationalai/early_access/lqp/constructors/__init__.py +18 -0
- v0/relationalai/early_access/lqp/executor/__init__.py +12 -0
- v0/relationalai/early_access/lqp/ir/__init__.py +12 -0
- v0/relationalai/early_access/lqp/passes/__init__.py +12 -0
- v0/relationalai/early_access/lqp/pragmas/__init__.py +12 -0
- v0/relationalai/early_access/lqp/primitives/__init__.py +12 -0
- v0/relationalai/early_access/lqp/types/__init__.py +12 -0
- v0/relationalai/early_access/lqp/utils/__init__.py +12 -0
- v0/relationalai/early_access/lqp/validators/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/__init__.py +58 -0
- v0/relationalai/early_access/metamodel/builtins/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/compiler/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/dependency/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/factory/__init__.py +17 -0
- v0/relationalai/early_access/metamodel/helpers/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/ir/__init__.py +14 -0
- v0/relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
- v0/relationalai/early_access/metamodel/typer/__init__.py +3 -0
- v0/relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
- v0/relationalai/early_access/metamodel/types/__init__.py +15 -0
- v0/relationalai/early_access/metamodel/util/__init__.py +15 -0
- v0/relationalai/early_access/metamodel/visitor/__init__.py +12 -0
- v0/relationalai/early_access/rel/__init__.py +12 -0
- v0/relationalai/early_access/rel/executor/__init__.py +12 -0
- v0/relationalai/early_access/rel/rel_utils/__init__.py +12 -0
- v0/relationalai/early_access/rel/rewrite/__init__.py +7 -0
- v0/relationalai/early_access/solvers/__init__.py +19 -0
- v0/relationalai/early_access/sql/__init__.py +11 -0
- v0/relationalai/early_access/sql/executor/__init__.py +3 -0
- v0/relationalai/early_access/sql/rewrite/__init__.py +3 -0
- v0/relationalai/early_access/tests/logging/__init__.py +12 -0
- v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
- v0/relationalai/early_access/tests/utils/__init__.py +12 -0
- v0/relationalai/environments/__init__.py +35 -0
- v0/relationalai/environments/base.py +381 -0
- v0/relationalai/environments/colab.py +14 -0
- v0/relationalai/environments/generic.py +71 -0
- v0/relationalai/environments/ipython.py +68 -0
- v0/relationalai/environments/jupyter.py +9 -0
- v0/relationalai/environments/snowbook.py +169 -0
- v0/relationalai/errors.py +2455 -0
- v0/relationalai/experimental/SF.py +38 -0
- v0/relationalai/experimental/inspect.py +47 -0
- v0/relationalai/experimental/pathfinder/__init__.py +158 -0
- v0/relationalai/experimental/pathfinder/api.py +160 -0
- v0/relationalai/experimental/pathfinder/automaton.py +584 -0
- v0/relationalai/experimental/pathfinder/bridge.py +226 -0
- v0/relationalai/experimental/pathfinder/compiler.py +416 -0
- v0/relationalai/experimental/pathfinder/datalog.py +214 -0
- v0/relationalai/experimental/pathfinder/diagnostics.py +56 -0
- v0/relationalai/experimental/pathfinder/filter.py +236 -0
- v0/relationalai/experimental/pathfinder/glushkov.py +439 -0
- v0/relationalai/experimental/pathfinder/options.py +265 -0
- v0/relationalai/experimental/pathfinder/rpq.py +344 -0
- v0/relationalai/experimental/pathfinder/transition.py +200 -0
- v0/relationalai/experimental/pathfinder/utils.py +26 -0
- v0/relationalai/experimental/paths/api.py +143 -0
- v0/relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
- v0/relationalai/experimental/paths/examples/basic_example.py +40 -0
- v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
- v0/relationalai/experimental/paths/examples/movie_example.py +77 -0
- v0/relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
- v0/relationalai/experimental/paths/examples/paths_example.py +116 -0
- v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
- v0/relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
- v0/relationalai/experimental/paths/graph.py +185 -0
- v0/relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
- v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
- v0/relationalai/experimental/paths/path_algorithms/single.py +59 -0
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
- v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
- v0/relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
- v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
- v0/relationalai/experimental/paths/path_algorithms/usp.py +150 -0
- v0/relationalai/experimental/paths/product_graph.py +93 -0
- v0/relationalai/experimental/paths/rpq/automaton.py +584 -0
- v0/relationalai/experimental/paths/rpq/diagnostics.py +56 -0
- v0/relationalai/experimental/paths/rpq/rpq.py +378 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
- v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
- v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
- v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
- v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
- v0/relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
- v0/relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
- v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
- v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
- v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
- v0/relationalai/experimental/paths/tree_agg.py +168 -0
- v0/relationalai/experimental/paths/utilities/iterators.py +27 -0
- v0/relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
- v0/relationalai/experimental/solvers.py +1087 -0
- v0/relationalai/loaders/csv.py +195 -0
- v0/relationalai/loaders/loader.py +177 -0
- v0/relationalai/loaders/types.py +23 -0
- v0/relationalai/rel_emitter.py +373 -0
- v0/relationalai/rel_utils.py +185 -0
- v0/relationalai/semantics/__init__.py +29 -0
- v0/relationalai/semantics/devtools/benchmark_lqp.py +536 -0
- v0/relationalai/semantics/devtools/compilation_manager.py +294 -0
- v0/relationalai/semantics/devtools/extract_lqp.py +110 -0
- v0/relationalai/semantics/internal/internal.py +3785 -0
- v0/relationalai/semantics/internal/snowflake.py +324 -0
- v0/relationalai/semantics/lqp/builtins.py +16 -0
- v0/relationalai/semantics/lqp/compiler.py +22 -0
- v0/relationalai/semantics/lqp/constructors.py +68 -0
- v0/relationalai/semantics/lqp/executor.py +469 -0
- v0/relationalai/semantics/lqp/intrinsics.py +24 -0
- v0/relationalai/semantics/lqp/ir.py +124 -0
- v0/relationalai/semantics/lqp/model2lqp.py +839 -0
- v0/relationalai/semantics/lqp/passes.py +680 -0
- v0/relationalai/semantics/lqp/primitives.py +252 -0
- v0/relationalai/semantics/lqp/result_helpers.py +202 -0
- v0/relationalai/semantics/lqp/rewrite/__init__.py +18 -0
- v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
- v0/relationalai/semantics/lqp/rewrite/cdc.py +216 -0
- v0/relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +449 -0
- v0/relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
- v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
- v0/relationalai/semantics/lqp/rewrite/splinter.py +76 -0
- v0/relationalai/semantics/lqp/types.py +101 -0
- v0/relationalai/semantics/lqp/utils.py +160 -0
- v0/relationalai/semantics/lqp/validators.py +57 -0
- v0/relationalai/semantics/metamodel/__init__.py +40 -0
- v0/relationalai/semantics/metamodel/builtins.py +774 -0
- v0/relationalai/semantics/metamodel/compiler.py +133 -0
- v0/relationalai/semantics/metamodel/dependency.py +862 -0
- v0/relationalai/semantics/metamodel/executor.py +61 -0
- v0/relationalai/semantics/metamodel/factory.py +287 -0
- v0/relationalai/semantics/metamodel/helpers.py +361 -0
- v0/relationalai/semantics/metamodel/ir.py +923 -0
- v0/relationalai/semantics/metamodel/rewrite/__init__.py +7 -0
- v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
- v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +549 -0
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
- v0/relationalai/semantics/metamodel/typer/checker.py +353 -0
- v0/relationalai/semantics/metamodel/typer/typer.py +1395 -0
- v0/relationalai/semantics/metamodel/util.py +505 -0
- v0/relationalai/semantics/metamodel/visitor.py +944 -0
- v0/relationalai/semantics/reasoners/__init__.py +10 -0
- v0/relationalai/semantics/reasoners/graph/__init__.py +37 -0
- v0/relationalai/semantics/reasoners/graph/core.py +9020 -0
- v0/relationalai/semantics/reasoners/optimization/__init__.py +68 -0
- v0/relationalai/semantics/reasoners/optimization/common.py +88 -0
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +1163 -0
- v0/relationalai/semantics/rel/builtins.py +40 -0
- v0/relationalai/semantics/rel/compiler.py +989 -0
- v0/relationalai/semantics/rel/executor.py +359 -0
- v0/relationalai/semantics/rel/rel.py +482 -0
- v0/relationalai/semantics/rel/rel_utils.py +276 -0
- v0/relationalai/semantics/snowflake/__init__.py +3 -0
- v0/relationalai/semantics/sql/compiler.py +2503 -0
- v0/relationalai/semantics/sql/executor/duck_db.py +52 -0
- v0/relationalai/semantics/sql/executor/result_helpers.py +64 -0
- v0/relationalai/semantics/sql/executor/snowflake.py +145 -0
- v0/relationalai/semantics/sql/rewrite/denormalize.py +222 -0
- v0/relationalai/semantics/sql/rewrite/double_negation.py +49 -0
- v0/relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
- v0/relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
- v0/relationalai/semantics/sql/sql.py +504 -0
- v0/relationalai/semantics/std/__init__.py +54 -0
- v0/relationalai/semantics/std/constraints.py +43 -0
- v0/relationalai/semantics/std/datetime.py +363 -0
- v0/relationalai/semantics/std/decimals.py +62 -0
- v0/relationalai/semantics/std/floats.py +7 -0
- v0/relationalai/semantics/std/integers.py +22 -0
- v0/relationalai/semantics/std/math.py +141 -0
- v0/relationalai/semantics/std/pragmas.py +11 -0
- v0/relationalai/semantics/std/re.py +83 -0
- v0/relationalai/semantics/std/std.py +14 -0
- v0/relationalai/semantics/std/strings.py +63 -0
- v0/relationalai/semantics/tests/__init__.py +0 -0
- v0/relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
- v0/relationalai/semantics/tests/test_snapshot_base.py +9 -0
- v0/relationalai/semantics/tests/utils.py +46 -0
- v0/relationalai/std/__init__.py +70 -0
- v0/relationalai/tools/__init__.py +0 -0
- v0/relationalai/tools/cli.py +1940 -0
- v0/relationalai/tools/cli_controls.py +1826 -0
- v0/relationalai/tools/cli_helpers.py +390 -0
- v0/relationalai/tools/debugger.py +183 -0
- v0/relationalai/tools/debugger_client.py +109 -0
- v0/relationalai/tools/debugger_server.py +302 -0
- v0/relationalai/tools/dev.py +685 -0
- v0/relationalai/tools/qb_debugger.py +425 -0
- v0/relationalai/util/clean_up_databases.py +95 -0
- v0/relationalai/util/format.py +123 -0
- v0/relationalai/util/list_databases.py +9 -0
- v0/relationalai/util/otel_configuration.py +25 -0
- v0/relationalai/util/otel_handler.py +484 -0
- v0/relationalai/util/snowflake_handler.py +88 -0
- v0/relationalai/util/span_format_test.py +43 -0
- v0/relationalai/util/span_tracker.py +207 -0
- v0/relationalai/util/spans_file_handler.py +72 -0
- v0/relationalai/util/tracing_handler.py +34 -0
- frontend/debugger/dist/.gitignore +0 -2
- frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
- frontend/debugger/dist/assets/index-Cssla-O7.js +0 -208
- frontend/debugger/dist/assets/index-DlHsYx1V.css +0 -9
- frontend/debugger/dist/index.html +0 -17
- relationalai/clients/__init__.py +0 -18
- relationalai/clients/client.py +0 -946
- relationalai/clients/config.py +0 -673
- relationalai/clients/direct_access_client.py +0 -118
- relationalai/clients/exec_txn_poller.py +0 -153
- relationalai/clients/hash_util.py +0 -31
- relationalai/clients/local.py +0 -594
- relationalai/clients/profile_polling.py +0 -73
- relationalai/clients/resources/__init__.py +0 -8
- relationalai/clients/resources/azure/azure.py +0 -502
- relationalai/clients/resources/snowflake/__init__.py +0 -20
- relationalai/clients/resources/snowflake/cli_resources.py +0 -98
- relationalai/clients/resources/snowflake/direct_access_resources.py +0 -739
- relationalai/clients/resources/snowflake/engine_service.py +0 -381
- relationalai/clients/resources/snowflake/engine_state_handlers.py +0 -315
- relationalai/clients/resources/snowflake/error_handlers.py +0 -240
- relationalai/clients/resources/snowflake/export_procedure.py.jinja +0 -249
- relationalai/clients/resources/snowflake/resources_factory.py +0 -99
- relationalai/clients/resources/snowflake/snowflake.py +0 -3193
- relationalai/clients/resources/snowflake/use_index_poller.py +0 -1019
- relationalai/clients/resources/snowflake/use_index_resources.py +0 -188
- relationalai/clients/resources/snowflake/util.py +0 -387
- relationalai/clients/result_helpers.py +0 -420
- relationalai/clients/types.py +0 -118
- relationalai/clients/util.py +0 -356
- relationalai/debugging.py +0 -389
- relationalai/dsl.py +0 -1749
- relationalai/early_access/builder/__init__.py +0 -30
- relationalai/early_access/builder/builder/__init__.py +0 -35
- relationalai/early_access/builder/snowflake/__init__.py +0 -12
- relationalai/early_access/builder/std/__init__.py +0 -25
- relationalai/early_access/builder/std/decimals/__init__.py +0 -12
- relationalai/early_access/builder/std/integers/__init__.py +0 -12
- relationalai/early_access/builder/std/math/__init__.py +0 -12
- relationalai/early_access/builder/std/strings/__init__.py +0 -14
- relationalai/early_access/devtools/__init__.py +0 -12
- relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
- relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
- relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
- relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
- relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
- relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
- relationalai/early_access/dsl/bindings/common.py +0 -402
- relationalai/early_access/dsl/bindings/csv.py +0 -170
- relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
- relationalai/early_access/dsl/bindings/snowflake.py +0 -64
- relationalai/early_access/dsl/codegen/binder.py +0 -411
- relationalai/early_access/dsl/codegen/common.py +0 -79
- relationalai/early_access/dsl/codegen/helpers.py +0 -23
- relationalai/early_access/dsl/codegen/relations.py +0 -700
- relationalai/early_access/dsl/codegen/weaver.py +0 -417
- relationalai/early_access/dsl/core/builders/__init__.py +0 -47
- relationalai/early_access/dsl/core/builders/logic.py +0 -19
- relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
- relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
- relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
- relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
- relationalai/early_access/dsl/core/context.py +0 -13
- relationalai/early_access/dsl/core/cset.py +0 -132
- relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
- relationalai/early_access/dsl/core/exprs/relational.py +0 -18
- relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
- relationalai/early_access/dsl/core/instances.py +0 -44
- relationalai/early_access/dsl/core/logic/__init__.py +0 -193
- relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
- relationalai/early_access/dsl/core/logic/exists.py +0 -223
- relationalai/early_access/dsl/core/logic/helper.py +0 -163
- relationalai/early_access/dsl/core/namespaces.py +0 -32
- relationalai/early_access/dsl/core/relations.py +0 -276
- relationalai/early_access/dsl/core/rules.py +0 -112
- relationalai/early_access/dsl/core/std/__init__.py +0 -45
- relationalai/early_access/dsl/core/temporal/recall.py +0 -6
- relationalai/early_access/dsl/core/types/__init__.py +0 -270
- relationalai/early_access/dsl/core/types/concepts.py +0 -128
- relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
- relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
- relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
- relationalai/early_access/dsl/core/types/standard.py +0 -92
- relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
- relationalai/early_access/dsl/core/types/variables.py +0 -203
- relationalai/early_access/dsl/ir/compiler.py +0 -318
- relationalai/early_access/dsl/ir/executor.py +0 -260
- relationalai/early_access/dsl/ontologies/constraints.py +0 -88
- relationalai/early_access/dsl/ontologies/export.py +0 -30
- relationalai/early_access/dsl/ontologies/models.py +0 -453
- relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
- relationalai/early_access/dsl/ontologies/readings.py +0 -60
- relationalai/early_access/dsl/ontologies/relationships.py +0 -322
- relationalai/early_access/dsl/ontologies/roles.py +0 -87
- relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
- relationalai/early_access/dsl/orm/constraints.py +0 -438
- relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
- relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
- relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
- relationalai/early_access/dsl/orm/measures/measures.py +0 -299
- relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
- relationalai/early_access/dsl/orm/models.py +0 -256
- relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
- relationalai/early_access/dsl/orm/printer.py +0 -469
- relationalai/early_access/dsl/orm/reasoners.py +0 -480
- relationalai/early_access/dsl/orm/relations.py +0 -19
- relationalai/early_access/dsl/orm/relationships.py +0 -251
- relationalai/early_access/dsl/orm/types.py +0 -42
- relationalai/early_access/dsl/orm/utils.py +0 -79
- relationalai/early_access/dsl/orm/verb.py +0 -204
- relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
- relationalai/early_access/dsl/relations.py +0 -170
- relationalai/early_access/dsl/rulesets.py +0 -69
- relationalai/early_access/dsl/schemas/__init__.py +0 -450
- relationalai/early_access/dsl/schemas/builder.py +0 -48
- relationalai/early_access/dsl/schemas/comp_names.py +0 -51
- relationalai/early_access/dsl/schemas/components.py +0 -203
- relationalai/early_access/dsl/schemas/contexts.py +0 -156
- relationalai/early_access/dsl/schemas/exprs.py +0 -89
- relationalai/early_access/dsl/schemas/fragments.py +0 -464
- relationalai/early_access/dsl/serialization.py +0 -79
- relationalai/early_access/dsl/serialize/exporter.py +0 -163
- relationalai/early_access/dsl/snow/api.py +0 -105
- relationalai/early_access/dsl/snow/common.py +0 -76
- relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
- relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
- relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
- relationalai/early_access/dsl/types/__init__.py +0 -40
- relationalai/early_access/dsl/types/concepts.py +0 -12
- relationalai/early_access/dsl/types/entities.py +0 -135
- relationalai/early_access/dsl/types/values.py +0 -17
- relationalai/early_access/dsl/utils.py +0 -102
- relationalai/early_access/graphs/__init__.py +0 -13
- relationalai/early_access/lqp/__init__.py +0 -12
- relationalai/early_access/lqp/compiler/__init__.py +0 -12
- relationalai/early_access/lqp/constructors/__init__.py +0 -18
- relationalai/early_access/lqp/executor/__init__.py +0 -12
- relationalai/early_access/lqp/ir/__init__.py +0 -12
- relationalai/early_access/lqp/passes/__init__.py +0 -12
- relationalai/early_access/lqp/pragmas/__init__.py +0 -12
- relationalai/early_access/lqp/primitives/__init__.py +0 -12
- relationalai/early_access/lqp/types/__init__.py +0 -12
- relationalai/early_access/lqp/utils/__init__.py +0 -12
- relationalai/early_access/lqp/validators/__init__.py +0 -12
- relationalai/early_access/metamodel/__init__.py +0 -58
- relationalai/early_access/metamodel/builtins/__init__.py +0 -12
- relationalai/early_access/metamodel/compiler/__init__.py +0 -12
- relationalai/early_access/metamodel/dependency/__init__.py +0 -12
- relationalai/early_access/metamodel/factory/__init__.py +0 -17
- relationalai/early_access/metamodel/helpers/__init__.py +0 -12
- relationalai/early_access/metamodel/ir/__init__.py +0 -14
- relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
- relationalai/early_access/metamodel/typer/__init__.py +0 -3
- relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
- relationalai/early_access/metamodel/types/__init__.py +0 -15
- relationalai/early_access/metamodel/util/__init__.py +0 -15
- relationalai/early_access/metamodel/visitor/__init__.py +0 -12
- relationalai/early_access/rel/__init__.py +0 -12
- relationalai/early_access/rel/executor/__init__.py +0 -12
- relationalai/early_access/rel/rel_utils/__init__.py +0 -12
- relationalai/early_access/rel/rewrite/__init__.py +0 -7
- relationalai/early_access/solvers/__init__.py +0 -19
- relationalai/early_access/sql/__init__.py +0 -11
- relationalai/early_access/sql/executor/__init__.py +0 -3
- relationalai/early_access/sql/rewrite/__init__.py +0 -3
- relationalai/early_access/tests/logging/__init__.py +0 -12
- relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
- relationalai/early_access/tests/utils/__init__.py +0 -12
- relationalai/environments/__init__.py +0 -35
- relationalai/environments/base.py +0 -381
- relationalai/environments/colab.py +0 -14
- relationalai/environments/generic.py +0 -71
- relationalai/environments/ipython.py +0 -68
- relationalai/environments/jupyter.py +0 -9
- relationalai/environments/snowbook.py +0 -169
- relationalai/errors.py +0 -2496
- relationalai/experimental/SF.py +0 -38
- relationalai/experimental/inspect.py +0 -47
- relationalai/experimental/pathfinder/__init__.py +0 -158
- relationalai/experimental/pathfinder/api.py +0 -160
- relationalai/experimental/pathfinder/automaton.py +0 -584
- relationalai/experimental/pathfinder/bridge.py +0 -226
- relationalai/experimental/pathfinder/compiler.py +0 -416
- relationalai/experimental/pathfinder/datalog.py +0 -214
- relationalai/experimental/pathfinder/diagnostics.py +0 -56
- relationalai/experimental/pathfinder/filter.py +0 -236
- relationalai/experimental/pathfinder/glushkov.py +0 -439
- relationalai/experimental/pathfinder/options.py +0 -265
- relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +0 -1951
- relationalai/experimental/pathfinder/rpq.py +0 -344
- relationalai/experimental/pathfinder/transition.py +0 -200
- relationalai/experimental/pathfinder/utils.py +0 -26
- relationalai/experimental/paths/README.md +0 -107
- relationalai/experimental/paths/api.py +0 -143
- relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
- relationalai/experimental/paths/code_organization.md +0 -2
- relationalai/experimental/paths/examples/Movies.ipynb +0 -16328
- relationalai/experimental/paths/examples/basic_example.py +0 -40
- relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
- relationalai/experimental/paths/examples/movie_example.py +0 -77
- relationalai/experimental/paths/examples/movies_data/actedin.csv +0 -193
- relationalai/experimental/paths/examples/movies_data/directed.csv +0 -45
- relationalai/experimental/paths/examples/movies_data/follows.csv +0 -7
- relationalai/experimental/paths/examples/movies_data/movies.csv +0 -39
- relationalai/experimental/paths/examples/movies_data/person.csv +0 -134
- relationalai/experimental/paths/examples/movies_data/produced.csv +0 -16
- relationalai/experimental/paths/examples/movies_data/ratings.csv +0 -10
- relationalai/experimental/paths/examples/movies_data/wrote.csv +0 -11
- relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
- relationalai/experimental/paths/examples/paths_example.py +0 -116
- relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
- relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
- relationalai/experimental/paths/graph.py +0 -185
- relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
- relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
- relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
- relationalai/experimental/paths/path_algorithms/single.py +0 -59
- relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
- relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
- relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
- relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
- relationalai/experimental/paths/path_algorithms/usp.py +0 -150
- relationalai/experimental/paths/product_graph.py +0 -93
- relationalai/experimental/paths/rpq/automaton.py +0 -584
- relationalai/experimental/paths/rpq/diagnostics.py +0 -56
- relationalai/experimental/paths/rpq/rpq.py +0 -378
- relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
- relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
- relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
- relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
- relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
- relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
- relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
- relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
- relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
- relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
- relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
- relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
- relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
- relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
- relationalai/experimental/paths/tree_agg.py +0 -168
- relationalai/experimental/paths/utilities/iterators.py +0 -27
- relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
- relationalai/experimental/solvers.py +0 -1095
- relationalai/loaders/csv.py +0 -195
- relationalai/loaders/loader.py +0 -177
- relationalai/loaders/types.py +0 -23
- relationalai/rel_emitter.py +0 -373
- relationalai/rel_utils.py +0 -185
- relationalai/semantics/designs/query_builder/identify_by.md +0 -106
- relationalai/semantics/devtools/benchmark_lqp.py +0 -535
- relationalai/semantics/devtools/compilation_manager.py +0 -294
- relationalai/semantics/devtools/extract_lqp.py +0 -110
- relationalai/semantics/internal/internal.py +0 -3785
- relationalai/semantics/internal/snowflake.py +0 -329
- relationalai/semantics/lqp/README.md +0 -34
- relationalai/semantics/lqp/algorithms.py +0 -173
- relationalai/semantics/lqp/builtins.py +0 -213
- relationalai/semantics/lqp/compiler.py +0 -22
- relationalai/semantics/lqp/constructors.py +0 -68
- relationalai/semantics/lqp/executor.py +0 -518
- relationalai/semantics/lqp/export_rewriter.py +0 -40
- relationalai/semantics/lqp/intrinsics.py +0 -24
- relationalai/semantics/lqp/ir.py +0 -150
- relationalai/semantics/lqp/model2lqp.py +0 -1056
- relationalai/semantics/lqp/passes.py +0 -38
- relationalai/semantics/lqp/primitives.py +0 -252
- relationalai/semantics/lqp/result_helpers.py +0 -266
- relationalai/semantics/lqp/rewrite/__init__.py +0 -32
- relationalai/semantics/lqp/rewrite/algorithm.py +0 -385
- relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -69
- relationalai/semantics/lqp/rewrite/cdc.py +0 -216
- relationalai/semantics/lqp/rewrite/constants_to_vars.py +0 -70
- relationalai/semantics/lqp/rewrite/deduplicate_vars.py +0 -104
- relationalai/semantics/lqp/rewrite/eliminate_data.py +0 -108
- relationalai/semantics/lqp/rewrite/extract_common.py +0 -340
- relationalai/semantics/lqp/rewrite/extract_keys.py +0 -577
- relationalai/semantics/lqp/rewrite/flatten_script.py +0 -301
- relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
- relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -348
- relationalai/semantics/lqp/rewrite/period_math.py +0 -77
- relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -339
- relationalai/semantics/lqp/rewrite/splinter.py +0 -76
- relationalai/semantics/lqp/rewrite/unify_definitions.py +0 -323
- relationalai/semantics/lqp/types.py +0 -101
- relationalai/semantics/lqp/utils.py +0 -170
- relationalai/semantics/lqp/validators.py +0 -70
- relationalai/semantics/metamodel/compiler.py +0 -134
- relationalai/semantics/metamodel/dependency.py +0 -880
- relationalai/semantics/metamodel/executor.py +0 -78
- relationalai/semantics/metamodel/factory.py +0 -287
- relationalai/semantics/metamodel/helpers.py +0 -368
- relationalai/semantics/metamodel/ir.py +0 -924
- relationalai/semantics/metamodel/rewrite/__init__.py +0 -8
- relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
- relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -220
- relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
- relationalai/semantics/metamodel/rewrite/flatten.py +0 -590
- relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -256
- relationalai/semantics/metamodel/rewrite/handle_aggregations_and_ranks.py +0 -237
- relationalai/semantics/metamodel/typer/checker.py +0 -355
- relationalai/semantics/metamodel/typer/typer.py +0 -1396
- relationalai/semantics/metamodel/util.py +0 -506
- relationalai/semantics/metamodel/visitor.py +0 -945
- relationalai/semantics/reasoners/__init__.py +0 -10
- relationalai/semantics/reasoners/graph/README.md +0 -620
- relationalai/semantics/reasoners/graph/__init__.py +0 -37
- relationalai/semantics/reasoners/graph/core.py +0 -9019
- relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +0 -797
- relationalai/semantics/reasoners/graph/tests/README.md +0 -21
- relationalai/semantics/reasoners/optimization/__init__.py +0 -68
- relationalai/semantics/reasoners/optimization/common.py +0 -88
- relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
- relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1407
- relationalai/semantics/rel/builtins.py +0 -40
- relationalai/semantics/rel/compiler.py +0 -994
- relationalai/semantics/rel/executor.py +0 -363
- relationalai/semantics/rel/rel.py +0 -482
- relationalai/semantics/rel/rel_utils.py +0 -276
- relationalai/semantics/snowflake/__init__.py +0 -3
- relationalai/semantics/sql/compiler.py +0 -2503
- relationalai/semantics/sql/executor/duck_db.py +0 -52
- relationalai/semantics/sql/executor/result_helpers.py +0 -64
- relationalai/semantics/sql/executor/snowflake.py +0 -149
- relationalai/semantics/sql/rewrite/denormalize.py +0 -222
- relationalai/semantics/sql/rewrite/double_negation.py +0 -49
- relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
- relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
- relationalai/semantics/sql/sql.py +0 -504
- relationalai/semantics/std/pragmas.py +0 -11
- relationalai/semantics/std/std.py +0 -14
- relationalai/semantics/tests/lqp/algorithms.py +0 -345
- relationalai/semantics/tests/test_snapshot_abstract.py +0 -144
- relationalai/semantics/tests/test_snapshot_base.py +0 -9
- relationalai/semantics/tests/utils.py +0 -46
- relationalai/std/__init__.py +0 -70
- relationalai/tools/cli.py +0 -2089
- relationalai/tools/cli_controls.py +0 -1975
- relationalai/tools/cli_helpers.py +0 -802
- relationalai/tools/debugger_client.py +0 -109
- relationalai/tools/debugger_server.py +0 -302
- relationalai/tools/dev.py +0 -685
- relationalai/tools/notes +0 -7
- relationalai/tools/qb_debugger.py +0 -425
- relationalai/tools/txn_progress.py +0 -188
- relationalai/util/clean_up_databases.py +0 -95
- relationalai/util/list_databases.py +0 -9
- relationalai/util/otel_configuration.py +0 -26
- relationalai/util/otel_handler.py +0 -484
- relationalai/util/snowflake_handler.py +0 -88
- relationalai/util/span_format_test.py +0 -43
- relationalai/util/span_tracker.py +0 -207
- relationalai/util/spans_file_handler.py +0 -72
- relationalai/util/tracing_handler.py +0 -34
- relationalai-0.13.5.dist-info/METADATA +0 -74
- relationalai-0.13.5.dist-info/RECORD +0 -473
- relationalai-0.13.5.dist-info/WHEEL +0 -4
- relationalai-0.13.5.dist-info/entry_points.txt +0 -3
- relationalai-0.13.5.dist-info/licenses/LICENSE +0 -202
- relationalai_test_util/__init__.py +0 -4
- relationalai_test_util/fixtures.py +0 -233
- relationalai_test_util/snapshot.py +0 -252
- relationalai_test_util/traceback.py +0 -118
- /relationalai/{analysis → semantics/frontend}/__init__.py +0 -0
- /relationalai/{auth/__init__.py → semantics/metamodel/metamodel_compiler.py} +0 -0
- /relationalai/{early_access → shims}/__init__.py +0 -0
- {relationalai/early_access/dsl/adapters → v0/relationalai/analysis}/__init__.py +0 -0
- {relationalai → v0/relationalai}/analysis/mechanistic.py +0 -0
- {relationalai → v0/relationalai}/analysis/whynot.py +0 -0
- {relationalai/early_access/dsl/adapters/orm → v0/relationalai/auth}/__init__.py +0 -0
- {relationalai → v0/relationalai}/auth/jwt_generator.py +0 -0
- {relationalai → v0/relationalai}/auth/oauth_callback_server.py +0 -0
- {relationalai → v0/relationalai}/auth/token_handler.py +0 -0
- {relationalai → v0/relationalai}/auth/util.py +0 -0
- {relationalai/clients/resources/snowflake → v0/relationalai/clients}/cache_store.py +0 -0
- {relationalai → v0/relationalai}/compiler.py +0 -0
- {relationalai → v0/relationalai}/dependencies.py +0 -0
- {relationalai → v0/relationalai}/docutils.py +0 -0
- {relationalai/early_access/dsl/adapters/owl → v0/relationalai/early_access}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/__init__.py +0 -0
- {relationalai/early_access/dsl/bindings → v0/relationalai/early_access/dsl/adapters}/__init__.py +0 -0
- {relationalai/early_access/dsl/bindings/legacy → v0/relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
- {relationalai/early_access/dsl/codegen → v0/relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
- {relationalai/early_access/dsl/core/temporal → v0/relationalai/early_access/dsl/bindings}/__init__.py +0 -0
- {relationalai/early_access/dsl/ir → v0/relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
- {relationalai/early_access/dsl/ontologies → v0/relationalai/early_access/dsl/codegen}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/constants.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/stack.py +0 -0
- {relationalai/early_access/dsl/orm → v0/relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/core/utils.py +0 -0
- {relationalai/early_access/dsl/orm/measures → v0/relationalai/early_access/dsl/ir}/__init__.py +0 -0
- {relationalai/early_access/dsl/physical_metadata → v0/relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
- {relationalai/early_access/dsl/serialize → v0/relationalai/early_access/dsl/orm}/__init__.py +0 -0
- {relationalai/early_access/dsl/snow → v0/relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
- {relationalai/loaders → v0/relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
- {relationalai/semantics/tests → v0/relationalai/early_access/dsl/serialize}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
- {relationalai → v0/relationalai}/early_access/dsl/serialize/model.py +0 -0
- {relationalai/semantics/tests/lqp → v0/relationalai/early_access/dsl/snow}/__init__.py +0 -0
- {relationalai → v0/relationalai}/early_access/tests/__init__.py +0 -0
- {relationalai → v0/relationalai}/environments/ci.py +0 -0
- {relationalai → v0/relationalai}/environments/hex.py +0 -0
- {relationalai → v0/relationalai}/environments/terminal.py +0 -0
- {relationalai → v0/relationalai}/experimental/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/graphs.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/filter.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/glushkov.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/rpq/transition.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/utilities/__init__.py +0 -0
- {relationalai → v0/relationalai}/experimental/paths/utilities/utilities.py +0 -0
- {relationalai/tools → v0/relationalai/loaders}/__init__.py +0 -0
- {relationalai → v0/relationalai}/metagen.py +0 -0
- {relationalai → v0/relationalai}/metamodel.py +0 -0
- {relationalai → v0/relationalai}/rel.py +0 -0
- {relationalai → v0/relationalai}/semantics/devtools/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/internal/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/internal/annotations.py +0 -0
- {relationalai → v0/relationalai}/semantics/lqp/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/lqp/pragmas.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/dataflow.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/typer/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/metamodel/types.py +0 -0
- {relationalai → v0/relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/rel/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/executor/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/sql/rewrite/__init__.py +0 -0
- {relationalai → v0/relationalai}/semantics/tests/logging.py +0 -0
- {relationalai → v0/relationalai}/std/aggregates.py +0 -0
- {relationalai → v0/relationalai}/std/dates.py +0 -0
- {relationalai → v0/relationalai}/std/graphs.py +0 -0
- {relationalai → v0/relationalai}/std/inspect.py +0 -0
- {relationalai → v0/relationalai}/std/math.py +0 -0
- {relationalai → v0/relationalai}/std/re.py +0 -0
- {relationalai → v0/relationalai}/std/strings.py +0 -0
- {relationalai → v0/relationalai}/tools/cleanup_snapshots.py +0 -0
- {relationalai → v0/relationalai}/tools/constants.py +0 -0
- {relationalai → v0/relationalai}/tools/query_utils.py +0 -0
- {relationalai → v0/relationalai}/tools/snapshot_viewer.py +0 -0
- {relationalai → v0/relationalai}/util/__init__.py +0 -0
- {relationalai → v0/relationalai}/util/constants.py +0 -0
- {relationalai → v0/relationalai}/util/graph.py +0 -0
- {relationalai → v0/relationalai}/util/timeout.py +0 -0
relationalai/errors.py
DELETED
|
@@ -1,2496 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
import sys
|
|
3
|
-
from types import TracebackType
|
|
4
|
-
import ast
|
|
5
|
-
import io
|
|
6
|
-
import json
|
|
7
|
-
import re
|
|
8
|
-
import textwrap
|
|
9
|
-
from typing import Any, List, Literal, NamedTuple, Tuple
|
|
10
|
-
|
|
11
|
-
import rich.markup
|
|
12
|
-
from rich.console import Console
|
|
13
|
-
from rich.text import Text
|
|
14
|
-
from rich.table import Table
|
|
15
|
-
from rich import box
|
|
16
|
-
from enum import Enum
|
|
17
|
-
import contextlib
|
|
18
|
-
import contextvars
|
|
19
|
-
|
|
20
|
-
from .metamodel import Action, Task
|
|
21
|
-
from .environments import runtime_env, IPythonEnvironment, SnowbookEnvironment
|
|
22
|
-
from . import debugging
|
|
23
|
-
from .tools.constants import SHOW_FULL_TRACES
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#--------------------------------------------------
|
|
27
|
-
# Print helpers
|
|
28
|
-
#--------------------------------------------------
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
_current_console = contextvars.ContextVar('current_console', default=None)
|
|
32
|
-
|
|
33
|
-
def get_console(*args, **kwargs):
|
|
34
|
-
current = _current_console.get()
|
|
35
|
-
if current is not None:
|
|
36
|
-
return current
|
|
37
|
-
return Console(*args, **kwargs)
|
|
38
|
-
|
|
39
|
-
@contextlib.contextmanager
|
|
40
|
-
def using_console(console):
|
|
41
|
-
token = _current_console.set(console)
|
|
42
|
-
try:
|
|
43
|
-
yield
|
|
44
|
-
finally:
|
|
45
|
-
_current_console.reset(token)
|
|
46
|
-
|
|
47
|
-
@contextlib.contextmanager
|
|
48
|
-
def record(retain_buffer = False):
|
|
49
|
-
buffer = io.StringIO()
|
|
50
|
-
console = Console(record=True, file=buffer)
|
|
51
|
-
with using_console(console):
|
|
52
|
-
try:
|
|
53
|
-
yield (console, buffer)
|
|
54
|
-
finally:
|
|
55
|
-
if not retain_buffer:
|
|
56
|
-
buffer.close()
|
|
57
|
-
|
|
58
|
-
def rich_str(item:Any, style:str|None = None) -> str:
|
|
59
|
-
output = io.StringIO()
|
|
60
|
-
console = Console(file=output, force_terminal=True)
|
|
61
|
-
console.print(item, style=style)
|
|
62
|
-
return output.getvalue()
|
|
63
|
-
|
|
64
|
-
def body_text(console, body:str):
|
|
65
|
-
body = textwrap.dedent(body)
|
|
66
|
-
for line in body.splitlines():
|
|
67
|
-
if not line.startswith(" "):
|
|
68
|
-
console.print(line)
|
|
69
|
-
else:
|
|
70
|
-
console.print(line, soft_wrap=True)
|
|
71
|
-
|
|
72
|
-
def mark_source(source: debugging.SourceInfo|None, start_line:int|None=None, end_line:int|None=None, indent=8, highlight="yellow", highlight_lines = []):
|
|
73
|
-
if source is None:
|
|
74
|
-
return ""
|
|
75
|
-
|
|
76
|
-
final_lines = []
|
|
77
|
-
all_lines = source.source.splitlines()
|
|
78
|
-
source_start = source.source_start_line or source.line or start_line or 0
|
|
79
|
-
start_line = start_line if start_line is not None else source.line
|
|
80
|
-
max_line = source.line + len(all_lines)
|
|
81
|
-
end_line = end_line if end_line is not None else max_line
|
|
82
|
-
line_number_len = len(str(max_line))
|
|
83
|
-
for ix, line in enumerate(source.source.splitlines()):
|
|
84
|
-
cur = line
|
|
85
|
-
cur_indent = indent if ix > 0 else 0
|
|
86
|
-
line_number = source_start + ix
|
|
87
|
-
color = "dim white"
|
|
88
|
-
if (line_number >= start_line and line_number <= end_line) or line_number in highlight_lines:
|
|
89
|
-
color = highlight + " bold"
|
|
90
|
-
cur = f"{' '*cur_indent}[{color}] {line_number :>{line_number_len}} | {cur}[/{color}]"
|
|
91
|
-
final_lines.append(cur)
|
|
92
|
-
return "\n".join(final_lines)
|
|
93
|
-
|
|
94
|
-
def print_source_error(source, name:str, content:str, color="red"):
|
|
95
|
-
fixed_content_length = len(name) + len(source.file) + len(str(source.line)) + 2 # 2 for the spaces around the dash
|
|
96
|
-
num_dashes = 74 - fixed_content_length
|
|
97
|
-
dashes = '-' * num_dashes
|
|
98
|
-
console = get_console(width=80, force_jupyter=False, stderr=True)
|
|
99
|
-
console.print("\n")
|
|
100
|
-
console.print(f"[{color}]--- {name} {dashes} {source.file}: {source.line}")
|
|
101
|
-
console.print()
|
|
102
|
-
body_text(console, content)
|
|
103
|
-
console.print()
|
|
104
|
-
console.print(f'[{color}]{"-" * 80}')
|
|
105
|
-
console.print()
|
|
106
|
-
|
|
107
|
-
def print_error(name:str, content:str, color="red"):
|
|
108
|
-
fixed_content_length = len(name) + 2 # 2 for the spaces around the dash
|
|
109
|
-
num_dashes = 76 - fixed_content_length
|
|
110
|
-
dashes = '-' * num_dashes
|
|
111
|
-
console = get_console(width=80, force_jupyter=False, stderr=True)
|
|
112
|
-
console.print("\n")
|
|
113
|
-
console.print(f"[{color}]--- {name} {dashes}")
|
|
114
|
-
console.print()
|
|
115
|
-
body_text(console, content)
|
|
116
|
-
console.print()
|
|
117
|
-
console.print(f'[{color}]{"-" * 80}')
|
|
118
|
-
console.print()
|
|
119
|
-
|
|
120
|
-
def print_error_name(name:str, color="red"):
|
|
121
|
-
console = get_console(width=80, force_jupyter=False, stderr=True)
|
|
122
|
-
console.print("\n")
|
|
123
|
-
console.print(f'[{color}]{"-" * 80}')
|
|
124
|
-
console.print()
|
|
125
|
-
body_text(console, name)
|
|
126
|
-
console.print()
|
|
127
|
-
console.print(f'[{color}]{"-" * 80}')
|
|
128
|
-
console.print()
|
|
129
|
-
|
|
130
|
-
SuggestionEnv = Literal["cli", "notebook", "python"]
|
|
131
|
-
class Suggestion(NamedTuple):
|
|
132
|
-
env: SuggestionEnv
|
|
133
|
-
cmd: str
|
|
134
|
-
style: str|None = "green"
|
|
135
|
-
|
|
136
|
-
def suggest(suggestions: list[Suggestion], prefix = "with the following command", prefix_multi: str|None = None):
|
|
137
|
-
"""Format a list of suggestions, adapted to the current runtime environment."""
|
|
138
|
-
if prefix_multi is None:
|
|
139
|
-
prefix_multi = prefix + "s"
|
|
140
|
-
|
|
141
|
-
relevant: list[Suggestion] = []
|
|
142
|
-
for suggestion in suggestions:
|
|
143
|
-
relevant += adapt_to_env(suggestion)
|
|
144
|
-
|
|
145
|
-
if len(relevant) == 1:
|
|
146
|
-
return "\n".join([
|
|
147
|
-
prefix + ":",
|
|
148
|
-
"",
|
|
149
|
-
fmt_suggestion(relevant[0]),
|
|
150
|
-
])
|
|
151
|
-
else:
|
|
152
|
-
return "\n".join([
|
|
153
|
-
prefix_multi + ":",
|
|
154
|
-
"",
|
|
155
|
-
"\n\n".join(fmt_suggestion(suggestion) for suggestion in relevant)
|
|
156
|
-
])
|
|
157
|
-
|
|
158
|
-
def fmt_suggestion(suggestion: Suggestion) -> str:
|
|
159
|
-
"""Format a single styled suggestion."""
|
|
160
|
-
cmd = textwrap.dedent(suggestion.cmd)
|
|
161
|
-
return "\n".join(f"[{suggestion.style}]{line}[/{suggestion.style}]" for line in cmd.splitlines()) if suggestion.style else cmd
|
|
162
|
-
|
|
163
|
-
def adapt_to_env(suggestion: Suggestion) -> list[Suggestion]:
|
|
164
|
-
"""Adapt a suggestion to work in the current runtime environment if possible, or exclude it if not."""
|
|
165
|
-
if suggestion.env == "cli":
|
|
166
|
-
if isinstance(runtime_env, SnowbookEnvironment) and runtime_env.runner == "warehouse":
|
|
167
|
-
return []
|
|
168
|
-
if isinstance(runtime_env, (IPythonEnvironment, SnowbookEnvironment)):
|
|
169
|
-
return [Suggestion("notebook", "\n".join(f"! {subcmd}" for subcmd in partition_by_indent(textwrap.dedent(suggestion.cmd))), suggestion.style)]
|
|
170
|
-
return [suggestion]
|
|
171
|
-
else:
|
|
172
|
-
return [suggestion]
|
|
173
|
-
|
|
174
|
-
def partition_by_indent(text):
|
|
175
|
-
# Split on newlines followed by non-whitespace
|
|
176
|
-
groups = re.split(r'\n(?=\S)', text)
|
|
177
|
-
# Strip any trailing whitespace from each group
|
|
178
|
-
return [group.rstrip() for group in groups]
|
|
179
|
-
|
|
180
|
-
#--------------------------------------------------
|
|
181
|
-
# Transformers
|
|
182
|
-
#--------------------------------------------------
|
|
183
|
-
|
|
184
|
-
class IfToWithTransformer(ast.NodeTransformer):
|
|
185
|
-
def visit_If(self, node):
|
|
186
|
-
with_node = ast.With(
|
|
187
|
-
items=[ast.withitem(context_expr=node.test, optional_vars=None)],
|
|
188
|
-
body=node.body,
|
|
189
|
-
lineno=node.lineno,
|
|
190
|
-
type_comment=None)
|
|
191
|
-
return with_node
|
|
192
|
-
|
|
193
|
-
class WithDynamic(ast.NodeTransformer):
|
|
194
|
-
def visit_With(self, node):
|
|
195
|
-
content = ast.unparse(node.items[0].context_expr).replace(")", "dynamic=True)")
|
|
196
|
-
with_node = ast.With(
|
|
197
|
-
items=[ast.withitem(context_expr=ast.Name(id=content), optional_vars=node.items[0].optional_vars)],
|
|
198
|
-
body=[],
|
|
199
|
-
lineno=node.lineno,
|
|
200
|
-
type_comment=None)
|
|
201
|
-
return with_node
|
|
202
|
-
|
|
203
|
-
class SetToMethod(ast.NodeTransformer):
|
|
204
|
-
def visit_Assign(self, node):
|
|
205
|
-
if isinstance(node.targets[0], ast.Attribute):
|
|
206
|
-
keyword = ast.keyword(arg=node.targets[0].attr, value=node.value)
|
|
207
|
-
return ast.Expr(value=ast.Call(
|
|
208
|
-
func=ast.Attribute(value=node.targets[0].value, attr="set", ctx=ast.Load()),
|
|
209
|
-
args=[],
|
|
210
|
-
keywords=[keyword],
|
|
211
|
-
lineno=node.lineno
|
|
212
|
-
))
|
|
213
|
-
return node
|
|
214
|
-
|
|
215
|
-
class AssignToCompare(ast.NodeTransformer):
|
|
216
|
-
def visit_Assign(self, node):
|
|
217
|
-
if isinstance(node.targets[0], ast.Attribute) and len(node.targets) == 1:
|
|
218
|
-
compare_node = ast.Compare(
|
|
219
|
-
left=node.targets[0],
|
|
220
|
-
ops=[ast.Eq()],
|
|
221
|
-
comparators=[node.value]
|
|
222
|
-
)
|
|
223
|
-
expr_node = ast.Expr(value=compare_node)
|
|
224
|
-
ast.copy_location(expr_node, node)
|
|
225
|
-
|
|
226
|
-
return expr_node
|
|
227
|
-
return node
|
|
228
|
-
|
|
229
|
-
class PropertyNameReplacer(ast.NodeTransformer):
|
|
230
|
-
def __init__(self, old_name, new_name):
|
|
231
|
-
self.old_name = old_name
|
|
232
|
-
self.new_name = new_name
|
|
233
|
-
|
|
234
|
-
def visit_Attribute(self, node):
|
|
235
|
-
# Check if the attribute name matches the old name
|
|
236
|
-
if isinstance(node.attr, str) and node.attr == self.old_name:
|
|
237
|
-
# Replace the attribute name with the new name
|
|
238
|
-
node.attr = self.new_name
|
|
239
|
-
return node
|
|
240
|
-
|
|
241
|
-
def visit_Name(self, node):
|
|
242
|
-
# Check if the variable name matches the old name
|
|
243
|
-
if isinstance(node.id, str) and node.id == self.old_name:
|
|
244
|
-
# Replace the variable name with the new name
|
|
245
|
-
node.id = self.new_name
|
|
246
|
-
return node
|
|
247
|
-
|
|
248
|
-
#--------------------------------------------------
|
|
249
|
-
# Finders
|
|
250
|
-
#--------------------------------------------------
|
|
251
|
-
|
|
252
|
-
class PropertyFinder(ast.NodeVisitor):
|
|
253
|
-
def __init__(self, start_line, properties):
|
|
254
|
-
self.errors = []
|
|
255
|
-
self.start_line = start_line
|
|
256
|
-
self.properties = properties
|
|
257
|
-
self.found_properties_lines = [] # To store lines where properties are found
|
|
258
|
-
self.dynamic_properties = [] # To store dynamic properties
|
|
259
|
-
|
|
260
|
-
def to_line_numbers(self, node):
|
|
261
|
-
return (node.lineno, node.end_lineno)
|
|
262
|
-
|
|
263
|
-
def visit_Attribute(self, node):
|
|
264
|
-
if node.attr in self.properties:
|
|
265
|
-
line_numbers = self.to_line_numbers(node)
|
|
266
|
-
if line_numbers[0] >= self.start_line:
|
|
267
|
-
self.found_properties_lines.append(node.lineno)
|
|
268
|
-
self.generic_visit(node)
|
|
269
|
-
|
|
270
|
-
def visit_Call(self, node):
|
|
271
|
-
# Check if this is a call to 'getattr'
|
|
272
|
-
if (isinstance(node.func, ast.Name) and node.func.id == 'getattr' and
|
|
273
|
-
len(node.args) >= 2):
|
|
274
|
-
if isinstance(node.args[1], ast.Str):
|
|
275
|
-
property_name = node.args[1].s
|
|
276
|
-
if property_name in self.properties:
|
|
277
|
-
line_numbers = self.to_line_numbers(node)
|
|
278
|
-
if line_numbers[0] >= self.start_line:
|
|
279
|
-
self.found_properties_lines.append(node.lineno)
|
|
280
|
-
else:
|
|
281
|
-
line_numbers = self.to_line_numbers(node)
|
|
282
|
-
if line_numbers[0] >= self.start_line:
|
|
283
|
-
self.dynamic_properties.append(node.lineno)
|
|
284
|
-
self.generic_visit(node)
|
|
285
|
-
|
|
286
|
-
#--------------------------------------------------
|
|
287
|
-
# Metaclass
|
|
288
|
-
#--------------------------------------------------
|
|
289
|
-
|
|
290
|
-
class RAIPostInitMeta(type):
|
|
291
|
-
def __call__(cls, *args, **kwargs):
|
|
292
|
-
instance = super().__call__(*args, **kwargs)
|
|
293
|
-
instance.__post_init__()
|
|
294
|
-
return instance
|
|
295
|
-
|
|
296
|
-
# --------------------------------------------------
|
|
297
|
-
# RAIException
|
|
298
|
-
# --------------------------------------------------
|
|
299
|
-
|
|
300
|
-
class RAIException(Exception, metaclass=RAIPostInitMeta):
|
|
301
|
-
def __init__(self, message, name=None, content=None, source: debugging.SourceInfo|None=None, **kwargs):
|
|
302
|
-
super().__init__(message)
|
|
303
|
-
self.name = name
|
|
304
|
-
self.message = message
|
|
305
|
-
self.content = content
|
|
306
|
-
self.source = source
|
|
307
|
-
|
|
308
|
-
# Store any additional keyword arguments as attributes
|
|
309
|
-
for key, value in kwargs.items():
|
|
310
|
-
setattr(self, key, value)
|
|
311
|
-
|
|
312
|
-
def __str__(self):
|
|
313
|
-
return self.message
|
|
314
|
-
|
|
315
|
-
def __post_init__(self):
|
|
316
|
-
self.raw_content = self.strip_rich_tags()
|
|
317
|
-
|
|
318
|
-
def pprint(self):
|
|
319
|
-
if self.source and self.content:
|
|
320
|
-
print_source_error(self.source, self.name or self.message, self.content.strip())
|
|
321
|
-
elif self.content:
|
|
322
|
-
print_error(self.name or self.message, self.content.strip())
|
|
323
|
-
else:
|
|
324
|
-
print_error_name(self.name or self.message)
|
|
325
|
-
|
|
326
|
-
def __eq__(self, other):
|
|
327
|
-
if isinstance(other, RAIException):
|
|
328
|
-
return self.message == other.message and self.__dict__ == other.__dict__
|
|
329
|
-
return False
|
|
330
|
-
|
|
331
|
-
def strip_rich_tags(self):
|
|
332
|
-
if self.content:
|
|
333
|
-
# Use Text.from_markup to remove rich tags
|
|
334
|
-
plain_text = Text.from_markup(self.content).plain
|
|
335
|
-
# Split the content into lines
|
|
336
|
-
lines = plain_text.split('\n')
|
|
337
|
-
# Remove leading empty lines or lines with only whitespace
|
|
338
|
-
while lines and not lines[0].strip():
|
|
339
|
-
lines.pop(0)
|
|
340
|
-
# Strip leading whitespace from the first non-empty line
|
|
341
|
-
if lines:
|
|
342
|
-
lines[0] = lines[0].lstrip()
|
|
343
|
-
# Join the lines back together
|
|
344
|
-
cleaned_content = '\n'.join(lines)
|
|
345
|
-
return cleaned_content
|
|
346
|
-
else:
|
|
347
|
-
return self.message
|
|
348
|
-
|
|
349
|
-
def clone(self, config=None):
|
|
350
|
-
show_full_traces = SHOW_FULL_TRACES
|
|
351
|
-
if config is not None:
|
|
352
|
-
show_full_traces = config.get("show_full_traces", SHOW_FULL_TRACES)
|
|
353
|
-
|
|
354
|
-
if show_full_traces:
|
|
355
|
-
return self
|
|
356
|
-
|
|
357
|
-
try:
|
|
358
|
-
raise AssertionError
|
|
359
|
-
except AssertionError:
|
|
360
|
-
traceback = sys.exc_info()[2]
|
|
361
|
-
back_frame = traceback.tb_frame.f_back if traceback else None
|
|
362
|
-
|
|
363
|
-
if back_frame is None:
|
|
364
|
-
return self.with_traceback(traceback)
|
|
365
|
-
|
|
366
|
-
back_tb = TracebackType(tb_next=None,
|
|
367
|
-
tb_frame=back_frame,
|
|
368
|
-
tb_lasti=back_frame.f_lasti,
|
|
369
|
-
tb_lineno=back_frame.f_lineno)
|
|
370
|
-
return self.with_traceback(back_tb)
|
|
371
|
-
|
|
372
|
-
def message_for_environment(self, message, cli_command, provider_call):
|
|
373
|
-
if isinstance(runtime_env, SnowbookEnvironment):
|
|
374
|
-
return textwrap.dedent(f"""
|
|
375
|
-
{message} with the following call:
|
|
376
|
-
|
|
377
|
-
[green]{provider_call}[/green]
|
|
378
|
-
""")
|
|
379
|
-
else:
|
|
380
|
-
return textwrap.dedent(f"""
|
|
381
|
-
{message} with either of the following commands:
|
|
382
|
-
|
|
383
|
-
[green]{cli_command}[/green]
|
|
384
|
-
|
|
385
|
-
[green]{provider_call}[/green]
|
|
386
|
-
""")
|
|
387
|
-
|
|
388
|
-
class NonExistentConfigFileError(RAIException):
|
|
389
|
-
def __init__(self, file_name: str):
|
|
390
|
-
self.file_name = file_name
|
|
391
|
-
self.message = f"The specified configuration file '{file_name}' does not exist."
|
|
392
|
-
self.name = "Configuration file not found"
|
|
393
|
-
|
|
394
|
-
# Format the content and raw_content
|
|
395
|
-
self.content = self.format_message()
|
|
396
|
-
|
|
397
|
-
super().__init__(self.message, self.name, self.content)
|
|
398
|
-
|
|
399
|
-
def format_message(self):
|
|
400
|
-
return textwrap.dedent(f"""
|
|
401
|
-
Configuration file '{self.file_name}' does not exist.
|
|
402
|
-
|
|
403
|
-
To initialize one, use:
|
|
404
|
-
|
|
405
|
-
[green]rai init[/green]
|
|
406
|
-
""")
|
|
407
|
-
|
|
408
|
-
class InvalidActiveProfileError(RAIException):
|
|
409
|
-
def __init__(self, active_profile_name: str):
|
|
410
|
-
self.active_profile_name = active_profile_name
|
|
411
|
-
self.message = f"The specified active profile '{self.active_profile_name}' set in the config is not found."
|
|
412
|
-
self.name = f"Active profile '{active_profile_name}' not found"
|
|
413
|
-
|
|
414
|
-
# Format the content and raw_content
|
|
415
|
-
self.content = self.format_message()
|
|
416
|
-
|
|
417
|
-
super().__init__(self.message, self.name, self.content)
|
|
418
|
-
|
|
419
|
-
def format_message(self):
|
|
420
|
-
return textwrap.dedent(f"""
|
|
421
|
-
The specified active profile '{self.active_profile_name}' is not found.
|
|
422
|
-
Please check your configuration and ensure that the profile exists.
|
|
423
|
-
|
|
424
|
-
To switch to a different profile, use the following command:
|
|
425
|
-
|
|
426
|
-
[green]rai profile:switch[/green]
|
|
427
|
-
|
|
428
|
-
To create a new profile, use:
|
|
429
|
-
|
|
430
|
-
[green]rai init[/green]
|
|
431
|
-
""")
|
|
432
|
-
|
|
433
|
-
class ModelError(RAIException):
|
|
434
|
-
error_locations = {}
|
|
435
|
-
|
|
436
|
-
def __init__(self, problems):
|
|
437
|
-
super().__init__("Error object added")
|
|
438
|
-
problem = problems[0]
|
|
439
|
-
self.problems = problems
|
|
440
|
-
self.source = ModelError.error_locations.get(problem["props"]["pyrel_id"], debugging.SourceInfo())
|
|
441
|
-
self.message = problem["message"]
|
|
442
|
-
self.name = "Model error"
|
|
443
|
-
self.content = self.format_message()
|
|
444
|
-
|
|
445
|
-
def get_formatted_props_string(self):
|
|
446
|
-
# Create a table
|
|
447
|
-
table = Table(show_header=True, box=box.ROUNDED, padding=(0, 1))
|
|
448
|
-
ks = list([k for k in self.problems[0]["props"].keys() if not k.startswith("pyrel_")])
|
|
449
|
-
for k in ks:
|
|
450
|
-
table.add_column(k)
|
|
451
|
-
|
|
452
|
-
for problem in self.problems:
|
|
453
|
-
row = []
|
|
454
|
-
for k in ks:
|
|
455
|
-
row.append(str(problem["props"].get(k, "")))
|
|
456
|
-
table.add_row(*row)
|
|
457
|
-
|
|
458
|
-
with io.StringIO() as string_io:
|
|
459
|
-
console = Console(file=string_io)
|
|
460
|
-
console.print(table)
|
|
461
|
-
table_string = string_io.getvalue()
|
|
462
|
-
|
|
463
|
-
return table_string
|
|
464
|
-
|
|
465
|
-
def format_message(self):
|
|
466
|
-
row_str = self.get_formatted_props_string()
|
|
467
|
-
|
|
468
|
-
marked = mark_source(self.source, self.source.line, self.source.line, indent=2) if self.source else ""
|
|
469
|
-
return "\n\n".join([self.message, " " + marked, row_str])
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
class RelQueryError(RAIException):
|
|
473
|
-
def __init__(self, problem, source: debugging.SourceInfo|None = None):
|
|
474
|
-
super().__init__("Rel query error")
|
|
475
|
-
self.problem = problem
|
|
476
|
-
self.source = source
|
|
477
|
-
self.message = "Query error"
|
|
478
|
-
self.name = "Query error"
|
|
479
|
-
self.content = self.format_message()
|
|
480
|
-
|
|
481
|
-
def format_message(self):
|
|
482
|
-
problem = self.problem
|
|
483
|
-
marked = mark_source(self.source, -1, -1) if self.source else ""
|
|
484
|
-
return textwrap.dedent(f"""
|
|
485
|
-
{rich.markup.escape(problem["report"])}
|
|
486
|
-
{rich.markup.escape(problem["message"])}
|
|
487
|
-
|
|
488
|
-
{marked}
|
|
489
|
-
""")
|
|
490
|
-
|
|
491
|
-
class RAIExceptionSet(RAIException):
|
|
492
|
-
def __init__(self, exceptions: List[RAIException]):
|
|
493
|
-
super().__init__("Multiple Errors, see above")
|
|
494
|
-
self.exceptions = exceptions
|
|
495
|
-
|
|
496
|
-
def pprint(self):
|
|
497
|
-
for exception in self.exceptions:
|
|
498
|
-
exception.pprint()
|
|
499
|
-
|
|
500
|
-
def clone(self, config=None):
|
|
501
|
-
return RAIExceptionSet([exception.clone(config) for exception in self.exceptions])
|
|
502
|
-
|
|
503
|
-
# --------------------------------------------------
|
|
504
|
-
# Warning
|
|
505
|
-
# --------------------------------------------------
|
|
506
|
-
|
|
507
|
-
class RAIWarning(Warning, metaclass=RAIPostInitMeta):
|
|
508
|
-
name = ""
|
|
509
|
-
message: str|None = None
|
|
510
|
-
content = None
|
|
511
|
-
source = None
|
|
512
|
-
|
|
513
|
-
def __init__(self, message="", name=None, content=None, source=None):
|
|
514
|
-
self.name = name
|
|
515
|
-
self.message = message
|
|
516
|
-
self.content = content
|
|
517
|
-
self.source = source
|
|
518
|
-
|
|
519
|
-
def pprint(self):
|
|
520
|
-
msg = self.name or self.message or ""
|
|
521
|
-
if self.source and self.content:
|
|
522
|
-
print_source_error(self.source, msg, self.content.strip(), color="yellow")
|
|
523
|
-
elif self.content:
|
|
524
|
-
print_error(msg, self.content.strip(), color="yellow")
|
|
525
|
-
else:
|
|
526
|
-
print_error_name(msg, color="yellow")
|
|
527
|
-
|
|
528
|
-
def __post_init__(self):
|
|
529
|
-
self.raw_content = self.strip_rich_tags()
|
|
530
|
-
debugging.warn(self)
|
|
531
|
-
|
|
532
|
-
def strip_rich_tags(self):
|
|
533
|
-
if self.content:
|
|
534
|
-
# Use Text.from_markup to remove rich tags
|
|
535
|
-
plain_text = Text.from_markup(self.content).plain
|
|
536
|
-
# Split the content into lines
|
|
537
|
-
lines = plain_text.split('\n')
|
|
538
|
-
# Remove leading empty lines or lines with only whitespace
|
|
539
|
-
while lines and not lines[0].strip():
|
|
540
|
-
lines.pop(0)
|
|
541
|
-
# Strip leading whitespace from the first non-empty line
|
|
542
|
-
if lines:
|
|
543
|
-
lines[0] = lines[0].lstrip()
|
|
544
|
-
# Join the lines back together
|
|
545
|
-
cleaned_content = '\n'.join(lines)
|
|
546
|
-
|
|
547
|
-
return cleaned_content
|
|
548
|
-
else:
|
|
549
|
-
return self.name
|
|
550
|
-
|
|
551
|
-
def __str__(self):
|
|
552
|
-
return f"{self.name}: {self.raw_content}"
|
|
553
|
-
|
|
554
|
-
class RelQueryWarning(RAIWarning):
|
|
555
|
-
def __init__(self, problem, source):
|
|
556
|
-
super().__init__("Query warning")
|
|
557
|
-
self.name = "Query warning"
|
|
558
|
-
self.problem = problem
|
|
559
|
-
self.source = source
|
|
560
|
-
self.message = "Query warning"
|
|
561
|
-
self.content = self.format_message()
|
|
562
|
-
|
|
563
|
-
def format_message(self):
|
|
564
|
-
problem = self.problem
|
|
565
|
-
marked = mark_source(self.source, -1, -1)
|
|
566
|
-
return textwrap.dedent(f"""
|
|
567
|
-
{problem["report"]}
|
|
568
|
-
{problem["message"]}
|
|
569
|
-
|
|
570
|
-
{marked}
|
|
571
|
-
""")
|
|
572
|
-
|
|
573
|
-
class UnknownSourceWarning(RAIWarning):
|
|
574
|
-
def __init__(self, source_fqns: list[str], role: str | None = None):
|
|
575
|
-
name = "Unknown Source"
|
|
576
|
-
|
|
577
|
-
if len(source_fqns) == 1:
|
|
578
|
-
msg = textwrap.fill(
|
|
579
|
-
f"Unable to access source '{source_fqns[0]}'. Ensure the fully qualified name is correct "
|
|
580
|
-
f"and that your active role(s) have the required privileges.",
|
|
581
|
-
78,
|
|
582
|
-
)
|
|
583
|
-
else:
|
|
584
|
-
prefix = "The following sources could not be accessed:"
|
|
585
|
-
suffix = textwrap.fill(
|
|
586
|
-
"Ensure the fully qualified names are correct and that your active role(s) have the required privileges.",
|
|
587
|
-
78,
|
|
588
|
-
)
|
|
589
|
-
msg = "\n".join(
|
|
590
|
-
[prefix, "", *[f"- {fqn}" for fqn in source_fqns], "", suffix]
|
|
591
|
-
)
|
|
592
|
-
|
|
593
|
-
if role:
|
|
594
|
-
msg += f"\n\nYour current primary role is '{role}', and by default all assigned roles are used as secondary roles."
|
|
595
|
-
|
|
596
|
-
msg += "\n\n" + textwrap.fill(
|
|
597
|
-
"While access via secondary roles is supported, we recommend granting the necessary privileges "
|
|
598
|
-
"directly to your primary role to ensure consistent access.",
|
|
599
|
-
78,
|
|
600
|
-
)
|
|
601
|
-
|
|
602
|
-
super().__init__(name, name, msg)
|
|
603
|
-
self.source_fqns = source_fqns
|
|
604
|
-
|
|
605
|
-
class InvalidSourceTypeWarning(RAIWarning):
|
|
606
|
-
def __init__(self, source_types: dict[str, str]):
|
|
607
|
-
if len(source_types) == 1:
|
|
608
|
-
[(fqn, type)] = source_types.items()
|
|
609
|
-
msg = f"RAI currently only supports tables and views.\n The source '{fqn}' has type '{type}'"
|
|
610
|
-
else:
|
|
611
|
-
prefix = "RAI currently only supports tables and views. These have other types:"
|
|
612
|
-
msg = "\n".join([
|
|
613
|
-
prefix,
|
|
614
|
-
"",
|
|
615
|
-
*[f"- {fqn} ({type})" for fqn, type in source_types.items()]
|
|
616
|
-
])
|
|
617
|
-
|
|
618
|
-
name = "Invalid source type"
|
|
619
|
-
super().__init__(name, name, msg)
|
|
620
|
-
self.source_types = source_types
|
|
621
|
-
|
|
622
|
-
class IntegrityConstraintViolation(RAIException):
|
|
623
|
-
def __init__(self, violation, source=None):
|
|
624
|
-
self.violation = violation
|
|
625
|
-
self.source = source
|
|
626
|
-
|
|
627
|
-
body = violation["message"]
|
|
628
|
-
self.name = "Integrity constraint violation"
|
|
629
|
-
self.message = "Integrity constraint violation"
|
|
630
|
-
self.content = self.format_message(body)
|
|
631
|
-
super().__init__(self.message, self.name, self.content, self.source)
|
|
632
|
-
|
|
633
|
-
def format_message(self, body):
|
|
634
|
-
marked = mark_source(self.source, -1, -1)
|
|
635
|
-
|
|
636
|
-
f"Integrity constraint violation\n{body}"
|
|
637
|
-
|
|
638
|
-
return textwrap.dedent(f"""
|
|
639
|
-
{body}
|
|
640
|
-
|
|
641
|
-
{marked}
|
|
642
|
-
""")
|
|
643
|
-
|
|
644
|
-
def clone(self, config=None):
|
|
645
|
-
return IntegrityConstraintViolation(self.violation, self.source)
|
|
646
|
-
|
|
647
|
-
class ModelWarning(RAIWarning):
|
|
648
|
-
error_locations = {}
|
|
649
|
-
|
|
650
|
-
def __init__(self, problems):
|
|
651
|
-
super().__init__("Error object added")
|
|
652
|
-
problem = problems[0]
|
|
653
|
-
self.problems = problems
|
|
654
|
-
self.source = ModelError.error_locations.get(problem["props"]["pyrel_id"], debugging.SourceInfo())
|
|
655
|
-
self.message = problem["message"]
|
|
656
|
-
self.name = "Model warning"
|
|
657
|
-
self.content = self.format_message()
|
|
658
|
-
|
|
659
|
-
def get_formatted_props_string(self):
|
|
660
|
-
# Create a table
|
|
661
|
-
table = Table(show_header=True, box=box.ROUNDED, padding=(0, 1))
|
|
662
|
-
ks = list([k for k in self.problems[0]["props"].keys() if not k.startswith("pyrel_")])
|
|
663
|
-
for k in ks:
|
|
664
|
-
table.add_column(k)
|
|
665
|
-
|
|
666
|
-
for problem in self.problems:
|
|
667
|
-
row = []
|
|
668
|
-
for k in ks:
|
|
669
|
-
row.append(str(problem["props"].get(k, "")))
|
|
670
|
-
table.add_row(*row)
|
|
671
|
-
|
|
672
|
-
with io.StringIO() as string_io:
|
|
673
|
-
console = Console(file=string_io)
|
|
674
|
-
console.print(table)
|
|
675
|
-
table_string = string_io.getvalue()
|
|
676
|
-
|
|
677
|
-
return table_string
|
|
678
|
-
|
|
679
|
-
def format_message(self):
|
|
680
|
-
row_str = self.get_formatted_props_string()
|
|
681
|
-
|
|
682
|
-
marked = mark_source(self.source, self.source.line, self.source.line, indent=2) if self.source else ""
|
|
683
|
-
return "\n\n".join([self.message or "", " " + marked, row_str])
|
|
684
|
-
|
|
685
|
-
class RowsDroppedFromTargetTableWarning(RAIWarning):
|
|
686
|
-
def __init__(self, rejected_rows: list[dict], rejected_rows_count: int, col_names_map: dict):
|
|
687
|
-
"""
|
|
688
|
-
Warning raised when loading data into a table (exec_into_table API) and some rows are rejected due to erroneous data.
|
|
689
|
-
"""
|
|
690
|
-
self.parsing_errors = self.replace_column_names(rejected_rows, col_names_map)
|
|
691
|
-
self.name = "Rejected rows"
|
|
692
|
-
self.content = self.format_message(rejected_rows, rejected_rows_count, self.parsing_errors)
|
|
693
|
-
self.rejected_rows = rejected_rows
|
|
694
|
-
self.rejected_rows_count = rejected_rows_count
|
|
695
|
-
self.col_names_map = col_names_map
|
|
696
|
-
super().__init__(self.name, self.name, self.content)
|
|
697
|
-
|
|
698
|
-
def format_message(self, rejected_rows: list[dict], rejected_rows_count: int, parsing_errors: list[dict]) -> str:
|
|
699
|
-
grouped_errors = {}
|
|
700
|
-
|
|
701
|
-
# Group errors by ROW_NUMBER
|
|
702
|
-
for i, row in enumerate(rejected_rows):
|
|
703
|
-
error_indices = {e["index"] for e in parsing_errors}
|
|
704
|
-
if i in error_indices:
|
|
705
|
-
continue
|
|
706
|
-
try:
|
|
707
|
-
row_number = row['ROW_NUMBER']
|
|
708
|
-
if row_number not in grouped_errors:
|
|
709
|
-
grouped_errors[row_number] = {'rejected_record': row['REJECTED_RECORD'], 'errors': []}
|
|
710
|
-
grouped_errors[row_number]['errors'].append(f"Erroneous column: {row['COLUMN_NAME']}\nError message: {row['ERROR']}")
|
|
711
|
-
except Exception as e:
|
|
712
|
-
parsing_errors.append({"index": i, "message": str(e), "row": str(row)})
|
|
713
|
-
|
|
714
|
-
msg = f"Your data has been loaded but {rejected_rows_count} rows were skipped due to erroneous data. Here are the first {len(grouped_errors)} rejected rows:\n"
|
|
715
|
-
for row_number, data in grouped_errors.items():
|
|
716
|
-
msg += f"""Rejected record: {data['rejected_record']}"""
|
|
717
|
-
for error in data['errors']:
|
|
718
|
-
msg += f"- {error}\n"
|
|
719
|
-
msg += "\n"
|
|
720
|
-
|
|
721
|
-
return msg
|
|
722
|
-
|
|
723
|
-
"""Replaces the autogenerated pyrel COLUMN_NAME in rejected_rows with the actual column names of the target table according to the mapping in col_names_map."""
|
|
724
|
-
def replace_column_names(self, rejected_rows: list[dict], col_names_map: dict) -> list[dict]:
|
|
725
|
-
# col_names_map looks like this: {col000: TIMESTAMPS, col002: id}
|
|
726
|
-
# COLUMN_NAME in rejected_rows looks like this: "OUT23A38988_10B1_4BCB_B566_89F7F3C8D1FB_INTERNAL"["COL000":1]
|
|
727
|
-
|
|
728
|
-
parsing_errors = []
|
|
729
|
-
|
|
730
|
-
# Convert col_names_map keys to lowercase for case-insensitive matching
|
|
731
|
-
col_names_map = {key.lower(): value for key, value in col_names_map.items()}
|
|
732
|
-
for i, row in enumerate(rejected_rows):
|
|
733
|
-
try:
|
|
734
|
-
col_name = row['COLUMN_NAME'].split('[')[1].split(':')[0].replace('"', '').strip().lower()
|
|
735
|
-
if col_name in col_names_map:
|
|
736
|
-
row['COLUMN_NAME'] = col_names_map[col_name]
|
|
737
|
-
except Exception as e:
|
|
738
|
-
parsing_errors.append({"index": i, "message": str(e), "row": str(row)})
|
|
739
|
-
return parsing_errors
|
|
740
|
-
|
|
741
|
-
# --------------------------------------------------
|
|
742
|
-
# ERP Exceptions
|
|
743
|
-
# --------------------------------------------------
|
|
744
|
-
|
|
745
|
-
class ERPNotRunningError(RAIException):
|
|
746
|
-
def __init__(self):
|
|
747
|
-
super().__init__("ERP service state")
|
|
748
|
-
self.name = "ERP service state"
|
|
749
|
-
self.content = self.format_message()
|
|
750
|
-
|
|
751
|
-
def format_message(self):
|
|
752
|
-
return textwrap.dedent("""
|
|
753
|
-
The ERP service is not available. Check your internet connection.
|
|
754
|
-
If this issue persists, please contact support.
|
|
755
|
-
""")
|
|
756
|
-
|
|
757
|
-
# --------------------------------------------------
|
|
758
|
-
# Rel Exceptions/warnings
|
|
759
|
-
# --------------------------------------------------
|
|
760
|
-
|
|
761
|
-
class NumericOverflow(RAIException):
|
|
762
|
-
def __init__(self, problem, source):
|
|
763
|
-
super().__init__("Numeric Overflow")
|
|
764
|
-
self.problem = problem
|
|
765
|
-
self.source = source
|
|
766
|
-
self.message = "Numeric Overflow"
|
|
767
|
-
self.name = "Numeric Overflow"
|
|
768
|
-
self.content = self.format_message()
|
|
769
|
-
|
|
770
|
-
def format_message(self):
|
|
771
|
-
problem = self.problem
|
|
772
|
-
marked = mark_source(self.source, -1, -1)
|
|
773
|
-
info = problem["report"].split("\n--------------")[0].strip()
|
|
774
|
-
return textwrap.dedent(f"""
|
|
775
|
-
{info}
|
|
776
|
-
|
|
777
|
-
{marked}
|
|
778
|
-
""")
|
|
779
|
-
|
|
780
|
-
class ArityMismatch(RAIWarning):
|
|
781
|
-
def __init__(self, problem, source):
|
|
782
|
-
self.name = "Arity mismatch"
|
|
783
|
-
self.message = problem["message"]
|
|
784
|
-
self.source = source
|
|
785
|
-
self.content = self.format_message()
|
|
786
|
-
|
|
787
|
-
def format_message(self):
|
|
788
|
-
source = self.source
|
|
789
|
-
match = re.search(r"`(.+?)` expects (.*)", self.message or "")
|
|
790
|
-
if match is None:
|
|
791
|
-
return self.message
|
|
792
|
-
fq_name = match.group(1)
|
|
793
|
-
rel_name = fq_name.split("::")[-1]
|
|
794
|
-
arity_sentence = match.group(2)
|
|
795
|
-
found = PropertyFinder(source.line, [rel_name])
|
|
796
|
-
if source.block:
|
|
797
|
-
found.visit(source.block)
|
|
798
|
-
found_lines = found.found_properties_lines or found.dynamic_properties
|
|
799
|
-
marked = mark_source(self.source, -1, -1, highlight_lines=found_lines)
|
|
800
|
-
return textwrap.dedent(f"""
|
|
801
|
-
The relation [yellow]{rel_name}[/yellow] expects {arity_sentence}
|
|
802
|
-
|
|
803
|
-
{marked}
|
|
804
|
-
""")
|
|
805
|
-
|
|
806
|
-
class UninitializedPropertyException(RAIException):
|
|
807
|
-
def __init__(self, undefined_list: List[Tuple[str, Any]]):
|
|
808
|
-
self.content = ""
|
|
809
|
-
self.raw_content = ""
|
|
810
|
-
self.source = None
|
|
811
|
-
message_chunks = []
|
|
812
|
-
chunks: list[str] = []
|
|
813
|
-
underscore_prefixed_list = list[Tuple[str, Any]]()
|
|
814
|
-
|
|
815
|
-
# Deduplicate items in undefined_list
|
|
816
|
-
seen = set()
|
|
817
|
-
undefined_list = [item for item in undefined_list if item[0] not in seen and not seen.add(item[0])]
|
|
818
|
-
|
|
819
|
-
# Check for properties that start with an underscore
|
|
820
|
-
for item in undefined_list:
|
|
821
|
-
if item[0].startswith("_"):
|
|
822
|
-
underscore_prefixed_list.append(item)
|
|
823
|
-
|
|
824
|
-
# Remove the underscore-prefixed items from the undefined_list
|
|
825
|
-
if len(underscore_prefixed_list) > 0:
|
|
826
|
-
for item in underscore_prefixed_list:
|
|
827
|
-
undefined_list.remove(item)
|
|
828
|
-
|
|
829
|
-
len_undefined = len(undefined_list)
|
|
830
|
-
len_underscore = len(underscore_prefixed_list)
|
|
831
|
-
|
|
832
|
-
if len_undefined > 0:
|
|
833
|
-
if len_undefined == 1:
|
|
834
|
-
message_chunks.append("Uninitialized property: " + undefined_list[0][0])
|
|
835
|
-
else:
|
|
836
|
-
message_chunks.append("Uninitialized properties: " + ", ".join([item[0] for item in undefined_list]))
|
|
837
|
-
|
|
838
|
-
if len_underscore > 0:
|
|
839
|
-
prefix = "renamed" if len_undefined > 0 else "Renamed"
|
|
840
|
-
suffix = " property:" if len_undefined == 0 else ":"
|
|
841
|
-
if len_underscore == 1:
|
|
842
|
-
message_chunks.append(f"{prefix}{suffix} " + underscore_prefixed_list[0][0])
|
|
843
|
-
else:
|
|
844
|
-
suffix = " properties:" if len_undefined == 0 else ":"
|
|
845
|
-
message_chunks.append(f"{prefix}{suffix} " + ", ".join([item[0] for item in underscore_prefixed_list]))
|
|
846
|
-
|
|
847
|
-
self.message = ", ".join(message_chunks)
|
|
848
|
-
self.name = self.message
|
|
849
|
-
|
|
850
|
-
if len(undefined_list) > 0:
|
|
851
|
-
source_map_undefined: dict[str, tuple[debugging.SourceInfo, list[str]]] = {}
|
|
852
|
-
for name, source in undefined_list:
|
|
853
|
-
if source.source not in source_map_undefined:
|
|
854
|
-
source_map_undefined[source.source] = (source, [name])
|
|
855
|
-
else:
|
|
856
|
-
source_map_undefined[source.source][1].append(name)
|
|
857
|
-
|
|
858
|
-
for (source, names) in source_map_undefined.values():
|
|
859
|
-
self.source = source
|
|
860
|
-
unique_names = list(set(names))
|
|
861
|
-
props = ", ".join([f"[yellow]{name}[/yellow]" for name in unique_names])
|
|
862
|
-
prop_line = (
|
|
863
|
-
f"property {props} has"
|
|
864
|
-
if len(unique_names) == 1
|
|
865
|
-
else f"properties {props} have"
|
|
866
|
-
)
|
|
867
|
-
found = PropertyFinder(source.line, unique_names)
|
|
868
|
-
if source.block:
|
|
869
|
-
found.visit(source.block)
|
|
870
|
-
found_lines = found.found_properties_lines or found.dynamic_properties
|
|
871
|
-
marked = mark_source(source, -1, -1, indent=0, highlight_lines=found_lines)
|
|
872
|
-
chunks.append(textwrap.dedent(f"""
|
|
873
|
-
The {prop_line} never been set or added to and so will always cause the rule or query to fail.
|
|
874
|
-
"""))
|
|
875
|
-
chunks.append(marked)
|
|
876
|
-
|
|
877
|
-
if len(underscore_prefixed_list) > 0:
|
|
878
|
-
source_map_underscore: dict[str, tuple[debugging.SourceInfo, list[str]]] = {}
|
|
879
|
-
for name, source in underscore_prefixed_list:
|
|
880
|
-
if source.source not in source_map_underscore:
|
|
881
|
-
source_map_underscore[source.source] = (source, [name])
|
|
882
|
-
else:
|
|
883
|
-
source_map_underscore[source.source][1].append(name)
|
|
884
|
-
|
|
885
|
-
for (source, names) in source_map_underscore.values():
|
|
886
|
-
self.source = source
|
|
887
|
-
unique_names = list(set(names))
|
|
888
|
-
found = PropertyFinder(source.line, unique_names)
|
|
889
|
-
if source.block:
|
|
890
|
-
found.visit(source.block)
|
|
891
|
-
found_lines = found.found_properties_lines or found.dynamic_properties
|
|
892
|
-
marked = mark_source(source, -1, -1, indent=0, highlight_lines=found_lines)
|
|
893
|
-
chunks.append(textwrap.dedent("""
|
|
894
|
-
Column names prefixed with '_' are clashing with internal properties.
|
|
895
|
-
In your code replace the column name with the alias shown below:
|
|
896
|
-
"""))
|
|
897
|
-
chunks.append("\n".join((f"[yellow]{name}[/yellow] \trename to:\t [green]{'col' + name}[/green]" for name, _ in underscore_prefixed_list)))
|
|
898
|
-
chunks.append(marked)
|
|
899
|
-
|
|
900
|
-
self.content = "\n\n".join(chunks)
|
|
901
|
-
|
|
902
|
-
super().__init__(self.name, self.message, self.content, self.source)
|
|
903
|
-
|
|
904
|
-
class RAITypeError(RAIException, TypeError):
|
|
905
|
-
def __init__(self, param: str, message: str):
|
|
906
|
-
self.param = param
|
|
907
|
-
self.message = message
|
|
908
|
-
self.name = "Invalid type error"
|
|
909
|
-
self.source = Errors.call_source(4)
|
|
910
|
-
|
|
911
|
-
# Mark the source if available
|
|
912
|
-
if self.source:
|
|
913
|
-
self.marked = mark_source(self.source, self.source.line, self.source.line)
|
|
914
|
-
else:
|
|
915
|
-
self.marked = ""
|
|
916
|
-
|
|
917
|
-
# Format the content and raw_content
|
|
918
|
-
self.content = self.format_message()
|
|
919
|
-
self.raw_content = self.strip_rich_tags()
|
|
920
|
-
|
|
921
|
-
super().__init__(self.message, self.name, self.content, self.source)
|
|
922
|
-
|
|
923
|
-
def format_message(self):
|
|
924
|
-
if self.source:
|
|
925
|
-
return textwrap.dedent(f"""
|
|
926
|
-
The parameter [yellow]{self.param}[/yellow] is of the wrong type.
|
|
927
|
-
|
|
928
|
-
{self.marked}
|
|
929
|
-
""")
|
|
930
|
-
else:
|
|
931
|
-
return textwrap.dedent(f"""
|
|
932
|
-
The parameter [yellow]{self.param}[/yellow] is of the wrong type.
|
|
933
|
-
""")
|
|
934
|
-
|
|
935
|
-
class RAIValueError(RAIException, ValueError):
|
|
936
|
-
def __init__(self, message: str):
|
|
937
|
-
self.message = message
|
|
938
|
-
self.name = "Invalid value error"
|
|
939
|
-
self.source = Errors.call_source(4)
|
|
940
|
-
|
|
941
|
-
# Mark the source if available
|
|
942
|
-
if self.source:
|
|
943
|
-
self.marked = mark_source(self.source, self.source.line, self.source.line)
|
|
944
|
-
else:
|
|
945
|
-
self.marked = ""
|
|
946
|
-
|
|
947
|
-
# Format the content and raw_content
|
|
948
|
-
self.content = self.format_message()
|
|
949
|
-
self.raw_content = self.strip_rich_tags()
|
|
950
|
-
|
|
951
|
-
super().__init__(self.message, self.name, self.content, self.source)
|
|
952
|
-
|
|
953
|
-
def format_message(self):
|
|
954
|
-
if self.source:
|
|
955
|
-
return textwrap.dedent(f"""
|
|
956
|
-
{self.message}
|
|
957
|
-
|
|
958
|
-
{self.marked}
|
|
959
|
-
""")
|
|
960
|
-
else:
|
|
961
|
-
return textwrap.dedent(f"""
|
|
962
|
-
{self.message}
|
|
963
|
-
""")
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
class RAIAbortedTransactionError(RAIException):
|
|
968
|
-
def __init__(self, type, message, report):
|
|
969
|
-
self.type = type
|
|
970
|
-
self.message = message
|
|
971
|
-
self.report = report
|
|
972
|
-
self.name = "Transaction aborted"
|
|
973
|
-
|
|
974
|
-
self.content = self.format_message()
|
|
975
|
-
self.raw_content = self.strip_rich_tags()
|
|
976
|
-
|
|
977
|
-
super().__init__(self.message, self.name, self.content)
|
|
978
|
-
|
|
979
|
-
def format_message(self):
|
|
980
|
-
clean_report = "\n".join(line.lstrip() for line in self.report.splitlines())
|
|
981
|
-
|
|
982
|
-
formatted = textwrap.dedent(f"""
|
|
983
|
-
[yellow]The transaction was aborted due to the following error:[/yellow]
|
|
984
|
-
|
|
985
|
-
Type: [yellow]{self.type}[/yellow]
|
|
986
|
-
|
|
987
|
-
Message: [yellow]{self.message}[/yellow]
|
|
988
|
-
|
|
989
|
-
Report: {clean_report}
|
|
990
|
-
""")
|
|
991
|
-
|
|
992
|
-
final_formatted = "\n".join(line.lstrip() for line in formatted.splitlines())
|
|
993
|
-
return final_formatted
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
# --------------------------------------------------
|
|
997
|
-
# DSL scope errors
|
|
998
|
-
# --------------------------------------------------
|
|
999
|
-
class OutOfContextException(RAIException):
|
|
1000
|
-
def __init__(self, source=None):
|
|
1001
|
-
self.name = "Outside of context"
|
|
1002
|
-
self.message = (
|
|
1003
|
-
"Looks like this object is being used outside of a rule or query."
|
|
1004
|
-
)
|
|
1005
|
-
self.source = source or Errors.call_source() or debugging.SourceInfo()
|
|
1006
|
-
self.content = self.format_message()
|
|
1007
|
-
|
|
1008
|
-
super().__init__(self.message, self.name, self.content, source=source)
|
|
1009
|
-
|
|
1010
|
-
def format_message(self):
|
|
1011
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1012
|
-
return textwrap.dedent(f"""
|
|
1013
|
-
Looks like this [yellow]object[/yellow] is being used outside of a rule or query.
|
|
1014
|
-
|
|
1015
|
-
{marked}
|
|
1016
|
-
""")
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
class VariableOutOfContextException(RAIException):
|
|
1020
|
-
def __init__(self, source, name: str|None, is_property=False):
|
|
1021
|
-
self.name = "Variable out of context"
|
|
1022
|
-
if name is None:
|
|
1023
|
-
self.message = "Looks like a variable is being used outside of the rule or query it was defined in."
|
|
1024
|
-
else:
|
|
1025
|
-
self.message = f"Looks like a variable representing '{name}' is being used outside of the rule or query it was defined in."
|
|
1026
|
-
self.source = source
|
|
1027
|
-
self.is_property = is_property
|
|
1028
|
-
self.content = self.format_message(name)
|
|
1029
|
-
|
|
1030
|
-
super().__init__(self.message, self.name, self.content, source=source)
|
|
1031
|
-
|
|
1032
|
-
def format_message(self, name):
|
|
1033
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1034
|
-
return textwrap.dedent(f"""
|
|
1035
|
-
Looks like a variable representing [yellow bold]{name}[/yellow bold] is being used outside of the rule or query it was defined in.
|
|
1036
|
-
|
|
1037
|
-
{marked}
|
|
1038
|
-
""")
|
|
1039
|
-
|
|
1040
|
-
class SelectOutOfContext(RAIException):
|
|
1041
|
-
def __init__(self):
|
|
1042
|
-
self.name = "Outside of context"
|
|
1043
|
-
self.message = (
|
|
1044
|
-
"Looks like this select is being used outside of the query it was originally from."
|
|
1045
|
-
)
|
|
1046
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
1047
|
-
self.content = self.format_message()
|
|
1048
|
-
|
|
1049
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
1050
|
-
|
|
1051
|
-
def format_message(self):
|
|
1052
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1053
|
-
return textwrap.dedent(f"""
|
|
1054
|
-
Looks like this [yellow]select[/yellow] is being used outside of the query it is from.
|
|
1055
|
-
|
|
1056
|
-
{marked}
|
|
1057
|
-
""")
|
|
1058
|
-
|
|
1059
|
-
#--------------------------------------------------
|
|
1060
|
-
# DSL Type errors
|
|
1061
|
-
#--------------------------------------------------
|
|
1062
|
-
|
|
1063
|
-
class FilterAsValue(RAIException):
|
|
1064
|
-
def __init__(self, source=None):
|
|
1065
|
-
self.name = "Filter used as a value"
|
|
1066
|
-
self.message = "Boolean expressions are filters and can't be used as values directly, use std.as_bool to cast them into True/False."
|
|
1067
|
-
self.source = source or Errors.call_source()
|
|
1068
|
-
self.content = self.format_message()
|
|
1069
|
-
|
|
1070
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
1071
|
-
|
|
1072
|
-
def format_message(self):
|
|
1073
|
-
marked = mark_source(self.source, self.source.line, self.source.line) if self.source else ""
|
|
1074
|
-
return textwrap.dedent(f"""
|
|
1075
|
-
Boolean expressions are filters and can't be used as values directly. You can use relationalai.std.as_bool(..) to cast them into True/False.
|
|
1076
|
-
|
|
1077
|
-
{marked}
|
|
1078
|
-
""")
|
|
1079
|
-
|
|
1080
|
-
class AsBoolForNonFilter(RAIException):
|
|
1081
|
-
def __init__(self, source=None):
|
|
1082
|
-
self.name = "as_bool used on non-filter"
|
|
1083
|
-
self.message = "as_bool can only be used on boolean expressions."
|
|
1084
|
-
self.source = source or Errors.call_source()
|
|
1085
|
-
self.content = self.format_message()
|
|
1086
|
-
|
|
1087
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
1088
|
-
|
|
1089
|
-
def format_message(self):
|
|
1090
|
-
marked = mark_source(self.source, self.source.line, self.source.line) if self.source else ""
|
|
1091
|
-
return textwrap.dedent(f"""
|
|
1092
|
-
as_bool can only be used on boolean expressions.
|
|
1093
|
-
|
|
1094
|
-
{marked}
|
|
1095
|
-
""")
|
|
1096
|
-
|
|
1097
|
-
class PropertyCaseMismatch(RAIWarning):
|
|
1098
|
-
def __init__(self, name, found_name):
|
|
1099
|
-
self.name = "Similar property name"
|
|
1100
|
-
self.message = ".{name} doesn't exist, but .{found_name} does"
|
|
1101
|
-
self.source = Errors.call_source()
|
|
1102
|
-
self.content = self.format_message(name, found_name)
|
|
1103
|
-
|
|
1104
|
-
def format_message(self, name, found_name):
|
|
1105
|
-
source = self.source or debugging.SourceInfo()
|
|
1106
|
-
found = PropertyFinder(source.line, [name])
|
|
1107
|
-
if source.block:
|
|
1108
|
-
found.visit(source.block)
|
|
1109
|
-
found_lines = found.found_properties_lines or found.dynamic_properties
|
|
1110
|
-
marked = mark_source(source, -1, -1, indent=8, highlight_lines=found_lines)
|
|
1111
|
-
|
|
1112
|
-
updated = mark_source(
|
|
1113
|
-
source.modify(PropertyNameReplacer(name, found_name)),
|
|
1114
|
-
-1, -1, indent=8, highlight_lines=found_lines, highlight="green",
|
|
1115
|
-
)
|
|
1116
|
-
|
|
1117
|
-
return textwrap.dedent(f"""
|
|
1118
|
-
The property [yellow]{name}[/yellow] doesn't exist, but the very similar [green]{found_name}[/green] does.
|
|
1119
|
-
|
|
1120
|
-
{marked}
|
|
1121
|
-
|
|
1122
|
-
Did you mean [green]{found_name}[/green] instead?
|
|
1123
|
-
|
|
1124
|
-
{updated}
|
|
1125
|
-
""")
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
class NonVarObject(RAIException, TypeError):
|
|
1129
|
-
def __init__(self, obj: Any, message: str):
|
|
1130
|
-
self.obj = obj
|
|
1131
|
-
self.message = message
|
|
1132
|
-
self.name = "non-var object used as variable"
|
|
1133
|
-
self.source = Errors.call_source(4)
|
|
1134
|
-
|
|
1135
|
-
# Mark the source if available
|
|
1136
|
-
if self.source:
|
|
1137
|
-
self.marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1138
|
-
else:
|
|
1139
|
-
self.marked = ""
|
|
1140
|
-
|
|
1141
|
-
# Format the content and raw_content
|
|
1142
|
-
self.content = self.format_message()
|
|
1143
|
-
self.raw_content = self.strip_rich_tags()
|
|
1144
|
-
|
|
1145
|
-
super().__init__(self.message, self.name, self.content, self.source)
|
|
1146
|
-
|
|
1147
|
-
def format_message(self):
|
|
1148
|
-
if self.source:
|
|
1149
|
-
return textwrap.dedent(f"""
|
|
1150
|
-
[yellow]{self.obj.__class__.__name__}[/yellow] objects can't be used as variables, such as in a .add(), .set(), or select().
|
|
1151
|
-
|
|
1152
|
-
{self.marked}
|
|
1153
|
-
""")
|
|
1154
|
-
else:
|
|
1155
|
-
return textwrap.dedent(f"""
|
|
1156
|
-
[yellow]{self.obj.__class__.__name__}[/yellow] objects can't be used as variables, such as in a .add(), .set(), or select().
|
|
1157
|
-
""")
|
|
1158
|
-
|
|
1159
|
-
class KeyedCantBeExtended(RAIException):
|
|
1160
|
-
def __init__(self):
|
|
1161
|
-
self.name = "Keyed types can't be extended"
|
|
1162
|
-
self.message = "Keyed types can't be extended."
|
|
1163
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
1164
|
-
self.content = self.format_message()
|
|
1165
|
-
|
|
1166
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
1167
|
-
|
|
1168
|
-
def format_message(self):
|
|
1169
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1170
|
-
return textwrap.dedent(f"""
|
|
1171
|
-
Keyed types can't be extended, use .add() in a rule instead.
|
|
1172
|
-
|
|
1173
|
-
{marked}
|
|
1174
|
-
""")
|
|
1175
|
-
|
|
1176
|
-
class KeyedWrongArity(RAIException):
|
|
1177
|
-
def __init__(self, name:str, keys:List[str], given:int):
|
|
1178
|
-
self.name = "Incorrect number of keys"
|
|
1179
|
-
verb = "was" if given == 1 else "were"
|
|
1180
|
-
self.message = f"{name} expects {len(keys)} keys ({', '.join(keys)}), but only {given} {verb} given."
|
|
1181
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
1182
|
-
self.content = self.format_message()
|
|
1183
|
-
|
|
1184
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
1185
|
-
|
|
1186
|
-
def format_message(self):
|
|
1187
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1188
|
-
return textwrap.dedent(f"""
|
|
1189
|
-
{self.message}
|
|
1190
|
-
|
|
1191
|
-
{marked}
|
|
1192
|
-
""")
|
|
1193
|
-
|
|
1194
|
-
class RowLiteralTooLargeWarning(RAIWarning):
|
|
1195
|
-
def __init__(self, size, max_size=1000):
|
|
1196
|
-
self.name = "Large row literal"
|
|
1197
|
-
self.message = f"Rows with more than {max_size} items can be slow to compile."
|
|
1198
|
-
self.size = size
|
|
1199
|
-
self.max_size = max_size
|
|
1200
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
1201
|
-
self.content = self.format_message()
|
|
1202
|
-
|
|
1203
|
-
def format_message(self):
|
|
1204
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1205
|
-
return textwrap.dedent(f"""
|
|
1206
|
-
Rows with more than {self.max_size} items can be slow to compile. This one has {self.size} items:
|
|
1207
|
-
|
|
1208
|
-
{marked}
|
|
1209
|
-
""")
|
|
1210
|
-
|
|
1211
|
-
class RowLiteralTooLarge(RAIException):
|
|
1212
|
-
def __init__(self, size, max_size=10000):
|
|
1213
|
-
self.name = "Row literal too large"
|
|
1214
|
-
self.message = f"Rows can't have more than {max_size} items."
|
|
1215
|
-
self.size = size
|
|
1216
|
-
self.max_size = max_size
|
|
1217
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
1218
|
-
self.content = self.format_message()
|
|
1219
|
-
|
|
1220
|
-
def format_message(self):
|
|
1221
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1222
|
-
return textwrap.dedent(f"""
|
|
1223
|
-
Rows can't have more than {self.max_size} items. This one has {self.size} items:
|
|
1224
|
-
|
|
1225
|
-
{marked}
|
|
1226
|
-
""")
|
|
1227
|
-
|
|
1228
|
-
class RowLiteralMismatch(RAIException):
|
|
1229
|
-
def __init__(self, mismatched_thing:str):
|
|
1230
|
-
self.name = "Row literal mismatch"
|
|
1231
|
-
self.message = f"All rows in a row literal must have the same {mismatched_thing}"
|
|
1232
|
-
self.mismatched_thing = mismatched_thing
|
|
1233
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
1234
|
-
self.content = self.format_message()
|
|
1235
|
-
|
|
1236
|
-
def format_message(self):
|
|
1237
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1238
|
-
return textwrap.dedent(f"""
|
|
1239
|
-
All rows in a row literal must have the same {self.mismatched_thing}
|
|
1240
|
-
|
|
1241
|
-
{marked}
|
|
1242
|
-
""")
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
# --------------------------------------------------
|
|
1246
|
-
# DSL property exceptions
|
|
1247
|
-
# --------------------------------------------------
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
class ReservedPropertyException(RAIException):
|
|
1251
|
-
def __init__(self, source, property_name: str):
|
|
1252
|
-
self.name = "Reserved property name"
|
|
1253
|
-
self.message = f"The property '{property_name}' is a reserved property name on RelationalAI types."
|
|
1254
|
-
self.source = source
|
|
1255
|
-
self.content = self.format_message(property_name)
|
|
1256
|
-
|
|
1257
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
1258
|
-
|
|
1259
|
-
def format_message(self, property_name):
|
|
1260
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1261
|
-
return textwrap.dedent(f"""
|
|
1262
|
-
The property '{property_name}' is a reserved property name on RelationalAI types.
|
|
1263
|
-
|
|
1264
|
-
{marked}
|
|
1265
|
-
""")
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
class NonCallablePropertyException(RAIException):
|
|
1269
|
-
def __init__(self, source, property_name: str):
|
|
1270
|
-
self.name = "Non-callable property"
|
|
1271
|
-
self.message = f"The property '{property_name}' is not callable."
|
|
1272
|
-
self.source = source
|
|
1273
|
-
self.content = self.format_message(property_name)
|
|
1274
|
-
|
|
1275
|
-
super().__init__(self.message, self.name, self.content, source=source)
|
|
1276
|
-
|
|
1277
|
-
def format_message(self, property_name):
|
|
1278
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1279
|
-
return textwrap.dedent(f"""
|
|
1280
|
-
The property '{property_name}' is not callable on RelationalAI types.
|
|
1281
|
-
|
|
1282
|
-
{marked}
|
|
1283
|
-
""")
|
|
1284
|
-
|
|
1285
|
-
class InvalidPropertySetException(RAIException):
|
|
1286
|
-
def __init__(self, source):
|
|
1287
|
-
self.name = "Invalid property set"
|
|
1288
|
-
self.message = "You can't set properties directly on a RAI object."
|
|
1289
|
-
self.source = source
|
|
1290
|
-
self.start_line = source.line
|
|
1291
|
-
self.end_line = source.line
|
|
1292
|
-
self.content = self.format_message()
|
|
1293
|
-
self.raw_content = self.strip_rich_tags()
|
|
1294
|
-
super().__init__(
|
|
1295
|
-
message=self.message, name=self.name, source=source, content=self.content
|
|
1296
|
-
)
|
|
1297
|
-
|
|
1298
|
-
def format_message(self):
|
|
1299
|
-
marked = self.mark_source()
|
|
1300
|
-
dynamic = mark_source(
|
|
1301
|
-
self.source.modify(SetToMethod()),
|
|
1302
|
-
self.start_line,
|
|
1303
|
-
self.end_line,
|
|
1304
|
-
highlight="green",
|
|
1305
|
-
)
|
|
1306
|
-
compare = mark_source(
|
|
1307
|
-
self.source.modify(AssignToCompare()),
|
|
1308
|
-
self.start_line,
|
|
1309
|
-
self.end_line,
|
|
1310
|
-
highlight="green",
|
|
1311
|
-
)
|
|
1312
|
-
|
|
1313
|
-
return textwrap.dedent(f"""
|
|
1314
|
-
You can't set properties directly on a RAI object.
|
|
1315
|
-
|
|
1316
|
-
{marked}
|
|
1317
|
-
|
|
1318
|
-
If you are trying to set the value of a property use [green]set()[/green]:
|
|
1319
|
-
|
|
1320
|
-
{dynamic}
|
|
1321
|
-
|
|
1322
|
-
Or maybe you meant [green]==[/green] instead?
|
|
1323
|
-
|
|
1324
|
-
{compare}
|
|
1325
|
-
""")
|
|
1326
|
-
|
|
1327
|
-
def mark_source(self):
|
|
1328
|
-
return mark_source(self.source, self.start_line, self.end_line)
|
|
1329
|
-
|
|
1330
|
-
class MultipleIdentities(RAIException):
|
|
1331
|
-
def __init__(self):
|
|
1332
|
-
source = Errors.call_source()
|
|
1333
|
-
self.name = "Multiple identities"
|
|
1334
|
-
self.message = "You can't pass multiple identity variables for a single object."
|
|
1335
|
-
self.source = source
|
|
1336
|
-
self.content = self.format_message()
|
|
1337
|
-
|
|
1338
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
1339
|
-
|
|
1340
|
-
def format_message(self):
|
|
1341
|
-
marked = mark_source(self.source, self.source.line, self.source.line) if self.source else ""
|
|
1342
|
-
return textwrap.dedent(f"""
|
|
1343
|
-
You can't pass multiple identity variables for a single object.
|
|
1344
|
-
|
|
1345
|
-
{marked}
|
|
1346
|
-
""")
|
|
1347
|
-
|
|
1348
|
-
# --------------------------------------------------
|
|
1349
|
-
# Graph library exceptions
|
|
1350
|
-
# --------------------------------------------------
|
|
1351
|
-
|
|
1352
|
-
class DirectedGraphNotApplicable(RAIValueError):
|
|
1353
|
-
def __init__(self, name: str):
|
|
1354
|
-
super().__init__(name)
|
|
1355
|
-
self.name = f"algorithm `{name}` is not applicable to directed graphs"
|
|
1356
|
-
|
|
1357
|
-
class DirectedGraphNotSupported(RAIValueError):
|
|
1358
|
-
def __init__(self, name: str, message_addendum: str = ""):
|
|
1359
|
-
message = f"algorithm `{name}` does not currently support directed graphs{'' if not message_addendum else f'. {message_addendum}'}"
|
|
1360
|
-
super().__init__(message)
|
|
1361
|
-
|
|
1362
|
-
class ParameterTypeMismatch(RAIValueError):
|
|
1363
|
-
def __init__(self, name: str, type_, value):
|
|
1364
|
-
super().__init__(name)
|
|
1365
|
-
self.name = (
|
|
1366
|
-
f"parameter `{name}` must be of type {type_.__name__.lower()}, "
|
|
1367
|
-
f"but its value {value!r} is of type {type(value)}"
|
|
1368
|
-
)
|
|
1369
|
-
|
|
1370
|
-
class ParameterBoundBelowInclusive(RAIValueError):
|
|
1371
|
-
def __init__(self, name: str, value, minimum):
|
|
1372
|
-
super().__init__(name)
|
|
1373
|
-
self.name = f"parameter `{name}` must be greater than or equal to {minimum}, but is {value!r}"
|
|
1374
|
-
|
|
1375
|
-
class ParameterBoundAboveInclusive(RAIValueError):
|
|
1376
|
-
def __init__(self, name: str, value, maximum):
|
|
1377
|
-
super().__init__(name)
|
|
1378
|
-
self.name = f"parameter `{name}` must be less than or equal to {maximum}, but is {value!r}"
|
|
1379
|
-
|
|
1380
|
-
class ParameterBoundBelowExclusive(RAIValueError):
|
|
1381
|
-
def __init__(self, name: str, value, minimum):
|
|
1382
|
-
super().__init__(name)
|
|
1383
|
-
self.name = f"parameter `{name}` must be strictly greater than {minimum}, but is {value!r}"
|
|
1384
|
-
|
|
1385
|
-
class ParameterBoundAboveExclusive(RAIValueError):
|
|
1386
|
-
def __init__(self, name: str, value, maximum):
|
|
1387
|
-
super().__init__(name)
|
|
1388
|
-
self.name = f"parameter `{name}` must be strictly less than {maximum}, but is {value!r}"
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
# --------------------------------------------------
|
|
1392
|
-
# Engine exceptions
|
|
1393
|
-
# --------------------------------------------------
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
class EngineNotFoundException(RAIException):
|
|
1397
|
-
def __init__(self, engine_name: str, message: str):
|
|
1398
|
-
self.engine_name = engine_name
|
|
1399
|
-
self.message = message
|
|
1400
|
-
self.name = "Engine unavailable"
|
|
1401
|
-
|
|
1402
|
-
# Format the content and raw_content
|
|
1403
|
-
self.content = self.format_message()
|
|
1404
|
-
self.raw_content = self.strip_rich_tags()
|
|
1405
|
-
|
|
1406
|
-
super().__init__(self.message, self.name, self.content)
|
|
1407
|
-
|
|
1408
|
-
def format_message(self):
|
|
1409
|
-
message = "We were unable to detect an existing engine or provision a new engine for you. To manually create an engine:"
|
|
1410
|
-
return self.message_for_environment(
|
|
1411
|
-
message,
|
|
1412
|
-
f"rai engines:create --name {self.engine_name}",
|
|
1413
|
-
f"rai.Provider().create_engine('{self.engine_name}')"
|
|
1414
|
-
)
|
|
1415
|
-
|
|
1416
|
-
class EngineProvisioningFailed(RAIException):
|
|
1417
|
-
def __init__(self, engine_name: str, original_exception: Exception | None = None):
|
|
1418
|
-
self.engine_name = engine_name
|
|
1419
|
-
self.original_exception = original_exception
|
|
1420
|
-
self.message = "Engine provisioning failed"
|
|
1421
|
-
self.name = self.message
|
|
1422
|
-
|
|
1423
|
-
# Format the content and raw_content
|
|
1424
|
-
self.content = self.format_message()
|
|
1425
|
-
self.raw_content = self.strip_rich_tags()
|
|
1426
|
-
|
|
1427
|
-
super().__init__(self.message, self.name, self.content)
|
|
1428
|
-
|
|
1429
|
-
def format_message(self):
|
|
1430
|
-
base_message = "\n".join([
|
|
1431
|
-
f"We tried to provision the engine [yellow]{self.engine_name}[/yellow] but it failed."
|
|
1432
|
-
"\n\n",
|
|
1433
|
-
"To learn more about how to manually manage engines, see:"
|
|
1434
|
-
"\n",
|
|
1435
|
-
"https://docs.relational.ai/api/cli/engines"
|
|
1436
|
-
])
|
|
1437
|
-
|
|
1438
|
-
# Add original exception details if available
|
|
1439
|
-
if self.original_exception:
|
|
1440
|
-
original_msg = str(self.original_exception).strip()
|
|
1441
|
-
if original_msg:
|
|
1442
|
-
base_message += f"\n\n[red]Error:[/red] {original_msg}"
|
|
1443
|
-
|
|
1444
|
-
return base_message
|
|
1445
|
-
|
|
1446
|
-
class DuoSecurityFailed(RAIException):
|
|
1447
|
-
def __init__(self, _: Exception):
|
|
1448
|
-
self.message = "Connection failed due to Duo security"
|
|
1449
|
-
self.name = "Connection failed"
|
|
1450
|
-
|
|
1451
|
-
# Format the content and raw_content
|
|
1452
|
-
self.content = self.format_message()
|
|
1453
|
-
self.raw_content = self.strip_rich_tags()
|
|
1454
|
-
|
|
1455
|
-
super().__init__(self.message, self.name, self.content)
|
|
1456
|
-
|
|
1457
|
-
def format_message(self):
|
|
1458
|
-
return "Establishing connection failed due to Duo security.\nPlease check your Duo security code/settings and try again."
|
|
1459
|
-
class EnginePending(RAIException):
|
|
1460
|
-
def __init__(self, engine_name: str):
|
|
1461
|
-
self.engine_name = engine_name
|
|
1462
|
-
self.message = "Engine is in a pending state"
|
|
1463
|
-
self.name = "Engine not ready"
|
|
1464
|
-
|
|
1465
|
-
# Format the content and raw_content
|
|
1466
|
-
self.content = self.format_message()
|
|
1467
|
-
self.raw_content = self.strip_rich_tags()
|
|
1468
|
-
|
|
1469
|
-
super().__init__(self.message, self.name, self.content)
|
|
1470
|
-
|
|
1471
|
-
def format_message(self):
|
|
1472
|
-
message = f"The engine [yellow]{self.engine_name}[/yellow] is in a pending state. You can see the status of engines"
|
|
1473
|
-
return self.message_for_environment(
|
|
1474
|
-
message,
|
|
1475
|
-
"rai engines:list",
|
|
1476
|
-
"rai.Provider().list_engines()"
|
|
1477
|
-
)
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
class EngineResumeFailed(RAIException):
|
|
1481
|
-
def __init__(self, engine_name: str):
|
|
1482
|
-
self.engine_name = engine_name
|
|
1483
|
-
self.message = "Unable to resume engine"
|
|
1484
|
-
self.name = "Unable to resume engine"
|
|
1485
|
-
# Format the content and raw_content
|
|
1486
|
-
self.content = self.format_message()
|
|
1487
|
-
self.raw_content = self.strip_rich_tags()
|
|
1488
|
-
super().__init__(self.message, self.name, self.content)
|
|
1489
|
-
|
|
1490
|
-
def format_message(self):
|
|
1491
|
-
suggestions = [
|
|
1492
|
-
Suggestion("cli", f"""
|
|
1493
|
-
rai engines:delete --name {self.engine_name}
|
|
1494
|
-
rai engines:create --name {self.engine_name}
|
|
1495
|
-
"""[1:-1]),
|
|
1496
|
-
Suggestion("python", f"""
|
|
1497
|
-
rai.Provider().delete_engine('{self.engine_name}')
|
|
1498
|
-
rai.Provider().create_engine('{self.engine_name}')
|
|
1499
|
-
"""[1:-1])
|
|
1500
|
-
]
|
|
1501
|
-
return "\n".join([
|
|
1502
|
-
f"The engine [yellow]{self.engine_name}[/yellow] could not be automatically resumed. Please ensure both your native app install and the `relationalai` package are up to date.",
|
|
1503
|
-
"",
|
|
1504
|
-
f"You can also manually recreate the engine {suggest(suggestions)}"
|
|
1505
|
-
])
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
class InvalidEngineSizeError(RAIException):
|
|
1509
|
-
def __init__(self, engine_size: str, valid_sizes: list[str]):
|
|
1510
|
-
msg = f"Invalid engine size '{engine_size}'"
|
|
1511
|
-
self.name = msg
|
|
1512
|
-
self.message = msg
|
|
1513
|
-
|
|
1514
|
-
# Format the content and raw_content
|
|
1515
|
-
self.content = "".join([
|
|
1516
|
-
f"Invalid engine size [yellow]{engine_size}[/yellow] provided. Please check your config."
|
|
1517
|
-
"\n",
|
|
1518
|
-
f"Valid engine sizes are: [green]{', '.join(valid_sizes)}[/green]"
|
|
1519
|
-
])
|
|
1520
|
-
|
|
1521
|
-
super().__init__(self.message, self.name, self.content)
|
|
1522
|
-
|
|
1523
|
-
class EngineNameValidationException(RAIException):
|
|
1524
|
-
def __init__(self, engine_name: str):
|
|
1525
|
-
self.engine_name = engine_name
|
|
1526
|
-
msg = "Engine auto-creation failed due to invalid engine name"
|
|
1527
|
-
self.name = msg
|
|
1528
|
-
self.message = msg
|
|
1529
|
-
|
|
1530
|
-
# Format the content and raw_content
|
|
1531
|
-
self.content = self.format_message()
|
|
1532
|
-
self.raw_content = self.strip_rich_tags()
|
|
1533
|
-
|
|
1534
|
-
super().__init__(self.message, self.name, self.content)
|
|
1535
|
-
|
|
1536
|
-
def format_message(self):
|
|
1537
|
-
from relationalai.tools.cli_helpers import ENGINE_NAME_ERROR
|
|
1538
|
-
return "\n".join([
|
|
1539
|
-
f"We tried to auto-create engine [yellow]{self.engine_name}[/yellow] but it failed."
|
|
1540
|
-
"\n",
|
|
1541
|
-
f"Engine name requirements are: [yellow]{ENGINE_NAME_ERROR}[/yellow]"
|
|
1542
|
-
])
|
|
1543
|
-
|
|
1544
|
-
# --------------------------------------------------
|
|
1545
|
-
# Hex exceptions
|
|
1546
|
-
# --------------------------------------------------
|
|
1547
|
-
|
|
1548
|
-
class HexSessionException(RAIException):
|
|
1549
|
-
def __init__(self):
|
|
1550
|
-
self.name = "Hex session error"
|
|
1551
|
-
self.message = "Missing Snowflake session object in Hex"
|
|
1552
|
-
self.content = self.format_message()
|
|
1553
|
-
self.raw_content = self.strip_rich_tags()
|
|
1554
|
-
|
|
1555
|
-
super().__init__(self.message, self.name, self.content)
|
|
1556
|
-
|
|
1557
|
-
def format_message(self):
|
|
1558
|
-
return textwrap.dedent("""
|
|
1559
|
-
To use RelationalAI in Hex, get your Snowflake session and supply it as a connection parameter to `rai.Model` or `rai.Provider`:
|
|
1560
|
-
|
|
1561
|
-
[green]
|
|
1562
|
-
import hextoolkit
|
|
1563
|
-
hex_snowflake_conn = hextoolkit.get_data_connection('<Your Connection Name>')
|
|
1564
|
-
hex_snowpark_session = hex_snowflake_conn.get_snowpark_session()
|
|
1565
|
-
[/green]
|
|
1566
|
-
""")
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
# --------------------------------------------------
|
|
1570
|
-
# Snowflake errors
|
|
1571
|
-
# --------------------------------------------------
|
|
1572
|
-
|
|
1573
|
-
def handle_missing_integration(e: Exception):
|
|
1574
|
-
in_warehouse = (
|
|
1575
|
-
isinstance(runtime_env, SnowbookEnvironment)
|
|
1576
|
-
and runtime_env.runner == "warehouse"
|
|
1577
|
-
)
|
|
1578
|
-
if in_warehouse and any((name in str(type(e)) for name in ("NameResolutionError", "ProgrammingError", "SnowflakeIntegrationMissingException"))):
|
|
1579
|
-
raise SnowflakeIntegrationMissingException() from None
|
|
1580
|
-
|
|
1581
|
-
access_integration_warning = textwrap.dedent("""
|
|
1582
|
-
Make sure that S3_RAI_INTERNAL_BUCKET_EGRESS_INTEGRATION is enabled. You can access the toggle from the vertical ellipsis button in the top-right corner of the Snowflake notebook window: ⋮ > Notebook settings > External access.
|
|
1583
|
-
|
|
1584
|
-
If you don't see S3_RAI_INTERNAL_BUCKET_EGRESS_INTEGRATION listed, get an account administrator to run Step 4 in the installation notebook here:
|
|
1585
|
-
|
|
1586
|
-
https://relational.ai/notebooks/relationalai-installation.ipynb
|
|
1587
|
-
""")
|
|
1588
|
-
|
|
1589
|
-
class SnowflakeMissingConfigValuesException(RAIException):
|
|
1590
|
-
def __init__(self, missing_keys: list[str], profile: str | None = None, config_file_path: str | None = None):
|
|
1591
|
-
sorted_missing_keys = sorted(missing_keys)
|
|
1592
|
-
self.name = f"Missing {len(missing_keys)} required configuration value{'s' if len(missing_keys) > 1 else ''}"
|
|
1593
|
-
config_info = f"File: {config_file_path}\n" if config_file_path else ""
|
|
1594
|
-
profile_info = f"Profile: '{profile}'\n" if profile else ""
|
|
1595
|
-
self.message = f"""
|
|
1596
|
-
{self.name}:
|
|
1597
|
-
|
|
1598
|
-
[green]{', '.join(sorted_missing_keys)}[/green]
|
|
1599
|
-
|
|
1600
|
-
{config_info}{profile_info}
|
|
1601
|
-
[yellow]Please update the configuration file above to include the missing values.
|
|
1602
|
-
"""
|
|
1603
|
-
self.content = self.format_message()
|
|
1604
|
-
|
|
1605
|
-
super().__init__(self.message, self.name, self.content)
|
|
1606
|
-
|
|
1607
|
-
def format_message(self):
|
|
1608
|
-
return f"""{self.message}"""
|
|
1609
|
-
|
|
1610
|
-
class SnowflakeDatabaseException(RAIException):
|
|
1611
|
-
def __init__(self, exception: Exception):
|
|
1612
|
-
self.message = str(exception)
|
|
1613
|
-
self.name = "Snowflake Database Error"
|
|
1614
|
-
self.content = self.format_message()
|
|
1615
|
-
|
|
1616
|
-
super().__init__(self.message, self.name, self.content)
|
|
1617
|
-
|
|
1618
|
-
def format_message(self):
|
|
1619
|
-
return f"""{self.message}"""
|
|
1620
|
-
|
|
1621
|
-
class SnowflakeIntegrationMissingException(RAIException):
|
|
1622
|
-
def __init__(self):
|
|
1623
|
-
self.message = access_integration_warning
|
|
1624
|
-
self.name = "Integration not enabled"
|
|
1625
|
-
self.content = self.format_message()
|
|
1626
|
-
|
|
1627
|
-
super().__init__(self.message, self.name, self.content)
|
|
1628
|
-
|
|
1629
|
-
def format_message(self):
|
|
1630
|
-
return access_integration_warning
|
|
1631
|
-
|
|
1632
|
-
class SnowflakeAppMissingException(RAIException):
|
|
1633
|
-
def __init__(self, app_name: str, role: str | None = None):
|
|
1634
|
-
self.app_name = app_name
|
|
1635
|
-
self.role = role
|
|
1636
|
-
self.message = (
|
|
1637
|
-
f"Either the app '{app_name}' isn't installed in this Snowflake account "
|
|
1638
|
-
f"or you don't have permission to access it."
|
|
1639
|
-
)
|
|
1640
|
-
self.name = "Couldn't find RelationalAI Snowflake application"
|
|
1641
|
-
self.content = self.format_message()
|
|
1642
|
-
super().__init__(self.message, self.name, self.content)
|
|
1643
|
-
|
|
1644
|
-
def format_message(self):
|
|
1645
|
-
base = (
|
|
1646
|
-
f"Either the app '{self.app_name}' isn't installed in this Snowflake account, "
|
|
1647
|
-
f"or your active role(s) don't have permission to access it.\n\n"
|
|
1648
|
-
f"If it's installed under a different name, run 'rai init' on the command line to set the app name."
|
|
1649
|
-
)
|
|
1650
|
-
|
|
1651
|
-
if self.role:
|
|
1652
|
-
base += f"\n\nYour current primary role is '{self.role}', and by default all assigned roles are used as secondary roles."
|
|
1653
|
-
|
|
1654
|
-
base += "\n\nWhile access via secondary roles is supported, we recommend granting the necessary privileges directly to your primary role to ensure consistent access."
|
|
1655
|
-
|
|
1656
|
-
return base
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
class SnowflakeImportMissingException(RAIException):
|
|
1660
|
-
def __init__(self, source: debugging.SourceInfo|None, import_name: str, model_name: str):
|
|
1661
|
-
self.source = source
|
|
1662
|
-
self.import_name = import_name
|
|
1663
|
-
self.model_name = model_name
|
|
1664
|
-
self.message = (
|
|
1665
|
-
f"The Snowflake object '{import_name}' hasn't been imported into RAI."
|
|
1666
|
-
)
|
|
1667
|
-
self.name = "Couldn't find import"
|
|
1668
|
-
self.content = self.format_message()
|
|
1669
|
-
|
|
1670
|
-
super().__init__(self.message, self.name, self.content)
|
|
1671
|
-
|
|
1672
|
-
def format_message(self):
|
|
1673
|
-
return textwrap.dedent(f"""
|
|
1674
|
-
The Snowflake object '{self.import_name}' hasn't been imported into RAI.
|
|
1675
|
-
To automatically handle imports please verify that `use_graph_index` is not set to `False` in your config.
|
|
1676
|
-
|
|
1677
|
-
You can also create an import for it using:
|
|
1678
|
-
|
|
1679
|
-
[green]rai imports:stream --source {self.import_name} --model {self.model_name}[/green]
|
|
1680
|
-
|
|
1681
|
-
[green]rai.Provider().create_streams(['{self.import_name}'], '{self.model_name}')[/green]
|
|
1682
|
-
""")
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
class SnowflakeChangeTrackingNotEnabledException(RAIException):
|
|
1686
|
-
def __init__(self, obj: tuple[str, str] | list[tuple[str, str]]):
|
|
1687
|
-
self.obj = obj
|
|
1688
|
-
if isinstance(obj, tuple):
|
|
1689
|
-
fqn, type = obj
|
|
1690
|
-
self.sql = f"[green]ALTER {type.upper()} {fqn} SET CHANGE_TRACKING = TRUE;[/green]"
|
|
1691
|
-
self.message = f"Change tracking isn't enabled for '{obj}'."
|
|
1692
|
-
elif isinstance(obj, list):
|
|
1693
|
-
self.sql = "\n".join([f"[green]ALTER {type.upper()} {fqn} SET CHANGE_TRACKING = TRUE;[/green]" for fqn, type in obj])
|
|
1694
|
-
self.message = f"Change tracking isn't enabled for the following sources: {', '.join(fqn for fqn, _ in obj)}"
|
|
1695
|
-
|
|
1696
|
-
self.name = "Change tracking not enabled"
|
|
1697
|
-
self.content = self.format_message()
|
|
1698
|
-
|
|
1699
|
-
super().__init__(self.message, self.name, self.content)
|
|
1700
|
-
|
|
1701
|
-
def format_message(self):
|
|
1702
|
-
if isinstance(self.obj, tuple):
|
|
1703
|
-
fqn_str, _ = self.obj
|
|
1704
|
-
elif isinstance(self.obj, list):
|
|
1705
|
-
fqn_str = ', '.join(fqn for fqn, _ in self.obj)
|
|
1706
|
-
else:
|
|
1707
|
-
fqn_str = self.obj
|
|
1708
|
-
return (
|
|
1709
|
-
f"Change tracking isn't enabled for [yellow]{fqn_str}[/yellow].\n\n"
|
|
1710
|
-
f"To enable change tracking, you'll need to run the following SQL:\n\n"
|
|
1711
|
-
f"{self.sql}\n\n"
|
|
1712
|
-
f"If you want automatic enabling, please add [cyan]ensure_change_tracking = true[/cyan] to your config or Model"
|
|
1713
|
-
).strip()
|
|
1714
|
-
|
|
1715
|
-
class ModelNotFoundException(RAIException):
|
|
1716
|
-
def __init__(self, model_name: str):
|
|
1717
|
-
self.model_name = model_name
|
|
1718
|
-
self.message = "Model not found"
|
|
1719
|
-
self.name = f"Model '{model_name}' not found"
|
|
1720
|
-
|
|
1721
|
-
# Format the content and raw_content
|
|
1722
|
-
self.content = self.format_message()
|
|
1723
|
-
self.raw_content = self.strip_rich_tags()
|
|
1724
|
-
|
|
1725
|
-
super().__init__(self.message, self.name, self.content)
|
|
1726
|
-
|
|
1727
|
-
def format_message(self):
|
|
1728
|
-
return textwrap.dedent(f"""
|
|
1729
|
-
The model '{self.model_name}' does not exist. You can create it by running a program containing the following:
|
|
1730
|
-
|
|
1731
|
-
model = relationalai.Model("{self.model_name}")
|
|
1732
|
-
""")
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
class SnowflakeTableObject:
|
|
1736
|
-
def __init__(self, message: str, source: str):
|
|
1737
|
-
self.message = message
|
|
1738
|
-
self.source = source
|
|
1739
|
-
|
|
1740
|
-
class SnowflakeTableObjectsException(RAIException):
|
|
1741
|
-
def __init__(self, table_objects: List[SnowflakeTableObject]):
|
|
1742
|
-
self.table_objects = table_objects
|
|
1743
|
-
table_count = len(table_objects)
|
|
1744
|
-
table_word = "table" if table_count == 1 else "tables"
|
|
1745
|
-
|
|
1746
|
-
self.message = f"Getting the following {table_word} failed with the error in Snowflake."
|
|
1747
|
-
self.name = f"Snowflake {table_word.capitalize()} Streaming Error"
|
|
1748
|
-
|
|
1749
|
-
self.content = self.format_message()
|
|
1750
|
-
|
|
1751
|
-
super().__init__(self.message, self.name, self.content)
|
|
1752
|
-
|
|
1753
|
-
def format_message(self):
|
|
1754
|
-
formatted_objects = [
|
|
1755
|
-
f"Source: [yellow bold]{obj.source}[/yellow bold]\n\n"
|
|
1756
|
-
f"Message: {obj.message.strip()}"
|
|
1757
|
-
for obj in self.table_objects
|
|
1758
|
-
]
|
|
1759
|
-
return "\n\n".join(formatted_objects)
|
|
1760
|
-
|
|
1761
|
-
class SnowflakeInvalidSource(RAIException):
|
|
1762
|
-
def __init__(self, source, type_source: str):
|
|
1763
|
-
self.source = source
|
|
1764
|
-
self.type_source = type_source
|
|
1765
|
-
self.message = "Invalid source"
|
|
1766
|
-
self.name = "Invalid source"
|
|
1767
|
-
self.content = self.format_message()
|
|
1768
|
-
|
|
1769
|
-
super().__init__(self.message, self.name, self.content, self.source)
|
|
1770
|
-
|
|
1771
|
-
def format_message(self):
|
|
1772
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
1773
|
-
return textwrap.dedent(f"""
|
|
1774
|
-
The source provided isn't a fully qualified Snowflake table or view name:
|
|
1775
|
-
|
|
1776
|
-
{marked}
|
|
1777
|
-
""")
|
|
1778
|
-
|
|
1779
|
-
class SnowflakeRaiAppNotStarted(RAIException):
|
|
1780
|
-
def __init__(self, app_name: str):
|
|
1781
|
-
self.app_name = app_name
|
|
1782
|
-
self.message = f"The RelationalAI app '{app_name}' isn't started."
|
|
1783
|
-
self.name = "The RelationalAI application isn't running"
|
|
1784
|
-
self.content = self.format_message()
|
|
1785
|
-
|
|
1786
|
-
super().__init__(self.message, self.name, self.content)
|
|
1787
|
-
|
|
1788
|
-
def format_message(self):
|
|
1789
|
-
return textwrap.dedent(f"""
|
|
1790
|
-
Your app '{self.app_name}' has not been activated.
|
|
1791
|
-
You can activate it by running the following SQL:
|
|
1792
|
-
|
|
1793
|
-
[green]call {self.app_name}.app.activate();[/green]
|
|
1794
|
-
""")
|
|
1795
|
-
|
|
1796
|
-
# --------------------------------------------------
|
|
1797
|
-
# RAI Warnings
|
|
1798
|
-
# --------------------------------------------------
|
|
1799
|
-
|
|
1800
|
-
class InvalidIfWarning(RAIWarning):
|
|
1801
|
-
def __init__(self, task: Task | Action, start_line: int, end_line: int):
|
|
1802
|
-
self.name = "Invalid if"
|
|
1803
|
-
self.source = debugging.get_source(task)
|
|
1804
|
-
self.start_line = start_line
|
|
1805
|
-
self.end_line = end_line
|
|
1806
|
-
self.content = self.format_message()
|
|
1807
|
-
|
|
1808
|
-
def format_message(self):
|
|
1809
|
-
marked = self.mark_source()
|
|
1810
|
-
updated = mark_source(
|
|
1811
|
-
self.source.modify(IfToWithTransformer()),
|
|
1812
|
-
self.start_line,
|
|
1813
|
-
self.end_line,
|
|
1814
|
-
highlight="green",
|
|
1815
|
-
) if self.source else ""
|
|
1816
|
-
dynamic = mark_source(
|
|
1817
|
-
self.source.modify(WithDynamic()),
|
|
1818
|
-
self.source.line,
|
|
1819
|
-
self.end_line,
|
|
1820
|
-
highlight="green",
|
|
1821
|
-
) if self.source else ""
|
|
1822
|
-
|
|
1823
|
-
return textwrap.dedent(f"""
|
|
1824
|
-
In a RelationalAI query, using an if statement dynamically modifies the structure of the query itself, rather than adding a conditional.
|
|
1825
|
-
|
|
1826
|
-
{marked}
|
|
1827
|
-
|
|
1828
|
-
If you're trying to do an action based on a condition, use a [green]with[/green] statement instead.
|
|
1829
|
-
|
|
1830
|
-
{updated}
|
|
1831
|
-
|
|
1832
|
-
If you are trying to create a dynamic query where parts are conditional, add the [green]dynamic=True[/green] flag to the query like so:
|
|
1833
|
-
|
|
1834
|
-
{dynamic}
|
|
1835
|
-
""")
|
|
1836
|
-
|
|
1837
|
-
def mark_source(self):
|
|
1838
|
-
return mark_source(self.source, self.start_line, self.end_line)
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
class InvalidLoopWarning(RAIWarning):
|
|
1842
|
-
def __init__(self, task: Task | Action, start_line: int, end_line: int):
|
|
1843
|
-
self.name = "Invalid loop"
|
|
1844
|
-
self.source = debugging.get_source(task)
|
|
1845
|
-
self.start_line = start_line
|
|
1846
|
-
self.end_line = end_line
|
|
1847
|
-
self.content = self.format_message()
|
|
1848
|
-
|
|
1849
|
-
def format_message(self):
|
|
1850
|
-
marked = self.mark_source() if self.source else ""
|
|
1851
|
-
dynamic = mark_source(
|
|
1852
|
-
self.source.modify(WithDynamic()),
|
|
1853
|
-
self.source.line,
|
|
1854
|
-
self.end_line,
|
|
1855
|
-
highlight="green",
|
|
1856
|
-
) if self.source else ""
|
|
1857
|
-
|
|
1858
|
-
return textwrap.dedent(f"""
|
|
1859
|
-
In a RelationalAI query, using a loop statement would dynamically modify the query itself, like a macro.
|
|
1860
|
-
|
|
1861
|
-
{marked}
|
|
1862
|
-
|
|
1863
|
-
If that's the goal, you can add the [green]dynamic=True[/green] flag to the query:
|
|
1864
|
-
|
|
1865
|
-
{dynamic}
|
|
1866
|
-
""")
|
|
1867
|
-
|
|
1868
|
-
def mark_source(self):
|
|
1869
|
-
return mark_source(self.source, self.start_line, self.end_line)
|
|
1870
|
-
|
|
1871
|
-
class InvalidTryWarning(RAIWarning):
|
|
1872
|
-
def __init__(self, task: Task | Action, start_line: int, end_line: int):
|
|
1873
|
-
self.name = "Invalid try"
|
|
1874
|
-
self.source = debugging.get_source(task)
|
|
1875
|
-
self.start_line = start_line
|
|
1876
|
-
self.end_line = end_line
|
|
1877
|
-
self.content = self.format_message()
|
|
1878
|
-
|
|
1879
|
-
def format_message(self):
|
|
1880
|
-
marked = self.mark_source()
|
|
1881
|
-
dynamic = mark_source(
|
|
1882
|
-
self.source.modify(WithDynamic()),
|
|
1883
|
-
self.source.line,
|
|
1884
|
-
self.end_line,
|
|
1885
|
-
highlight="green",
|
|
1886
|
-
) if self.source else ""
|
|
1887
|
-
|
|
1888
|
-
return textwrap.dedent(f"""
|
|
1889
|
-
In a RelationalAI query, using a try statement will have no effect unless a macro-like function is being called and can fail.
|
|
1890
|
-
|
|
1891
|
-
{marked}
|
|
1892
|
-
|
|
1893
|
-
If macro-like behavior is the goal, you can add the [green]dynamic=True[/green] flag to the query:
|
|
1894
|
-
|
|
1895
|
-
{dynamic}
|
|
1896
|
-
""")
|
|
1897
|
-
|
|
1898
|
-
def mark_source(self):
|
|
1899
|
-
return mark_source(self.source, self.start_line, self.end_line)
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
class InvalidBoolWarning(RAIWarning):
|
|
1903
|
-
def __init__(self, source):
|
|
1904
|
-
self.name = "Invalid boolean expression with Producer"
|
|
1905
|
-
self.source = source
|
|
1906
|
-
self.start_line = source.line
|
|
1907
|
-
self.end_line = source.line
|
|
1908
|
-
self.content = self.format_message()
|
|
1909
|
-
self.raw_content = self.strip_rich_tags()
|
|
1910
|
-
|
|
1911
|
-
def format_message(self):
|
|
1912
|
-
marked = self.mark_source()
|
|
1913
|
-
return textwrap.dedent(f"""
|
|
1914
|
-
In a RelationalAI query, the truth values of Producer objects are unknown
|
|
1915
|
-
until the query has been evaluated. You may not use Producers in boolean
|
|
1916
|
-
expressions, such as those involving [green]if[/green], [green]while[/green], [green]and[/green], [green]or[/green], and [green]not[/green]:
|
|
1917
|
-
|
|
1918
|
-
{marked}
|
|
1919
|
-
|
|
1920
|
-
Producer objects include:
|
|
1921
|
-
|
|
1922
|
-
- [green]Instance[/green] objects, such as [green]person[/green].
|
|
1923
|
-
- [green]InstanceProperty[/green] objects, such as [green]person.age[/green].
|
|
1924
|
-
- [green]Expression[/green] objects, such as [green]person.age >= 18[/green].
|
|
1925
|
-
""")
|
|
1926
|
-
|
|
1927
|
-
def mark_source(self):
|
|
1928
|
-
return mark_source(self.source, self.start_line, self.end_line)
|
|
1929
|
-
|
|
1930
|
-
# whether the python library or the Rel schema is out of date (or both!)
|
|
1931
|
-
class InvalidVersionKind(Enum):
|
|
1932
|
-
LibraryOutOfDate = 'LibraryOutOfDate'
|
|
1933
|
-
SchemaOutOfDate = 'SchemaOutOfDate'
|
|
1934
|
-
Incompatible = 'Incompatible'
|
|
1935
|
-
|
|
1936
|
-
class RAIInvalidVersionWarning(RAIWarning):
|
|
1937
|
-
def __init__(
|
|
1938
|
-
self,
|
|
1939
|
-
kind: InvalidVersionKind,
|
|
1940
|
-
expected: List[Tuple[str, str, str]],
|
|
1941
|
-
lock: dict,
|
|
1942
|
-
platform: str,
|
|
1943
|
-
model_name: str,
|
|
1944
|
-
app_name: str,
|
|
1945
|
-
engine_name: str,
|
|
1946
|
-
errors: List[Tuple[InvalidVersionKind, str, str]] = []
|
|
1947
|
-
):
|
|
1948
|
-
super().__init__()
|
|
1949
|
-
self.libraries = self.get_current_libs(errors)
|
|
1950
|
-
self.expected_lib_version = self.get_expected_lib_version(expected)
|
|
1951
|
-
self.current_lib_version = self.get_current_lib_version(lock)
|
|
1952
|
-
self.model_name = model_name
|
|
1953
|
-
self.app_name = app_name
|
|
1954
|
-
self.engine_name = engine_name
|
|
1955
|
-
|
|
1956
|
-
self.name = "Invalid version"
|
|
1957
|
-
|
|
1958
|
-
if kind == InvalidVersionKind.LibraryOutOfDate:
|
|
1959
|
-
msg = textwrap.dedent("""
|
|
1960
|
-
The RelationalAI Python library is out of date with respect to the schema. You can update the library using the following command:
|
|
1961
|
-
|
|
1962
|
-
[green]pip install relationalai --upgrade[/green]
|
|
1963
|
-
""").strip()
|
|
1964
|
-
elif kind == InvalidVersionKind.SchemaOutOfDate:
|
|
1965
|
-
# if the "schema" is out of date, we need to delete the engine and recreate it
|
|
1966
|
-
msg = textwrap.dedent(
|
|
1967
|
-
f"""The engine [yellow]{self.engine_name}[/yellow] is out of date with your version of the relationalai Python package.
|
|
1968
|
-
Please delete the engine using the CLI command below and re-run your program:
|
|
1969
|
-
|
|
1970
|
-
[green]rai engines:delete --name {self.engine_name}[/green]""").strip()
|
|
1971
|
-
else:
|
|
1972
|
-
command = "pip install relationalai --upgrade"
|
|
1973
|
-
warehouse_notebook = (
|
|
1974
|
-
isinstance(runtime_env, SnowbookEnvironment)
|
|
1975
|
-
and runtime_env.runner == "warehouse"
|
|
1976
|
-
)
|
|
1977
|
-
if isinstance(runtime_env, IPythonEnvironment) and not warehouse_notebook:
|
|
1978
|
-
command = "!" + command
|
|
1979
|
-
if warehouse_notebook:
|
|
1980
|
-
lib_msg = textwrap.dedent("""
|
|
1981
|
-
The RelationalAI Python library is incompatible with the current schema.
|
|
1982
|
-
|
|
1983
|
-
You can update the library by downloading the latest ZIP file from https://relational.ai/relationalai.zip and uploading it here.
|
|
1984
|
-
""").strip()
|
|
1985
|
-
else:
|
|
1986
|
-
lib_msg = textwrap.dedent(f"""
|
|
1987
|
-
The RelationalAI Python library is incompatible with the current schema.
|
|
1988
|
-
You can update the library using the following command:
|
|
1989
|
-
|
|
1990
|
-
[green]{command}[/green]
|
|
1991
|
-
""").strip()
|
|
1992
|
-
|
|
1993
|
-
msg = f"{lib_msg}\n\nIn addition, the schema must be recreated."
|
|
1994
|
-
|
|
1995
|
-
if len(self.libraries) > 0:
|
|
1996
|
-
library_info = self.generate_library_info()
|
|
1997
|
-
full_msg = f"{msg}\n\n{library_info}"
|
|
1998
|
-
else:
|
|
1999
|
-
full_msg = msg
|
|
2000
|
-
|
|
2001
|
-
self.content = full_msg
|
|
2002
|
-
|
|
2003
|
-
def extract_version(self, version_obj):
|
|
2004
|
-
match = re.search(r"\('([^']+)'\)", repr(version_obj))
|
|
2005
|
-
if match:
|
|
2006
|
-
return match.group(1)
|
|
2007
|
-
else:
|
|
2008
|
-
return ""
|
|
2009
|
-
|
|
2010
|
-
def get_expected_lib_version(self, expected):
|
|
2011
|
-
libraries = {}
|
|
2012
|
-
for name, _, version_range in expected:
|
|
2013
|
-
from_version = self.extract_version(version_range[0])
|
|
2014
|
-
to_version = self.extract_version(version_range[1])
|
|
2015
|
-
libraries[name] = {"from": from_version, "to": to_version}
|
|
2016
|
-
return libraries
|
|
2017
|
-
|
|
2018
|
-
def get_current_lib_version(self, lock):
|
|
2019
|
-
libraries = {}
|
|
2020
|
-
for k, v in lock.items():
|
|
2021
|
-
libraries[k[0]] = v
|
|
2022
|
-
return libraries
|
|
2023
|
-
|
|
2024
|
-
def get_current_libs(self, errors=[]):
|
|
2025
|
-
if len(errors) == 0:
|
|
2026
|
-
return []
|
|
2027
|
-
|
|
2028
|
-
libraries = []
|
|
2029
|
-
for _, library, _ in errors:
|
|
2030
|
-
libraries.append(library)
|
|
2031
|
-
return libraries
|
|
2032
|
-
|
|
2033
|
-
def generate_library_info(self):
|
|
2034
|
-
if len(self.libraries) == 1:
|
|
2035
|
-
lib = self.libraries[0]
|
|
2036
|
-
current_version = self.current_lib_version.get(lib, "unknown")
|
|
2037
|
-
expected_version = self.expected_lib_version.get(lib, {})
|
|
2038
|
-
from_version = expected_version.get("from", "unknown")
|
|
2039
|
-
to_version = expected_version.get("to", "unknown")
|
|
2040
|
-
return (
|
|
2041
|
-
f"Your current library '{lib}' has version {current_version}. "
|
|
2042
|
-
f"However, it is expected to have a version range between {from_version} and {to_version}."
|
|
2043
|
-
)
|
|
2044
|
-
else:
|
|
2045
|
-
info = "Your current libraries have the following issues:\n"
|
|
2046
|
-
for lib in self.libraries:
|
|
2047
|
-
current_version = self.current_lib_version.get(lib, "unknown")
|
|
2048
|
-
expected_version = self.expected_lib_version.get(lib, {})
|
|
2049
|
-
from_version = expected_version.get("from", "unknown")
|
|
2050
|
-
to_version = expected_version.get("to", "unknown")
|
|
2051
|
-
info += (
|
|
2052
|
-
f"- Library '{lib}' has version {current_version}. "
|
|
2053
|
-
f"Expected version range: {from_version} to {to_version}.\n"
|
|
2054
|
-
)
|
|
2055
|
-
return info.strip()
|
|
2056
|
-
|
|
2057
|
-
class UnsupportedVisualizationError(RAIException):
|
|
2058
|
-
def __init__(self):
|
|
2059
|
-
name = "Unsupported Visualization Error"
|
|
2060
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
2061
|
-
super().__init__(name, name, self.format_message(), self.source)
|
|
2062
|
-
|
|
2063
|
-
def format_message(self):
|
|
2064
|
-
marked = mark_source(self.source)
|
|
2065
|
-
return textwrap.dedent(f"""
|
|
2066
|
-
This environment does not support embedding interactive visualizations via Graph.visualize().
|
|
2067
|
-
|
|
2068
|
-
{marked}
|
|
2069
|
-
|
|
2070
|
-
You can use graph.fetch() [1] to retrieve the necessary data to render your own visualization via whatever means the environment supports. Alternatively, consider running your program in another supported environment like a python script or jupyter.
|
|
2071
|
-
|
|
2072
|
-
[1]: https://relational.ai/docs/reference/python/std/graphs/Graph/fetch
|
|
2073
|
-
""")
|
|
2074
|
-
|
|
2075
|
-
class EngineSizeMismatchWarning(RAIWarning):
|
|
2076
|
-
def __init__(self, engine_name: str, existing_size: str, requested_size: str):
|
|
2077
|
-
self.engine_name = engine_name
|
|
2078
|
-
self.existing_size = existing_size
|
|
2079
|
-
self.requested_size = requested_size
|
|
2080
|
-
self.message = f"An engine named '{engine_name}' already exists with a different size ('{existing_size}'). The existing engine will be used."
|
|
2081
|
-
self.name = "Engine size mismatch"
|
|
2082
|
-
|
|
2083
|
-
# Format the content and raw_content
|
|
2084
|
-
self.content = self.format_message()
|
|
2085
|
-
self.raw_content = self.strip_rich_tags()
|
|
2086
|
-
|
|
2087
|
-
super().__init__(self.message, self.name, self.content)
|
|
2088
|
-
|
|
2089
|
-
def format_message(self):
|
|
2090
|
-
return textwrap.dedent(f"""
|
|
2091
|
-
An engine named '{self.engine_name}' already exists with a different size ('{self.existing_size}'). The existing engine will be used. You can change the size of '{self.engine_name}' by deleting the existing engine.
|
|
2092
|
-
|
|
2093
|
-
You can delete the engine with either of the following commands:
|
|
2094
|
-
|
|
2095
|
-
[green]rai engines:delete --name {self.engine_name}[/green]
|
|
2096
|
-
|
|
2097
|
-
[green]rai.Provider().delete_engine('{self.engine_name}')[/green]
|
|
2098
|
-
""")
|
|
2099
|
-
|
|
2100
|
-
#--------------------------------------------------
|
|
2101
|
-
# Deprecations + Migrations
|
|
2102
|
-
#--------------------------------------------------
|
|
2103
|
-
|
|
2104
|
-
class DeprecationWarning(RAIWarning):
|
|
2105
|
-
def __init__(self, api: str, fix: str|None = None):
|
|
2106
|
-
self.api = api
|
|
2107
|
-
self.fix = fix
|
|
2108
|
-
|
|
2109
|
-
self.name = "Deprecation Warning"
|
|
2110
|
-
self.prefix = f"'{api}' is deprecated and should no longer be used."
|
|
2111
|
-
self.message = f"{self.prefix} {fix}" if fix else self.prefix
|
|
2112
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
2113
|
-
self.content = self.format_message()
|
|
2114
|
-
|
|
2115
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
2116
|
-
|
|
2117
|
-
def format_message(self):
|
|
2118
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
2119
|
-
return textwrap.dedent(f"""
|
|
2120
|
-
{self.prefix}
|
|
2121
|
-
|
|
2122
|
-
{marked}
|
|
2123
|
-
|
|
2124
|
-
{self.fix}
|
|
2125
|
-
""")
|
|
2126
|
-
|
|
2127
|
-
class SnowflakeProxyAPIDeprecationWarning(DeprecationWarning):
|
|
2128
|
-
def __init__(self):
|
|
2129
|
-
super().__init__("clients.snowflake.Snowflake", "Instead, use the `source` keyword argument when defining types. See https://relational.ai/docs/reference/python/Model/Type for details.")
|
|
2130
|
-
|
|
2131
|
-
class SnowflakeProxySourceError(RAIException):
|
|
2132
|
-
def __init__(self):
|
|
2133
|
-
self.name = "Snowflake Proxy Source Error"
|
|
2134
|
-
self.message = "Data sources derived from the deprecated snowflake proxy 'clients.snowflake.Snowflake' aren't supported with your current configuration."
|
|
2135
|
-
self.source = Errors.call_source() or debugging.SourceInfo()
|
|
2136
|
-
self.content = self.format_message()
|
|
2137
|
-
|
|
2138
|
-
super().__init__(self.message, self.name, self.content, source=self.source)
|
|
2139
|
-
|
|
2140
|
-
def format_message(self):
|
|
2141
|
-
marked = mark_source(self.source, self.source.line, self.source.line)
|
|
2142
|
-
return textwrap.dedent(f"""
|
|
2143
|
-
{self.message}
|
|
2144
|
-
|
|
2145
|
-
{marked}
|
|
2146
|
-
|
|
2147
|
-
Instead, use the `source` keyword argument when defining types. See https://relational.ai/docs/reference/python/Model/Type for details.
|
|
2148
|
-
""")
|
|
2149
|
-
|
|
2150
|
-
#--------------------------------------------------
|
|
2151
|
-
# Direct Access Warnings
|
|
2152
|
-
#--------------------------------------------------
|
|
2153
|
-
|
|
2154
|
-
class DirectAccessInvalidAuthWarning(RAIWarning):
|
|
2155
|
-
def __init__(self, authenticator, valid_authenticators):
|
|
2156
|
-
self.authenticator = authenticator
|
|
2157
|
-
self.valid_authenticators = valid_authenticators
|
|
2158
|
-
self.name = "Direct Access Invalid Auth"
|
|
2159
|
-
self.message = f"Due to security constraints in Snowflake, the selected authenticator '{authenticator}' is not supported when using direct access."
|
|
2160
|
-
self.content = self.format_message()
|
|
2161
|
-
super().__init__(self.message, self.name, self.content)
|
|
2162
|
-
|
|
2163
|
-
def format_message(self):
|
|
2164
|
-
valid_authenticators_str = ", ".join(self.valid_authenticators)
|
|
2165
|
-
return textwrap.dedent(f"""
|
|
2166
|
-
{self.message}
|
|
2167
|
-
Falling back to 'use_direct_access=False'. This comes with a potential negative latency impact.
|
|
2168
|
-
Direct access requires one of the following authenticators: {valid_authenticators_str}
|
|
2169
|
-
""")
|
|
2170
|
-
|
|
2171
|
-
class NonDefaultLQPSemanticsVersionWarning(RAIWarning):
|
|
2172
|
-
def __init__(self, current_version: str, default_version: str):
|
|
2173
|
-
self.current_version = current_version
|
|
2174
|
-
self.default_version = default_version
|
|
2175
|
-
self.name = "Non-default LQP Semantics Version"
|
|
2176
|
-
self.message = f"Using non-default LQP semantics version {current_version}. Default is {default_version}."
|
|
2177
|
-
self.content = self.format_message()
|
|
2178
|
-
super().__init__(self.message, self.name, self.content)
|
|
2179
|
-
|
|
2180
|
-
def format_message(self):
|
|
2181
|
-
return textwrap.dedent(f"""
|
|
2182
|
-
{self.message}
|
|
2183
|
-
|
|
2184
|
-
You are using a non-default LQP semantics version, likely to avoid a change in
|
|
2185
|
-
behaviour that broke one of your models. This is a reminder to ensure you switch
|
|
2186
|
-
back to the default version once any blocking issues have been resolved.
|
|
2187
|
-
|
|
2188
|
-
To do so you need to remove the following section from your raiconfig.toml:
|
|
2189
|
-
|
|
2190
|
-
[reasoner.rule]
|
|
2191
|
-
lqp.semantics_version = {self.current_version}
|
|
2192
|
-
""")
|
|
2193
|
-
|
|
2194
|
-
class InsecureKeychainWarning(RAIWarning):
|
|
2195
|
-
def __init__(self):
|
|
2196
|
-
self.message = "Insecure keyring detected. Please use a secure keyring backend."
|
|
2197
|
-
self.name = "Insecure Keyring Warning"
|
|
2198
|
-
self.content = self.format_message()
|
|
2199
|
-
|
|
2200
|
-
super().__init__(self.message, self.name, self.content)
|
|
2201
|
-
|
|
2202
|
-
def format_message(self):
|
|
2203
|
-
return textwrap.dedent(f"""
|
|
2204
|
-
{self.message}
|
|
2205
|
-
|
|
2206
|
-
A secure keychain is used to cache refresh tokens for oauth authentication. Without
|
|
2207
|
-
this caching mechanism a re-authentication will be required for each execution.
|
|
2208
|
-
""")
|
|
2209
|
-
|
|
2210
|
-
class KeychainFailureWarning(RAIWarning):
|
|
2211
|
-
def __init__(self, message):
|
|
2212
|
-
self.message = message
|
|
2213
|
-
self.name = "Keychain Failure Warning"
|
|
2214
|
-
self.content = self.format_message()
|
|
2215
|
-
|
|
2216
|
-
super().__init__(self.message, self.name, self.content)
|
|
2217
|
-
|
|
2218
|
-
def format_message(self):
|
|
2219
|
-
return textwrap.dedent(f"""
|
|
2220
|
-
{self.message}
|
|
2221
|
-
|
|
2222
|
-
This may be due to a misconfiguration or an issue with the keyring backend.
|
|
2223
|
-
A secure keychain is used to cache refresh tokens for oauth authentication. Without
|
|
2224
|
-
this caching mechanism a re-authentication will be required for each execution.
|
|
2225
|
-
""")
|
|
2226
|
-
|
|
2227
|
-
#--------------------------------------------------
|
|
2228
|
-
# Direct Access Exceptions
|
|
2229
|
-
#--------------------------------------------------
|
|
2230
|
-
|
|
2231
|
-
class DirectAccessInvalidAuthException(RAIException):
|
|
2232
|
-
def __init__(self, authenticator, valid_authenticators):
|
|
2233
|
-
self.authenticator = authenticator
|
|
2234
|
-
self.valid_authenticators = valid_authenticators
|
|
2235
|
-
self.name = "Direct Access Invalid Auth"
|
|
2236
|
-
self.message = f"Due to security constraints in Snowflake, the selected authenticator '{authenticator}' is not supported when using direct access."
|
|
2237
|
-
self.content = self.format_message()
|
|
2238
|
-
super().__init__(self.message, self.name, self.content)
|
|
2239
|
-
|
|
2240
|
-
def format_message(self):
|
|
2241
|
-
valid_authenticators_str = ", ".join(self.valid_authenticators)
|
|
2242
|
-
return textwrap.dedent(f"""
|
|
2243
|
-
{self.message}
|
|
2244
|
-
Use a valid authenticator or deactivate direct access via 'use_direct_access=False'.
|
|
2245
|
-
Direct access requires one of the following authenticators: {valid_authenticators_str}
|
|
2246
|
-
""")
|
|
2247
|
-
|
|
2248
|
-
class ResponseStatusException(RAIException):
|
|
2249
|
-
def __init__(self, message: str, response):
|
|
2250
|
-
self.message = message
|
|
2251
|
-
self.response = response
|
|
2252
|
-
self.response_message = self._extract_response_message()
|
|
2253
|
-
self.name = "Response Status Error"
|
|
2254
|
-
self.content = self.format_message()
|
|
2255
|
-
|
|
2256
|
-
super().__init__(self.message, self.name, self.content)
|
|
2257
|
-
|
|
2258
|
-
def format_message(self):
|
|
2259
|
-
return f"{self.message} Status Code: {self.response.status_code}, Reason: {self.response.reason} {self.response_message}"
|
|
2260
|
-
|
|
2261
|
-
def _extract_response_message(self):
|
|
2262
|
-
try:
|
|
2263
|
-
return self.response.json().get("message", self.response.text)
|
|
2264
|
-
except Exception:
|
|
2265
|
-
return self.response.text
|
|
2266
|
-
|
|
2267
|
-
class OAuthFailedPortBinding(RAIException):
|
|
2268
|
-
def __init__(self, redirect_port, exception):
|
|
2269
|
-
self.redirect_port = redirect_port
|
|
2270
|
-
self.message = f"Failed to bind to the OAuth redirect URI port {self.redirect_port}."
|
|
2271
|
-
self.name = "OAuth Failed Port Binding Error"
|
|
2272
|
-
self.exception = exception
|
|
2273
|
-
self.content = self.format_message()
|
|
2274
|
-
super().__init__(self.message, self.name, self.content)
|
|
2275
|
-
|
|
2276
|
-
def format_message(self):
|
|
2277
|
-
return textwrap.dedent(f"""
|
|
2278
|
-
{self.message}
|
|
2279
|
-
|
|
2280
|
-
This usually happens when the specified port for the OAuth redirect URI is not available.
|
|
2281
|
-
You can fix the issue by ensuring that the port {self.redirect_port} is open and accessible.
|
|
2282
|
-
You can check which processes are using this port with the command 'lsof -t -i :{self.redirect_port}' on Unix-based systems or 'netstat -ano | findstr :{self.redirect_port}' on Windows.
|
|
2283
|
-
If you are sure no process should be running on this port, you can try killing any processes that are using it.
|
|
2284
|
-
|
|
2285
|
-
The original exception was: {self.exception}
|
|
2286
|
-
""")
|
|
2287
|
-
|
|
2288
|
-
#TODO: Take out call_source from Errors class and put it in the main module
|
|
2289
|
-
class Errors:
|
|
2290
|
-
@staticmethod
|
|
2291
|
-
def call_source(steps=None):
|
|
2292
|
-
if steps is None:
|
|
2293
|
-
return runtime_env.get_source()
|
|
2294
|
-
return runtime_env.get_source(steps + 1)
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
class InvalidAliasError(RAIException):
|
|
2298
|
-
def __init__(self, msg: str):
|
|
2299
|
-
self.message = msg
|
|
2300
|
-
self.name = "Invalid property alias found"
|
|
2301
|
-
self.content = self.format_message()
|
|
2302
|
-
|
|
2303
|
-
super().__init__(self.message, self.name, self.content)
|
|
2304
|
-
|
|
2305
|
-
def format_message(self):
|
|
2306
|
-
return self.message
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
class LeftOverRelationException(RAIException):
|
|
2310
|
-
def __init__(self, import_name: str, model: str):
|
|
2311
|
-
self.import_name = import_name
|
|
2312
|
-
self.model = model
|
|
2313
|
-
self.message = f"Relations are not empty for import '{import_name}'"
|
|
2314
|
-
self.name = f"Stream relations for '{import_name}' are not empty"
|
|
2315
|
-
self.content = self.format_message()
|
|
2316
|
-
|
|
2317
|
-
super().__init__(self.message, self.name, self.content)
|
|
2318
|
-
|
|
2319
|
-
def format_message(self):
|
|
2320
|
-
return textwrap.dedent(f"""
|
|
2321
|
-
The import '{self.import_name}' cannot be created due to existing non-empty relationships.
|
|
2322
|
-
|
|
2323
|
-
You can use the [green]--force[/green] parameter to overwrite these:
|
|
2324
|
-
|
|
2325
|
-
[green]rai imports:stream --source {self.import_name.upper()} --model {self.model} --force[/green]
|
|
2326
|
-
|
|
2327
|
-
To delete the import and all its relationships, use the [green]--force[/green] parameter:
|
|
2328
|
-
|
|
2329
|
-
[green]rai imports:delete --object {self.import_name.upper()} --model {self.model} --force[/green]
|
|
2330
|
-
""")
|
|
2331
|
-
|
|
2332
|
-
class UnsupportedColumnTypesWarning(RAIWarning):
|
|
2333
|
-
def __init__(self, table_invalid_columns: dict[str, dict[str, str]]):
|
|
2334
|
-
self.table_invalid_columns = table_invalid_columns
|
|
2335
|
-
self.message = self._generate_message()
|
|
2336
|
-
self.name = self._generate_name()
|
|
2337
|
-
self.content = self.format_message()
|
|
2338
|
-
|
|
2339
|
-
super().__init__(self.message, self.name, self.content)
|
|
2340
|
-
|
|
2341
|
-
def _generate_message(self):
|
|
2342
|
-
total_tables = len(self.table_invalid_columns)
|
|
2343
|
-
total_columns = sum(len(invalid_columns) for invalid_columns in self.table_invalid_columns.values())
|
|
2344
|
-
|
|
2345
|
-
if total_tables == 1:
|
|
2346
|
-
table_name = next(iter(self.table_invalid_columns.keys()))
|
|
2347
|
-
return f"Found {total_columns} unsupported column type(s) in table '{table_name}'"
|
|
2348
|
-
else:
|
|
2349
|
-
return f"Found {total_columns} unsupported column type(s) across {total_tables} tables"
|
|
2350
|
-
|
|
2351
|
-
def _generate_name(self):
|
|
2352
|
-
total_tables = len(self.table_invalid_columns)
|
|
2353
|
-
total_columns = sum(len(invalid_columns) for invalid_columns in self.table_invalid_columns.values())
|
|
2354
|
-
|
|
2355
|
-
if total_tables == 1:
|
|
2356
|
-
table_name = next(iter(self.table_invalid_columns.keys()))
|
|
2357
|
-
return f"Unsupported Column Types - {total_columns} column(s) in {table_name}"
|
|
2358
|
-
else:
|
|
2359
|
-
return f"Unsupported Column Types - {total_columns} column(s) across {total_tables} tables"
|
|
2360
|
-
|
|
2361
|
-
def format_message(self):
|
|
2362
|
-
def extract_type(data_type):
|
|
2363
|
-
# Extract just the basic type from Snowflake data_type
|
|
2364
|
-
# data_type might be a JSON string like {"type":"VARIANT","nullable":true}
|
|
2365
|
-
# or a simple string like "VARIANT"
|
|
2366
|
-
try:
|
|
2367
|
-
parsed = json.loads(data_type)
|
|
2368
|
-
if isinstance(parsed, dict) and "type" in parsed:
|
|
2369
|
-
return parsed["type"]
|
|
2370
|
-
return str(parsed)
|
|
2371
|
-
except (json.JSONDecodeError, TypeError):
|
|
2372
|
-
# If it's not JSON, treat it as a simple string
|
|
2373
|
-
if '(' in data_type:
|
|
2374
|
-
return data_type.split('(')[0]
|
|
2375
|
-
return data_type
|
|
2376
|
-
|
|
2377
|
-
total_tables = len(self.table_invalid_columns)
|
|
2378
|
-
total_columns = sum(len(invalid_columns) for invalid_columns in self.table_invalid_columns.values())
|
|
2379
|
-
|
|
2380
|
-
# Build organized display by table
|
|
2381
|
-
table_sections = []
|
|
2382
|
-
columns_displayed = 0
|
|
2383
|
-
MAX_COLUMNS_TO_DISPLAY = 10
|
|
2384
|
-
|
|
2385
|
-
for table_name, invalid_columns in self.table_invalid_columns.items():
|
|
2386
|
-
table_columns = []
|
|
2387
|
-
for col_name, data_type in invalid_columns.items():
|
|
2388
|
-
if columns_displayed >= MAX_COLUMNS_TO_DISPLAY:
|
|
2389
|
-
break
|
|
2390
|
-
type_only = extract_type(data_type)
|
|
2391
|
-
table_columns.append(f" [yellow]{col_name}[/yellow]: [red]{type_only}[/red]")
|
|
2392
|
-
columns_displayed += 1
|
|
2393
|
-
|
|
2394
|
-
if table_columns:
|
|
2395
|
-
table_sections.append(f"[yellow]{table_name}[/yellow]:\n" + "\n".join(table_columns))
|
|
2396
|
-
|
|
2397
|
-
if columns_displayed >= MAX_COLUMNS_TO_DISPLAY:
|
|
2398
|
-
break
|
|
2399
|
-
|
|
2400
|
-
# Build the message
|
|
2401
|
-
if total_tables == 1:
|
|
2402
|
-
table_name = next(iter(self.table_invalid_columns.keys()))
|
|
2403
|
-
message = f"Found {total_columns} unsupported column type(s) in table '{table_name}':"
|
|
2404
|
-
else:
|
|
2405
|
-
message = f"Found {total_columns} unsupported column type(s) across {total_tables} tables:"
|
|
2406
|
-
|
|
2407
|
-
if total_columns > MAX_COLUMNS_TO_DISPLAY:
|
|
2408
|
-
message += f" (showing first {MAX_COLUMNS_TO_DISPLAY} for brevity)"
|
|
2409
|
-
|
|
2410
|
-
note = "These columns will not be accessible in your model.\n\nFor the list of supported column types see: https://docs.relational.ai/api/cli/imports/stream/#supported-column-types"
|
|
2411
|
-
|
|
2412
|
-
columns_text = "\n\n".join(table_sections)
|
|
2413
|
-
|
|
2414
|
-
return textwrap.dedent(f"""
|
|
2415
|
-
{message}
|
|
2416
|
-
|
|
2417
|
-
{columns_text}
|
|
2418
|
-
|
|
2419
|
-
{note}
|
|
2420
|
-
""")
|
|
2421
|
-
|
|
2422
|
-
class QueryTimeoutExceededException(RAIException):
|
|
2423
|
-
def __init__(self, timeout_mins: int, query_id: str | None = None, config_file_path: str | None = None):
|
|
2424
|
-
self.timeout_mins = timeout_mins
|
|
2425
|
-
self.name = "Query Timeout Exceeded"
|
|
2426
|
-
self.message = f"Query execution time exceeded the specified timeout of {self.timeout_mins} minutes."
|
|
2427
|
-
self.query_id = query_id or ""
|
|
2428
|
-
self.config_file_path = config_file_path or ""
|
|
2429
|
-
self.content = self.format_message()
|
|
2430
|
-
super().__init__(self.message, self.name, self.content)
|
|
2431
|
-
|
|
2432
|
-
def format_message(self):
|
|
2433
|
-
return textwrap.dedent(f"""
|
|
2434
|
-
Query execution time exceeded the specified timeout of {self.timeout_mins} minutes{f' for query with ID: {self.query_id}' if self.query_id else ''}.
|
|
2435
|
-
|
|
2436
|
-
Consider increasing the 'query_timeout_mins' parameter in your configuration file{f' (stored in {self.config_file_path})' if self.config_file_path else ''} to allow more time for query execution.
|
|
2437
|
-
""")
|
|
2438
|
-
|
|
2439
|
-
class GuardRailsException(RAIException):
|
|
2440
|
-
def __init__(self, progress: dict[str, Any]={}):
|
|
2441
|
-
self.name = "Guard Rails Violation"
|
|
2442
|
-
self.message = "Transaction aborted due to guard rails violation."
|
|
2443
|
-
self.progress = progress
|
|
2444
|
-
self.content = self.format_message()
|
|
2445
|
-
super().__init__(self.message, self.name, self.content)
|
|
2446
|
-
|
|
2447
|
-
def format_message(self):
|
|
2448
|
-
messages = [] if self.progress else [self.message]
|
|
2449
|
-
for task in self.progress.get("tasks", {}).values():
|
|
2450
|
-
for warning_type, warning_data in task.get("warnings", {}).items():
|
|
2451
|
-
messages.append(textwrap.dedent(f"""
|
|
2452
|
-
Relation Name: [yellow]{task["task_name"]}[/yellow]
|
|
2453
|
-
Warning: {warning_type}
|
|
2454
|
-
Message: {warning_data["message"]}
|
|
2455
|
-
"""))
|
|
2456
|
-
return "\n".join(messages)
|
|
2457
|
-
|
|
2458
|
-
#--------------------------------------------------
|
|
2459
|
-
# Azure Exceptions
|
|
2460
|
-
#--------------------------------------------------
|
|
2461
|
-
|
|
2462
|
-
class AzureUnsupportedQueryTimeoutException(RAIException):
|
|
2463
|
-
def __init__(self, config_file_path: str | None = None):
|
|
2464
|
-
self.message = "Query timeouts aren't supported on platform Azure."
|
|
2465
|
-
self.name = "Azure Unsupported Query Timeout Error"
|
|
2466
|
-
self.config_file_path = config_file_path or ""
|
|
2467
|
-
self.content = self.format_message()
|
|
2468
|
-
super().__init__(self.message, self.name, self.content)
|
|
2469
|
-
|
|
2470
|
-
def format_message(self):
|
|
2471
|
-
return textwrap.dedent(f"""
|
|
2472
|
-
{self.message}
|
|
2473
|
-
|
|
2474
|
-
Please remove the 'query_timeout_mins' from your configuration file{f' (stored in {self.config_file_path})' if self.config_file_path else ''} when running on platform Azure.
|
|
2475
|
-
""")
|
|
2476
|
-
|
|
2477
|
-
class AzureLegacyDependencyMissingException(RAIException):
|
|
2478
|
-
def __init__(self):
|
|
2479
|
-
self.message = "The Azure platform requires the 'legacy' extras to be installed."
|
|
2480
|
-
self.name = "Azure Legacy Dependency Missing"
|
|
2481
|
-
self.content = self.format_message()
|
|
2482
|
-
super().__init__(self.message, self.name, self.content)
|
|
2483
|
-
|
|
2484
|
-
def format_message(self):
|
|
2485
|
-
return textwrap.dedent("""
|
|
2486
|
-
The Azure platform requires the 'rai-sdk' package, which is not installed.
|
|
2487
|
-
|
|
2488
|
-
To use the Azure platform, please install the legacy extras:
|
|
2489
|
-
|
|
2490
|
-
pip install relationalai[legacy]
|
|
2491
|
-
|
|
2492
|
-
Or if upgrading an existing installation:
|
|
2493
|
-
|
|
2494
|
-
pip install --upgrade relationalai[legacy]
|
|
2495
|
-
""")
|
|
2496
|
-
|