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
pyfect/case.py
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Case conversion: the case functions of Effect's ``String`` module.
|
|
2
|
+
|
|
3
|
+
The word-based converters split their input into words at case changes
|
|
4
|
+
(``"camelCase"``, ``"CAMELCase"``), letter/digit boundaries and any run of
|
|
5
|
+
characters other than ASCII letters and digits, then join the words again::
|
|
6
|
+
|
|
7
|
+
case.snake_case("helloWorld") # "hello_world"
|
|
8
|
+
case.camel_case("hello world") # "helloWorld"
|
|
9
|
+
case.constant_case("api-v2 xml") # "API_V_2_XML"
|
|
10
|
+
case.config_case("api-v2 xml") # "API_V2_XML" (digit groups kept)
|
|
11
|
+
|
|
12
|
+
The ``snake_to_*``, ``*_to_snake`` and ``kebab_to_snake`` functions only
|
|
13
|
+
swap separators or split at capitals, for input known to be in that case.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
from collections.abc import Callable, Sequence
|
|
20
|
+
|
|
21
|
+
type Patterns = re.Pattern[str] | Sequence[re.Pattern[str]]
|
|
22
|
+
"""A pattern or several, applied in order. Each replaces every match."""
|
|
23
|
+
|
|
24
|
+
# Camel case ("camelCase" -> "camel Case", "CAMELCase" -> "CAMEL Case") and
|
|
25
|
+
# digit boundaries ("camel2case" -> "camel 2 case").
|
|
26
|
+
_SPLIT = (
|
|
27
|
+
re.compile(r"([a-z0-9])([A-Z])"),
|
|
28
|
+
re.compile(r"([A-Z])([A-Z][a-z])"),
|
|
29
|
+
re.compile(r"([A-Za-z])([0-9])"),
|
|
30
|
+
re.compile(r"([0-9])([A-Za-z])"),
|
|
31
|
+
)
|
|
32
|
+
# Config paths keep digit groups such as "v2".
|
|
33
|
+
_CONFIG_SPLIT = _SPLIT[:2]
|
|
34
|
+
_STRIP = re.compile(r"[^A-Za-z0-9]+")
|
|
35
|
+
_CAPITAL = re.compile(r"([A-Z])")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _patterns(patterns: Patterns) -> Sequence[re.Pattern[str]]:
|
|
39
|
+
return (patterns,) if isinstance(patterns, re.Pattern) else patterns
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _words(
|
|
43
|
+
text: str, split: Sequence[re.Pattern[str]], strip: Sequence[re.Pattern[str]]
|
|
44
|
+
) -> list[str]:
|
|
45
|
+
result = text
|
|
46
|
+
for pattern in split:
|
|
47
|
+
result = pattern.sub("\\1\0\\2", result)
|
|
48
|
+
for pattern in strip:
|
|
49
|
+
result = pattern.sub("\0", result)
|
|
50
|
+
result = result.strip("\0")
|
|
51
|
+
return result.split("\0") if result else []
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def no_case(
|
|
55
|
+
text: str,
|
|
56
|
+
*,
|
|
57
|
+
split: Patterns = _SPLIT,
|
|
58
|
+
strip: Patterns = _STRIP,
|
|
59
|
+
delimiter: str = " ",
|
|
60
|
+
transform: Callable[[str], str] = str.lower,
|
|
61
|
+
) -> str:
|
|
62
|
+
"""The words of ``text``, each through ``transform``, joined by
|
|
63
|
+
``delimiter`` (``no_case("helloWorld")`` is ``"hello world"``).
|
|
64
|
+
|
|
65
|
+
Each ``split`` pattern has two groups, and a word boundary goes between
|
|
66
|
+
them; each match of a ``strip`` pattern becomes a boundary. Unlike a
|
|
67
|
+
JavaScript regex without the ``g`` flag, a pattern replaces every match.
|
|
68
|
+
"""
|
|
69
|
+
words = _words(text, _patterns(split), _patterns(strip))
|
|
70
|
+
return delimiter.join(transform(word) for word in words)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _capitalized(word: str) -> str:
|
|
74
|
+
return word[:1].upper() + word[1:].lower()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def pascal_case(text: str) -> str:
|
|
78
|
+
"""``"hello world"`` -> ``"HelloWorld"``."""
|
|
79
|
+
return no_case(text, delimiter="", transform=_capitalized)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def camel_case(text: str) -> str:
|
|
83
|
+
"""``"hello world"`` -> ``"helloWorld"``."""
|
|
84
|
+
words = _words(text, _SPLIT, (_STRIP,))
|
|
85
|
+
return "".join(w.lower() if i == 0 else _capitalized(w) for i, w in enumerate(words))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def constant_case(text: str) -> str:
|
|
89
|
+
"""``"hello world"`` -> ``"HELLO_WORLD"``; digits split off
|
|
90
|
+
(``"api-v2"`` -> ``"API_V_2"``)."""
|
|
91
|
+
return no_case(text, delimiter="_", transform=str.upper)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def config_case(text: str) -> str:
|
|
95
|
+
"""CONSTANT_CASE that keeps digit groups (``"api-v2 xml"`` ->
|
|
96
|
+
``"API_V2_XML"``), as configuration keys are written. Not idempotent:
|
|
97
|
+
``"field2value"`` gives ``"FIELD2VALUE"``, which gives ``"FIELD2_VALUE"``."""
|
|
98
|
+
return "_".join(word.upper() for word in _words(text, _CONFIG_SPLIT, (_STRIP,)))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def kebab_case(text: str) -> str:
|
|
102
|
+
"""``"helloWorld"`` -> ``"hello-world"``."""
|
|
103
|
+
return no_case(text, delimiter="-")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def snake_case(text: str) -> str:
|
|
107
|
+
"""``"helloWorld"`` -> ``"hello_world"``."""
|
|
108
|
+
return no_case(text, delimiter="_")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _from_snake(text: str, first: Callable[[str], str]) -> str:
|
|
112
|
+
if not text or (len(text) % 2 == 0 and text.strip("_") == ""):
|
|
113
|
+
return ""
|
|
114
|
+
out = [first(text[0])]
|
|
115
|
+
i = 1
|
|
116
|
+
while i < len(text):
|
|
117
|
+
if text[i] == "_":
|
|
118
|
+
i += 1
|
|
119
|
+
out.append(text[i : i + 1].upper())
|
|
120
|
+
else:
|
|
121
|
+
out.append(text[i])
|
|
122
|
+
i += 1
|
|
123
|
+
return "".join(out)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def snake_to_camel(text: str) -> str:
|
|
127
|
+
"""``"hello_world"`` -> ``"helloWorld"``: each ``_`` is dropped and
|
|
128
|
+
the next character upper-cased."""
|
|
129
|
+
return _from_snake(text, lambda c: c)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def snake_to_pascal(text: str) -> str:
|
|
133
|
+
"""``"hello_world"`` -> ``"HelloWorld"``."""
|
|
134
|
+
return _from_snake(text, str.upper)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def snake_to_kebab(text: str) -> str:
|
|
138
|
+
"""Every ``_`` becomes ``-``."""
|
|
139
|
+
return text.replace("_", "-")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def camel_to_snake(text: str) -> str:
|
|
143
|
+
"""``_`` before every capital, then lower case:
|
|
144
|
+
``"XMLHttpRequest"`` -> ``"_x_m_l_http_request"``."""
|
|
145
|
+
return _CAPITAL.sub("_\\1", text).lower()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def pascal_to_snake(text: str) -> str:
|
|
149
|
+
"""``camel_to_snake`` that leaves the first character alone:
|
|
150
|
+
``"XMLHttpRequest"`` -> ``"x_m_l_http_request"``."""
|
|
151
|
+
return (text[:1] + _CAPITAL.sub("_\\1", text[1:])).lower()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def kebab_to_snake(text: str) -> str:
|
|
155
|
+
"""Every ``-`` becomes ``_``."""
|
|
156
|
+
return text.replace("-", "_")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
__all__ = [
|
|
160
|
+
"Patterns",
|
|
161
|
+
"camel_case",
|
|
162
|
+
"camel_to_snake",
|
|
163
|
+
"config_case",
|
|
164
|
+
"constant_case",
|
|
165
|
+
"kebab_case",
|
|
166
|
+
"kebab_to_snake",
|
|
167
|
+
"no_case",
|
|
168
|
+
"pascal_case",
|
|
169
|
+
"pascal_to_snake",
|
|
170
|
+
"snake_case",
|
|
171
|
+
"snake_to_camel",
|
|
172
|
+
"snake_to_kebab",
|
|
173
|
+
"snake_to_pascal",
|
|
174
|
+
]
|
pyfect/cause.py
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"""Why an effect failed.
|
|
2
|
+
|
|
3
|
+
A ``Cause`` is a flat list of reasons, as in Effect 4:
|
|
4
|
+
|
|
5
|
+
- ``Fail``: an expected, typed error. This is what the ``E`` channel tracks.
|
|
6
|
+
- ``Die``: a defect. An exception nobody planned for.
|
|
7
|
+
- ``Interrupt``: the fiber was interrupted, optionally by another fiber.
|
|
8
|
+
|
|
9
|
+
Most causes hold one reason. More appear when finalizers fail while
|
|
10
|
+
unwinding, or when concurrent work fails in several places at once.
|
|
11
|
+
|
|
12
|
+
Each reason carries annotations: services keyed by tag, such as the
|
|
13
|
+
``tracer.StackTrace`` frame a failure inside a span records. They take part
|
|
14
|
+
in equality, as in Effect, but not in the hash.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import operator
|
|
20
|
+
import traceback
|
|
21
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
22
|
+
from dataclasses import dataclass, field, replace
|
|
23
|
+
from types import MappingProxyType
|
|
24
|
+
from typing import TYPE_CHECKING, Any, Generic, Never, Self, TypeVar, cast
|
|
25
|
+
|
|
26
|
+
from pyfect._equal import dedupe
|
|
27
|
+
from pyfect.result import Err, Ok, Result
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from pyfect._core import Effect
|
|
31
|
+
from pyfect._stack_frame import StackFrame
|
|
32
|
+
from pyfect.context import Context, Tag
|
|
33
|
+
from pyfect.errors import Done
|
|
34
|
+
|
|
35
|
+
E_co = TypeVar("E_co", covariant=True)
|
|
36
|
+
|
|
37
|
+
type Annotations = Mapping[type[Tag[Any]], object]
|
|
38
|
+
|
|
39
|
+
_NO_ANNOTATIONS: Annotations = MappingProxyType({})
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _Annotated:
|
|
43
|
+
"""``annotate`` for the reason classes."""
|
|
44
|
+
|
|
45
|
+
__slots__ = ()
|
|
46
|
+
annotations: Annotations
|
|
47
|
+
|
|
48
|
+
def annotate(self, annotations: Context, *, overwrite: bool = False) -> Self:
|
|
49
|
+
"""A copy carrying ``annotations`` too. Keys it already has keep
|
|
50
|
+
their value unless ``overwrite``; with nothing new, ``self``."""
|
|
51
|
+
merged = _merge_annotations(self.annotations, annotations.map_unsafe, overwrite)
|
|
52
|
+
return self if merged is None else replace(self, annotations=merged) # pyright: ignore[reportArgumentType, reportUnknownVariableType]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _merge_annotations(
|
|
56
|
+
current: Annotations, added: Annotations, overwrite: bool
|
|
57
|
+
) -> Annotations | None:
|
|
58
|
+
"""``current`` with ``added``, or ``None`` when that changes nothing."""
|
|
59
|
+
new = {
|
|
60
|
+
k: v for k, v in added.items() if k not in current or (overwrite and current[k] is not v)
|
|
61
|
+
}
|
|
62
|
+
return MappingProxyType({**current, **new}) if new else None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True, slots=True)
|
|
66
|
+
class Fail(_Annotated, Generic[E_co]):
|
|
67
|
+
error: E_co
|
|
68
|
+
annotations: Annotations = field(default=_NO_ANNOTATIONS, hash=False, repr=False, kw_only=True)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True, slots=True)
|
|
72
|
+
class Die(_Annotated):
|
|
73
|
+
defect: object
|
|
74
|
+
annotations: Annotations = field(default=_NO_ANNOTATIONS, hash=False, repr=False, kw_only=True)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True, slots=True)
|
|
78
|
+
class Interrupt(_Annotated):
|
|
79
|
+
fiber_id: int | None = None
|
|
80
|
+
annotations: Annotations = field(default=_NO_ANNOTATIONS, hash=False, repr=False, kw_only=True)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
type Reason[E] = Fail[E] | Die | Interrupt
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True, slots=True, weakref_slot=True)
|
|
87
|
+
class Cause(Generic[E_co]):
|
|
88
|
+
reasons: tuple[Reason[E_co], ...]
|
|
89
|
+
|
|
90
|
+
def __add__[E2](self, other: Cause[E2]) -> Cause[E_co | E2]:
|
|
91
|
+
"""Both causes' reasons in order, without repeating equal ones."""
|
|
92
|
+
if not self.reasons:
|
|
93
|
+
return other
|
|
94
|
+
if not other.reasons:
|
|
95
|
+
return self
|
|
96
|
+
combined = _dedupe((*self.reasons, *other.reasons))
|
|
97
|
+
unchanged = len(combined) == len(self.reasons) and all(
|
|
98
|
+
map(operator.is_, combined, self.reasons)
|
|
99
|
+
)
|
|
100
|
+
return self if unchanged else Cause(combined)
|
|
101
|
+
|
|
102
|
+
def add_interrupts(self, interrupts: Cause[Never]) -> Cause[E_co]:
|
|
103
|
+
"""Add each interruption not recorded yet: one per interrupting
|
|
104
|
+
fiber and the frame it interrupted from."""
|
|
105
|
+
seen = {_interruptor(r) for r in self.reasons if isinstance(r, Interrupt)}
|
|
106
|
+
new: list[Reason[E_co]] = []
|
|
107
|
+
for r in interrupts.reasons:
|
|
108
|
+
if isinstance(r, Interrupt) and (key := _interruptor(r)) not in seen:
|
|
109
|
+
seen.add(key)
|
|
110
|
+
new.append(r)
|
|
111
|
+
return Cause((*self.reasons, *new)) if new else self
|
|
112
|
+
|
|
113
|
+
def annotate(self, annotations: Context, *, overwrite: bool = False) -> Cause[E_co]:
|
|
114
|
+
"""Every reason carrying ``annotations`` too (see ``Fail.annotate``)."""
|
|
115
|
+
if not annotations.map_unsafe:
|
|
116
|
+
return self
|
|
117
|
+
reasons = tuple(r.annotate(annotations, overwrite=overwrite) for r in self.reasons)
|
|
118
|
+
changed = any(a is not b for a, b in zip(reasons, self.reasons, strict=True))
|
|
119
|
+
return Cause(reasons) if changed else self
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def annotations(self) -> Context:
|
|
123
|
+
"""Every reason's annotations; a later reason wins a shared key."""
|
|
124
|
+
merged: dict[type[Tag[Any]], object] = {}
|
|
125
|
+
for r in self.reasons:
|
|
126
|
+
merged.update(r.annotations)
|
|
127
|
+
return _context(merged)
|
|
128
|
+
|
|
129
|
+
def map[E2](self, f: Callable[[E_co], E2]) -> Cause[E2]:
|
|
130
|
+
"""Apply ``f`` to every typed error, keeping its annotations;
|
|
131
|
+
defects and interruptions stay."""
|
|
132
|
+
return Cause(
|
|
133
|
+
tuple(
|
|
134
|
+
Fail(f(r.error), annotations=r.annotations) if isinstance(r, Fail) else r
|
|
135
|
+
for r in self.reasons
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def failures(self) -> tuple[E_co, ...]:
|
|
141
|
+
return tuple(r.error for r in self.reasons if isinstance(r, Fail))
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def defects(self) -> tuple[object, ...]:
|
|
145
|
+
return tuple(r.defect for r in self.reasons if isinstance(r, Die))
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def interruptors(self) -> frozenset[int]:
|
|
149
|
+
"""The ids of the fibers that interrupted; external interrupts have none."""
|
|
150
|
+
return frozenset(
|
|
151
|
+
r.fiber_id for r in self.reasons if isinstance(r, Interrupt) and r.fiber_id is not None
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def has_fails(self) -> bool:
|
|
156
|
+
return any(isinstance(r, Fail) for r in self.reasons)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def has_dies(self) -> bool:
|
|
160
|
+
return any(isinstance(r, Die) for r in self.reasons)
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def has_interrupts(self) -> bool:
|
|
164
|
+
return any(isinstance(r, Interrupt) for r in self.reasons)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def has_interrupts_only(self) -> bool:
|
|
168
|
+
return bool(self.reasons) and all(isinstance(r, Interrupt) for r in self.reasons)
|
|
169
|
+
|
|
170
|
+
def elect_defects(self) -> Cause[E_co]:
|
|
171
|
+
"""If there is a defect, every typed error becomes a defect too.
|
|
172
|
+
A ``Done`` stays a failure: it is a producer's end, not an error."""
|
|
173
|
+
if not self.has_dies:
|
|
174
|
+
return self
|
|
175
|
+
from pyfect.errors import Done
|
|
176
|
+
|
|
177
|
+
return Cause(
|
|
178
|
+
tuple(
|
|
179
|
+
Die(r.error, annotations=r.annotations)
|
|
180
|
+
if isinstance(r, Fail) and not isinstance(r.error, Done)
|
|
181
|
+
else r
|
|
182
|
+
for r in self.reasons
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def find_fail(self) -> Fail[E_co] | Cause[Never]:
|
|
187
|
+
"""The first ``Fail``, even beside a defect; otherwise this cause,
|
|
188
|
+
which then holds no typed error. For observing errors (``tap_error``),
|
|
189
|
+
never for recovering from them: use ``find_recoverable_error``."""
|
|
190
|
+
for r in self.reasons:
|
|
191
|
+
if isinstance(r, Fail):
|
|
192
|
+
return r
|
|
193
|
+
return self.without_failures()
|
|
194
|
+
|
|
195
|
+
def find_error(self) -> Result[E_co, Cause[Never]]:
|
|
196
|
+
"""``Ok`` of the first typed error, else ``Err`` of this cause, which
|
|
197
|
+
then holds no typed error."""
|
|
198
|
+
found = self.find_fail()
|
|
199
|
+
return Ok(found.error) if isinstance(found, Fail) else Err(found)
|
|
200
|
+
|
|
201
|
+
def find_die(self) -> Result[Die, Cause[E_co]]:
|
|
202
|
+
found = next((r for r in self.reasons if isinstance(r, Die)), None)
|
|
203
|
+
return Err(self) if found is None else Ok(found)
|
|
204
|
+
|
|
205
|
+
def find_defect(self) -> Result[object, Cause[E_co]]:
|
|
206
|
+
return self.find_die().map(lambda die: die.defect)
|
|
207
|
+
|
|
208
|
+
def find_interrupt(self) -> Result[Interrupt, Cause[E_co]]:
|
|
209
|
+
found = next((r for r in self.reasons if isinstance(r, Interrupt)), None)
|
|
210
|
+
return Err(self) if found is None else Ok(found)
|
|
211
|
+
|
|
212
|
+
def filter_interruptors(self) -> Result[frozenset[int], Cause[E_co]]:
|
|
213
|
+
"""``interruptors`` if the cause holds an interruption (the set is
|
|
214
|
+
empty when every one was external), else ``Err`` of the cause."""
|
|
215
|
+
return Ok(self.interruptors) if self.has_interrupts else Err(self)
|
|
216
|
+
|
|
217
|
+
def find_recoverable_error(self) -> Fail[E_co] | Cause[Never]:
|
|
218
|
+
"""What a typed recovery handler may act on.
|
|
219
|
+
|
|
220
|
+
The first ``Fail``, unless the cause holds a defect: defects take
|
|
221
|
+
precedence, so the result is then the cause to re-fail with, every
|
|
222
|
+
typed error elected to a defect. A cause with no typed error is
|
|
223
|
+
returned as is. Interruptions do not block recovery.
|
|
224
|
+
"""
|
|
225
|
+
if not self.has_dies:
|
|
226
|
+
for r in self.reasons:
|
|
227
|
+
if isinstance(r, Fail):
|
|
228
|
+
return r
|
|
229
|
+
return self.without_failures()
|
|
230
|
+
# Only a ``Done`` is left as a failure, and it is not recoverable.
|
|
231
|
+
elected: Cause[Any] = self.elect_defects()
|
|
232
|
+
return elected
|
|
233
|
+
|
|
234
|
+
def without_failures(self) -> Cause[Never]:
|
|
235
|
+
"""Keep defects and interruptions; drop typed errors."""
|
|
236
|
+
return Cause(tuple(r for r in self.reasons if not isinstance(r, Fail)))
|
|
237
|
+
|
|
238
|
+
def squash(self) -> object:
|
|
239
|
+
"""The single value that best represents this cause, as it is.
|
|
240
|
+
|
|
241
|
+
Priority: the first typed error, then the first defect, then
|
|
242
|
+
interruption.
|
|
243
|
+
"""
|
|
244
|
+
if self.failures:
|
|
245
|
+
return self.failures[0]
|
|
246
|
+
if self.defects:
|
|
247
|
+
return self.defects[0]
|
|
248
|
+
return _interrupted(self) if self.reasons else EmptyCauseError("Empty cause")
|
|
249
|
+
|
|
250
|
+
def raisable(self) -> BaseException:
|
|
251
|
+
"""``squash`` as something Python can raise: a value that is not an
|
|
252
|
+
exception is wrapped in ``UnhandledError``."""
|
|
253
|
+
return _as_exception(self.squash())
|
|
254
|
+
|
|
255
|
+
def pretty_errors(self) -> tuple[BaseException, ...]:
|
|
256
|
+
"""One exception per typed error and defect, in order. Typed errors
|
|
257
|
+
that are not exceptions are wrapped in ``UnhandledError``. A cause of
|
|
258
|
+
interruptions alone gives one ``Interrupted`` naming the interruptors;
|
|
259
|
+
an empty cause gives none."""
|
|
260
|
+
errors = tuple(
|
|
261
|
+
_as_exception(r.error if isinstance(r, Fail) else r.defect)
|
|
262
|
+
for r in self.reasons
|
|
263
|
+
if not isinstance(r, Interrupt)
|
|
264
|
+
)
|
|
265
|
+
return errors if errors or not self.reasons else (_interrupted(self),)
|
|
266
|
+
|
|
267
|
+
def pretty(self) -> str:
|
|
268
|
+
"""``pretty_errors`` rendered with their tracebacks and cause chains,
|
|
269
|
+
each followed by the span frames its reason records (Effect's
|
|
270
|
+
`` at span (location)`` lines)."""
|
|
271
|
+
from pyfect._stack_frame import StackTrace, render
|
|
272
|
+
|
|
273
|
+
errors = self.pretty_errors()
|
|
274
|
+
reasons = [r for r in self.reasons if not isinstance(r, Interrupt)] or self.reasons[:1]
|
|
275
|
+
return "\n\n".join(
|
|
276
|
+
"\n".join(
|
|
277
|
+
[
|
|
278
|
+
_render_error(e),
|
|
279
|
+
*render(_frame(r.annotations, StackTrace)),
|
|
280
|
+
]
|
|
281
|
+
)
|
|
282
|
+
for e, r in zip(errors, reasons, strict=True)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _render_error(error: BaseException) -> str:
|
|
287
|
+
"""``error`` with its traceback and cause chain; a defect decoded from
|
|
288
|
+
JSON as Effect renders the decoded error, under the name it was written
|
|
289
|
+
with."""
|
|
290
|
+
from pyfect.schema import ErrorInstance
|
|
291
|
+
|
|
292
|
+
if isinstance(error, ErrorInstance):
|
|
293
|
+
return error.stack or f"{error.name}: {error.message}"
|
|
294
|
+
if isinstance(error, UnhandledError):
|
|
295
|
+
return _render_no_error(error.error)
|
|
296
|
+
if error.__traceback__ is not None:
|
|
297
|
+
return "".join(traceback.format_exception(error)).rstrip()
|
|
298
|
+
return _render_value(error, "", {id(error)})
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _render_no_error(value: object) -> str:
|
|
302
|
+
"""A value that is no exception, as ``causePrettyError`` makes an
|
|
303
|
+
``Error`` of it."""
|
|
304
|
+
from pyfect.logger._text import json_text
|
|
305
|
+
|
|
306
|
+
if value is None or value in (0, ""):
|
|
307
|
+
return f"Error: Unknown error: {value}"
|
|
308
|
+
return f"Error: {value if isinstance(value, str) else json_text(value)}"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _render_value(error: BaseException, prefix: str, seen: set[int]) -> str:
|
|
312
|
+
"""An error that was never raised (a typed failure is a value) as
|
|
313
|
+
Effect's ``renderPrettyError`` renders one: its line (as Python names
|
|
314
|
+
it), then its ``__cause__`` nested in ``{ [cause]: ... }``. Python's chain would put
|
|
315
|
+
the cause first and end with the error that was logged."""
|
|
316
|
+
text = "".join(traceback.format_exception(error, chain=False)).rstrip()
|
|
317
|
+
# The cause gets its own nested block here, so the line keeps Effect's message alone.
|
|
318
|
+
message = getattr(error, "message", None)
|
|
319
|
+
if isinstance(message, str):
|
|
320
|
+
text = text.replace(str(error), message, 1)
|
|
321
|
+
lines = text.splitlines()
|
|
322
|
+
if prefix:
|
|
323
|
+
lines = [f"{prefix}[cause]: {lines[0]}", *(prefix + line for line in lines[1:])]
|
|
324
|
+
cause = error.__cause__
|
|
325
|
+
if cause is None or id(cause) in seen:
|
|
326
|
+
return "\n".join(lines)
|
|
327
|
+
seen.add(id(cause))
|
|
328
|
+
inner = _render_value(cause, prefix + " ", seen)
|
|
329
|
+
return "\n".join(lines) + f" {{\n{inner}\n{prefix}}}"
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class UnhandledError(Exception):
|
|
333
|
+
"""Raised at a run boundary for a typed error that is not an exception."""
|
|
334
|
+
|
|
335
|
+
def __init__(self, error: object) -> None:
|
|
336
|
+
super().__init__(error if isinstance(error, str) else repr(error))
|
|
337
|
+
self.error = error
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class Interrupted(Exception):
|
|
341
|
+
"""Raised at a run boundary when the fiber was interrupted."""
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class EmptyCauseError(Exception):
|
|
345
|
+
"""What an empty cause squashes to: it records no reason at all."""
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _interrupted(cause: Cause[object]) -> Interrupted:
|
|
349
|
+
from pyfect._interrupt_reason import InterruptReason
|
|
350
|
+
from pyfect._stack_frame import InterruptorStackTrace, render
|
|
351
|
+
|
|
352
|
+
error = Interrupted("All fibers interrupted without error")
|
|
353
|
+
for r in cause.reasons:
|
|
354
|
+
if isinstance(r, Interrupt):
|
|
355
|
+
by = "externally" if r.fiber_id is None else f"by fiber #{r.fiber_id}"
|
|
356
|
+
reason = r.annotations.get(InterruptReason)
|
|
357
|
+
because = "" if reason is None else f" because {reason}"
|
|
358
|
+
frames = render(_frame(r.annotations, InterruptorStackTrace))
|
|
359
|
+
error.add_note("\n".join([f"interrupted {by}{because}", *frames]))
|
|
360
|
+
return error
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _frame(annotations: Annotations, tag: type[Tag[StackFrame]]) -> StackFrame | None:
|
|
364
|
+
return cast("StackFrame | None", annotations.get(tag))
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _interruptor(reason: Interrupt) -> tuple[int | None, object]:
|
|
368
|
+
"""What tells interruptions apart: the fiber, and the frame it
|
|
369
|
+
interrupted from."""
|
|
370
|
+
from pyfect._stack_frame import InterruptorStackTrace
|
|
371
|
+
|
|
372
|
+
return reason.fiber_id, reason.annotations.get(InterruptorStackTrace)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _context(services: Mapping[type[Tag[Any]], object]) -> Context:
|
|
376
|
+
from pyfect.context import Context
|
|
377
|
+
|
|
378
|
+
return Context(services)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def reason_annotations(reason: Reason[object]) -> Context:
|
|
382
|
+
"""A reason's annotations as a ``Context``."""
|
|
383
|
+
return _context(reason.annotations)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _as_exception(error: object) -> BaseException:
|
|
387
|
+
return error if isinstance(error, BaseException) else UnhandledError(error)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _dedupe[E](reasons: tuple[Reason[E], ...]) -> tuple[Reason[E], ...]:
|
|
391
|
+
"""Drop repeated reasons, keeping order, as Effect's dedupeReasons:
|
|
392
|
+
reasons of one kind whose payloads ``Equal.equals`` calls equal
|
|
393
|
+
(``fail(1)`` and ``fail(True)`` stay apart) and whose annotations match."""
|
|
394
|
+
if len(reasons) < 2:
|
|
395
|
+
return reasons
|
|
396
|
+
return tuple(dedupe(reasons, _reason_key))
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _reason_key(reason: Reason[object]) -> object:
|
|
400
|
+
match reason:
|
|
401
|
+
case Fail(error=payload) | Die(defect=payload) | Interrupt(fiber_id=payload):
|
|
402
|
+
annotations = reason.annotations
|
|
403
|
+
return (type(reason).__name__, payload, dict(annotations) if annotations else None)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def merge[E](*causes: Cause[E]) -> Cause[E]:
|
|
407
|
+
"""Combine causes of failures that happened together: a body and its
|
|
408
|
+
finalizers, or concurrent siblings. A defect in any of them makes
|
|
409
|
+
every typed error a defect, so the result cannot be recovered."""
|
|
410
|
+
combined: Cause[E] = Cause(())
|
|
411
|
+
for c in causes:
|
|
412
|
+
combined = combined + c
|
|
413
|
+
return combined.elect_defects()
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
empty: Cause[Never] = Cause(())
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def from_reasons[E = Never](reasons: Iterable[Reason[E]]) -> Cause[E]:
|
|
420
|
+
"""A cause holding exactly ``reasons``, in order, repeats included."""
|
|
421
|
+
return Cause(tuple(reasons))
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def fail[E](error: E) -> Cause[E]:
|
|
425
|
+
return Cause((Fail(error),))
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def done[A = None](value: A = None) -> Effect[Never, Done[A]]:
|
|
429
|
+
"""An effect failing with ``Done(value)``, ending a pull."""
|
|
430
|
+
from pyfect.effect import fail as fail_effect
|
|
431
|
+
from pyfect.errors import Done
|
|
432
|
+
|
|
433
|
+
return fail_effect(Done(value))
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def die(defect: object) -> Cause[Never]:
|
|
437
|
+
return Cause((Die(defect),))
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def interrupt(fiber_id: int | None = None) -> Cause[Never]:
|
|
441
|
+
return Cause((Interrupt(fiber_id),))
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
__all__ = [
|
|
445
|
+
"Annotations",
|
|
446
|
+
"Cause",
|
|
447
|
+
"Die",
|
|
448
|
+
"EmptyCauseError",
|
|
449
|
+
"Fail",
|
|
450
|
+
"Interrupt",
|
|
451
|
+
"Interrupted",
|
|
452
|
+
"Reason",
|
|
453
|
+
"UnhandledError",
|
|
454
|
+
"die",
|
|
455
|
+
"done",
|
|
456
|
+
"empty",
|
|
457
|
+
"fail",
|
|
458
|
+
"from_reasons",
|
|
459
|
+
"interrupt",
|
|
460
|
+
"merge",
|
|
461
|
+
"reason_annotations",
|
|
462
|
+
]
|