precis-cli 0.1.3__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.
- app/__init__.py +16 -0
- app/api/__init__.py +32 -0
- app/api/dependencies.py +185 -0
- app/api/main.py +301 -0
- app/api/middleware/__init__.py +34 -0
- app/api/middleware/exception_handler.py +126 -0
- app/api/middleware/request_logging.py +97 -0
- app/api/middleware/token_auth.py +215 -0
- app/api/models/__init__.py +151 -0
- app/api/models/connection_rules.py +98 -0
- app/api/models/files.py +99 -0
- app/api/models/full_validation.py +554 -0
- app/api/models/project.py +117 -0
- app/api/models/projects.py +80 -0
- app/api/models/schema.py +121 -0
- app/api/models/v2_responses.py +46 -0
- app/api/models/validation.py +303 -0
- app/api/models/workspace.py +118 -0
- app/api/routers/__init__.py +56 -0
- app/api/routers/ai/__init__.py +36 -0
- app/api/routers/ai/chat.py +243 -0
- app/api/routers/ai/generate.py +156 -0
- app/api/routers/ai/hardware.py +199 -0
- app/api/routers/ai/jobs.py +544 -0
- app/api/routers/ai/migrate.py +471 -0
- app/api/routers/ai/models.py +352 -0
- app/api/routers/ai/ollama.py +108 -0
- app/api/routers/ai/providers.py +491 -0
- app/api/routers/ai/router.py +44 -0
- app/api/routers/ai/stream.py +281 -0
- app/api/routers/ai/utils.py +182 -0
- app/api/routers/core/__init__.py +34 -0
- app/api/routers/core/connection_rules.py +188 -0
- app/api/routers/core/data_sources.py +374 -0
- app/api/routers/core/regex.py +333 -0
- app/api/routers/core/reporting.py +263 -0
- app/api/routers/files/__init__.py +24 -0
- app/api/routers/files/ops.py +179 -0
- app/api/routers/files/transfer.py +101 -0
- app/api/routers/preview/__init__.py +35 -0
- app/api/routers/preview/content_mode.py +264 -0
- app/api/routers/preview/header_row.py +154 -0
- app/api/routers/preview/models.py +81 -0
- app/api/routers/preview/path_mode.py +440 -0
- app/api/routers/preview/router.py +30 -0
- app/api/routers/project/__init__.py +61 -0
- app/api/routers/project/base.py +111 -0
- app/api/routers/project/constraint.py +345 -0
- app/api/routers/project/full_config.py +411 -0
- app/api/routers/project/full_config_writer.py +327 -0
- app/api/routers/project/helpers.py +167 -0
- app/api/routers/project/inspection_fix.py +330 -0
- app/api/routers/project/manifest.py +605 -0
- app/api/routers/project/models.py +177 -0
- app/api/routers/project/pattern.py +268 -0
- app/api/routers/project/regex.py +358 -0
- app/api/routers/project/scanner.py +157 -0
- app/api/routers/project/schema.py +567 -0
- app/api/routers/project/schema_helpers.py +149 -0
- app/api/routers/project/settings.py +399 -0
- app/api/routers/project/template.py +325 -0
- app/api/routers/project/validation.py +227 -0
- app/api/routers/project/view.py +177 -0
- app/api/routers/project/workspaces.py +167 -0
- app/api/routers/projects/__init__.py +25 -0
- app/api/routers/projects/create.py +121 -0
- app/api/routers/projects/open.py +103 -0
- app/api/routers/projects/scan.py +100 -0
- app/api/routers/validation/__init__.py +39 -0
- app/api/routers/validation/common.py +263 -0
- app/api/routers/validation/content_mode.py +401 -0
- app/api/routers/validation/history.py +235 -0
- app/api/routers/validation/inline_mode.py +196 -0
- app/api/routers/validation/path_mode.py +314 -0
- app/api/routers/validation/router.py +51 -0
- app/api/services/full_validation_response_builder.py +412 -0
- app/api/services/io_error_messages.py +41 -0
- app/api/services/preview_service.py +248 -0
- app/cli/__init__.py +18 -0
- app/cli/__main__.py +30 -0
- app/cli/shared_services/__init__.py +42 -0
- app/cli/shared_services/config_ops.py +581 -0
- app/cli/shared_services/generation_ops.py +160 -0
- app/cli/shared_services/project_ops.py +288 -0
- app/cli/shell/__init__.py +48 -0
- app/cli/shell/commands/__init__.py +63 -0
- app/cli/shell/commands/ai/__init__.py +260 -0
- app/cli/shell/commands/ai/chat.py +331 -0
- app/cli/shell/commands/ai/delete.py +165 -0
- app/cli/shell/commands/ai/diff.py +70 -0
- app/cli/shell/commands/ai/display.py +428 -0
- app/cli/shell/commands/ai/executor.py +225 -0
- app/cli/shell/commands/ai/executor_utils.py +271 -0
- app/cli/shell/commands/ai/generate.py +285 -0
- app/cli/shell/commands/ai/interaction.py +262 -0
- app/cli/shell/commands/ai/migrate.py +281 -0
- app/cli/shell/commands/ai/resolver.py +224 -0
- app/cli/shell/commands/ai/status.py +101 -0
- app/cli/shell/commands/ai/switch.py +158 -0
- app/cli/shell/commands/ai/utils.py +52 -0
- app/cli/shell/commands/base.py +343 -0
- app/cli/shell/commands/config/__init__.py +131 -0
- app/cli/shell/commands/config/base.py +80 -0
- app/cli/shell/commands/config/check.py +246 -0
- app/cli/shell/commands/config/edit.py +148 -0
- app/cli/shell/commands/config/get.py +111 -0
- app/cli/shell/commands/config/init.py +123 -0
- app/cli/shell/commands/config/inspect.py +164 -0
- app/cli/shell/commands/config/list.py +104 -0
- app/cli/shell/commands/config/set.py +119 -0
- app/cli/shell/commands/config/show.py +150 -0
- app/cli/shell/commands/exit.py +67 -0
- app/cli/shell/commands/help.py +104 -0
- app/cli/shell/commands/infer_schema.py +156 -0
- app/cli/shell/commands/open.py +201 -0
- app/cli/shell/commands/project.py +215 -0
- app/cli/shell/commands/provider.py +619 -0
- app/cli/shell/commands/system.py +170 -0
- app/cli/shell/commands/validate.py +455 -0
- app/cli/shell/completer.py +231 -0
- app/cli/shell/config_storage.py +191 -0
- app/cli/shell/exceptions.py +90 -0
- app/cli/shell/formatter.py +371 -0
- app/cli/shell/interactive_menu.py +348 -0
- app/cli/shell/main.py +286 -0
- app/cli/shell/parser.py +266 -0
- app/cli/start.py +180 -0
- app/cli_main.py +52 -0
- app/mcp_server.py +311 -0
- app/shared/core/__init__.py +18 -0
- app/shared/core/app_version.py +59 -0
- app/shared/core/config/__init__.py +119 -0
- app/shared/core/config/server.py +193 -0
- app/shared/core/data_source/__init__.py +106 -0
- app/shared/core/data_source/loader.py +348 -0
- app/shared/core/data_source/loaders/__init__.py +213 -0
- app/shared/core/data_source/loaders/base.py +215 -0
- app/shared/core/data_source/loaders/converter.py +337 -0
- app/shared/core/data_source/loaders/csv_loader.py +252 -0
- app/shared/core/data_source/loaders/excel_loader.py +666 -0
- app/shared/core/data_source/loaders/extractor.py +268 -0
- app/shared/core/data_source/loaders/json_loader.py +354 -0
- app/shared/core/data_source/loaders/registry.py +88 -0
- app/shared/core/data_source/loaders/sql_loader.py +313 -0
- app/shared/core/data_source/loaders/strategies/__init__.py +100 -0
- app/shared/core/data_source/loaders/strategies/array_parser.py +277 -0
- app/shared/core/data_source/loaders/strategies/lines_parser.py +355 -0
- app/shared/core/data_source/loaders/strategies/object_parser.py +278 -0
- app/shared/core/data_source/schema_info.py +50 -0
- app/shared/core/data_source/specs/__init__.py +128 -0
- app/shared/core/data_source/specs/base.py +258 -0
- app/shared/core/data_source/specs/csv_source.py +137 -0
- app/shared/core/data_source/specs/excel_source.py +141 -0
- app/shared/core/data_source/specs/file_base.py +252 -0
- app/shared/core/data_source/specs/json_source.py +226 -0
- app/shared/core/data_source/specs/sql_source.py +161 -0
- app/shared/core/encoding.py +36 -0
- app/shared/core/io/__init__.py +24 -0
- app/shared/core/io/yaml.py +379 -0
- app/shared/core/manifest_schema/__init__.py +52 -0
- app/shared/core/manifest_schema/types.py +99 -0
- app/shared/core/manifest_schema/version.py +175 -0
- app/shared/core/patterns/__init__.py +24 -0
- app/shared/core/patterns/loader.py +134 -0
- app/shared/core/patterns/writer.py +244 -0
- app/shared/core/project/__init__.py +47 -0
- app/shared/core/project/constraint/builders/__init__.py +50 -0
- app/shared/core/project/constraint/builders/base.py +100 -0
- app/shared/core/project/constraint/builders/composite.py +77 -0
- app/shared/core/project/constraint/builders/conditional.py +67 -0
- app/shared/core/project/constraint/builders/foreign_key.py +53 -0
- app/shared/core/project/constraint/builders/registry.py +64 -0
- app/shared/core/project/constraint/builders/scripted.py +51 -0
- app/shared/core/project/constraint/builders/single_column.py +86 -0
- app/shared/core/project/constraint/builders/unique.py +53 -0
- app/shared/core/project/constraint/factory.py +170 -0
- app/shared/core/project/constraint/reader.py +214 -0
- app/shared/core/project/constraint/registry.py +233 -0
- app/shared/core/project/constraint/types/__init__.py +63 -0
- app/shared/core/project/constraint/types/constraint_file.py +261 -0
- app/shared/core/project/constraint/types/refs.py +460 -0
- app/shared/core/project/constraint/types.py +28 -0
- app/shared/core/project/constraint/writer.py +181 -0
- app/shared/core/project/loader/__init__.py +56 -0
- app/shared/core/project/loader/loader.py +30 -0
- app/shared/core/project/loader/loader_parts/config_inspector.py +137 -0
- app/shared/core/project/loader/loader_parts/embedded_constraints.py +224 -0
- app/shared/core/project/loader/loader_parts/file_loaders.py +58 -0
- app/shared/core/project/loader/loader_parts/inspection_ids.py +85 -0
- app/shared/core/project/loader/loader_parts/inspector_helpers.py +312 -0
- app/shared/core/project/loader/loader_parts/inspector_id_checks.py +274 -0
- app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +690 -0
- app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +286 -0
- app/shared/core/project/loader/loader_parts/loading_error_messages.py +298 -0
- app/shared/core/project/loader/loader_parts/main.py +432 -0
- app/shared/core/project/loader/loader_parts/path_validation.py +117 -0
- app/shared/core/project/loader/loader_parts/runtime.py +125 -0
- app/shared/core/project/loader/types.py +246 -0
- app/shared/core/project/manifest/coverage.py +391 -0
- app/shared/core/project/manifest/reader.py +260 -0
- app/shared/core/project/manifest/types.py +91 -0
- app/shared/core/project/manifest/types_parts/__init__.py +60 -0
- app/shared/core/project/manifest/types_parts/constants.py +40 -0
- app/shared/core/project/manifest/types_parts/data_source.py +68 -0
- app/shared/core/project/manifest/types_parts/info.py +59 -0
- app/shared/core/project/manifest/types_parts/manifest.py +294 -0
- app/shared/core/project/manifest/types_parts/refs.py +153 -0
- app/shared/core/project/manifest/types_parts/settings.py +92 -0
- app/shared/core/project/manifest/types_parts/settings_file_processing.py +64 -0
- app/shared/core/project/manifest/types_parts/settings_script_security.py +78 -0
- app/shared/core/project/manifest/types_parts/settings_validation.py +83 -0
- app/shared/core/project/manifest/types_parts/template.py +66 -0
- app/shared/core/project/manifest/writer.py +262 -0
- app/shared/core/project/manual_data/__init__.py +27 -0
- app/shared/core/project/manual_data/types.py +75 -0
- app/shared/core/project/regex/reader.py +197 -0
- app/shared/core/project/regex/types.py +405 -0
- app/shared/core/project/regex/writer.py +123 -0
- app/shared/core/project/schema/reader.py +170 -0
- app/shared/core/project/schema/types.py +47 -0
- app/shared/core/project/schema/types_parts/__init__.py +36 -0
- app/shared/core/project/schema/types_parts/column.py +174 -0
- app/shared/core/project/schema/types_parts/column_utils.py +72 -0
- app/shared/core/project/schema/types_parts/constraint.py +165 -0
- app/shared/core/project/schema/types_parts/schema_id.py +66 -0
- app/shared/core/project/schema/types_parts/source.py +255 -0
- app/shared/core/project/schema/types_parts/source_options.py +347 -0
- app/shared/core/project/schema/types_parts/table.py +230 -0
- app/shared/core/project/schema/writer.py +139 -0
- app/shared/core/project/schema_ref_check.py +95 -0
- app/shared/core/project/template/__init__.py +27 -0
- app/shared/core/project/template/expander.py +263 -0
- app/shared/core/project/template/reader.py +120 -0
- app/shared/core/project/template/types.py +114 -0
- app/shared/core/project/transform/reader.py +76 -0
- app/shared/core/project/transform/types.py +116 -0
- app/shared/core/project/transform/writer.py +84 -0
- app/shared/core/pydantic_messages.py +59 -0
- app/shared/core/reporter/__init__.py +46 -0
- app/shared/core/reporter/reporter.py +220 -0
- app/shared/core/reporter/reporters/__init__.py +65 -0
- app/shared/core/reporter/reporters/base.py +188 -0
- app/shared/core/reporter/reporters/dingtalk_app_reporter.py +274 -0
- app/shared/core/reporter/reporters/email_reporter.py +271 -0
- app/shared/core/reporter/reporters/feishu_app_reporter.py +467 -0
- app/shared/core/reporter/reporters/local_file_reporter.py +208 -0
- app/shared/core/reporter/reporters/wecom_app_reporter.py +268 -0
- app/shared/core/utils/__init__.py +18 -0
- app/shared/core/utils/path_utils.py +60 -0
- app/shared/core/utils/regex_extract.py +113 -0
- app/shared/domain/__init__.py +114 -0
- app/shared/domain/constraints/__init__.py +76 -0
- app/shared/domain/constraints/allowed_values.py +211 -0
- app/shared/domain/constraints/base.py +141 -0
- app/shared/domain/constraints/charset.py +337 -0
- app/shared/domain/constraints/composite.py +174 -0
- app/shared/domain/constraints/condition_registry.py +130 -0
- app/shared/domain/constraints/conditional.py +629 -0
- app/shared/domain/constraints/date_logic.py +731 -0
- app/shared/domain/constraints/foreign_key.py +261 -0
- app/shared/domain/constraints/key_normalization.py +56 -0
- app/shared/domain/constraints/not_null.py +185 -0
- app/shared/domain/constraints/range.py +360 -0
- app/shared/domain/constraints/regex.py +218 -0
- app/shared/domain/constraints/scripted.py +276 -0
- app/shared/domain/constraints/unique.py +233 -0
- app/shared/domain/data_engine.py +384 -0
- app/shared/domain/data_types.py +113 -0
- app/shared/domain/data_types_parts/__init__.py +67 -0
- app/shared/domain/data_types_parts/base.py +204 -0
- app/shared/domain/data_types_parts/composite.py +250 -0
- app/shared/domain/data_types_parts/expression.py +202 -0
- app/shared/domain/data_types_parts/extracted.py +106 -0
- app/shared/domain/data_types_parts/json_types.py +234 -0
- app/shared/domain/data_types_parts/scalars.py +719 -0
- app/shared/domain/data_types_parts/sequence.py +129 -0
- app/shared/domain/dataset_schema.py +48 -0
- app/shared/domain/eval_sandbox.py +63 -0
- app/shared/domain/expression_system.py +366 -0
- app/shared/domain/regex_flags.py +53 -0
- app/shared/domain/schema/builder.py +223 -0
- app/shared/domain/schema/models.py +339 -0
- app/shared/domain/transforms/__init__.py +28 -0
- app/shared/domain/transforms/aggregate.py +141 -0
- app/shared/domain/transforms/base.py +171 -0
- app/shared/domain/transforms/cast_type.py +120 -0
- app/shared/domain/transforms/concat.py +103 -0
- app/shared/domain/transforms/conditional_assign.py +114 -0
- app/shared/domain/transforms/date_format.py +81 -0
- app/shared/domain/transforms/digits.py +77 -0
- app/shared/domain/transforms/drop_duplicates.py +94 -0
- app/shared/domain/transforms/fill_na.py +94 -0
- app/shared/domain/transforms/filter_rows.py +97 -0
- app/shared/domain/transforms/lookup.py +78 -0
- app/shared/domain/transforms/lower_case.py +70 -0
- app/shared/domain/transforms/map_value.py +90 -0
- app/shared/domain/transforms/math_expr.py +112 -0
- app/shared/domain/transforms/modulo.py +82 -0
- app/shared/domain/transforms/regex_extract.py +103 -0
- app/shared/domain/transforms/registry.py +95 -0
- app/shared/domain/transforms/replace.py +88 -0
- app/shared/domain/transforms/sort_rows.py +94 -0
- app/shared/domain/transforms/string_split.py +84 -0
- app/shared/domain/transforms/strip.py +73 -0
- app/shared/domain/transforms/substring.py +92 -0
- app/shared/domain/transforms/upper_case.py +70 -0
- app/shared/domain/transforms/weighted_sum.py +104 -0
- app/shared/domain/validation_constraints.py +69 -0
- app/shared/services/__init__.py +62 -0
- app/shared/services/ai/__init__.py +48 -0
- app/shared/services/ai/agent/__init__.py +40 -0
- app/shared/services/ai/agent/chat_tools/__init__.py +47 -0
- app/shared/services/ai/agent/chat_tools/apply_actions.py +592 -0
- app/shared/services/ai/agent/chat_tools/ask_user.py +237 -0
- app/shared/services/ai/agent/chat_tools/read_canvas.py +140 -0
- app/shared/services/ai/agent/chat_tools/read_project.py +160 -0
- app/shared/services/ai/agent/chat_tools/read_table.py +301 -0
- app/shared/services/ai/agent/chat_tools/schemas.py +105 -0
- app/shared/services/ai/agent/chat_tools/validate_table.py +131 -0
- app/shared/services/ai/agent/executor.py +554 -0
- app/shared/services/ai/agent/memory.py +256 -0
- app/shared/services/ai/agent/planner.py +282 -0
- app/shared/services/ai/agent/tool_registry.py +296 -0
- app/shared/services/ai/agent/tools/__init__.py +32 -0
- app/shared/services/ai/agent/tools/config_generate.py +133 -0
- app/shared/services/ai/agent/tools/config_refine.py +109 -0
- app/shared/services/ai/agent/tools/config_validate.py +403 -0
- app/shared/services/ai/agent/tools/merge_results.py +136 -0
- app/shared/services/ai/agent/tools/plan_chunks.py +79 -0
- app/shared/services/ai/agent/tools/script_parse.py +359 -0
- app/shared/services/ai/agent/types.py +138 -0
- app/shared/services/ai/chat_agent_runner.py +596 -0
- app/shared/services/ai/chat_orchestrator.py +725 -0
- app/shared/services/ai/failure_messages.py +65 -0
- app/shared/services/ai/job_storage.py +274 -0
- app/shared/services/ai/migrate_service.py +550 -0
- app/shared/services/ai/streaming/__init__.py +65 -0
- app/shared/services/ai/streaming/event_journal.py +197 -0
- app/shared/services/ai/streaming/orchestrator.py +262 -0
- app/shared/services/ai/streaming/pending_interaction_store.py +227 -0
- app/shared/services/ai/streaming/sse_response.py +148 -0
- app/shared/services/ai/streaming/types.py +73 -0
- app/shared/services/ai/types.py +213 -0
- app/shared/services/ai/utils.py +322 -0
- app/shared/services/diff/config_diff.py +320 -0
- app/shared/services/hardware.py +237 -0
- app/shared/services/llm/__init__.py +62 -0
- app/shared/services/llm/actions/__init__.py +42 -0
- app/shared/services/llm/actions/_canvas_validator.py +147 -0
- app/shared/services/llm/actions/_constraint_validator.py +401 -0
- app/shared/services/llm/actions/_regex_validator.py +85 -0
- app/shared/services/llm/actions/_schema_validator.py +82 -0
- app/shared/services/llm/actions/_settings_validator.py +76 -0
- app/shared/services/llm/actions/_transform_validator.py +78 -0
- app/shared/services/llm/actions/action_handlers.py +400 -0
- app/shared/services/llm/actions/action_parser.py +63 -0
- app/shared/services/llm/actions/action_processor.py +468 -0
- app/shared/services/llm/actions/action_validator.py +311 -0
- app/shared/services/llm/actions/diff_compute.py +195 -0
- app/shared/services/llm/actions/regex_handlers.py +284 -0
- app/shared/services/llm/actions/registry.py +336 -0
- app/shared/services/llm/actions/schema_handlers.py +335 -0
- app/shared/services/llm/actions/settings_handlers.py +180 -0
- app/shared/services/llm/actions/specs.py +259 -0
- app/shared/services/llm/actions/transform_handlers.py +279 -0
- app/shared/services/llm/actions/validation_types.py +147 -0
- app/shared/services/llm/cache/__init__.py +18 -0
- app/shared/services/llm/cache/response_cache.py +80 -0
- app/shared/services/llm/chat/__init__.py +35 -0
- app/shared/services/llm/chat/chat_system_prompt.py +561 -0
- app/shared/services/llm/chat/response_parser.py +296 -0
- app/shared/services/llm/config/__init__.py +45 -0
- app/shared/services/llm/config/crypto.py +135 -0
- app/shared/services/llm/config/loader.py +258 -0
- app/shared/services/llm/config/models.py +202 -0
- app/shared/services/llm/config/presets.py +128 -0
- app/shared/services/llm/config_generator.py +163 -0
- app/shared/services/llm/constraints/__init__.py +41 -0
- app/shared/services/llm/constraints/constraint_builder.py +177 -0
- app/shared/services/llm/constraints/constraint_deletion.py +84 -0
- app/shared/services/llm/constraints/constraint_id.py +174 -0
- app/shared/services/llm/constraints/frontend_instructions.py +333 -0
- app/shared/services/llm/constraints/inline_batch.py +279 -0
- app/shared/services/llm/discovery/__init__.py +35 -0
- app/shared/services/llm/discovery/scanner.py +181 -0
- app/shared/services/llm/generation/__init__.py +42 -0
- app/shared/services/llm/generation/agent_wiring.py +156 -0
- app/shared/services/llm/generation/config_builder.py +386 -0
- app/shared/services/llm/generation/errors.py +36 -0
- app/shared/services/llm/generation/existing_config.py +99 -0
- app/shared/services/llm/generation/profiler.py +279 -0
- app/shared/services/llm/generation/prompt_builder.py +199 -0
- app/shared/services/llm/generation/response_parser.py +144 -0
- app/shared/services/llm/generation/service.py +622 -0
- app/shared/services/llm/models.py +104 -0
- app/shared/services/llm/providers/__init__.py +48 -0
- app/shared/services/llm/providers/base.py +310 -0
- app/shared/services/llm/providers/cached_provider.py +90 -0
- app/shared/services/llm/providers/ollama.py +498 -0
- app/shared/services/llm/providers/openai.py +334 -0
- app/shared/services/llm/providers/registry.py +108 -0
- app/shared/services/llm/schema_resolver.py +141 -0
- app/shared/services/llm/suggestion_utils.py +219 -0
- app/shared/services/llm/validate_executor.py +163 -0
- app/shared/services/llm/yaml_io.py +406 -0
- app/shared/services/preview/__init__.py +18 -0
- app/shared/services/preview/loader.py +145 -0
- app/shared/services/preview/path_validation.py +138 -0
- app/shared/services/project_loader.py +57 -0
- app/shared/services/schema_inference.py +260 -0
- app/shared/services/schema_runtime_builder.py +120 -0
- app/shared/services/validation/__init__.py +52 -0
- app/shared/services/validation/chunked_loader.py +509 -0
- app/shared/services/validation/dag/__init__.py +35 -0
- app/shared/services/validation/dag/builder.py +141 -0
- app/shared/services/validation/dag/executor.py +225 -0
- app/shared/services/validation/dag/sorter.py +77 -0
- app/shared/services/validation/data_loader.py +231 -0
- app/shared/services/validation/engine.py +446 -0
- app/shared/services/validation/executor.py +1030 -0
- app/shared/services/validation/extractors.py +266 -0
- app/shared/services/validation/history.py +209 -0
- app/shared/services/validation/json_payload.py +136 -0
- app/shared/services/validation/loader.py +152 -0
- app/shared/services/validation/memory_monitor.py +179 -0
- app/shared/services/validation/postprocess.py +208 -0
- app/shared/services/validation/progress.py +64 -0
- app/shared/services/validation/report_export.py +222 -0
- app/shared/services/validation/resolver.py +180 -0
- app/shared/services/validation/service.py +418 -0
- app/shared/services/validation/types.py +238 -0
- app/shared/services/validation/validators/__init__.py +36 -0
- app/shared/services/validation/validators/adapter.py +182 -0
- app/shared/services/validation/validators/base.py +332 -0
- app/shared/services/validation/validators/composite.py +233 -0
- app/shared/services/validation/validators/date_logic.py +276 -0
- app/start_server.py +133 -0
- precis_cli-0.1.3.dist-info/METADATA +180 -0
- precis_cli-0.1.3.dist-info/RECORD +442 -0
- precis_cli-0.1.3.dist-info/WHEEL +5 -0
- precis_cli-0.1.3.dist-info/entry_points.txt +4 -0
- precis_cli-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""@fileoverview 通用 IO 工具模块入口
|
|
17
|
+
|
|
18
|
+
功能概述:
|
|
19
|
+
- 统一导出 YAML 读写工具(read_yaml、write_yaml)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from .yaml import read_yaml, write_yaml
|
|
23
|
+
|
|
24
|
+
__all__ = ["read_yaml", "write_yaml"]
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""@fileoverview YAML 文件读写工具模块
|
|
17
|
+
|
|
18
|
+
功能概述:
|
|
19
|
+
- 提供 YAML 配置文件的统一读写接口(read_yaml / write_yaml / write_yaml_atomic)
|
|
20
|
+
- 使用 safe_load/safe_dump 防止代码注入,自动处理 UTF-8 编码
|
|
21
|
+
- write_yaml_atomic 提供原子写入(临时文件 + os.replace),确保崩溃安全
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
import tempfile
|
|
28
|
+
import time
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Any, cast
|
|
31
|
+
|
|
32
|
+
import yaml
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def read_yaml(path: Path) -> dict[str, Any]:
|
|
36
|
+
"""
|
|
37
|
+
@methoddesc 读取 YAML 文件并解析为字典。
|
|
38
|
+
|
|
39
|
+
============================================================================
|
|
40
|
+
配置文件示例 (本函数处理的配置文件长这样)
|
|
41
|
+
============================================================================
|
|
42
|
+
本函数可以处理以下格式的 YAML 配置文件:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
# ============================================================
|
|
46
|
+
# Precis 项目清单文件示例
|
|
47
|
+
# ============================================================
|
|
48
|
+
|
|
49
|
+
# 项目配置版本号
|
|
50
|
+
version: 2
|
|
51
|
+
|
|
52
|
+
# 项目基本信息
|
|
53
|
+
project:
|
|
54
|
+
id: my-data-project
|
|
55
|
+
name: My Data Project
|
|
56
|
+
|
|
57
|
+
# 项目设置
|
|
58
|
+
settings:
|
|
59
|
+
validation:
|
|
60
|
+
auto_validate: true
|
|
61
|
+
strict_mode: false
|
|
62
|
+
error_handling: continue
|
|
63
|
+
timeout_seconds: 30
|
|
64
|
+
file_processing:
|
|
65
|
+
default_encoding: utf-8
|
|
66
|
+
csv_delimiter: ","
|
|
67
|
+
script_security:
|
|
68
|
+
allow_eval: false
|
|
69
|
+
sandbox_mode: true
|
|
70
|
+
|
|
71
|
+
# Schema 文件索引
|
|
72
|
+
schemas:
|
|
73
|
+
- id: users
|
|
74
|
+
path: schemas/users.schema.yaml
|
|
75
|
+
|
|
76
|
+
# Constraint 文件索引
|
|
77
|
+
constraints:
|
|
78
|
+
- id: email_notnull
|
|
79
|
+
path: constraints/email_notnull.constraint.yaml
|
|
80
|
+
|
|
81
|
+
# Regex 节点文件索引
|
|
82
|
+
regex_nodes:
|
|
83
|
+
- id: phone_validation
|
|
84
|
+
path: regex_nodes/phone_validation.regex.yaml
|
|
85
|
+
|
|
86
|
+
# Patterns 目录
|
|
87
|
+
patterns_dir: patterns
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
============================================================================
|
|
91
|
+
业务场景 (什么情况下会调用这个函数)
|
|
92
|
+
============================================================================
|
|
93
|
+
- 场景1: CLI 启动时读取项目配置文件
|
|
94
|
+
用户在终端执行 `precis validate --project ./my-project` 命令时,
|
|
95
|
+
系统需要读取项目根目录下的 project.precis.yaml 文件来获取
|
|
96
|
+
项目的所有配置引用(schemas、constraints、regex_nodes 等)。
|
|
97
|
+
|
|
98
|
+
- 场景2: API 上传项目时读取配置
|
|
99
|
+
用户通过 Web界面上传一个项目压缩包时,后端需要解压并读取
|
|
100
|
+
配置文件来解析项目结构。
|
|
101
|
+
|
|
102
|
+
- 场景3: 加载表结构定义
|
|
103
|
+
系统需要读取 *.schema.yaml 文件来获取表的列定义和数据源信息。
|
|
104
|
+
|
|
105
|
+
- 场景4: 加载约束规则
|
|
106
|
+
系统需要读取 *.constraint.yaml 文件来获取数据校验规则。
|
|
107
|
+
|
|
108
|
+
============================================================================
|
|
109
|
+
数据流 (输入如何变成输出)
|
|
110
|
+
============================================================================
|
|
111
|
+
输入参数:
|
|
112
|
+
- path: Path 对象,表示 YAML 文件的路径
|
|
113
|
+
示例值: Path("project.precis.yaml")
|
|
114
|
+
|
|
115
|
+
处理步骤:
|
|
116
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
117
|
+
│ Step 1: 打开文件 │
|
|
118
|
+
├─────────────────────────────────────────────────────────────┤
|
|
119
|
+
│ 输入: Path 对象 │
|
|
120
|
+
│ 操作: 使用 open() 函数以只读模式打开文件,指定 UTF-8 编码 │
|
|
121
|
+
│ 输出: 文件句柄对象 │
|
|
122
|
+
│ 示例: │
|
|
123
|
+
│ 输入: Path("config.yaml") │
|
|
124
|
+
│ 处理: open("config.yaml", "r", encoding="utf-8") │
|
|
125
|
+
│ 输出: <_io.TextIOWrapper name='config.yaml' ...> │
|
|
126
|
+
└─────────────────────────────────────────────────────────────┘
|
|
127
|
+
|
|
128
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
129
|
+
│ Step 2: 解析 YAML 内容 │
|
|
130
|
+
├─────────────────────────────────────────────────────────────┤
|
|
131
|
+
│ 输入: 文件句柄对象 │
|
|
132
|
+
│ 操作: 使用 yaml.safe_load() 安全解析 YAML 文本 │
|
|
133
|
+
│ safe_load 会自动识别 YAML 语法结构 │
|
|
134
|
+
│ 输出: Python 字典对象 │
|
|
135
|
+
│ 示例: │
|
|
136
|
+
│ 输入: "version: 2\nproject:\n id: test" 的文本 │
|
|
137
|
+
│ 处理: yaml.safe_load(文本) │
|
|
138
|
+
│ 输出: {"version": 2, "project": {"id": "test"}} │
|
|
139
|
+
└─────────────────────────────────────────────────────────────┘
|
|
140
|
+
|
|
141
|
+
最终输出: Dict[str, Any] - 包含所有配置项的字典对象
|
|
142
|
+
示例:
|
|
143
|
+
{
|
|
144
|
+
"version": 2,
|
|
145
|
+
"project": {"id": "my-project", "name": "My Project"},
|
|
146
|
+
"schemas": [{"id": "users", "path": "schemas/users.yaml"}],
|
|
147
|
+
...
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
============================================================================
|
|
151
|
+
异常处理 (可能出什么问题)
|
|
152
|
+
============================================================================
|
|
153
|
+
| 异常类型 | 触发条件 | 处理方式 |
|
|
154
|
+
|---------|---------|---------|
|
|
155
|
+
| FileNotFoundError | 指定的文件路径不存在 | 调用方需要检查文件是否存在 |
|
|
156
|
+
| PermissionError | 没有文件读取权限 | 调用方需要检查文件权限设置 |
|
|
157
|
+
| yaml.YAMLError | YAML 格式错误(如缩进错误、语法错误)| 返回带文件路径的错误信息 |
|
|
158
|
+
| UnicodeDecodeError | 文件编码不是 UTF-8 | 需要指定正确的编码格式 |
|
|
159
|
+
|
|
160
|
+
:param path: YAML 文件的路径对象,支持绝对路径和相对路径
|
|
161
|
+
:return: YAML 解析后的字典,文件内容将被映射为 Python 字典结构
|
|
162
|
+
:raises FileNotFoundError: 当指定路径的文件不存在时抛出
|
|
163
|
+
:raises yaml.YAMLError: 当 YAML 格式错误时抛出
|
|
164
|
+
"""
|
|
165
|
+
with open(path, encoding="utf-8") as f:
|
|
166
|
+
return cast(dict[str, Any], yaml.safe_load(f))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def write_yaml(path: Path, data: dict[str, Any]) -> None:
|
|
170
|
+
"""
|
|
171
|
+
@methoddesc 将字典数据写入 YAML 文件。
|
|
172
|
+
|
|
173
|
+
============================================================================
|
|
174
|
+
输入数据示例 (这个函数接收什么数据)
|
|
175
|
+
============================================================================
|
|
176
|
+
本函数接收以下格式的 Python 字典数据:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
# 示例1: 项目清单数据
|
|
180
|
+
{
|
|
181
|
+
"version": 2,
|
|
182
|
+
"project": {
|
|
183
|
+
"id": "my-data-project",
|
|
184
|
+
"name": "My Data Project"
|
|
185
|
+
},
|
|
186
|
+
"settings": {
|
|
187
|
+
"validation": {
|
|
188
|
+
"auto_validate": True,
|
|
189
|
+
"strict_mode": False
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"schemas": [
|
|
193
|
+
{"id": "users", "path": "schemas/users.schema.yaml"}
|
|
194
|
+
],
|
|
195
|
+
"constraints": [
|
|
196
|
+
{"id": "email_notnull", "path": "constraints/email_notnull.constraint.yaml"}
|
|
197
|
+
],
|
|
198
|
+
"regex_nodes": [],
|
|
199
|
+
"patterns_dir": "patterns"
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# 示例2: 约束配置数据
|
|
203
|
+
{
|
|
204
|
+
"version": 2,
|
|
205
|
+
"id": "unique_user_email",
|
|
206
|
+
"type": "Unique",
|
|
207
|
+
"enabled": True,
|
|
208
|
+
"description": "用户邮箱必须唯一",
|
|
209
|
+
"refs": {
|
|
210
|
+
"table_id": "users",
|
|
211
|
+
"column_ids": ["email"]
|
|
212
|
+
},
|
|
213
|
+
"params": {}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# 示例3: Schema 配置数据
|
|
217
|
+
{
|
|
218
|
+
"version": 2,
|
|
219
|
+
"id": "users",
|
|
220
|
+
"name": "users",
|
|
221
|
+
"source": {
|
|
222
|
+
"mode": "relative_file",
|
|
223
|
+
"path": "data/users.xlsx",
|
|
224
|
+
"sheet": "Sheet1",
|
|
225
|
+
"header_row": 0
|
|
226
|
+
},
|
|
227
|
+
"columns": [
|
|
228
|
+
{"id": "user_id", "name": "user_id", "type": "string", "primary_key": True},
|
|
229
|
+
{"id": "email", "name": "email", "type": "string"}
|
|
230
|
+
],
|
|
231
|
+
"constraints": [],
|
|
232
|
+
"script_checks": []
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
============================================================================
|
|
237
|
+
业务场景 (什么情况下会调用这个函数)
|
|
238
|
+
============================================================================
|
|
239
|
+
- 场景1: 用户通过 UI 创建新的约束规则
|
|
240
|
+
用户在约束创建页面填写完配置表单后,点击保存按钮。
|
|
241
|
+
系统将表单数据构建为字典,然后调用 write_yaml 将其
|
|
242
|
+
写入到 constraints/ 目录下的 *.constraint.yaml 文件。
|
|
243
|
+
|
|
244
|
+
- 场景2: 保存项目设置
|
|
245
|
+
用户在项目设置页面修改了校验超时时间、文件编码等配置,
|
|
246
|
+
点击保存后,系统将更新后的设置写入 project.precis.yaml。
|
|
247
|
+
|
|
248
|
+
- 场景3: 导出项目配置
|
|
249
|
+
用户希望将当前项目配置导出为可分享的配置文件,
|
|
250
|
+
系统将项目所有配置序列化为 YAML 文件供用户下载。
|
|
251
|
+
|
|
252
|
+
============================================================================
|
|
253
|
+
数据流 (输入如何变成输出)
|
|
254
|
+
============================================================================
|
|
255
|
+
输入参数:
|
|
256
|
+
- path: Path 对象,表示目标 YAML 文件的路径
|
|
257
|
+
示例值: Path("constraints/email_notnull.constraint.yaml")
|
|
258
|
+
- data: Dict[str, Any],要写入的字典数据
|
|
259
|
+
示例值: {"version": 2, "id": "email_notnull", ...}
|
|
260
|
+
|
|
261
|
+
处理步骤:
|
|
262
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
263
|
+
│ Step 1: 检查并创建父目录 │
|
|
264
|
+
├─────────────────────────────────────────────────────────────┤
|
|
265
|
+
│ 输入: 目标文件路径 │
|
|
266
|
+
│ 操作: 检查父目录是否存在,不存在则递归创建 │
|
|
267
|
+
│ parents=True: 递归创建所有不存在的父目录 │
|
|
268
|
+
│ exist_ok=True: 如果目录已存在,不抛出异常 │
|
|
269
|
+
│ 输出: 无(目录已就绪) │
|
|
270
|
+
│ 示例: │
|
|
271
|
+
│ 输入: Path("constraints/new/email.yaml") │
|
|
272
|
+
│ 处理: 创建 constraints/ 和 constraints/new/ 目录 │
|
|
273
|
+
│ 输出: (无返回值,目录已创建) │
|
|
274
|
+
└─────────────────────────────────────────────────────────────┘
|
|
275
|
+
|
|
276
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
277
|
+
│ Step 2: 打开文件准备写入 │
|
|
278
|
+
├─────────────────────────────────────────────────────────────┤
|
|
279
|
+
│ 输入: 目标路径 │
|
|
280
|
+
│ 操作: 使用 open() 函数以写入模式创建/覆盖文件 │
|
|
281
|
+
│ encoding="utf-8" 确保正确处理中文 │
|
|
282
|
+
│ 输出: 文件句柄对象 │
|
|
283
|
+
│ 示例: │
|
|
284
|
+
│ 输入: Path("config.yaml") │
|
|
285
|
+
│ 处理: open("config.yaml", "w", encoding="utf-8") │
|
|
286
|
+
│ 输出: <_io.TextIOWrapper name='config.yaml' mode='w'> │
|
|
287
|
+
└─────────────────────────────────────────────────────────────┘
|
|
288
|
+
|
|
289
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
290
|
+
│ Step 3: 序列化为 YAML 并写入 │
|
|
291
|
+
├─────────────────────────────────────────────────────────────┤
|
|
292
|
+
│ 输入: 字典数据 │
|
|
293
|
+
│ 操作: 使用 yaml.safe_dump() 将字典序列化为 YAML 格式 │
|
|
294
|
+
│ allow_unicode=True: 支持中文字符 │
|
|
295
|
+
│ sort_keys=False: 保持键的原始顺序,便于版本控制 │
|
|
296
|
+
│ 输出: 无(直接写入文件) │
|
|
297
|
+
│ 示例: │
|
|
298
|
+
│ 输入: {"version": 2, "project": {"id": "test"}} │
|
|
299
|
+
│ 处理: yaml.safe_dump(字典, 文件句柄, allow_unicode=True, sort_keys=False) │
|
|
300
|
+
│ 输出: (YAML 文本已写入文件) │
|
|
301
|
+
│ version: 2 │
|
|
302
|
+
│ project: │
|
|
303
|
+
│ id: test │
|
|
304
|
+
└─────────────────────────────────────────────────────────────┘
|
|
305
|
+
|
|
306
|
+
最终输出: None (无返回值,写入成功后直接返回)
|
|
307
|
+
|
|
308
|
+
============================================================================
|
|
309
|
+
异常处理 (可能出什么问题)
|
|
310
|
+
============================================================================
|
|
311
|
+
| 异常类型 | 触发条件 | 处理方式 |
|
|
312
|
+
|---------|---------|---------|
|
|
313
|
+
| PermissionError | 没有文件写入权限或父目录写权限 | 需要检查文件/目录权限设置 |
|
|
314
|
+
| FileNotFoundError | 父目录不存在且无法创建(如磁盘满)| 需要检查磁盘空间和路径有效性 |
|
|
315
|
+
| IsADirectoryError | path 指向一个已存在的目录 | 需要确保 path 是文件路径而非目录 |
|
|
316
|
+
|
|
317
|
+
注意事项:
|
|
318
|
+
- 如果文件已存在,write_yaml 会覆盖原有内容
|
|
319
|
+
- 写入前会自动创建所有必要的父目录
|
|
320
|
+
- YAML 键的顺序由字典本身决定,不会自动排序
|
|
321
|
+
|
|
322
|
+
:param path: 目标 YAML 文件的路径对象
|
|
323
|
+
:param data: 要写入的字典数据,支持嵌套字典和列表
|
|
324
|
+
:raises PermissionError: 没有写入权限时抛出
|
|
325
|
+
:raises FileNotFoundError: 父目录无法创建时抛出
|
|
326
|
+
"""
|
|
327
|
+
# 创建父目录,确保路径存在
|
|
328
|
+
# parents=True: 递归创建所有父目录
|
|
329
|
+
# exist_ok=True: 如果目录已存在,不抛出异常
|
|
330
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
331
|
+
|
|
332
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
333
|
+
# allow_unicode=True: 支持 Unicode 字符(如中文)
|
|
334
|
+
# sort_keys=False: 保持字典键的原始顺序,便于版本控制和审查
|
|
335
|
+
yaml.safe_dump(data, f, allow_unicode=True, sort_keys=False)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _replace_file_with_retry(src: str, dst: Path, max_retries: int = 3, delay: float = 0.2) -> None:
|
|
339
|
+
"""原子替换文件,Windows 上目标文件被锁定时自动重试。"""
|
|
340
|
+
for attempt in range(max_retries + 1):
|
|
341
|
+
try:
|
|
342
|
+
os.replace(src, dst)
|
|
343
|
+
return
|
|
344
|
+
except PermissionError:
|
|
345
|
+
if attempt < max_retries:
|
|
346
|
+
time.sleep(delay)
|
|
347
|
+
else:
|
|
348
|
+
raise
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def write_yaml_atomic(path: Path, data: dict[str, Any]) -> None:
|
|
352
|
+
"""使用原子写入方式将数据写入 YAML 文件。
|
|
353
|
+
|
|
354
|
+
使用临时文件 + os.replace 原子重命名策略,确保:
|
|
355
|
+
- 写入过程中如果失败,不会影响原文件
|
|
356
|
+
- 写入完成后原子性地替换原文件
|
|
357
|
+
- Windows 上目标文件被锁定时自动重试
|
|
358
|
+
|
|
359
|
+
使用 yaml.safe_dump(安全序列化器),防止代码注入。
|
|
360
|
+
"""
|
|
361
|
+
path = Path(path)
|
|
362
|
+
dir_path = path.parent
|
|
363
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
364
|
+
|
|
365
|
+
fd, temp_path = tempfile.mkstemp(dir=dir_path, suffix=".tmp")
|
|
366
|
+
try:
|
|
367
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
368
|
+
yaml.safe_dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False)
|
|
369
|
+
f.flush()
|
|
370
|
+
os.fsync(f.fileno())
|
|
371
|
+
# os.replace 在同一文件系统上是原子操作(POSIX 和 Windows 均保证)
|
|
372
|
+
_replace_file_with_retry(temp_path, path)
|
|
373
|
+
except Exception:
|
|
374
|
+
if os.path.exists(temp_path):
|
|
375
|
+
try:
|
|
376
|
+
os.remove(temp_path)
|
|
377
|
+
except OSError:
|
|
378
|
+
pass
|
|
379
|
+
raise
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""
|
|
17
|
+
@fileoverview Schema 版本管理模块包入口
|
|
18
|
+
|
|
19
|
+
职责:
|
|
20
|
+
- 管理配置格式版本
|
|
21
|
+
- 提供版本检查工具
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- version.py: 版本常量定义
|
|
25
|
+
- types.py: 配置类型定义
|
|
26
|
+
|
|
27
|
+
输入示例:
|
|
28
|
+
from app.shared.core.schemas import CURRENT_VERSION, is_supported_version
|
|
29
|
+
if is_supported_version(2):
|
|
30
|
+
print("当前版本受支持")
|
|
31
|
+
|
|
32
|
+
输出示例:
|
|
33
|
+
CURRENT_VERSION -> 2
|
|
34
|
+
is_supported_version(2) -> True
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from .version import (
|
|
38
|
+
CURRENT_VERSION,
|
|
39
|
+
MAX_SUPPORTED_VERSION,
|
|
40
|
+
MIN_SUPPORTED_VERSION,
|
|
41
|
+
get_version_info,
|
|
42
|
+
is_supported_version,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
# 版本相关
|
|
47
|
+
"CURRENT_VERSION",
|
|
48
|
+
"MIN_SUPPORTED_VERSION",
|
|
49
|
+
"MAX_SUPPORTED_VERSION",
|
|
50
|
+
"is_supported_version",
|
|
51
|
+
"get_version_info",
|
|
52
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""
|
|
17
|
+
@fileoverview Schema 类型定义模块
|
|
18
|
+
|
|
19
|
+
功能概述:
|
|
20
|
+
- 定义 V2 版本配置格式的 Pydantic 数据模型(V2Schema)
|
|
21
|
+
- 提供影响分析结果类型(ImpactAnalysisResult)
|
|
22
|
+
|
|
23
|
+
架构设计:
|
|
24
|
+
- 使用 Pydantic BaseModel 定义数据模型,自动完成数据校验和序列化
|
|
25
|
+
- V2Schema 对应 project.precis.yaml 的完整结构
|
|
26
|
+
- ImpactAnalysisResult 用于配置变更影响分析接口的响应
|
|
27
|
+
|
|
28
|
+
输入示例:
|
|
29
|
+
schema = V2Schema(
|
|
30
|
+
version=2,
|
|
31
|
+
project={"id": "demo", "name": "Demo Project"},
|
|
32
|
+
settings={"validation": {"auto_validate": True}},
|
|
33
|
+
schemas=[{"id": "users", "path": "schemas/users.schema.yaml"}],
|
|
34
|
+
constraints=[{"id": "unique_email", "path": "constraints/unique_email.constraint.yaml"}],
|
|
35
|
+
regex_nodes=[{"id": "phone", "path": "regex/phone.regex.yaml"}],
|
|
36
|
+
patterns_dir="patterns",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
输出示例:
|
|
40
|
+
schema.dict() -> {
|
|
41
|
+
"version": 2,
|
|
42
|
+
"project": {"id": "demo", "name": "Demo Project"},
|
|
43
|
+
"settings": {"validation": {"auto_validate": True}},
|
|
44
|
+
"schemas": [...],
|
|
45
|
+
"constraints": [...],
|
|
46
|
+
"regex_nodes": [...],
|
|
47
|
+
"patterns_dir": "patterns",
|
|
48
|
+
}
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
from typing import Any
|
|
52
|
+
|
|
53
|
+
from pydantic import BaseModel, Field
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class V2Schema(BaseModel):
|
|
57
|
+
"""
|
|
58
|
+
@classdesc V2 版本的 Schema 配置模型。
|
|
59
|
+
|
|
60
|
+
字段说明:
|
|
61
|
+
- version: 配置格式版本号,当前固定为 2
|
|
62
|
+
- project: 项目基本信息字典,包含 id、name 等
|
|
63
|
+
- settings: 项目设置字典,包含校验行为、文件处理、脚本安全等配置
|
|
64
|
+
- schemas: Schema 引用列表,每个元素包含 id 和 path
|
|
65
|
+
- constraints: Constraint 引用列表,每个元素包含 id 和 path
|
|
66
|
+
- regex_nodes: Regex 节点引用列表,每个元素包含 id 和 path
|
|
67
|
+
- patterns_dir: 预定义正则模式存放的目录路径
|
|
68
|
+
|
|
69
|
+
使用场景:
|
|
70
|
+
用于解析和校验 project.precis.yaml 配置文件的结构化数据
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
version: int = Field(2, description="配置版本")
|
|
74
|
+
project: dict[str, Any] = Field(default_factory=dict, description="项目信息")
|
|
75
|
+
settings: dict[str, Any] = Field(default_factory=dict, description="项目设置")
|
|
76
|
+
schemas: list[dict[str, Any]] = Field(default_factory=list, description="Schema 引用列表")
|
|
77
|
+
constraints: list[dict[str, Any]] = Field(default_factory=list, description="Constraint 引用列表")
|
|
78
|
+
regex_nodes: list[dict[str, Any]] = Field(default_factory=list, description="Regex 节点引用列表")
|
|
79
|
+
patterns_dir: str = Field("patterns", description="patterns 目录")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ImpactAnalysisResult(BaseModel):
|
|
83
|
+
"""
|
|
84
|
+
@classdesc 影响分析结果模型。
|
|
85
|
+
|
|
86
|
+
字段说明:
|
|
87
|
+
- summary: 分析摘要信息字典
|
|
88
|
+
- changes: 检测到的变更列表,每项为一个变更描述字典
|
|
89
|
+
- risks: 识别到的风险列表,每项为一个风险描述字典
|
|
90
|
+
- recommendations: 给出的建议列表,每项为一个建议描述字典
|
|
91
|
+
|
|
92
|
+
使用场景:
|
|
93
|
+
用于 AI 配置生成或配置变更分析接口,返回变更影响的结构化分析结果
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
summary: dict[str, Any] = Field(default_factory=dict, description="摘要")
|
|
97
|
+
changes: list[dict[str, Any]] = Field(default_factory=list, description="变更列表")
|
|
98
|
+
risks: list[dict[str, Any]] = Field(default_factory=list, description="风险列表")
|
|
99
|
+
recommendations: list[dict[str, Any]] = Field(default_factory=list, description="建议列表")
|