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/_primitives.py
ADDED
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""The primitives the fiber runtime interprets, and the helpers built on them.
|
|
2
|
+
|
|
3
|
+
Each primitive reduces itself in ``_step``; continuation frames consume
|
|
4
|
+
results in ``_on_success`` / ``_on_failure`` (see ``pyfect._core``).
|
|
5
|
+
|
|
6
|
+
Import these through ``pyfect._core``, which loads this module once
|
|
7
|
+
``Effect`` exists: importing this module first is a circular import.
|
|
8
|
+
"""
|
|
9
|
+
# pyright: reportPrivateUsage=false
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Callable, Generator
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Never, Protocol, cast, final
|
|
15
|
+
|
|
16
|
+
from pyfect._core import Effect, Frame, Step
|
|
17
|
+
from pyfect.cause import Cause, merge
|
|
18
|
+
from pyfect.cause import die as die_cause
|
|
19
|
+
from pyfect.exit import Exit, Failure, Success
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from pyfect._fiber import Fiber, Uninterruptible
|
|
23
|
+
from pyfect.context import Context
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# --- primitives ---------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@final
|
|
30
|
+
class _Succeed[A](Effect[A, Never, Never]):
|
|
31
|
+
__slots__ = ("value",)
|
|
32
|
+
|
|
33
|
+
def __init__(self, value: A) -> None:
|
|
34
|
+
self.value = value
|
|
35
|
+
|
|
36
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
37
|
+
return Success(self.value)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@final
|
|
41
|
+
class _FailCause[E](Effect[Never, E, Never]):
|
|
42
|
+
__slots__ = ("cause",)
|
|
43
|
+
|
|
44
|
+
def __init__(self, cause: Cause[E]) -> None:
|
|
45
|
+
self.cause = cause
|
|
46
|
+
|
|
47
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
48
|
+
return Failure(self.cause)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@final
|
|
52
|
+
class _FromExit[A, E](Effect[A, E, Never]):
|
|
53
|
+
__slots__ = ("outcome",)
|
|
54
|
+
|
|
55
|
+
def __init__(self, ex: Exit[A, E]) -> None:
|
|
56
|
+
self.outcome = ex
|
|
57
|
+
|
|
58
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
59
|
+
return self.outcome
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def known_exit[A, E](eff: Effect[A, E, Any]) -> Exit[A, E] | None:
|
|
63
|
+
"""The outcome of an effect that is nothing but its outcome
|
|
64
|
+
(``succeed``, ``fail_cause``, ``from_exit``), without running it; else
|
|
65
|
+
``None``. Lets a caller stay synchronous where Effect returns an
|
|
66
|
+
``Exit`` (which is an ``Effect`` there)."""
|
|
67
|
+
if isinstance(eff, _Succeed):
|
|
68
|
+
return Success(eff.value)
|
|
69
|
+
if isinstance(eff, _FailCause):
|
|
70
|
+
return Failure(eff.cause)
|
|
71
|
+
if isinstance(eff, _FromExit):
|
|
72
|
+
return eff.outcome
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def run_known(eff: Effect[Any, Any, Any]) -> Step:
|
|
77
|
+
"""``eff``'s outcome if it is nothing but that (``known_exit``), else
|
|
78
|
+
``eff``. A step or continuation that has just claimed state returns
|
|
79
|
+
this, so the claimed result reaches its continuation in the same step:
|
|
80
|
+
returned as an effect, it would run in a later step, and a yield in
|
|
81
|
+
between could let an interrupt drop it."""
|
|
82
|
+
ex = known_exit(eff)
|
|
83
|
+
return eff if ex is None else ex
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@final
|
|
87
|
+
class _Sync[A](Effect[A, Never, Never]):
|
|
88
|
+
"""Run a side-effecting thunk. If it raises, that is a defect."""
|
|
89
|
+
|
|
90
|
+
__slots__ = ("thunk",)
|
|
91
|
+
|
|
92
|
+
def __init__(self, thunk: Callable[[], A]) -> None:
|
|
93
|
+
self.thunk = thunk
|
|
94
|
+
|
|
95
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
96
|
+
return Success(self.thunk())
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@final
|
|
100
|
+
class _Suspend[A, E, R](Effect[A, E, R]):
|
|
101
|
+
"""Build the effect lazily, each time it runs."""
|
|
102
|
+
|
|
103
|
+
__slots__ = ("thunk",)
|
|
104
|
+
|
|
105
|
+
def __init__(self, thunk: Callable[[], Effect[A, E, R]]) -> None:
|
|
106
|
+
self.thunk = thunk
|
|
107
|
+
|
|
108
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
109
|
+
# A thunk that has already decided its result (a queue take, a permit
|
|
110
|
+
# grab) hands it on in this same step: as a separate step it could be
|
|
111
|
+
# parked at a yield, where an interrupt would drop it.
|
|
112
|
+
return run_known(self.thunk())
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@final
|
|
116
|
+
class _Map(Effect[Any, Any, Any]):
|
|
117
|
+
__slots__ = ("f", "inner")
|
|
118
|
+
|
|
119
|
+
def __init__(self, inner: Effect[Any, Any, Any], f: Callable[[Any], Any]) -> None:
|
|
120
|
+
self.inner = inner
|
|
121
|
+
self.f = f
|
|
122
|
+
|
|
123
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
124
|
+
fiber._push(self)
|
|
125
|
+
return self.inner
|
|
126
|
+
|
|
127
|
+
def _on_success(self, value: Any, fiber: Fiber[Any, Any]) -> Step:
|
|
128
|
+
return Success(self.f(value))
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@final
|
|
132
|
+
class _FlatMap(Effect[Any, Any, Any]):
|
|
133
|
+
__slots__ = ("f", "inner")
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self, inner: Effect[Any, Any, Any], f: Callable[[Any], Effect[Any, Any, Any]]
|
|
137
|
+
) -> None:
|
|
138
|
+
self.inner = inner
|
|
139
|
+
self.f = f
|
|
140
|
+
|
|
141
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
142
|
+
fiber._push(self)
|
|
143
|
+
return self.inner
|
|
144
|
+
|
|
145
|
+
def _on_success(self, value: Any, fiber: Fiber[Any, Any]) -> Step:
|
|
146
|
+
return self.f(value)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@final
|
|
150
|
+
class _MatchCause(Effect[Any, Any, Any]):
|
|
151
|
+
"""The one recovery primitive. ``on_failure`` is skipped when the fiber
|
|
152
|
+
has been interrupted and is interruptible: an interrupted fiber can
|
|
153
|
+
clean up (finalizers run in uninterruptible regions) but not recover.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
__slots__ = ("inner", "on_failure", "on_success")
|
|
157
|
+
|
|
158
|
+
def __init__(
|
|
159
|
+
self,
|
|
160
|
+
inner: Effect[Any, Any, Any],
|
|
161
|
+
on_failure: Callable[[Cause[Any]], Step] | None,
|
|
162
|
+
on_success: Callable[[Any], Step] | None,
|
|
163
|
+
) -> None:
|
|
164
|
+
self.inner = inner
|
|
165
|
+
self.on_failure = on_failure
|
|
166
|
+
self.on_success = on_success
|
|
167
|
+
|
|
168
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
169
|
+
fiber._push(self)
|
|
170
|
+
return self.inner
|
|
171
|
+
|
|
172
|
+
def _on_success(self, value: Any, fiber: Fiber[Any, Any]) -> Step:
|
|
173
|
+
return Success(value) if self.on_success is None else self.on_success(value)
|
|
174
|
+
|
|
175
|
+
def _on_failure(self, cause: Cause[Any], fiber: Fiber[Any, Any]) -> Step:
|
|
176
|
+
if self.on_failure is None:
|
|
177
|
+
return Failure(cause)
|
|
178
|
+
if fiber._interrupted_and_interruptible():
|
|
179
|
+
# Skipping a recovery handler: the fiber can no longer act on
|
|
180
|
+
# typed errors, so only defects and the interruption remain.
|
|
181
|
+
return Failure(cause.without_failures())
|
|
182
|
+
return self.on_failure(cause)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
type Register[A, E, R] = Callable[
|
|
186
|
+
[Callable[[Effect[A, E, R]], None]], Effect[object, Never, R] | None
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@final
|
|
191
|
+
class _Callback[A, E, R](Effect[A, E, R]):
|
|
192
|
+
"""Suspend the fiber until ``register``'s resume callback is called.
|
|
193
|
+
|
|
194
|
+
``register`` may return a canceler effect, run if the fiber is
|
|
195
|
+
interrupted while waiting.
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
__slots__ = ("register",)
|
|
199
|
+
|
|
200
|
+
def __init__(self, register: Register[A, E, R]) -> None:
|
|
201
|
+
self.register = register
|
|
202
|
+
|
|
203
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
204
|
+
return fiber._suspend(self.register)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@final
|
|
208
|
+
class _WithFiber[A, E, R](Effect[A, E, R]):
|
|
209
|
+
"""Read the running fiber. Internal building block for concurrency."""
|
|
210
|
+
|
|
211
|
+
__slots__ = ("f",)
|
|
212
|
+
|
|
213
|
+
def __init__(self, f: Callable[[Fiber[Any, Any]], Effect[A, E, R]]) -> None:
|
|
214
|
+
self.f = f
|
|
215
|
+
|
|
216
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
217
|
+
return self.f(fiber)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@final
|
|
221
|
+
class _SetInterruptible[A, E, R](Effect[A, E, R]):
|
|
222
|
+
__slots__ = ("flag", "inner")
|
|
223
|
+
|
|
224
|
+
def __init__(self, inner: Effect[A, E, R], flag: bool) -> None:
|
|
225
|
+
self.inner = inner
|
|
226
|
+
self.flag = flag
|
|
227
|
+
|
|
228
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
229
|
+
if fiber._interruptible != self.flag:
|
|
230
|
+
fiber._push(_RestoreInterruptible(fiber._interruptible))
|
|
231
|
+
fiber._interruptible = self.flag
|
|
232
|
+
return self.inner
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@final
|
|
236
|
+
class _RestoreInterruptible(Frame):
|
|
237
|
+
__slots__ = ("flag",)
|
|
238
|
+
|
|
239
|
+
def __init__(self, flag: bool) -> None:
|
|
240
|
+
self.flag = flag
|
|
241
|
+
|
|
242
|
+
def _on_success(self, value: Any, fiber: Fiber[Any, Any]) -> Step:
|
|
243
|
+
fiber._interruptible = self.flag
|
|
244
|
+
return Success(value)
|
|
245
|
+
|
|
246
|
+
def _on_failure(self, cause: Cause[Any], fiber: Fiber[Any, Any]) -> Step:
|
|
247
|
+
fiber._interruptible = self.flag
|
|
248
|
+
return Failure(cause)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@final
|
|
252
|
+
class _Locally[A, E, R](Effect[A, E, R]):
|
|
253
|
+
"""Run ``inner`` with a modified context, restoring it afterwards."""
|
|
254
|
+
|
|
255
|
+
__slots__ = ("f", "inner")
|
|
256
|
+
|
|
257
|
+
def __init__(self, inner: Effect[A, E, Any], f: Callable[[Context], Context]) -> None:
|
|
258
|
+
self.inner = inner
|
|
259
|
+
self.f = f
|
|
260
|
+
|
|
261
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
262
|
+
fiber._push(_RestoreContext(fiber._context))
|
|
263
|
+
fiber._set_context(self.f(fiber._context))
|
|
264
|
+
return self.inner
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@final
|
|
268
|
+
class _RestoreContext(Frame):
|
|
269
|
+
__slots__ = ("context",)
|
|
270
|
+
|
|
271
|
+
def __init__(self, context: Context) -> None:
|
|
272
|
+
self.context = context
|
|
273
|
+
|
|
274
|
+
def _on_success(self, value: Any, fiber: Fiber[Any, Any]) -> Step:
|
|
275
|
+
fiber._set_context(self.context)
|
|
276
|
+
return Success(value)
|
|
277
|
+
|
|
278
|
+
def _on_failure(self, cause: Cause[Any], fiber: Fiber[Any, Any]) -> Step:
|
|
279
|
+
fiber._set_context(self.context)
|
|
280
|
+
return Failure(cause)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@final
|
|
284
|
+
class _Fork[A, E, R](Effect[Any, Never, R]):
|
|
285
|
+
__slots__ = ("daemon", "inner", "start_immediately", "start_uninterruptible")
|
|
286
|
+
|
|
287
|
+
def __init__(
|
|
288
|
+
self,
|
|
289
|
+
inner: Effect[A, E, R],
|
|
290
|
+
*,
|
|
291
|
+
daemon: bool,
|
|
292
|
+
start_immediately: bool,
|
|
293
|
+
uninterruptible: Uninterruptible,
|
|
294
|
+
) -> None:
|
|
295
|
+
self.inner = inner
|
|
296
|
+
self.daemon = daemon
|
|
297
|
+
self.start_immediately = start_immediately
|
|
298
|
+
self.start_uninterruptible: Uninterruptible = uninterruptible
|
|
299
|
+
|
|
300
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
301
|
+
child = fiber._fork(
|
|
302
|
+
self.inner,
|
|
303
|
+
daemon=self.daemon,
|
|
304
|
+
start_immediately=self.start_immediately,
|
|
305
|
+
uninterruptible=self.start_uninterruptible,
|
|
306
|
+
)
|
|
307
|
+
return fiber._after_started(Success(child))
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@final
|
|
311
|
+
class _AfterStarted[A](Effect[A, Never, Never]):
|
|
312
|
+
"""Succeed with ``value`` once the fibers this run started immediately
|
|
313
|
+
have run, up to their first wait or yield."""
|
|
314
|
+
|
|
315
|
+
__slots__ = ("value",)
|
|
316
|
+
|
|
317
|
+
def __init__(self, value: A) -> None:
|
|
318
|
+
self.value = value
|
|
319
|
+
|
|
320
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
321
|
+
return fiber._after_started(Success(self.value))
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
@final
|
|
325
|
+
class _YieldNow(Effect[None, Never, Never]):
|
|
326
|
+
__slots__ = ("priority",)
|
|
327
|
+
|
|
328
|
+
def __init__(self, priority: int = 0) -> None:
|
|
329
|
+
self.priority = priority
|
|
330
|
+
|
|
331
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
332
|
+
return fiber._yield_now(self.priority)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@final
|
|
336
|
+
class _Gen[A, E, R](Effect[A, E, R]):
|
|
337
|
+
__slots__ = ("factory",)
|
|
338
|
+
|
|
339
|
+
def __init__(self, factory: Callable[[], Generator[Effect[Any, E, R], Any, A]]) -> None:
|
|
340
|
+
self.factory = factory
|
|
341
|
+
|
|
342
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
343
|
+
gen = cast(object, self.factory()) # a lambda may return anything
|
|
344
|
+
if not isinstance(gen, Generator):
|
|
345
|
+
return Failure(
|
|
346
|
+
die_cause(TypeError(f"gen expected a generator, got {type(gen).__name__}"))
|
|
347
|
+
)
|
|
348
|
+
return _GenFrame(cast("Generator[Effect[Any, Any, Any], Any, Any]", gen))._on_success(
|
|
349
|
+
None, fiber
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
@final
|
|
354
|
+
class _GenFrame(Frame):
|
|
355
|
+
"""One running generator. It is resumed with each yielded effect's
|
|
356
|
+
value. On failure it is closed, so ``finally`` and ``with`` blocks run.
|
|
357
|
+
(They cannot yield effects; use ``ensuring`` for effectful cleanup.)
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
__slots__ = ("gen",)
|
|
361
|
+
|
|
362
|
+
def __init__(self, gen: Generator[Effect[Any, Any, Any], Any, Any]) -> None:
|
|
363
|
+
self.gen = gen
|
|
364
|
+
|
|
365
|
+
def _on_success(self, value: Any, fiber: Fiber[Any, Any]) -> Step:
|
|
366
|
+
try:
|
|
367
|
+
nxt = self.gen.send(value)
|
|
368
|
+
except StopIteration as stop:
|
|
369
|
+
return Success(stop.value)
|
|
370
|
+
fiber._push(self)
|
|
371
|
+
return nxt
|
|
372
|
+
|
|
373
|
+
def _on_failure(self, cause: Cause[Any], fiber: Fiber[Any, Any]) -> Step:
|
|
374
|
+
try:
|
|
375
|
+
self.gen.close()
|
|
376
|
+
except KeyboardInterrupt:
|
|
377
|
+
raise # Ctrl-C: the loop interrupts the fiber (Fiber._raised)
|
|
378
|
+
except BaseException as exc:
|
|
379
|
+
return Failure(merge(cause, die_cause(exc)))
|
|
380
|
+
return Failure(cause)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# --- interruptibility regions -------------------------------------------------
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class Restore(Protocol):
|
|
387
|
+
"""Runs an effect with the interruptibility from outside the mask."""
|
|
388
|
+
|
|
389
|
+
def __call__[A, E, R](self, eff: Effect[A, E, R], /) -> Effect[A, E, R]: ...
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def uninterruptible_mask[A, E, R](f: Callable[[Restore], Effect[A, E, R]]) -> Effect[A, E, R]:
|
|
393
|
+
"""Run ``f`` uninterruptibly. ``f`` receives ``restore``, which runs an
|
|
394
|
+
inner effect with the interruptibility the caller had."""
|
|
395
|
+
|
|
396
|
+
def body(fiber: Fiber[Any, Any]) -> Effect[A, E, R]:
|
|
397
|
+
restore = _interruptible if fiber._interruptible else _uninterruptible
|
|
398
|
+
return _SetInterruptible(_Suspend(lambda: f(restore)), False)
|
|
399
|
+
|
|
400
|
+
return _WithFiber(body)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def interruptible_mask[A, E, R](f: Callable[[Restore], Effect[A, E, R]]) -> Effect[A, E, R]:
|
|
404
|
+
"""Run ``f`` interruptibly. ``f`` receives ``restore``, which runs an
|
|
405
|
+
inner effect with the interruptibility the caller had. An interrupt that
|
|
406
|
+
arrived while the caller was uninterruptible lands at once."""
|
|
407
|
+
|
|
408
|
+
def body(fiber: Fiber[Any, Any]) -> Effect[A, E, R]:
|
|
409
|
+
if fiber._interruptible:
|
|
410
|
+
return f(_keep_interruptibility)
|
|
411
|
+
# As in Effect, ``f`` runs even when an interrupt is already waiting.
|
|
412
|
+
return _SetInterruptible(f(_uninterruptible), True)
|
|
413
|
+
|
|
414
|
+
return _WithFiber(body)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class _KeepInterruptibility:
|
|
418
|
+
def __call__[A, E, R](self, eff: Effect[A, E, R], /) -> Effect[A, E, R]:
|
|
419
|
+
return eff
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class _Uninterruptible:
|
|
423
|
+
def __call__[A, E, R](self, eff: Effect[A, E, R], /) -> Effect[A, E, R]:
|
|
424
|
+
return _SetInterruptible(eff, False)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
class _Interruptible:
|
|
428
|
+
def __call__[A, E, R](self, eff: Effect[A, E, R], /) -> Effect[A, E, R]:
|
|
429
|
+
return _SetInterruptible(eff, True)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
_keep_interruptibility = _KeepInterruptibility()
|
|
433
|
+
_interruptible = _Interruptible()
|
|
434
|
+
_uninterruptible = _Uninterruptible()
|
pyfect/_public.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Public names for classes defined in private modules."""
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def public_home(module: str) -> str:
|
|
9
|
+
"""``module`` without its private part: ``pyfect.config._error`` is at
|
|
10
|
+
home in ``pyfect.config``, ``pyfect._root`` in ``pyfect``."""
|
|
11
|
+
return ".".join(itertools.takewhile(lambda part: not part.startswith("_"), module.split(".")))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_private(module: str) -> bool:
|
|
15
|
+
return any(part.startswith("_") for part in module.split("."))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def publish(namespace: Mapping[str, object]) -> None:
|
|
19
|
+
"""Give each exception this public module exports from a private module
|
|
20
|
+
under it this module's name as ``__module__``, so tracebacks and reprs
|
|
21
|
+
show ``pyfect.config.ConfigError`` rather than its private file. The
|
|
22
|
+
first public module to export it names it (a package's ``__init__``
|
|
23
|
+
runs after its submodules'). Call it after ``__all__`` as
|
|
24
|
+
``publish(globals())``; ``tests/test_public_names.py`` checks."""
|
|
25
|
+
module = namespace["__name__"]
|
|
26
|
+
exported = cast("Sequence[str]", namespace.get("__all__", ()))
|
|
27
|
+
assert isinstance(module, str)
|
|
28
|
+
for name in exported:
|
|
29
|
+
value = namespace.get(name)
|
|
30
|
+
if (
|
|
31
|
+
isinstance(value, type)
|
|
32
|
+
and issubclass(value, BaseException)
|
|
33
|
+
and is_private(value.__module__)
|
|
34
|
+
and (module + ".").startswith(public_home(value.__module__) + ".")
|
|
35
|
+
):
|
|
36
|
+
value.__module__ = module
|
pyfect/_reasons.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Reason errors: an error that wraps a more specific error as ``reason``.
|
|
2
|
+
|
|
3
|
+
Effect v4's ``catchReason`` / ``catchReasons`` / ``unwrapReason`` work on
|
|
4
|
+
any error with a ``reason`` field, usually a ``TaggedError`` dataclass::
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class AiError(fx.TaggedError):
|
|
8
|
+
reason: RateLimitError | QuotaExceededError
|
|
9
|
+
|
|
10
|
+
``HasReason`` is the protocol those combinators use to read the reason's
|
|
11
|
+
type off the parent error's class. ``ReasonCatch`` is the runtime half
|
|
12
|
+
shared by the Effect, Stream and Channel versions.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Callable, Sequence
|
|
18
|
+
from typing import Any, Protocol
|
|
19
|
+
|
|
20
|
+
type ReasonHandler[H] = Callable[[Any, Any], H]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class HasReason[RE](Protocol):
|
|
24
|
+
"""An error carrying a ``reason`` of type ``RE`` (Effect's errors with a
|
|
25
|
+
tagged ``reason``; ``TagsWithReason`` is ``type[HasReason[Any]]``)."""
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def reason(self) -> RE: ...
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ReasonCatch[H]:
|
|
32
|
+
"""Decides which errors a reason combinator catches and runs the handler.
|
|
33
|
+
|
|
34
|
+
An instance of ``tag`` is caught when its reason is an instance of one of
|
|
35
|
+
the ``cases`` classes, or always when there is an ``or_else``. A caught
|
|
36
|
+
error goes to the first matching case's handler, else to ``or_else``;
|
|
37
|
+
both get ``(reason, error)``."""
|
|
38
|
+
|
|
39
|
+
__slots__ = ("cases", "or_else", "refail", "tag")
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
tag: type[Any],
|
|
44
|
+
cases: Sequence[tuple[type[Any], ReasonHandler[H]]],
|
|
45
|
+
or_else: ReasonHandler[H] | None,
|
|
46
|
+
refail: Callable[[Any], H],
|
|
47
|
+
) -> None:
|
|
48
|
+
self.tag = tag
|
|
49
|
+
self.cases = tuple(cases)
|
|
50
|
+
self.or_else = or_else
|
|
51
|
+
self.refail = refail
|
|
52
|
+
|
|
53
|
+
def accepts(self, error: Any) -> bool:
|
|
54
|
+
if not isinstance(error, self.tag):
|
|
55
|
+
return False
|
|
56
|
+
reason = error.reason
|
|
57
|
+
return self.or_else is not None or any(isinstance(reason, cls) for cls, _ in self.cases)
|
|
58
|
+
|
|
59
|
+
def __call__(self, error: Any) -> H:
|
|
60
|
+
reason = error.reason
|
|
61
|
+
for cls, f in self.cases:
|
|
62
|
+
if isinstance(reason, cls):
|
|
63
|
+
return f(reason, error)
|
|
64
|
+
return self.refail(error) if self.or_else is None else self.or_else(reason, error)
|
pyfect/_released.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Use after release: a service kept past the scope that owns it.
|
|
2
|
+
|
|
3
|
+
A value made inside ``.pipe(provide(layer))`` can outlive the layer, which
|
|
4
|
+
is released once the effect it was provided to ends. Every resource pyfect
|
|
5
|
+
releases fails a later use with its own typed error carrying
|
|
6
|
+
``closed_message(owner)``, rather than hanging or raising a defect from a
|
|
7
|
+
thread pool or event loop that has stopped.
|
|
8
|
+
|
|
9
|
+
``Lifetime`` is that check for a resource that hands work to a thread or
|
|
10
|
+
loop of its own: ``submit`` gives a future failed with ``Released`` once
|
|
11
|
+
the lifetime is closed, also when the executor or loop stopped first.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
from concurrent.futures import Future
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def closed_message(owner: str) -> str:
|
|
21
|
+
"""Why ``owner`` can't be used: the scope that owns it closed."""
|
|
22
|
+
return f"the {owner} is closed (used after the scope that owns it closed)"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Released(RuntimeError):
|
|
26
|
+
"""``owner`` was used after the scope that owns it closed."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, owner: str) -> None:
|
|
29
|
+
super().__init__(closed_message(owner))
|
|
30
|
+
self.owner = owner
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Lifetime:
|
|
34
|
+
"""Whether a resource owning a thread or loop is still open."""
|
|
35
|
+
|
|
36
|
+
__slots__ = ("_closed", "owner")
|
|
37
|
+
|
|
38
|
+
def __init__(self, owner: str) -> None:
|
|
39
|
+
self.owner = owner
|
|
40
|
+
self._closed = False
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def closed(self) -> bool:
|
|
44
|
+
return self._closed
|
|
45
|
+
|
|
46
|
+
def close(self) -> None:
|
|
47
|
+
self._closed = True
|
|
48
|
+
|
|
49
|
+
def error(self) -> Released:
|
|
50
|
+
return Released(self.owner)
|
|
51
|
+
|
|
52
|
+
def submit[A](self, start: Callable[[], Future[A]]) -> Future[A]:
|
|
53
|
+
"""``start()``, or a future failed with ``Released`` once closed.
|
|
54
|
+
A ``RuntimeError`` from ``start`` after closing (an executor shut
|
|
55
|
+
down, a loop closed) is that same failure."""
|
|
56
|
+
if not self._closed:
|
|
57
|
+
try:
|
|
58
|
+
return start()
|
|
59
|
+
except RuntimeError:
|
|
60
|
+
if not self._closed:
|
|
61
|
+
raise
|
|
62
|
+
failed: Future[A] = Future()
|
|
63
|
+
failed.set_exception(self.error())
|
|
64
|
+
return failed
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
__all__ = ["Lifetime", "Released", "closed_message"]
|
pyfect/_root.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Root fibers: the fibers the run functions and runtimes start."""
|
|
2
|
+
# pyright: reportPrivateUsage=false
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from pyfect._core import Effect
|
|
10
|
+
from pyfect._fiber import Fiber, current_run_scheduler
|
|
11
|
+
from pyfect.context import Context
|
|
12
|
+
from pyfect.scheduler import AsyncioScheduler, CurrentScheduler, Scheduler
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def new_root(
|
|
16
|
+
run_scheduler: Scheduler,
|
|
17
|
+
context: Context,
|
|
18
|
+
*,
|
|
19
|
+
scheduler: Scheduler | None = None,
|
|
20
|
+
uninterruptible: bool = False,
|
|
21
|
+
) -> Fiber[Any, Any]:
|
|
22
|
+
"""An unstarted fiber with no parent, of the run on ``run_scheduler``.
|
|
23
|
+
``scheduler`` is provided as its ``CurrentScheduler``."""
|
|
24
|
+
if scheduler is not None:
|
|
25
|
+
context = context.add(CurrentScheduler, scheduler)
|
|
26
|
+
return Fiber(run_scheduler, context, None, interruptible=not uninterruptible)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def start_root(fiber: Fiber[Any, Any], eff: Effect[Any, Any, Any], *, now: bool = False) -> None:
|
|
30
|
+
"""Start a root fiber. Called from a fiber of the same run, or with
|
|
31
|
+
``now`` (the caller is on the run's thread and no fiber of it is
|
|
32
|
+
running), it runs at once, up to its first wait, before this returns
|
|
33
|
+
(Effect evaluates it synchronously); otherwise it is scheduled."""
|
|
34
|
+
if now or current_run_scheduler() is fiber._run_scheduler:
|
|
35
|
+
fiber._evaluate(eff)
|
|
36
|
+
else:
|
|
37
|
+
fiber._start(eff)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run_loop() -> asyncio.AbstractEventLoop | None:
|
|
41
|
+
"""The asyncio loop driving the run of the fiber running on this thread,
|
|
42
|
+
or ``None`` (no fiber, or a ``run_blocking`` run). A loop merely running on
|
|
43
|
+
this thread does not count: ``run_blocking`` blocks it until it returns, so
|
|
44
|
+
nothing scheduled on it runs before then."""
|
|
45
|
+
run = current_run_scheduler()
|
|
46
|
+
return run.loop if isinstance(run, AsyncioScheduler) else None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def loop_running_here() -> bool:
|
|
50
|
+
"""Whether an asyncio loop is running on this thread."""
|
|
51
|
+
try:
|
|
52
|
+
asyncio.get_running_loop()
|
|
53
|
+
except RuntimeError:
|
|
54
|
+
return False
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def run_identity(run_scheduler: Scheduler) -> object:
|
|
59
|
+
"""What fibers of one run share: the asyncio loop for runs on a loop
|
|
60
|
+
(each ``run_async`` has a scheduler of its own, all driven by the
|
|
61
|
+
loop), else the run's scheduler, which stops when its run ends."""
|
|
62
|
+
return run_scheduler._loop if isinstance(run_scheduler, AsyncioScheduler) else run_scheduler
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def ambient_run(scheduler: Scheduler | None) -> tuple[Scheduler, bool]:
|
|
66
|
+
"""The run a root fiber started here joins, and whether it can start
|
|
67
|
+
at once: the current fiber's (it can), else the running asyncio loop's
|
|
68
|
+
(it can: this is the loop's thread), else ``scheduler``, which the
|
|
69
|
+
caller then drives (it is scheduled)."""
|
|
70
|
+
run = current_run_scheduler()
|
|
71
|
+
if run is not None:
|
|
72
|
+
return run, True
|
|
73
|
+
try:
|
|
74
|
+
loop = asyncio.get_running_loop()
|
|
75
|
+
except RuntimeError:
|
|
76
|
+
if scheduler is None:
|
|
77
|
+
raise NoRunError(
|
|
78
|
+
"run_fork needs a running fiber, a running asyncio loop, or a scheduler "
|
|
79
|
+
"to run on; use run_blocking or run_async otherwise"
|
|
80
|
+
) from None
|
|
81
|
+
return scheduler, False
|
|
82
|
+
return AsyncioScheduler(loop), True
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class NoRunError(RuntimeError):
|
|
86
|
+
"""``run_fork`` or ``run_callback`` was called with nothing to run the
|
|
87
|
+
fiber: no fiber, no asyncio loop and no scheduler."""
|