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,65 @@
|
|
|
1
|
+
"""Product and sum nodes built from their parts (Effect's
|
|
2
|
+
``SchemaAST.struct``, ``tuple``, ``union``, ``structWithRest`` and
|
|
3
|
+
``tupleWithRest``), shared by the schema constructors."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
8
|
+
|
|
9
|
+
from pyfect import _json_text
|
|
10
|
+
from pyfect.schema.ast._arrays import Arrays, Element
|
|
11
|
+
from pyfect.schema.ast._base import AST, Annotations, Checks, join_checks
|
|
12
|
+
from pyfect.schema.ast._objects import Objects, PropertySignature
|
|
13
|
+
from pyfect.schema.ast._union import Union, UnionMode
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def struct(
|
|
17
|
+
fields: Mapping[str, AST], checks: Checks | None = None, annotations: Annotations | None = None
|
|
18
|
+
) -> Objects:
|
|
19
|
+
"""A struct of ``fields``, keys in order; a field is optional when its
|
|
20
|
+
node is ``optional_key``."""
|
|
21
|
+
return Objects(
|
|
22
|
+
tuple(PropertySignature(name, node, node.optional_key) for name, node in fields.items()),
|
|
23
|
+
annotations=annotations,
|
|
24
|
+
checks=checks,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def tuple_(elements: Sequence[AST], checks: Checks | None = None) -> Arrays:
|
|
29
|
+
"""A fixed tuple of ``elements``; an element is optional when its node
|
|
30
|
+
is ``optional_key``."""
|
|
31
|
+
return Arrays(tuple(Element(node, node.optional_key) for node in elements), checks=checks)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def union(
|
|
35
|
+
members: Sequence[AST], mode: UnionMode = "any_of", checks: Checks | None = None
|
|
36
|
+
) -> Union:
|
|
37
|
+
"""A union of ``members`` in order."""
|
|
38
|
+
return Union(tuple(members), mode, checks=checks)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def struct_with_rest(ast: Objects, records: Sequence[Objects]) -> Objects:
|
|
42
|
+
"""``ast``'s keys and every record's keys and key patterns, with all
|
|
43
|
+
their checks. A node with an encoding raises ``ValueError``, as does a
|
|
44
|
+
key two of them declare."""
|
|
45
|
+
if ast.encoding is not None or any(r.encoding is not None for r in records):
|
|
46
|
+
raise ValueError("StructWithRest does not support encodings")
|
|
47
|
+
properties = list(ast.properties)
|
|
48
|
+
index_signatures = list(ast.index_signatures)
|
|
49
|
+
checks = ast.checks
|
|
50
|
+
for record in records:
|
|
51
|
+
for p in record.properties:
|
|
52
|
+
if any(q.name == p.name for q in properties):
|
|
53
|
+
raise ValueError(f"Duplicate identifier: {_json_text.stringify(p.name)}. ts(2300)")
|
|
54
|
+
properties += record.properties
|
|
55
|
+
index_signatures += record.index_signatures
|
|
56
|
+
checks = join_checks(checks, record.checks)
|
|
57
|
+
return Objects(tuple(properties), tuple(index_signatures), checks=checks)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def tuple_with_rest(ast: Arrays, rest: Sequence[AST]) -> Arrays:
|
|
61
|
+
"""``ast``'s elements, then ``rest`` (see ``Arrays``), with its checks;
|
|
62
|
+
a node with an encoding raises ``ValueError``."""
|
|
63
|
+
if ast.encoding is not None:
|
|
64
|
+
raise ValueError("TupleWithRest does not support encodings")
|
|
65
|
+
return Arrays(ast.elements, tuple(rest), ast.mutable, checks=ast.checks)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""``Declaration``: values a function parses, given the nodes of its type
|
|
2
|
+
parameters (Effect's ``Declaration``): ``Option(A)``, ``frozenset[A]``,
|
|
3
|
+
``timedelta``, ...
|
|
4
|
+
|
|
5
|
+
``run(type_parameters)`` makes the parser on first use; the parser gets
|
|
6
|
+
the input, the declaration itself (for its issues) and the parse options.
|
|
7
|
+
Flipping a declaration flips its type parameters, so the same parser
|
|
8
|
+
encodes with them.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Callable, Hashable
|
|
14
|
+
from dataclasses import dataclass, field, replace
|
|
15
|
+
from typing import cast
|
|
16
|
+
|
|
17
|
+
from pyfect.exit import Exit, Failure, Success
|
|
18
|
+
from pyfect.exit import die as die_exit
|
|
19
|
+
from pyfect.exit import fail as fail_exit
|
|
20
|
+
from pyfect.schema._options import ParseOptions
|
|
21
|
+
from pyfect.schema.ast._base import AST, issue_of, join_checks
|
|
22
|
+
from pyfect.schema.ast._result import ParseResult, then
|
|
23
|
+
from pyfect.schema.issue import Composite, Issue, Pointer, reported
|
|
24
|
+
|
|
25
|
+
type DeclarationParser = Callable[[object, Declaration, ParseOptions], ParseResult]
|
|
26
|
+
type DeclarationRun = Callable[[tuple[AST, ...]], DeclarationParser]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
30
|
+
class Declaration(AST):
|
|
31
|
+
"""Values ``run(type_parameters)`` accepts. Without an ``expected``
|
|
32
|
+
annotation it expects ``<Declaration>``."""
|
|
33
|
+
|
|
34
|
+
type_parameters: tuple[AST, ...]
|
|
35
|
+
run: DeclarationRun
|
|
36
|
+
_parser: list[DeclarationParser] = field(
|
|
37
|
+
default_factory=list[DeclarationParser], init=False, repr=False
|
|
38
|
+
)
|
|
39
|
+
tag = "Declaration"
|
|
40
|
+
|
|
41
|
+
def get_expected(self) -> str:
|
|
42
|
+
expected = (self.annotations or {}).get("expected")
|
|
43
|
+
return expected if isinstance(expected, str) else "<Declaration>"
|
|
44
|
+
|
|
45
|
+
def _parse(self, input: object, options: ParseOptions) -> ParseResult:
|
|
46
|
+
# Making the parser is the declaration's own setup: an error there
|
|
47
|
+
# is raised, not turned into a defect of this input.
|
|
48
|
+
if not self._parser:
|
|
49
|
+
self._parser.append(self.run(self.type_parameters))
|
|
50
|
+
try:
|
|
51
|
+
return self._parser[0](input, self, options)
|
|
52
|
+
except Exception as defect:
|
|
53
|
+
return die_exit(defect)
|
|
54
|
+
|
|
55
|
+
def _map(self, f: Callable[[AST], AST]) -> Declaration:
|
|
56
|
+
parameters = tuple(map(f, self.type_parameters))
|
|
57
|
+
if all(a is b for a, b in zip(parameters, self.type_parameters, strict=True)):
|
|
58
|
+
return self
|
|
59
|
+
return replace(self, type_parameters=parameters)
|
|
60
|
+
|
|
61
|
+
def _flip(self) -> Declaration:
|
|
62
|
+
# Checks describe the decoded side: flipped, they check the input.
|
|
63
|
+
flipped = self._map(lambda t: t.flip())
|
|
64
|
+
if self.checks is None and self.encoding_checks is None:
|
|
65
|
+
return flipped
|
|
66
|
+
return replace(flipped, checks=self.encoding_checks, encoding_checks=self.checks)
|
|
67
|
+
|
|
68
|
+
def _to_type(self) -> Declaration:
|
|
69
|
+
typed = self._map(lambda t: t.to_type())
|
|
70
|
+
if self.encoding_checks is None:
|
|
71
|
+
return typed
|
|
72
|
+
checks = join_checks(self.checks, self.encoding_checks) if typed is self else self.checks
|
|
73
|
+
return replace(typed, checks=checks, encoding_checks=None)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def parse_at_key(
|
|
77
|
+
ast: AST,
|
|
78
|
+
key: Hashable,
|
|
79
|
+
node: AST,
|
|
80
|
+
value: object,
|
|
81
|
+
*,
|
|
82
|
+
input: object,
|
|
83
|
+
options: ParseOptions,
|
|
84
|
+
) -> ParseResult:
|
|
85
|
+
"""``value`` (``input``'s part at ``key``) parsed by ``node``; an issue
|
|
86
|
+
is reported as ``ast``'s at ``key`` (Effect's ``makeCompositeAtKey``)."""
|
|
87
|
+
|
|
88
|
+
def at_key(exit: Exit[object, Issue]) -> Exit[object, Issue]:
|
|
89
|
+
if type(exit) is Success:
|
|
90
|
+
return exit
|
|
91
|
+
failure = cast("Failure[Issue]", exit)
|
|
92
|
+
issue = issue_of(failure)
|
|
93
|
+
if issue is None:
|
|
94
|
+
return failure
|
|
95
|
+
return fail_exit(Composite(ast, (Pointer((key,), issue),), reported(input, options)))
|
|
96
|
+
|
|
97
|
+
return then(node.parse(value, options), at_key)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Encodings: how a node is reached from what it decodes (Effect's ``Link``
|
|
2
|
+
and ``Encoding``).
|
|
3
|
+
|
|
4
|
+
A node's ``encoding`` is a chain of links, the last one the most encoded.
|
|
5
|
+
Decoding parses the input with the last link's ``to``, applies that link's
|
|
6
|
+
transformation, parses with the previous link's ``to``, and so on; the
|
|
7
|
+
first link's transformation gives a value the node itself then parses (with
|
|
8
|
+
its checks). Flipping a node reverses the chain and flips each
|
|
9
|
+
transformation, so encoding is decoding the flipped node.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass, replace
|
|
15
|
+
from typing import TYPE_CHECKING, Protocol
|
|
16
|
+
|
|
17
|
+
from pyfect.exit import Success
|
|
18
|
+
from pyfect.schema._options import ParseOptions
|
|
19
|
+
from pyfect.schema.ast._result import MISSING, ParseResult, flat_map, map_issue
|
|
20
|
+
from pyfect.schema.issue import Encoding as EncodingIssue
|
|
21
|
+
from pyfect.schema.issue import reported
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pyfect.schema.ast._base import AST
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LinkTransformation(Protocol):
|
|
28
|
+
"""What a link applies: a ``transformation.Transformation`` (a getter
|
|
29
|
+
each way) or a ``transformation.Middleware`` (a function of the whole
|
|
30
|
+
parse each way)."""
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def sees_failures(self) -> bool:
|
|
34
|
+
"""Its decoding runs on failures too (a middleware that may recover),
|
|
35
|
+
so a union cannot pick its member by the source's kind or literals."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
def flip(self) -> LinkTransformation: ...
|
|
39
|
+
|
|
40
|
+
def decode_result(self, result: ParseResult, options: ParseOptions) -> ParseResult:
|
|
41
|
+
"""The decoded side of ``result``, the parse of the source."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class Link:
|
|
47
|
+
"""``transformation`` from values of ``to`` (Effect's ``Link``)."""
|
|
48
|
+
|
|
49
|
+
to: AST
|
|
50
|
+
transformation: LinkTransformation
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
type Encoding = tuple[Link, *tuple[Link, ...]]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def decode_chain(ast: AST, links: Encoding, input: object, options: ParseOptions) -> ParseResult:
|
|
57
|
+
"""``input`` decoded through ``links``, ``ast``'s encoding, then parsed
|
|
58
|
+
by ``ast`` itself; an issue raised on the way is wrapped in
|
|
59
|
+
``Encoding``."""
|
|
60
|
+
result = links[-1].to.parse(input, options)
|
|
61
|
+
for i in range(len(links) - 1, -1, -1):
|
|
62
|
+
result = links[i].transformation.decode_result(result, options)
|
|
63
|
+
if i:
|
|
64
|
+
node = links[i - 1].to
|
|
65
|
+
result = flat_map(result, lambda value, node=node: node.parse(value, options))
|
|
66
|
+
if type(result) is not Success:
|
|
67
|
+
shown = None if input is MISSING else reported(input, options)
|
|
68
|
+
result = map_issue(result, lambda issue: EncodingIssue(ast, issue, shown))
|
|
69
|
+
return flat_map(result, lambda value: ast.parse_local(value, options))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def flip_encoding(ast: AST, links: Encoding) -> AST:
|
|
73
|
+
"""The node that encodes what ``ast`` decodes: its most encoded node,
|
|
74
|
+
with ``links`` (``ast``'s encoding) reversed (Effect's ``flipEncoding``)."""
|
|
75
|
+
flipped = [Link(replace(ast, encoding=None).flip(), links[0].transformation.flip())]
|
|
76
|
+
for i in range(1, len(links)):
|
|
77
|
+
flipped.insert(0, Link(links[i - 1].to.flip(), links[i].transformation.flip()))
|
|
78
|
+
to = links[-1].to.flip()
|
|
79
|
+
chain = (*to.encoding, *flipped) if to.encoding else tuple(flipped)
|
|
80
|
+
out = replace(to, encoding=chain)
|
|
81
|
+
# Its decoded side is ``to``'s: a schema's encoded side stays one node.
|
|
82
|
+
object.__setattr__(out, "context_owner", to.owner())
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def decode_to[A: AST](from_: AST, to: A, transformation: LinkTransformation) -> A:
|
|
87
|
+
"""``to``, decoding from values of ``from_`` through ``transformation``
|
|
88
|
+
(Effect's ``decodeTo``): a link appended to ``to``'s encoding."""
|
|
89
|
+
link = Link(from_, transformation)
|
|
90
|
+
out = replace(to, encoding=(*to.encoding, link) if to.encoding else (link,))
|
|
91
|
+
object.__setattr__(out, "body_owner", to.body_owner or to.owner())
|
|
92
|
+
return out
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def optional_key[A: AST](ast: A) -> A:
|
|
96
|
+
"""``ast`` as a struct key that may be missing on the encoded side, down
|
|
97
|
+
to its most encoded node (Effect's ``optionalKey``)."""
|
|
98
|
+
# _base imports this module.
|
|
99
|
+
from pyfect.schema.ast._base import replace_context
|
|
100
|
+
|
|
101
|
+
encoding = ast.encoding
|
|
102
|
+
if encoding is None:
|
|
103
|
+
return ast if ast.optional_key else replace_context(ast, optional_key=True)
|
|
104
|
+
last = encoding[-1]
|
|
105
|
+
chain: Encoding = (*encoding[:-1], Link(optional_key(last.to), last.transformation))
|
|
106
|
+
return replace(ast, encoding=chain, optional_key=True)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def to_encoded(ast: AST) -> AST:
|
|
110
|
+
"""The node's encoded side, as a node of its own (Effect's ``toEncoded``)."""
|
|
111
|
+
return ast.flip().to_type()
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""JSON values and string trees (Effect's ``isJson`` / ``isStringTree`` and
|
|
2
|
+
their ``Json`` / ``StringTree`` declarations)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import math
|
|
7
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
8
|
+
from typing import cast
|
|
9
|
+
|
|
10
|
+
from pyfect.exit import Exit, Success
|
|
11
|
+
from pyfect.schema._options import ParseOptions
|
|
12
|
+
from pyfect.schema.ast._base import invalid_type
|
|
13
|
+
from pyfect.schema.ast._declaration import Declaration, DeclarationParser
|
|
14
|
+
from pyfect.schema.ast._encoding import Link
|
|
15
|
+
from pyfect.schema.issue import Issue
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _is_json_leaf(value: object) -> bool:
|
|
19
|
+
if value is None or isinstance(value, str | bool | int):
|
|
20
|
+
return True
|
|
21
|
+
return isinstance(value, float) and math.isfinite(value)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _children(value: object) -> Iterator[object] | None:
|
|
25
|
+
"""The values inside a JSON container; ``None`` for anything else."""
|
|
26
|
+
if isinstance(value, list | tuple):
|
|
27
|
+
return iter(cast("Sequence[object]", value))
|
|
28
|
+
if isinstance(value, Mapping):
|
|
29
|
+
mapping = cast("Mapping[object, object]", value)
|
|
30
|
+
if all(isinstance(key, str) for key in mapping):
|
|
31
|
+
return iter(mapping.values())
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_json(value: object) -> bool:
|
|
36
|
+
"""``value`` is ``None``, a string, a boolean, an ``int``, a finite
|
|
37
|
+
``float``, or a ``list`` / ``tuple`` / string-keyed ``Mapping`` of JSON
|
|
38
|
+
values. A cycle is not JSON; a value shared twice (a DAG) is. Iterative,
|
|
39
|
+
so any depth works."""
|
|
40
|
+
return _is_tree(value, _is_json_leaf)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _is_string_tree_leaf(value: object) -> bool:
|
|
44
|
+
return value is None or isinstance(value, str)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def is_string_tree(value: object) -> bool:
|
|
48
|
+
"""``value`` is a string, ``None`` (Effect's ``undefined``), or a
|
|
49
|
+
``list`` / ``tuple`` / string-keyed ``Mapping`` of string trees (Effect's
|
|
50
|
+
``isStringTree``); a cycle is not one."""
|
|
51
|
+
return _is_tree(value, _is_string_tree_leaf)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _is_tree(value: object, is_leaf: Callable[[object], bool]) -> bool:
|
|
55
|
+
done: set[int] = set()
|
|
56
|
+
on_path: set[int] = set()
|
|
57
|
+
stack: list[tuple[object, Iterator[object]]] = []
|
|
58
|
+
current = value
|
|
59
|
+
while True:
|
|
60
|
+
if is_leaf(current) or id(current) in done:
|
|
61
|
+
pass
|
|
62
|
+
elif id(current) in on_path:
|
|
63
|
+
return False
|
|
64
|
+
else:
|
|
65
|
+
children = _children(current)
|
|
66
|
+
if children is None:
|
|
67
|
+
return False
|
|
68
|
+
on_path.add(id(current))
|
|
69
|
+
stack.append((current, children))
|
|
70
|
+
while stack:
|
|
71
|
+
container, children = stack[-1]
|
|
72
|
+
nxt = next(children, _END)
|
|
73
|
+
if nxt is not _END:
|
|
74
|
+
current = nxt
|
|
75
|
+
break
|
|
76
|
+
stack.pop()
|
|
77
|
+
on_path.discard(id(container))
|
|
78
|
+
done.add(id(container))
|
|
79
|
+
else:
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
_END = object()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _run(_: tuple[object, ...]) -> DeclarationParser:
|
|
87
|
+
def parse(input: object, ast: Declaration, options: ParseOptions) -> Exit[object, Issue]:
|
|
88
|
+
return Success(input) if is_json(input) else invalid_type(ast, input, options)
|
|
89
|
+
|
|
90
|
+
return parse
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _string_tree_run(_: tuple[object, ...]) -> DeclarationParser:
|
|
94
|
+
def parse(input: object, ast: Declaration, options: ParseOptions) -> Exit[object, Issue]:
|
|
95
|
+
return Success(input) if is_string_tree(input) else invalid_type(ast, input, options)
|
|
96
|
+
|
|
97
|
+
return parse
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _already_canonical(_: tuple[object, ...]) -> None:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _from_string_tree(_: tuple[object, ...]) -> Link:
|
|
105
|
+
from pyfect.schema._codec_links import UNKNOWN_TO_STRING_TREE
|
|
106
|
+
|
|
107
|
+
return UNKNOWN_TO_STRING_TREE
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
string_tree = Declaration(
|
|
111
|
+
(),
|
|
112
|
+
_string_tree_run,
|
|
113
|
+
annotations={"expected": "StringTree", "to_codec_string_tree": _already_canonical},
|
|
114
|
+
)
|
|
115
|
+
"""Any string tree (``is_string_tree``), unchanged: an opaque value's
|
|
116
|
+
``to_codec_string_tree`` form."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
json = Declaration(
|
|
120
|
+
(),
|
|
121
|
+
_run,
|
|
122
|
+
annotations={
|
|
123
|
+
"expected": "JSON value",
|
|
124
|
+
"representation": {"id": "effect/schema/Json", "payload": None},
|
|
125
|
+
"to_codec_json": _already_canonical,
|
|
126
|
+
"to_codec_string_tree": _from_string_tree,
|
|
127
|
+
},
|
|
128
|
+
)
|
|
129
|
+
"""Any JSON value (``is_json``), unchanged."""
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""The keyword nodes: one kind of value each, accepted unchanged.
|
|
2
|
+
|
|
3
|
+
Python mappings: ``Number`` accepts ``int`` and ``float`` (JavaScript has one
|
|
4
|
+
number type), ``BigInt`` accepts ``int``, ``Null`` is ``None``; ``bool`` is
|
|
5
|
+
neither a number nor a bigint.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from pyfect.exit import Exit, Success
|
|
13
|
+
from pyfect.schema._options import ParseOptions
|
|
14
|
+
from pyfect.schema.ast._base import Refinement
|
|
15
|
+
from pyfect.schema.issue import Issue
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
19
|
+
class Never(Refinement):
|
|
20
|
+
tag = "Never"
|
|
21
|
+
|
|
22
|
+
def get_expected(self) -> str:
|
|
23
|
+
return "never"
|
|
24
|
+
|
|
25
|
+
def accepts(self, input: object) -> bool:
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
30
|
+
class Any(Refinement):
|
|
31
|
+
tag = "Any"
|
|
32
|
+
|
|
33
|
+
def get_expected(self) -> str:
|
|
34
|
+
return "any"
|
|
35
|
+
|
|
36
|
+
def accepts(self, input: object) -> bool:
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
41
|
+
class Unknown(Refinement):
|
|
42
|
+
tag = "Unknown"
|
|
43
|
+
|
|
44
|
+
def get_expected(self) -> str:
|
|
45
|
+
return "unknown"
|
|
46
|
+
|
|
47
|
+
def accepts(self, input: object) -> bool:
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
52
|
+
class Null(Refinement):
|
|
53
|
+
tag = "Null"
|
|
54
|
+
|
|
55
|
+
def get_expected(self) -> str:
|
|
56
|
+
return "null"
|
|
57
|
+
|
|
58
|
+
def accepts(self, input: object) -> bool:
|
|
59
|
+
return input is None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
63
|
+
class String(Refinement):
|
|
64
|
+
tag = "String"
|
|
65
|
+
|
|
66
|
+
def get_expected(self) -> str:
|
|
67
|
+
return "string"
|
|
68
|
+
|
|
69
|
+
def accepts(self, input: object) -> bool:
|
|
70
|
+
return isinstance(input, str)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
74
|
+
class Number(Refinement):
|
|
75
|
+
"""A number; ``whole`` is the one ``S.Int`` starts from. JavaScript has one
|
|
76
|
+
number, so Effect's ``Int`` takes ``1.0``. A Python ``int`` schema gives it
|
|
77
|
+
as ``1``, which is also how JavaScript prints it; its check refuses every
|
|
78
|
+
other float."""
|
|
79
|
+
|
|
80
|
+
whole: bool = False
|
|
81
|
+
tag = "Number"
|
|
82
|
+
|
|
83
|
+
def get_expected(self) -> str:
|
|
84
|
+
return "number"
|
|
85
|
+
|
|
86
|
+
def accepts(self, input: object) -> bool:
|
|
87
|
+
return isinstance(input, int | float) and not isinstance(input, bool)
|
|
88
|
+
|
|
89
|
+
def _parse(self, input: object, options: ParseOptions) -> Exit[object, Issue]:
|
|
90
|
+
if self.whole and isinstance(input, float) and input.is_integer():
|
|
91
|
+
return Success(int(input))
|
|
92
|
+
return Refinement._parse(self, input, options)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
96
|
+
class Boolean(Refinement):
|
|
97
|
+
tag = "Boolean"
|
|
98
|
+
|
|
99
|
+
def get_expected(self) -> str:
|
|
100
|
+
return "boolean"
|
|
101
|
+
|
|
102
|
+
def accepts(self, input: object) -> bool:
|
|
103
|
+
return isinstance(input, bool)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
107
|
+
class BigInt(Refinement):
|
|
108
|
+
tag = "BigInt"
|
|
109
|
+
|
|
110
|
+
def get_expected(self) -> str:
|
|
111
|
+
return "bigint"
|
|
112
|
+
|
|
113
|
+
def accepts(self, input: object) -> bool:
|
|
114
|
+
return isinstance(input, int) and not isinstance(input, bool)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
never = Never()
|
|
118
|
+
any_ = Any()
|
|
119
|
+
unknown = Unknown()
|
|
120
|
+
null = Null()
|
|
121
|
+
string = String()
|
|
122
|
+
number = Number()
|
|
123
|
+
whole_number = Number(whole=True)
|
|
124
|
+
boolean = Boolean()
|
|
125
|
+
big_int = BigInt()
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""Exact-value nodes: ``Literal`` (one value) and ``Enum`` (the members of
|
|
2
|
+
a Python ``enum.Enum``).
|
|
3
|
+
|
|
4
|
+
JavaScript has one number, so a number literal matches an ``int`` or a
|
|
5
|
+
``float`` of that value: ``1`` and ``1.0`` are one literal, ``0.0`` and
|
|
6
|
+
``-0.0`` match each other, and the input is kept (its kind and sign too). A
|
|
7
|
+
``bool`` is not a number: ``1`` is not ``True``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import enum
|
|
13
|
+
import math
|
|
14
|
+
from dataclasses import dataclass, replace
|
|
15
|
+
from typing import Literal as _Literal
|
|
16
|
+
from typing import cast
|
|
17
|
+
|
|
18
|
+
from pyfect._js import js_number
|
|
19
|
+
from pyfect.exit import Exit, Success
|
|
20
|
+
from pyfect.logger._text import json_quote
|
|
21
|
+
from pyfect.schema._options import ParseOptions
|
|
22
|
+
from pyfect.schema.ast._base import AST, invalid_type
|
|
23
|
+
from pyfect.schema.ast._result import ParseResult, map_value
|
|
24
|
+
from pyfect.schema.issue import Issue
|
|
25
|
+
|
|
26
|
+
type LiteralValue = str | int | float | bool | None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def same_literal(literal: LiteralValue, input: object) -> bool:
|
|
30
|
+
"""``input`` is ``literal``, as JavaScript's ``===`` reads them."""
|
|
31
|
+
match literal:
|
|
32
|
+
case None:
|
|
33
|
+
return input is None
|
|
34
|
+
case bool():
|
|
35
|
+
return isinstance(input, bool) and input == literal
|
|
36
|
+
case int() | float():
|
|
37
|
+
return (
|
|
38
|
+
isinstance(input, int | float) and not isinstance(input, bool) and input == literal
|
|
39
|
+
)
|
|
40
|
+
case str():
|
|
41
|
+
return isinstance(input, str) and input == literal
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def format_literal(literal: LiteralValue) -> str:
|
|
45
|
+
"""A literal as Effect prints it: strings quoted, ``null``, ``true``."""
|
|
46
|
+
match literal:
|
|
47
|
+
case None:
|
|
48
|
+
return "null"
|
|
49
|
+
case bool():
|
|
50
|
+
return "true" if literal else "false"
|
|
51
|
+
case int() | float():
|
|
52
|
+
return js_number(literal)
|
|
53
|
+
case str():
|
|
54
|
+
return json_quote(literal)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _require_finite(value: float, what: str) -> None:
|
|
58
|
+
if not math.isfinite(value):
|
|
59
|
+
raise ValueError(f"A numeric {what} must be finite, got {js_number(value)}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
63
|
+
class Literal(AST):
|
|
64
|
+
"""Accepts ``literal`` (Effect's ``Literal``). A non-finite number is
|
|
65
|
+
rejected when the node is made."""
|
|
66
|
+
|
|
67
|
+
literal: LiteralValue
|
|
68
|
+
tag = "Literal"
|
|
69
|
+
|
|
70
|
+
def __post_init__(self) -> None:
|
|
71
|
+
if isinstance(self.literal, float):
|
|
72
|
+
_require_finite(self.literal, "literal")
|
|
73
|
+
|
|
74
|
+
def get_expected(self) -> str:
|
|
75
|
+
return format_literal(self.literal)
|
|
76
|
+
|
|
77
|
+
def _parse(self, input: object, options: ParseOptions) -> Exit[object, Issue]:
|
|
78
|
+
if same_literal(self.literal, input):
|
|
79
|
+
return Success(input)
|
|
80
|
+
return invalid_type(self, input, options)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
type EnumSide = _Literal["decode", "encode", "type", "encoded"]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
87
|
+
class Enum(AST):
|
|
88
|
+
"""The members of ``enum``, encoded as their values (Effect's ``Enum``,
|
|
89
|
+
whose values are the members). Decoding takes a value (or a member) and
|
|
90
|
+
gives the member; encoding gives the member's value. Values must be
|
|
91
|
+
strings or finite numbers.
|
|
92
|
+
|
|
93
|
+
``side`` says what the node does: ``"decode"`` (value to member),
|
|
94
|
+
``"encode"`` (member to value), ``"type"`` (members) or ``"encoded"``
|
|
95
|
+
(values). Checks apply to members.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
enum: type[enum.Enum]
|
|
99
|
+
side: EnumSide = "decode"
|
|
100
|
+
tag = "Enum"
|
|
101
|
+
|
|
102
|
+
def __post_init__(self) -> None:
|
|
103
|
+
for member in self.enum:
|
|
104
|
+
value: object = member.value
|
|
105
|
+
if not isinstance(value, str | int | float) or isinstance(value, bool):
|
|
106
|
+
raise TypeError(
|
|
107
|
+
f"An enum value must be a string or a number, got {value!r} ({member!r})"
|
|
108
|
+
)
|
|
109
|
+
if isinstance(value, float):
|
|
110
|
+
_require_finite(value, "enum value")
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def members(self) -> tuple[enum.Enum, ...]:
|
|
114
|
+
return tuple(self.enum)
|
|
115
|
+
|
|
116
|
+
def get_expected(self) -> str:
|
|
117
|
+
if self.side in ("type", "encode"):
|
|
118
|
+
return self.enum.__name__
|
|
119
|
+
return " | ".join(format_literal(m.value) for m in self.enum)
|
|
120
|
+
|
|
121
|
+
def parse(self, input: object, options: ParseOptions) -> ParseResult:
|
|
122
|
+
if self.encoding is not None:
|
|
123
|
+
return AST.parse(self, input, options)
|
|
124
|
+
return self.parse_local(input, options)
|
|
125
|
+
|
|
126
|
+
def parse_local(self, input: object, options: ParseOptions) -> ParseResult:
|
|
127
|
+
if self.side != "encode":
|
|
128
|
+
return AST.parse_local(self, input, options)
|
|
129
|
+
# Checks describe members: run them on the member, then encode it.
|
|
130
|
+
return map_value(
|
|
131
|
+
replace(self, side="type").parse_local(input, options),
|
|
132
|
+
lambda member: cast("enum.Enum", member).value,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def _parse(self, input: object, options: ParseOptions) -> Exit[object, Issue]:
|
|
136
|
+
if self.side in ("type", "encode"):
|
|
137
|
+
if isinstance(input, self.enum):
|
|
138
|
+
return Success(input)
|
|
139
|
+
return invalid_type(self, input, options)
|
|
140
|
+
for member in self.enum:
|
|
141
|
+
if input is member or same_literal(member.value, input):
|
|
142
|
+
return Success(member if self.side == "decode" else input)
|
|
143
|
+
return invalid_type(self, input, options)
|
|
144
|
+
|
|
145
|
+
def _flip(self) -> Enum:
|
|
146
|
+
flipped: dict[EnumSide, EnumSide] = {"decode": "encode", "encode": "decode"}
|
|
147
|
+
side = flipped.get(self.side)
|
|
148
|
+
return self if side is None else replace(self, side=side)
|
|
149
|
+
|
|
150
|
+
def _to_type(self) -> Enum:
|
|
151
|
+
match self.side:
|
|
152
|
+
case "decode":
|
|
153
|
+
return replace(self, side="type")
|
|
154
|
+
case "encode":
|
|
155
|
+
return replace(self, side="encoded", checks=None)
|
|
156
|
+
case _:
|
|
157
|
+
return self
|