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,150 @@
|
|
|
1
|
+
"""The ``Duration`` transformations (Effect's ``durationFromString``,
|
|
2
|
+
``durationFromNanos``, ``durationFromMillis``) between a ``timedelta`` and
|
|
3
|
+
its text, nanoseconds or milliseconds.
|
|
4
|
+
|
|
5
|
+
A ``timedelta`` counts whole microseconds within about 2.7 million years,
|
|
6
|
+
so a duration it cannot hold once rounded to whole nanoseconds as Effect
|
|
7
|
+
rounds it (infinite, ``1 nano``, out of range) fails to decode with
|
|
8
|
+
``Expected a duration a timedelta can hold``, where Effect's ``Duration``
|
|
9
|
+
would take it; a ``timedelta`` a float of milliseconds cannot hold
|
|
10
|
+
exactly (past about 285 years, with microseconds) fails to encode. Text
|
|
11
|
+
is Effect's ``Duration`` text: ``"<n> <unit>"`` with ``nanos``,
|
|
12
|
+
``micros``, ``millis``, ``seconds``, ``minutes``, ``hours``, ``days`` or
|
|
13
|
+
``weeks`` (singular too), written back as ``"<n> millis"``, or
|
|
14
|
+
``"<n> nanos"`` when it is not whole milliseconds.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import math
|
|
20
|
+
import re
|
|
21
|
+
from datetime import timedelta
|
|
22
|
+
from fractions import Fraction
|
|
23
|
+
from typing import cast
|
|
24
|
+
|
|
25
|
+
from pyfect._digits import digits_value
|
|
26
|
+
from pyfect._js import js_number
|
|
27
|
+
from pyfect.exit import Exit, Success
|
|
28
|
+
from pyfect.exit import fail as fail_exit
|
|
29
|
+
from pyfect.schema._options import ParseOptions
|
|
30
|
+
from pyfect.schema.issue import InvalidValue, Issue, reported
|
|
31
|
+
from pyfect.schema.transformation._transformation import Transformation, transform_effect
|
|
32
|
+
|
|
33
|
+
_PATTERN = re.compile(
|
|
34
|
+
r"(-?\d+(?:\.\d+)?)\s+(nanos?|micros?|millis?|seconds?|minutes?|hours?|days?|weeks?)\Z",
|
|
35
|
+
re.ASCII,
|
|
36
|
+
)
|
|
37
|
+
_NANOS_PER = {
|
|
38
|
+
"nano": 1,
|
|
39
|
+
"micro": 1_000,
|
|
40
|
+
"milli": 1_000_000,
|
|
41
|
+
"second": 1_000_000_000,
|
|
42
|
+
"minute": 60_000_000_000,
|
|
43
|
+
"hour": 3_600_000_000_000,
|
|
44
|
+
"day": 86_400_000_000_000,
|
|
45
|
+
"week": 604_800_000_000_000,
|
|
46
|
+
}
|
|
47
|
+
_HOLDS = "a duration a timedelta can hold"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
_INFINITIES = frozenset({"Infinity", "-Infinity"})
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _from_nanos(nanos: int) -> timedelta | None:
|
|
54
|
+
micros, rest = divmod(nanos, 1000)
|
|
55
|
+
if rest:
|
|
56
|
+
return None
|
|
57
|
+
try:
|
|
58
|
+
return timedelta(microseconds=micros)
|
|
59
|
+
except OverflowError:
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _exact(text: str) -> Fraction:
|
|
64
|
+
"""``-?digits(.digits)?`` exactly, however long."""
|
|
65
|
+
negative = text.startswith("-")
|
|
66
|
+
whole, _, fraction = text.removeprefix("-").partition(".")
|
|
67
|
+
numerator = cast("int", digits_value(whole + fraction))
|
|
68
|
+
value = Fraction(numerator, 10 ** len(fraction))
|
|
69
|
+
return -value if negative else value
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _round_nanos(nanos: Fraction) -> int:
|
|
73
|
+
"""Whole nanoseconds, ties away from zero (Effect's ``parseNanos``)."""
|
|
74
|
+
rounded = math.floor(abs(nanos) + Fraction(1, 2))
|
|
75
|
+
return -rounded if nanos < 0 else rounded
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _millis_nanos(millis: float) -> int:
|
|
79
|
+
"""Whole nanoseconds, ties away from zero, of the float's shortest text
|
|
80
|
+
(``0.1`` is 0.1 ms, not its binary value). Effect rounds the float
|
|
81
|
+
product ``millis * 1e6``, which agrees but where its error crosses half
|
|
82
|
+
a nanosecond; the text keeps every encoded ``timedelta`` decodable."""
|
|
83
|
+
exact = Fraction(millis) if isinstance(millis, int) else Fraction(js_number(millis))
|
|
84
|
+
return _round_nanos(exact * 1_000_000)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _nanos_of(duration: timedelta) -> int:
|
|
88
|
+
return (duration // timedelta(microseconds=1)) * 1000
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _invalid(expected: str, input: object, options: ParseOptions) -> Exit[timedelta, Issue]:
|
|
92
|
+
return fail_exit(InvalidValue({"expected": expected}, reported(input, options)))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _decode_text(text: str, options: ParseOptions) -> Exit[timedelta, Issue]:
|
|
96
|
+
if text in _INFINITIES:
|
|
97
|
+
return _invalid(_HOLDS, text, options)
|
|
98
|
+
match = _PATTERN.match(text)
|
|
99
|
+
if match is None:
|
|
100
|
+
return _invalid("a valid Duration string", text, options)
|
|
101
|
+
value, unit = match.groups()
|
|
102
|
+
duration = _from_nanos(_round_nanos(_exact(value) * _NANOS_PER[unit.removesuffix("s")]))
|
|
103
|
+
return _invalid(_HOLDS, text, options) if duration is None else Success(duration)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _encode_text(duration: timedelta, _: ParseOptions) -> Exit[str, Issue]:
|
|
107
|
+
nanos = _nanos_of(duration)
|
|
108
|
+
millis, rest = divmod(nanos, 1_000_000)
|
|
109
|
+
return Success(f"{nanos} nanos" if rest else f"{millis} millis")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _decode_nanos(nanos: int, options: ParseOptions) -> Exit[timedelta, Issue]:
|
|
113
|
+
duration = _from_nanos(nanos)
|
|
114
|
+
return _invalid(_HOLDS, nanos, options) if duration is None else Success(duration)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _encode_nanos(duration: timedelta, _: ParseOptions) -> Exit[int, Issue]:
|
|
118
|
+
return Success(_nanos_of(duration))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _decode_millis(millis: float, options: ParseOptions) -> Exit[timedelta, Issue]:
|
|
122
|
+
if isinstance(millis, float) and math.isnan(millis): # NaN is zero, as Effect's.
|
|
123
|
+
return Success(timedelta(0))
|
|
124
|
+
finite = isinstance(millis, int) or math.isfinite(millis)
|
|
125
|
+
duration = _from_nanos(_millis_nanos(millis)) if finite else None
|
|
126
|
+
return _invalid(_HOLDS, millis, options) if duration is None else Success(duration)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _encode_millis(duration: timedelta, options: ParseOptions) -> Exit[float, Issue]:
|
|
130
|
+
nanos = _nanos_of(duration)
|
|
131
|
+
millis = nanos / 1_000_000
|
|
132
|
+
if _millis_nanos(millis) != nanos:
|
|
133
|
+
# Past about 285 years a float has no room for the microseconds.
|
|
134
|
+
expected = "a duration a number of milliseconds can hold exactly"
|
|
135
|
+
return fail_exit(InvalidValue({"expected": expected}, reported(duration, options)))
|
|
136
|
+
return Success(int(millis) if millis.is_integer() else millis)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
duration_from_string: Transformation[timedelta, str] = transform_effect(
|
|
140
|
+
decode=_decode_text, encode=_encode_text
|
|
141
|
+
)
|
|
142
|
+
"""A ``timedelta`` from Effect's ``Duration`` text (``"5 seconds"``)."""
|
|
143
|
+
duration_from_nanos: Transformation[timedelta, int] = transform_effect(
|
|
144
|
+
decode=_decode_nanos, encode=_encode_nanos
|
|
145
|
+
)
|
|
146
|
+
"""A ``timedelta`` from a count of nanoseconds."""
|
|
147
|
+
duration_from_millis: Transformation[timedelta, float] = transform_effect(
|
|
148
|
+
decode=_decode_millis, encode=_encode_millis
|
|
149
|
+
)
|
|
150
|
+
"""A ``timedelta`` from a count of milliseconds."""
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""``Transformation`` (a getter each way) and ``Middleware`` (a function of
|
|
2
|
+
the whole parse each way), Effect's ``SchemaTransformation``. A schema made
|
|
3
|
+
with ``decode_to`` holds one in its encoding."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Any, Generic, Never, Self, TypeIs, TypeVar, cast
|
|
10
|
+
|
|
11
|
+
from pyfect._core import Effect
|
|
12
|
+
from pyfect.option import Option, Some
|
|
13
|
+
from pyfect.schema._options import ParseOptions
|
|
14
|
+
from pyfect.schema.ast import MISSING, ParseResult, call, map_value, to_effect
|
|
15
|
+
from pyfect.schema.getter import (
|
|
16
|
+
Getter,
|
|
17
|
+
Outcome,
|
|
18
|
+
)
|
|
19
|
+
from pyfect.schema.getter import (
|
|
20
|
+
passthrough as passthrough_getter,
|
|
21
|
+
)
|
|
22
|
+
from pyfect.schema.getter import (
|
|
23
|
+
transform as transform_getter,
|
|
24
|
+
)
|
|
25
|
+
from pyfect.schema.getter import (
|
|
26
|
+
transform_effect as transform_effect_getter,
|
|
27
|
+
)
|
|
28
|
+
from pyfect.schema.getter import (
|
|
29
|
+
transform_optional as transform_optional_getter,
|
|
30
|
+
)
|
|
31
|
+
from pyfect.schema.getter._getter import apply_result
|
|
32
|
+
from pyfect.schema.issue import Issue
|
|
33
|
+
|
|
34
|
+
T = TypeVar("T")
|
|
35
|
+
E = TypeVar("E")
|
|
36
|
+
RD_co = TypeVar("RD_co", covariant=True, default=Never)
|
|
37
|
+
RE_co = TypeVar("RE_co", covariant=True, default=Never)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True, slots=True)
|
|
41
|
+
class Content:
|
|
42
|
+
"""The document a getter writes: its ``media_type``, and whether the
|
|
43
|
+
schema of its content is that of the values it encodes
|
|
44
|
+
(``preserve_schema``)."""
|
|
45
|
+
|
|
46
|
+
media_type: str
|
|
47
|
+
preserve_schema: bool
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
51
|
+
class Transformation(Generic[T, E, RD_co, RE_co]):
|
|
52
|
+
"""``decode`` turns an ``E`` into a ``T``, ``encode`` back, needing the
|
|
53
|
+
services ``RD`` / ``RE`` (Effect's ``Transformation``)."""
|
|
54
|
+
|
|
55
|
+
decode: Getter[T, E, RD_co]
|
|
56
|
+
encode: Getter[E, T, RE_co]
|
|
57
|
+
content: Content | None = None
|
|
58
|
+
"""What ``encode`` writes, when it writes a document of a media type
|
|
59
|
+
(JSON text): JSON Schema says it with ``contentMediaType``."""
|
|
60
|
+
decode_content: Content | None = None
|
|
61
|
+
"""``content`` of ``decode``, carried for ``flip``."""
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def sees_failures(self) -> bool:
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
def flip(self) -> Transformation[E, T, RE_co, RD_co]:
|
|
68
|
+
"""The transformation the other way."""
|
|
69
|
+
return Transformation(self.encode, self.decode, self.decode_content, self.content)
|
|
70
|
+
|
|
71
|
+
def pipe[B](self, f: Callable[[Self], B], /) -> B:
|
|
72
|
+
return f(self)
|
|
73
|
+
|
|
74
|
+
def compose[T2, RD2 = Never, RE2 = Never](
|
|
75
|
+
self, other: Transformation[T2, T, RD2, RE2], /
|
|
76
|
+
) -> Transformation[T2, E, RD_co | RD2, RE_co | RE2]:
|
|
77
|
+
"""This transformation, then ``other`` (Effect's
|
|
78
|
+
``composeTransformation``)."""
|
|
79
|
+
return Transformation(self.decode.compose(other.decode), other.encode.compose(self.encode))
|
|
80
|
+
|
|
81
|
+
def decode_result(self, result: ParseResult, options: ParseOptions) -> ParseResult:
|
|
82
|
+
return apply_result(self.decode, result, options)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
type _Parse[A, R] = Effect[Option[A], Issue, R]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def unchanged[A, R](parse: _Parse[A, R], options: ParseOptions, /) -> _Parse[A, R]:
|
|
89
|
+
"""The side of a ``Middleware`` that leaves the parse as it is (Effect's
|
|
90
|
+
``identity`` in ``middlewareDecoding`` / ``middlewareEncoding``)."""
|
|
91
|
+
return parse
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
95
|
+
class Middleware(Generic[T, E, RD_co, RE_co]):
|
|
96
|
+
"""A function of the whole parse each way: ``decode(effect, options)``
|
|
97
|
+
gets the source's parse (an effect giving ``Some(value)``, or ``None``
|
|
98
|
+
for a missing value, or failing with its issue) and gives the target's
|
|
99
|
+
(Effect's ``Middleware``). It sees failures, so it can recover from
|
|
100
|
+
them (``catch_decoding``)."""
|
|
101
|
+
|
|
102
|
+
decode: Callable[[_Parse[E, Any], ParseOptions], _Parse[T, RD_co]]
|
|
103
|
+
encode: Callable[[_Parse[T, Any], ParseOptions], _Parse[E, RE_co]]
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def sees_failures(self) -> bool:
|
|
107
|
+
return self.decode is not unchanged
|
|
108
|
+
|
|
109
|
+
def flip(self) -> Middleware[E, T, RE_co, RD_co]:
|
|
110
|
+
return Middleware(self.encode, self.decode)
|
|
111
|
+
|
|
112
|
+
def pipe[B](self, f: Callable[[Self], B], /) -> B:
|
|
113
|
+
return f(self)
|
|
114
|
+
|
|
115
|
+
def decode_result(self, result: ParseResult, options: ParseOptions) -> ParseResult:
|
|
116
|
+
source = cast("_Parse[E, Any]", to_effect(map_value(result, _option)))
|
|
117
|
+
return map_value(call(self.decode, source, options), _value)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _option(value: object) -> Option[object]:
|
|
121
|
+
return None if value is MISSING else Some(value)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _value(option: object) -> object:
|
|
125
|
+
return MISSING if option is None else cast("Some[object]", option).value
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def is_transformation(value: object) -> TypeIs[Transformation[Any, Any, Any, Any]]:
|
|
129
|
+
"""``value`` is a ``Transformation`` (Effect's ``isTransformation``)."""
|
|
130
|
+
return isinstance(value, Transformation)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def transform[T2, E2](
|
|
134
|
+
*, decode: Callable[[E2], T2], encode: Callable[[T2], E2]
|
|
135
|
+
) -> Transformation[T2, E2]:
|
|
136
|
+
"""A function each way (Effect's ``transform``)."""
|
|
137
|
+
return Transformation(transform_getter(decode), transform_getter(encode))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def transform_effect[T2, E2, RD = Never, RE = Never](
|
|
141
|
+
*,
|
|
142
|
+
decode: Callable[[E2, ParseOptions], Outcome[T2, RD]],
|
|
143
|
+
encode: Callable[[T2, ParseOptions], Outcome[E2, RE]],
|
|
144
|
+
) -> Transformation[T2, E2, RD, RE]:
|
|
145
|
+
"""An effectful function each way, failing with an ``Issue`` (Effect's
|
|
146
|
+
``transformEffect``)."""
|
|
147
|
+
return Transformation(transform_effect_getter(decode), transform_effect_getter(encode))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def transform_optional[T2, E2](
|
|
151
|
+
*,
|
|
152
|
+
decode: Callable[[Option[E2]], Option[T2]],
|
|
153
|
+
encode: Callable[[Option[T2]], Option[E2]],
|
|
154
|
+
) -> Transformation[T2, E2]:
|
|
155
|
+
"""A function of an ``Option`` each way (Effect's ``transformOptional``)."""
|
|
156
|
+
return Transformation(transform_optional_getter(decode), transform_optional_getter(encode))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
_PASSTHROUGH: Transformation[Any, Any] = Transformation(passthrough_getter(), passthrough_getter())
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def passthrough[T2]() -> Transformation[T2, T2]:
|
|
163
|
+
"""The value unchanged each way (Effect's ``passthrough``)."""
|
|
164
|
+
return _PASSTHROUGH
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def passthrough_supertype() -> Transformation[Any, Any]:
|
|
168
|
+
"""The value unchanged each way, from a supertype ``E`` to its subtype
|
|
169
|
+
``T`` (Effect's ``passthroughSupertype``). Python cannot say that ``T``
|
|
170
|
+
is a subtype of ``E``, so it is typed ``Any``."""
|
|
171
|
+
return _PASSTHROUGH
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def passthrough_subtype() -> Transformation[Any, Any]:
|
|
175
|
+
"""The value unchanged each way, from a subtype ``E`` to its supertype
|
|
176
|
+
``T`` (Effect's ``passthroughSubtype``)."""
|
|
177
|
+
return _PASSTHROUGH
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Variant schemas (Effect's ``effect/schema/VariantSchema``): related
|
|
2
|
+
schemas for named variants built from one set of field definitions::
|
|
3
|
+
|
|
4
|
+
from pyfect.schema import variant_schema as VS
|
|
5
|
+
|
|
6
|
+
Test = VS.make(variants=["a", "b"], default_variant="a")
|
|
7
|
+
struct = Test.Struct({"common": S.String, "only_b": Test.field_only(["b"])(S.Number)})
|
|
8
|
+
Test.extract(struct, "b") # the struct schema of common and only_b
|
|
9
|
+
|
|
10
|
+
class User(Test.Class):
|
|
11
|
+
id: int = VS.field(Test.field_only(["a"])(S.Int))
|
|
12
|
+
name: str
|
|
13
|
+
|
|
14
|
+
Test.extract(User, "b") # the struct schema of name; User is the "a" schema
|
|
15
|
+
|
|
16
|
+
``pyfect.schema.model`` is the toolkit for database and JSON API models.
|
|
17
|
+
Pyright cannot compute a variant's keys from the definitions (no mapped
|
|
18
|
+
types), so an extracted struct's Type is a ``Mapping``; ``Model.Class``
|
|
19
|
+
takes TypedDicts for its variants (see there).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from pyfect.schema.variant_schema._class import Class, VariantAttribute, field
|
|
23
|
+
from pyfect.schema.variant_schema._defaults import (
|
|
24
|
+
Overrideable,
|
|
25
|
+
WithConstructorDefault,
|
|
26
|
+
override,
|
|
27
|
+
with_constructor_default,
|
|
28
|
+
)
|
|
29
|
+
from pyfect.schema.variant_schema._field import Field, is_field
|
|
30
|
+
from pyfect.schema.variant_schema._struct import (
|
|
31
|
+
Definition,
|
|
32
|
+
HasVariants,
|
|
33
|
+
Struct,
|
|
34
|
+
VariantStructSchema,
|
|
35
|
+
extract,
|
|
36
|
+
fields,
|
|
37
|
+
is_struct,
|
|
38
|
+
)
|
|
39
|
+
from pyfect.schema.variant_schema._toolkit import (
|
|
40
|
+
FieldEvolve,
|
|
41
|
+
FieldOnly,
|
|
42
|
+
VariantSchema,
|
|
43
|
+
VariantUnionSchema,
|
|
44
|
+
make,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
"Class",
|
|
49
|
+
"Definition",
|
|
50
|
+
"Field",
|
|
51
|
+
"FieldEvolve",
|
|
52
|
+
"FieldOnly",
|
|
53
|
+
"HasVariants",
|
|
54
|
+
"Overrideable",
|
|
55
|
+
"Struct",
|
|
56
|
+
"VariantAttribute",
|
|
57
|
+
"VariantSchema",
|
|
58
|
+
"VariantStructSchema",
|
|
59
|
+
"VariantUnionSchema",
|
|
60
|
+
"WithConstructorDefault",
|
|
61
|
+
"extract",
|
|
62
|
+
"field",
|
|
63
|
+
"fields",
|
|
64
|
+
"is_field",
|
|
65
|
+
"is_struct",
|
|
66
|
+
"make",
|
|
67
|
+
"override",
|
|
68
|
+
"with_constructor_default",
|
|
69
|
+
]
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Variant classes (Effect's ``VariantSchema.Class``): a schema class of
|
|
2
|
+
the default variant whose other variants are extracted from the same
|
|
3
|
+
field definitions::
|
|
4
|
+
|
|
5
|
+
class User(Model.Class):
|
|
6
|
+
id: int = Model.field(Model.GeneratedByDb(S.Int)) # select and json only
|
|
7
|
+
name: str # every variant
|
|
8
|
+
password = Model.Sensitive(S.String) # not in select: no annotation
|
|
9
|
+
|
|
10
|
+
An annotated field is a field of the class (the default variant), its
|
|
11
|
+
schema given by ``field(...)`` or derived from the annotation; a variant
|
|
12
|
+
field that the default variant leaves out is assigned without an
|
|
13
|
+
annotation, so the class does not have it. A subclass extends: a field it
|
|
14
|
+
redefines replaces the inherited one in every variant.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
# pyright: reportPrivateUsage=false
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import dataclasses
|
|
22
|
+
import inspect
|
|
23
|
+
from collections.abc import Mapping
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from typing import Any, ClassVar, Protocol, cast, dataclass_transform, overload
|
|
26
|
+
|
|
27
|
+
from pyfect.schema._class import _SCHEMA, _annotation, _shape_of
|
|
28
|
+
from pyfect.schema._class import Class as SchemaClass
|
|
29
|
+
from pyfect.schema._class import field as schema_field
|
|
30
|
+
from pyfect.schema._schema import Codec
|
|
31
|
+
from pyfect.schema.variant_schema._field import AnyCodec, Field
|
|
32
|
+
from pyfect.schema.variant_schema._struct import (
|
|
33
|
+
Definition,
|
|
34
|
+
Struct,
|
|
35
|
+
VariantStructSchema,
|
|
36
|
+
extract,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
_VARIANT = "pyfect.schema.variant_schema"
|
|
40
|
+
"""The ``dataclasses.Field.metadata`` key of a field's variant definition."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@overload
|
|
44
|
+
def field[A](spec: Field[A], /) -> A: ...
|
|
45
|
+
@overload
|
|
46
|
+
def field(spec: Struct, /) -> Mapping[str, object]: ...
|
|
47
|
+
@overload
|
|
48
|
+
def field[A](spec: Codec[A, Any, Any, Any], /) -> A: ...
|
|
49
|
+
def field(spec: Field[Any] | Struct | AnyCodec, /) -> Any:
|
|
50
|
+
"""A variant class field defined by a variant field, a nested variant
|
|
51
|
+
struct or a schema (one that may need services); the class's
|
|
52
|
+
annotation must be its Type in the default variant."""
|
|
53
|
+
return dataclasses.field(metadata={_VARIANT: spec})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class VariantSet(Protocol):
|
|
57
|
+
"""The variants a toolkit defines."""
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def variants(self) -> tuple[str, ...]: ...
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def default_variant(self) -> str: ...
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True, slots=True)
|
|
67
|
+
class _Annotated:
|
|
68
|
+
"""A field defined by its annotation, read on first use (so it may
|
|
69
|
+
refer to classes defined later)."""
|
|
70
|
+
|
|
71
|
+
owner: type[Class]
|
|
72
|
+
name: str
|
|
73
|
+
|
|
74
|
+
def resolve(self) -> Definition:
|
|
75
|
+
annotation = _annotation(self.owner, self.name)
|
|
76
|
+
if isinstance(annotation, type) and issubclass(annotation, Class):
|
|
77
|
+
return annotation
|
|
78
|
+
return _shape_of(self.owner).schemas[self.name]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass_transform(
|
|
82
|
+
kw_only_default=True, frozen_default=True, field_specifiers=(field, schema_field)
|
|
83
|
+
)
|
|
84
|
+
class Class(SchemaClass):
|
|
85
|
+
"""A variant class base; a toolkit's ``Class`` (``Model.Class``) is the
|
|
86
|
+
one to subclass. See ``pyfect.schema.variant_schema``."""
|
|
87
|
+
|
|
88
|
+
__variant_set__: ClassVar[VariantSet]
|
|
89
|
+
|
|
90
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
91
|
+
variant_set = cast("VariantSet | None", getattr(cls, "__variant_set__", None))
|
|
92
|
+
if variant_set is None:
|
|
93
|
+
raise TypeError(f"{cls.__name__}: subclass a toolkit's Class (Model.Class, ...)")
|
|
94
|
+
default = variant_set.default_variant
|
|
95
|
+
annotated = inspect.get_annotations(cls)
|
|
96
|
+
unannotated = _take_unannotated(cls, annotated, default)
|
|
97
|
+
_set_default_schemas(cls, annotated, default)
|
|
98
|
+
super().__init_subclass__(**kwargs)
|
|
99
|
+
own: dict[str, object] = {}
|
|
100
|
+
for f in dataclasses.fields(cast("Any", cls)):
|
|
101
|
+
if f.name in annotated:
|
|
102
|
+
own[f.name] = f.metadata.get(_VARIANT) or _Annotated(cls, f.name)
|
|
103
|
+
own.update(unannotated)
|
|
104
|
+
type.__setattr__(cls, "_variant_own", own)
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def __variant_fields__(cls) -> Mapping[str, Definition]:
|
|
108
|
+
merged: dict[str, object] = {}
|
|
109
|
+
for klass in reversed(cls.__mro__):
|
|
110
|
+
merged.update(klass.__dict__.get("_variant_own", {}))
|
|
111
|
+
return {
|
|
112
|
+
name: value.resolve() if isinstance(value, _Annotated) else cast("Definition", value)
|
|
113
|
+
for name, value in merged.items()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def __variant_cache__(cls, is_default: bool) -> dict[str, Any]:
|
|
118
|
+
name = "_variant_default_cache" if is_default else "_variant_cache"
|
|
119
|
+
cache = cls.__dict__.get(name)
|
|
120
|
+
if cache is None:
|
|
121
|
+
cache = {}
|
|
122
|
+
type.__setattr__(cls, name, cache)
|
|
123
|
+
return cast("dict[str, Any]", cache)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _take_unannotated(
|
|
127
|
+
cls: type, annotated: Mapping[str, object], default: str
|
|
128
|
+
) -> dict[str, object]:
|
|
129
|
+
"""The variant fields assigned without an annotation, removed from the
|
|
130
|
+
class (it does not have them)."""
|
|
131
|
+
found: dict[str, object] = {}
|
|
132
|
+
for name, value in list(vars(cls).items()):
|
|
133
|
+
if name in annotated or not isinstance(value, Field | Struct):
|
|
134
|
+
continue
|
|
135
|
+
if isinstance(value, Struct) or default in value.schemas:
|
|
136
|
+
raise TypeError(
|
|
137
|
+
f"{cls.__name__}.{name} is in the {default!r} variant: annotate it with its type"
|
|
138
|
+
)
|
|
139
|
+
found[name] = value
|
|
140
|
+
delattr(cls, name)
|
|
141
|
+
return found
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _set_default_schemas(cls: type, annotated: Mapping[str, object], default: str) -> None:
|
|
145
|
+
"""Give each ``field(...)`` field its default variant's schema, the one
|
|
146
|
+
the class itself decodes with."""
|
|
147
|
+
for name in annotated:
|
|
148
|
+
value = vars(cls).get(name)
|
|
149
|
+
if not isinstance(value, dataclasses.Field):
|
|
150
|
+
continue
|
|
151
|
+
spec = cast("dataclasses.Field[Any]", value).metadata.get(_VARIANT)
|
|
152
|
+
if spec is None:
|
|
153
|
+
continue
|
|
154
|
+
if isinstance(spec, Field):
|
|
155
|
+
schema = cast("Field[Any]", spec).schemas.get(default)
|
|
156
|
+
if schema is None:
|
|
157
|
+
raise TypeError(
|
|
158
|
+
f"{cls.__name__}.{name} is not in the {default!r} variant: "
|
|
159
|
+
"assign it without an annotation"
|
|
160
|
+
)
|
|
161
|
+
elif isinstance(spec, Struct):
|
|
162
|
+
schema = extract(spec, default)
|
|
163
|
+
else:
|
|
164
|
+
schema = spec
|
|
165
|
+
setattr(cls, name, dataclasses.field(metadata={_SCHEMA: schema, _VARIANT: spec}))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class VariantAttribute:
|
|
169
|
+
"""``Cls.<variant>``: the variant's struct, annotated with the class's
|
|
170
|
+
identifier (``User.insert``)."""
|
|
171
|
+
|
|
172
|
+
__slots__ = ("variant",)
|
|
173
|
+
|
|
174
|
+
def __init__(self, variant: str) -> None:
|
|
175
|
+
self.variant = variant
|
|
176
|
+
|
|
177
|
+
def __get__(self, instance: object, owner: type[Class]) -> VariantStructSchema[Any]:
|
|
178
|
+
attributes = owner.__dict__.get("_variant_attributes")
|
|
179
|
+
if attributes is None:
|
|
180
|
+
attributes = {}
|
|
181
|
+
type.__setattr__(owner, "_variant_attributes", attributes)
|
|
182
|
+
found = cast("dict[str, VariantStructSchema[Any]]", attributes).get(self.variant)
|
|
183
|
+
if found is None:
|
|
184
|
+
name = f"{_shape_of(owner).identifier}.{self.variant}"
|
|
185
|
+
schema = cast("VariantStructSchema[Any]", extract(owner, self.variant))
|
|
186
|
+
found = schema.annotate(identifier=name, title=name)
|
|
187
|
+
attributes[self.variant] = found
|
|
188
|
+
return found
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Constructor defaults for variant fields (``S.with_constructor_default``,
|
|
2
|
+
and Effect's ``VariantSchema.Overrideable``): a value a variant struct's
|
|
3
|
+
``make`` fills in for a missing key. Decoding never uses it: a missing key
|
|
4
|
+
stays an error there."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Never
|
|
9
|
+
|
|
10
|
+
from pyfect._core import Effect
|
|
11
|
+
from pyfect._effect_args import require_effect
|
|
12
|
+
from pyfect.schema._constructor_default import WithConstructorDefault, with_constructor_default
|
|
13
|
+
from pyfect.schema._schema import Codec
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def Overrideable[T, E, RD, RE](
|
|
17
|
+
schema: Codec[T, E, RD, RE], /, *, default_value: Effect[T, Never, Never]
|
|
18
|
+
) -> WithConstructorDefault[T, E, RD, RE]:
|
|
19
|
+
"""``schema`` with ``default_value`` as its constructor default, for
|
|
20
|
+
values the application makes (timestamps) but a caller may set with
|
|
21
|
+
``override`` (Effect's ``Overrideable``)."""
|
|
22
|
+
require_effect(default_value, "default_value")
|
|
23
|
+
return with_constructor_default(default_value)(schema)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def override[A](value: A, /) -> A:
|
|
27
|
+
"""An explicit value for an ``Overrideable`` field (Effect's
|
|
28
|
+
``Override``). Python has no phantom brand for any ``A``, so it marks the
|
|
29
|
+
intent and returns ``value``."""
|
|
30
|
+
return value
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Variant fields (Effect's ``VariantSchema.Field``): one logical field's
|
|
2
|
+
schema in each variant that has it."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable, Mapping
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Generic, TypeIs, TypeVar
|
|
9
|
+
|
|
10
|
+
from pyfect.schema._schema import Codec
|
|
11
|
+
|
|
12
|
+
A_co = TypeVar("A_co", covariant=True)
|
|
13
|
+
|
|
14
|
+
type AnyCodec = Codec[Any, Any, Any, Any]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
18
|
+
class Field(Generic[A_co]):
|
|
19
|
+
"""A field's schema per variant; a variant missing from ``schemas``
|
|
20
|
+
leaves the field out of that variant. ``A`` is the field's Type in the
|
|
21
|
+
default variant (the annotation a class field must have)."""
|
|
22
|
+
|
|
23
|
+
schemas: Mapping[str, AnyCodec]
|
|
24
|
+
|
|
25
|
+
def pipe[B](self, f: Callable[[Field[A_co]], B], /) -> B:
|
|
26
|
+
"""``f(self)``: ``S.Int.pipe(model.field_only(["select"]))`` and so on."""
|
|
27
|
+
return f(self)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def is_field(value: object) -> TypeIs[Field[Any]]:
|
|
31
|
+
"""``value`` is a variant field (Effect's ``isField``)."""
|
|
32
|
+
return isinstance(value, Field)
|