pyfect-dev 0.1.0__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.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""Which members of a union may take an input (Effect's candidate index).
|
|
2
|
+
|
|
3
|
+
A member is a candidate by the input's kind (``"string"``, ``"object"``,
|
|
4
|
+
...), a ``Literal`` member only for its literal. Members that are mappings
|
|
5
|
+
with required literal keys (``"_tag": "A"``) or tuples with required
|
|
6
|
+
literal elements (``("a", ...)``), Effect's sentinels, are discriminated:
|
|
7
|
+
an input picks them by the values it holds under those keys or indexes,
|
|
8
|
+
before any member is parsed.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Hashable, Mapping, Sequence
|
|
14
|
+
from dataclasses import dataclass, replace
|
|
15
|
+
from typing import TYPE_CHECKING, cast
|
|
16
|
+
|
|
17
|
+
from pyfect.logger._text import format_unknown, json_quote
|
|
18
|
+
from pyfect.schema.ast._arrays import Arrays
|
|
19
|
+
from pyfect.schema.ast._base import AST
|
|
20
|
+
from pyfect.schema.ast._class import Class
|
|
21
|
+
from pyfect.schema.ast._encoding import Encoding, to_encoded
|
|
22
|
+
from pyfect.schema.ast._keywords import BigInt, Boolean, Never, Null, Number, String, Unknown
|
|
23
|
+
from pyfect.schema.ast._literal import Enum, Literal, LiteralValue, same_literal
|
|
24
|
+
from pyfect.schema.ast._objects import ObjectKeys, Objects, reads_properties
|
|
25
|
+
from pyfect.schema.ast._suspend import Suspend
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from pyfect.schema.ast._union import Union
|
|
29
|
+
|
|
30
|
+
ALL = frozenset({"null", "string", "number", "bigint", "boolean", "array", "object"})
|
|
31
|
+
NOT_NULL = ALL - {"null"}
|
|
32
|
+
OBJECT = frozenset({"object"})
|
|
33
|
+
ARRAY = frozenset({"array"})
|
|
34
|
+
|
|
35
|
+
_VALUE_KINDS: tuple[tuple[type, frozenset[str]], ...] = (
|
|
36
|
+
(type(None), frozenset({"null"})),
|
|
37
|
+
(bool, frozenset({"boolean"})),
|
|
38
|
+
(int, frozenset({"number", "bigint"})),
|
|
39
|
+
(float, frozenset({"number"})),
|
|
40
|
+
(str, frozenset({"string"})),
|
|
41
|
+
(list, frozenset({"array"})),
|
|
42
|
+
(tuple, frozenset({"array"})),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
_NODE_KINDS: tuple[tuple[type[AST], frozenset[str]], ...] = (
|
|
46
|
+
(Null, frozenset({"null"})),
|
|
47
|
+
(String, frozenset({"string"})),
|
|
48
|
+
(Number, frozenset({"number"})),
|
|
49
|
+
(BigInt, frozenset({"bigint"})),
|
|
50
|
+
(Boolean, frozenset({"boolean"})),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def kind_of(value: object) -> frozenset[str]:
|
|
55
|
+
"""The kinds a value is (a Python ``int`` is a number and a bigint)."""
|
|
56
|
+
return next((kinds for t, kinds in _VALUE_KINDS if isinstance(value, t)), OBJECT)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _literal_kind(literal: LiteralValue) -> frozenset[str]:
|
|
60
|
+
return (
|
|
61
|
+
frozenset({"number"})
|
|
62
|
+
if isinstance(literal, int | float) and not isinstance(literal, bool)
|
|
63
|
+
else kind_of(literal)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _members(ast: AST) -> tuple[AST, ...] | None:
|
|
68
|
+
"""A union's members; ``None`` for any other node."""
|
|
69
|
+
from pyfect.schema.ast._union import Union
|
|
70
|
+
|
|
71
|
+
return ast.types if isinstance(ast, Union) else None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def kinds(ast: AST) -> frozenset[str]: # noqa: PLR0911
|
|
75
|
+
"""The kinds of input ``ast`` may accept (Effect's ``getCandidateTypes``)."""
|
|
76
|
+
members = _members(ast)
|
|
77
|
+
if members is not None:
|
|
78
|
+
return frozenset[str]().union(*map(kinds, members))
|
|
79
|
+
match ast:
|
|
80
|
+
case Literal(literal=literal):
|
|
81
|
+
return _literal_kind(literal)
|
|
82
|
+
case Enum(side="decode" | "encoded"):
|
|
83
|
+
return frozenset[str]().union(*(_literal_kind(m.value) for m in ast.enum))
|
|
84
|
+
case Objects(properties=(), index_signatures=()):
|
|
85
|
+
return NOT_NULL
|
|
86
|
+
case Objects() | Class(side="decode" | "encoded"):
|
|
87
|
+
return OBJECT
|
|
88
|
+
case Arrays():
|
|
89
|
+
return ARRAY
|
|
90
|
+
case _:
|
|
91
|
+
return next((kinds for t, kinds in _NODE_KINDS if isinstance(ast, t)), ALL)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
type Sentinel = tuple[Hashable, LiteralValue]
|
|
95
|
+
"""A required key (or a tuple's index) and the literal it must hold."""
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _input_side(ast: AST) -> AST:
|
|
99
|
+
"""The node of what the member takes as input (Effect's ``toCandidate``).
|
|
100
|
+
A suspended member takes anything: selecting candidates never makes it
|
|
101
|
+
(it may be the union itself)."""
|
|
102
|
+
return Unknown() if isinstance(ast, Suspend) else _settled(ast).flip().to_type()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _settled(ast: AST) -> AST:
|
|
106
|
+
"""``ast`` with every part whose failures a middleware sees (and may
|
|
107
|
+
recover from, as ``catch_decoding`` does) taken as ``Unknown``: such a
|
|
108
|
+
part may take any input and holds no sentinel (Effect's ``toCandidate``)."""
|
|
109
|
+
encoding = ast.encoding
|
|
110
|
+
if encoding is not None:
|
|
111
|
+
return _settled_encoding(ast, encoding)
|
|
112
|
+
members = _members(ast)
|
|
113
|
+
if members is not None:
|
|
114
|
+
return cast("Union", ast).map_types(_settled)
|
|
115
|
+
match ast:
|
|
116
|
+
case Objects(properties=properties):
|
|
117
|
+
types = [_settled(p.type) for p in properties]
|
|
118
|
+
if all(t is p.type for t, p in zip(types, properties, strict=True)):
|
|
119
|
+
return ast
|
|
120
|
+
settled = (replace(p, type=t) for t, p in zip(types, properties, strict=True))
|
|
121
|
+
return replace(ast, properties=tuple(settled))
|
|
122
|
+
case Arrays():
|
|
123
|
+
return ast.map_types(_settled)
|
|
124
|
+
case _:
|
|
125
|
+
return ast
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _settled_encoding(ast: AST, encoding: Encoding) -> AST:
|
|
129
|
+
if any(link.transformation.sees_failures for link in encoding):
|
|
130
|
+
return Unknown()
|
|
131
|
+
*links, last = encoding
|
|
132
|
+
source = _settled(last.to)
|
|
133
|
+
return ast if source is last.to else replace(ast, encoding=(*links, replace(last, to=source)))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def sentinels(ast: AST) -> tuple[Sentinel, ...]:
|
|
137
|
+
"""The required literal keys of a mapping node and the required literal
|
|
138
|
+
elements of a tuple, by index (Effect's ``collectSentinels``); a union's
|
|
139
|
+
are those all its members share."""
|
|
140
|
+
members = _members(ast)
|
|
141
|
+
if members is not None:
|
|
142
|
+
if not members:
|
|
143
|
+
return ()
|
|
144
|
+
each = [sentinels(_input_side(m)) for m in members]
|
|
145
|
+
return tuple(s for s in each[0] if all(_has(other, s) for other in each[1:]))
|
|
146
|
+
match ast:
|
|
147
|
+
case Objects(properties=properties):
|
|
148
|
+
return tuple(
|
|
149
|
+
(p.name, p.type.literal)
|
|
150
|
+
for p in properties
|
|
151
|
+
if not p.optional and isinstance(p.type, Literal)
|
|
152
|
+
)
|
|
153
|
+
case Class(side="encoded" | "decode"):
|
|
154
|
+
return sentinels(ast.shape.encoded)
|
|
155
|
+
case Arrays(elements=elements):
|
|
156
|
+
return tuple(
|
|
157
|
+
(i, e.type.literal)
|
|
158
|
+
for i, e in enumerate(elements)
|
|
159
|
+
if not e.optional and isinstance(e.type, Literal)
|
|
160
|
+
)
|
|
161
|
+
case _:
|
|
162
|
+
return ()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _has(found: Sequence[Sentinel], sentinel: Sentinel) -> bool:
|
|
166
|
+
key, literal = sentinel
|
|
167
|
+
return any(k == key and same_literal(literal, v) for k, v in found)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@dataclass(frozen=True, slots=True)
|
|
171
|
+
class _Member:
|
|
172
|
+
literal: tuple[LiteralValue] | None
|
|
173
|
+
"""``(literal,)`` for a ``Literal`` member."""
|
|
174
|
+
sentinels: tuple[Sentinel, ...]
|
|
175
|
+
kinds: frozenset[str]
|
|
176
|
+
never: bool
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _member(ast: AST) -> _Member:
|
|
180
|
+
side = _input_side(ast)
|
|
181
|
+
found = sentinels(side)
|
|
182
|
+
literal = (side.literal,) if isinstance(side, Literal) else None
|
|
183
|
+
return _Member(literal, found, frozenset() if found else kinds(side), isinstance(side, Never))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
_ABSENT = object()
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _observed(
|
|
190
|
+
input: object, keys: tuple[Hashable, ...]
|
|
191
|
+
) -> tuple[dict[Hashable, object], frozenset[str]] | None:
|
|
192
|
+
"""The values ``input`` holds under the sentinel ``keys`` (a mapping's
|
|
193
|
+
keys, an object's attributes, a list's or tuple's indexes) and its kind;
|
|
194
|
+
``None`` when it is none of these."""
|
|
195
|
+
if isinstance(input, Mapping) or reads_properties(input):
|
|
196
|
+
mapping = (
|
|
197
|
+
cast("Mapping[object, object]", input)
|
|
198
|
+
if isinstance(input, Mapping)
|
|
199
|
+
else ObjectKeys(input)
|
|
200
|
+
)
|
|
201
|
+
return {key: mapping[key] for key in keys if key in mapping}, OBJECT
|
|
202
|
+
if isinstance(input, list | tuple):
|
|
203
|
+
items = cast("Sequence[object]", input)
|
|
204
|
+
n = len(items)
|
|
205
|
+
return {key: items[key] for key in keys if type(key) is int and 0 <= key < n}, ARRAY
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class CandidateIndex:
|
|
210
|
+
"""The candidates of a union's members for an input."""
|
|
211
|
+
|
|
212
|
+
__slots__ = ("_members", "_only_literals", "_sentinel_keys")
|
|
213
|
+
|
|
214
|
+
def __init__(self, types: Sequence[AST]) -> None:
|
|
215
|
+
self._members = [_member(t) for t in types]
|
|
216
|
+
live = [m for m in self._members if not m.never]
|
|
217
|
+
self._only_literals = bool(live) and all(m.literal is not None for m in live)
|
|
218
|
+
keys: dict[Hashable, None] = {}
|
|
219
|
+
for m in live:
|
|
220
|
+
for key, _ in m.sentinels:
|
|
221
|
+
keys[key] = None
|
|
222
|
+
self._sentinel_keys = tuple(keys)
|
|
223
|
+
|
|
224
|
+
def __call__(self, input: object, constructing: bool = False) -> list[int]:
|
|
225
|
+
"""The candidates for ``input``; under a constructor (Effect's
|
|
226
|
+
constructor mode) a missing sentinel key selects every member that
|
|
227
|
+
has it, as a constructor default may fill it in."""
|
|
228
|
+
members = self._members
|
|
229
|
+
if self._only_literals:
|
|
230
|
+
return [
|
|
231
|
+
i
|
|
232
|
+
for i, m in enumerate(members)
|
|
233
|
+
if m.literal is not None and not m.never and same_literal(m.literal[0], input)
|
|
234
|
+
]
|
|
235
|
+
if self._sentinel_keys:
|
|
236
|
+
observed = _observed(input, self._sentinel_keys)
|
|
237
|
+
if observed is not None:
|
|
238
|
+
return self._by_sentinels(*observed, constructing)
|
|
239
|
+
return self._by_kind(input)
|
|
240
|
+
|
|
241
|
+
def _by_kind(self, input: object) -> list[int]:
|
|
242
|
+
kind = kind_of(input)
|
|
243
|
+
|
|
244
|
+
def takes(m: _Member) -> bool:
|
|
245
|
+
if m.literal is not None:
|
|
246
|
+
return same_literal(m.literal[0], input)
|
|
247
|
+
return not m.kinds.isdisjoint(kind)
|
|
248
|
+
|
|
249
|
+
return [
|
|
250
|
+
i for i, m in enumerate(self._members) if not m.never and not m.sentinels and takes(m)
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
def _by_sentinels(
|
|
254
|
+
self, observed: dict[Hashable, object], kind: frozenset[str], constructing: bool
|
|
255
|
+
) -> list[int]:
|
|
256
|
+
"""Discriminated members whose sentinels the input does not
|
|
257
|
+
contradict and that one of its sentinel values selects (or, when
|
|
258
|
+
``constructing``, that has a key the input lacks), plus the other
|
|
259
|
+
members taking the input's kind. A missing key is neutral."""
|
|
260
|
+
selected: list[int] = []
|
|
261
|
+
for i, m in enumerate(self._members):
|
|
262
|
+
if m.never:
|
|
263
|
+
continue
|
|
264
|
+
if not m.sentinels:
|
|
265
|
+
if not m.kinds.isdisjoint(kind):
|
|
266
|
+
selected.append(i)
|
|
267
|
+
continue
|
|
268
|
+
matched = False
|
|
269
|
+
contradicted = False
|
|
270
|
+
for key, literal in m.sentinels:
|
|
271
|
+
value = observed.get(key, _ABSENT)
|
|
272
|
+
if value is _ABSENT:
|
|
273
|
+
matched = matched or constructing
|
|
274
|
+
continue
|
|
275
|
+
if same_literal(literal, value):
|
|
276
|
+
matched = True
|
|
277
|
+
else:
|
|
278
|
+
contradicted = True
|
|
279
|
+
if matched and not contradicted:
|
|
280
|
+
selected.append(i)
|
|
281
|
+
return selected
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def member_expected(ast: AST) -> str:
|
|
285
|
+
"""How a union names a member: a mapping with literal keys as
|
|
286
|
+
``{ readonly "_tag": "A", ... }``, a tuple with literal elements as
|
|
287
|
+
``readonly [ "a", ... ]``, any other as its encoded side expects (what
|
|
288
|
+
the union decodes from)."""
|
|
289
|
+
side = to_encoded(ast)
|
|
290
|
+
if isinstance(side, Class) and side.side == "encoded":
|
|
291
|
+
side = side.shape.encoded
|
|
292
|
+
if isinstance(side, Arrays):
|
|
293
|
+
literals = [e for e in side.elements if isinstance(e.type, Literal)]
|
|
294
|
+
if literals:
|
|
295
|
+
entries = ", ".join(f"{e.type.expected()}{'?' if e.optional else ''}" for e in literals)
|
|
296
|
+
return f"{'' if side.mutable else 'readonly '}[ {entries}, ... ]"
|
|
297
|
+
if isinstance(side, Objects):
|
|
298
|
+
literals = [p for p in side.properties if isinstance(p.type, Literal)]
|
|
299
|
+
if literals:
|
|
300
|
+
entries = ", ".join(
|
|
301
|
+
f"readonly {_format_key(p.name)}{'?' if p.optional else ''}: {p.type.expected()}"
|
|
302
|
+
for p in literals
|
|
303
|
+
)
|
|
304
|
+
return f"{{ {entries}, ... }}"
|
|
305
|
+
return side.expected()
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _format_key(key: Hashable) -> str:
|
|
309
|
+
return json_quote(key) if isinstance(key, str) else format_unknown(key)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Checks (Effect's ``SchemaAST.Filter`` / ``FilterGroup``): validations a
|
|
2
|
+
node runs on a value of its own kind.
|
|
3
|
+
|
|
4
|
+
A ``Filter`` runs a function giving ``None`` (pass) or an issue; a
|
|
5
|
+
``FilterGroup`` runs several checks as one. Each failure is wrapped in an
|
|
6
|
+
``issue.Filter`` naming the check, so the formatter can print the check's
|
|
7
|
+
``message`` or ``"Expected <expected>"``. With ``errors="first"`` the first
|
|
8
|
+
failure stops the checks; with ``errors="all"`` every check runs unless one
|
|
9
|
+
marked ``abort()`` fails.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
15
|
+
from dataclasses import dataclass, replace
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
17
|
+
|
|
18
|
+
from pyfect.schema import issue as _issue
|
|
19
|
+
from pyfect.schema.issue import Composite, InvalidValue, Issue, Path, Pointer, is_issue, reported
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from pyfect.schema._options import ParseOptions
|
|
23
|
+
from pyfect.schema.ast._base import AST, Annotations
|
|
24
|
+
|
|
25
|
+
type FilterIssue = str | Issue | tuple[Path, str | Issue]
|
|
26
|
+
"""One failure a filter function reports: a message, an issue, or either
|
|
27
|
+
at a path below the value (Effect's ``{ path, issue }``)."""
|
|
28
|
+
|
|
29
|
+
type FilterOutput = bool | FilterIssue | list[FilterIssue] | None
|
|
30
|
+
"""What a filter function gives: ``True`` / ``None`` / ``[]`` to accept,
|
|
31
|
+
``False`` (``"Expected <expected>"``), one failure or a list of them."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
T_contra = TypeVar("T_contra", contravariant=True)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
38
|
+
class Filter(Generic[T_contra]):
|
|
39
|
+
"""One check. ``run(value, ast, options)`` gives ``None`` or the issue;
|
|
40
|
+
``annotations`` describe it (``expected``, ``message``, ``title``, ...);
|
|
41
|
+
an ``aborted`` filter stops the checks after it when it fails."""
|
|
42
|
+
|
|
43
|
+
run: Callable[[T_contra, AST, ParseOptions], Issue | None]
|
|
44
|
+
annotations: Annotations | None = None
|
|
45
|
+
aborted: bool = False
|
|
46
|
+
|
|
47
|
+
def annotate(self, /, **annotations: object) -> Filter[T_contra]:
|
|
48
|
+
"""This filter with ``annotations`` merged over its own."""
|
|
49
|
+
return replace(self, annotations={**(self.annotations or {}), **annotations})
|
|
50
|
+
|
|
51
|
+
def abort(self) -> Filter[T_contra]:
|
|
52
|
+
"""This filter, stopping the checks after it when it fails."""
|
|
53
|
+
return replace(self, aborted=True)
|
|
54
|
+
|
|
55
|
+
def and_[T](self: Filter[T], other: Check[T], /, **annotations: object) -> FilterGroup[T]:
|
|
56
|
+
"""This check and ``other`` as one group (Effect's ``and``)."""
|
|
57
|
+
return FilterGroup((self, other), annotations or None)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
61
|
+
class FilterGroup(Generic[T_contra]):
|
|
62
|
+
"""Several checks run as one; ``annotations`` describe the group
|
|
63
|
+
(its ``expected`` is the default text of its members' failures)."""
|
|
64
|
+
|
|
65
|
+
checks: tuple[Check[T_contra], *tuple[Check[T_contra], ...]]
|
|
66
|
+
annotations: Annotations | None = None
|
|
67
|
+
|
|
68
|
+
def annotate(self, /, **annotations: object) -> FilterGroup[T_contra]:
|
|
69
|
+
return replace(self, annotations={**(self.annotations or {}), **annotations})
|
|
70
|
+
|
|
71
|
+
def and_[T](self: FilterGroup[T], other: Check[T], /, **annotations: object) -> FilterGroup[T]:
|
|
72
|
+
return FilterGroup((self, other), annotations or None)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
type Check[T] = Filter[T] | FilterGroup[T]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def has_check(checks: Sequence[Check[Any]] | None, id: str) -> bool:
|
|
79
|
+
"""Whether a check with the ``representation`` id ``id`` is among
|
|
80
|
+
``checks`` or the groups inside them (Effect's ``hasCheck``)."""
|
|
81
|
+
for check in checks or ():
|
|
82
|
+
if isinstance(check, FilterGroup):
|
|
83
|
+
if has_check(check.checks, id):
|
|
84
|
+
return True
|
|
85
|
+
continue
|
|
86
|
+
representation = (check.annotations or {}).get("representation")
|
|
87
|
+
if isinstance(representation, Mapping) and representation.get("id") == id: # pyright: ignore[reportUnknownMemberType]
|
|
88
|
+
return True
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def make_filter[T](
|
|
93
|
+
predicate: Callable[[T], FilterOutput], /, *, abort: bool = False, **annotations: object
|
|
94
|
+
) -> Filter[T]:
|
|
95
|
+
"""A filter from a function returning a ``FilterOutput`` (Effect's
|
|
96
|
+
``makeFilter``)."""
|
|
97
|
+
|
|
98
|
+
return filter_of(predicate, annotations or None, abort)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def filter_of[T](
|
|
102
|
+
predicate: Callable[[T], FilterOutput],
|
|
103
|
+
annotations: Annotations | None = None,
|
|
104
|
+
abort: bool = False,
|
|
105
|
+
) -> Filter[T]:
|
|
106
|
+
"""``make_filter`` with the annotations as one mapping."""
|
|
107
|
+
|
|
108
|
+
def run(input: T, ast: AST, options: ParseOptions) -> Issue | None:
|
|
109
|
+
return normalize_filter_output(ast, predicate(input), input, options)
|
|
110
|
+
|
|
111
|
+
return Filter(run, annotations, abort)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def make_filter_by_guard[T](
|
|
115
|
+
guard: Callable[[T], bool], annotations: Annotations | None = None
|
|
116
|
+
) -> Filter[T]:
|
|
117
|
+
"""A filter accepting what ``guard`` accepts; it always aborts, as the
|
|
118
|
+
checks after a guard may rely on it."""
|
|
119
|
+
|
|
120
|
+
def run(input: T, ast: AST, options: ParseOptions) -> Issue | None:
|
|
121
|
+
return None if guard(input) else InvalidValue(None, reported(input, options))
|
|
122
|
+
|
|
123
|
+
return Filter(run, annotations, True)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def normalize_filter_output(
|
|
127
|
+
ast: AST, out: FilterOutput, input: object, options: ParseOptions
|
|
128
|
+
) -> Issue | None:
|
|
129
|
+
"""The issue a filter function's output stands for, or ``None``."""
|
|
130
|
+
if out is None or out is True:
|
|
131
|
+
return None
|
|
132
|
+
if out is False:
|
|
133
|
+
return InvalidValue(None, reported(input, options))
|
|
134
|
+
if not isinstance(out, list):
|
|
135
|
+
return _filter_issue(out, input, options)
|
|
136
|
+
issues = [_filter_issue(entry, input, options) for entry in out]
|
|
137
|
+
if not issues:
|
|
138
|
+
return None
|
|
139
|
+
if len(issues) == 1:
|
|
140
|
+
return issues[0]
|
|
141
|
+
return Composite(ast, (issues[0], *issues[1:]), reported(input, options))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _filter_issue(entry: FilterIssue, input: object, options: ParseOptions) -> Issue:
|
|
145
|
+
if isinstance(entry, str):
|
|
146
|
+
return InvalidValue({"message": entry}, reported(input, options))
|
|
147
|
+
if is_issue(entry):
|
|
148
|
+
return entry
|
|
149
|
+
path, inner = entry
|
|
150
|
+
return Pointer(path, _filter_issue(inner, input, options) if isinstance(inner, str) else inner)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def collect_issues(
|
|
154
|
+
checks: Sequence[Check[object]], value: object, ast: AST, options: ParseOptions
|
|
155
|
+
) -> tuple[Issue, *tuple[Issue, ...]] | None:
|
|
156
|
+
"""The failures of ``checks`` on ``value``, or ``None``."""
|
|
157
|
+
issues: list[_issue.Filter] = []
|
|
158
|
+
_collect(checks, value, ast, options, issues)
|
|
159
|
+
return (issues[0], *issues[1:]) if issues else None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _collect(
|
|
163
|
+
checks: Sequence[Check[object]],
|
|
164
|
+
value: object,
|
|
165
|
+
ast: AST,
|
|
166
|
+
options: ParseOptions,
|
|
167
|
+
issues: list[_issue.Filter],
|
|
168
|
+
) -> bool:
|
|
169
|
+
"""Append the failures; ``True`` when the checks must stop."""
|
|
170
|
+
every = options.get("errors") == "all"
|
|
171
|
+
for check in checks:
|
|
172
|
+
if isinstance(check, FilterGroup):
|
|
173
|
+
if _collect(check.checks, value, ast, options, issues):
|
|
174
|
+
return True
|
|
175
|
+
continue
|
|
176
|
+
failure = check.run(value, ast, options)
|
|
177
|
+
if failure is not None:
|
|
178
|
+
issues.append(_issue.Filter(check, failure, reported(value, options)))
|
|
179
|
+
if not every or check.aborted:
|
|
180
|
+
return True
|
|
181
|
+
return False
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""``Class``: instances of a schema class, encoded as a mapping of their
|
|
2
|
+
fields (Effect's class declaration, linked to its struct)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass, field, replace
|
|
8
|
+
from functools import partial
|
|
9
|
+
from typing import Literal as _Literal
|
|
10
|
+
from typing import Protocol, cast
|
|
11
|
+
|
|
12
|
+
from pyfect._stack import stack_nearly_full, with_fresh_stack
|
|
13
|
+
from pyfect.exit import Exit, Success
|
|
14
|
+
from pyfect.exit import die as die_exit
|
|
15
|
+
from pyfect.exit import fail as fail_exit
|
|
16
|
+
from pyfect.schema._options import ParseOptions, is_constructing
|
|
17
|
+
from pyfect.schema.ast._base import AST, Checks, invalid_type
|
|
18
|
+
from pyfect.schema.ast._check import collect_issues
|
|
19
|
+
from pyfect.schema.ast._objects import Objects
|
|
20
|
+
from pyfect.schema.ast._result import ParseResult, flat_map
|
|
21
|
+
from pyfect.schema.issue import Composite, Issue, reported
|
|
22
|
+
|
|
23
|
+
type ClassSide = _Literal["decode", "encode", "type", "encoded"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ClassShape(Protocol):
|
|
27
|
+
"""What a ``Class`` node needs from a schema class."""
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def cls(self) -> type: ...
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def fields(self) -> Objects:
|
|
34
|
+
"""The decoding struct of the fields (a tagged class's tag too)."""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def encoded(self) -> AST:
|
|
39
|
+
"""The struct's encoded side."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def checks(self) -> Checks | None:
|
|
44
|
+
"""The class's own checks, run on each instance it makes."""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
def construct(self, values: dict[object, object]) -> object:
|
|
48
|
+
"""An instance from parsed values, not checked again."""
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
def values_of(self, instance: object) -> dict[object, object]:
|
|
52
|
+
"""The struct value of ``instance`` (a tagged class's tag too)."""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
57
|
+
class Class(AST):
|
|
58
|
+
"""Instances of ``shape.cls``. ``side`` says what the node does:
|
|
59
|
+
``"decode"`` (a mapping to an instance), ``"encode"`` (an instance to a
|
|
60
|
+
mapping), ``"type"`` (instances) or ``"encoded"`` (mappings). Checks
|
|
61
|
+
apply to instances."""
|
|
62
|
+
|
|
63
|
+
shape: ClassShape
|
|
64
|
+
side: ClassSide = "decode"
|
|
65
|
+
_typed: list[Class] = field(default_factory=list["Class"], init=False, repr=False)
|
|
66
|
+
tag = "Class"
|
|
67
|
+
|
|
68
|
+
def get_expected(self) -> str:
|
|
69
|
+
return self.shape.cls.__name__
|
|
70
|
+
|
|
71
|
+
def expected(self) -> str:
|
|
72
|
+
# Decoding reads a mapping first: name what the struct expects, as
|
|
73
|
+
# Effect does (its identifier names the instances).
|
|
74
|
+
if self.side in ("decode", "encoded"):
|
|
75
|
+
return self.shape.encoded.expected()
|
|
76
|
+
return AST.expected(self)
|
|
77
|
+
|
|
78
|
+
def parse(self, input: object, options: ParseOptions) -> ParseResult:
|
|
79
|
+
# A class may hold itself (its fields are made on first use), so a
|
|
80
|
+
# parse recurses through it as deep as its input.
|
|
81
|
+
parse = self.parse_local if self.encoding is None else partial(AST.parse, self)
|
|
82
|
+
if not stack_nearly_full():
|
|
83
|
+
return parse(input, options)
|
|
84
|
+
return with_fresh_stack(lambda: parse(input, options))
|
|
85
|
+
|
|
86
|
+
def parse_local(self, input: object, options: ParseOptions) -> ParseResult:
|
|
87
|
+
if self.side != "encode":
|
|
88
|
+
return AST.parse_local(self, input, options)
|
|
89
|
+
# Checks describe instances: run them, then encode the fields.
|
|
90
|
+
shape = self.shape
|
|
91
|
+
return flat_map(
|
|
92
|
+
replace(self, side="type").parse_local(input, options),
|
|
93
|
+
lambda instance: shape.fields.flip().parse(shape.values_of(instance), options),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def _parse(self, input: object, options: ParseOptions) -> ParseResult:
|
|
97
|
+
match self.side:
|
|
98
|
+
case "type" | "encode":
|
|
99
|
+
if isinstance(input, self.shape.cls):
|
|
100
|
+
return Success(input)
|
|
101
|
+
if self.side == "type" and is_constructing(options) and isinstance(input, Mapping):
|
|
102
|
+
# A constructor makes an instance from its fields'
|
|
103
|
+
# values (Effect's constructor descriptor).
|
|
104
|
+
fields = cast("Mapping[object, object]", input)
|
|
105
|
+
return self._construct(self.shape.fields.to_type(), fields, options)
|
|
106
|
+
return invalid_type(self, input, options)
|
|
107
|
+
case "encoded":
|
|
108
|
+
return self.shape.encoded.parse(input, options)
|
|
109
|
+
case "decode":
|
|
110
|
+
return self._decode(input, options)
|
|
111
|
+
|
|
112
|
+
def _decode(self, input: object, options: ParseOptions) -> ParseResult:
|
|
113
|
+
return self._construct(self.shape.fields, input, options)
|
|
114
|
+
|
|
115
|
+
def _construct(self, fields: AST, input: object, options: ParseOptions) -> ParseResult:
|
|
116
|
+
shape = self.shape
|
|
117
|
+
|
|
118
|
+
def construct(values: object) -> Exit[object, Issue]:
|
|
119
|
+
try:
|
|
120
|
+
instance = shape.construct(cast("dict[object, object]", values))
|
|
121
|
+
except Exception as defect:
|
|
122
|
+
return die_exit(defect)
|
|
123
|
+
return check_instance(shape, instance, options)
|
|
124
|
+
|
|
125
|
+
return flat_map(fields.parse(input, options), construct)
|
|
126
|
+
|
|
127
|
+
def _flip(self) -> Class:
|
|
128
|
+
flipped: dict[ClassSide, ClassSide] = {"decode": "encode", "encode": "decode"}
|
|
129
|
+
side = flipped.get(self.side)
|
|
130
|
+
return self if side is None else replace(self, side=side)
|
|
131
|
+
|
|
132
|
+
def _to_type(self) -> Class:
|
|
133
|
+
match self.side:
|
|
134
|
+
case "decode":
|
|
135
|
+
# Made once: every occurrence of the class's decoded side
|
|
136
|
+
# is one node (one definition in a document).
|
|
137
|
+
if not self._typed:
|
|
138
|
+
self._typed.append(replace(self, side="type"))
|
|
139
|
+
return self._typed[0]
|
|
140
|
+
case "encode":
|
|
141
|
+
return replace(self, side="encoded", checks=None)
|
|
142
|
+
case _:
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def check_instance(
|
|
147
|
+
shape: ClassShape, instance: object, options: ParseOptions
|
|
148
|
+
) -> Exit[object, Issue]:
|
|
149
|
+
"""``instance`` after the class's own checks."""
|
|
150
|
+
checks = shape.checks
|
|
151
|
+
if checks is None or options.get("disable_checks"):
|
|
152
|
+
return Success(instance)
|
|
153
|
+
try:
|
|
154
|
+
issues = collect_issues(checks, instance, shape.fields, options)
|
|
155
|
+
except Exception as defect:
|
|
156
|
+
return die_exit(defect)
|
|
157
|
+
if issues is None:
|
|
158
|
+
return Success(instance)
|
|
159
|
+
return fail_exit(Composite(shape.fields, issues, reported(instance, options)))
|