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/_core.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""``Effect[A, E, R]``: the lazy description of a computation, and its
|
|
2
|
+
sequencing methods.
|
|
3
|
+
|
|
4
|
+
An effect is an immutable description. Running it is the fiber's job
|
|
5
|
+
(``pyfect._fiber``). The runtime loop knows nothing about specific
|
|
6
|
+
primitives: each primitive reduces itself in ``_step``, and continuation
|
|
7
|
+
frames (often the primitive itself, pushed onto the fiber's stack) consume
|
|
8
|
+
the result in ``_on_success`` / ``_on_failure``. The step protocol is in
|
|
9
|
+
``pyfect._effect_base``.
|
|
10
|
+
|
|
11
|
+
``Effect`` gathers its methods from one module per concern:
|
|
12
|
+
|
|
13
|
+
- sequencing: here
|
|
14
|
+
- error handling: ``pyfect._effect_errors``
|
|
15
|
+
- folding (``match``, ``exit``, ``result``): ``pyfect._effect_folding``
|
|
16
|
+
- finalizers and interruptibility: ``pyfect._effect_resources``
|
|
17
|
+
- log annotations, log spans and loggers: ``pyfect._effect_logging``
|
|
18
|
+
- tracing spans: ``pyfect._effect_tracing``
|
|
19
|
+
- forking, racing and timeouts: ``pyfect._effect_concurrency``
|
|
20
|
+
- delays, caching and schedules: ``pyfect._effect_timing``
|
|
21
|
+
|
|
22
|
+
Every method is built from about a dozen primitives, defined in
|
|
23
|
+
``pyfect._primitives`` and re-exported from this module. Import effects and
|
|
24
|
+
primitives from here: loading this module loads the rest in the right order.
|
|
25
|
+
"""
|
|
26
|
+
# pyright: reportPrivateUsage=false
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from collections.abc import Callable, Generator, Iterable
|
|
31
|
+
from typing import TYPE_CHECKING, Any, Literal, Never, Self, TypeIs, cast, overload
|
|
32
|
+
|
|
33
|
+
from pyfect._effect_args import require_effect
|
|
34
|
+
from pyfect._effect_base import SUSPEND, A_co, E_co, EffectBase, Frame, R_co, Step
|
|
35
|
+
from pyfect._effect_concurrency import ConcurrencyMethods
|
|
36
|
+
from pyfect._effect_eager import EagerMethods
|
|
37
|
+
from pyfect._effect_errors import (
|
|
38
|
+
ErrorMethods,
|
|
39
|
+
_catching_if,
|
|
40
|
+
_fail,
|
|
41
|
+
_ignore_failure,
|
|
42
|
+
_recovering,
|
|
43
|
+
_succeed_none,
|
|
44
|
+
)
|
|
45
|
+
from pyfect._effect_folding import FoldingMethods
|
|
46
|
+
from pyfect._effect_logging import LoggingMethods
|
|
47
|
+
from pyfect._effect_metrics import MetricMethods
|
|
48
|
+
from pyfect._effect_resources import ResourceMethods, _call_user, _run_finalizer
|
|
49
|
+
from pyfect._effect_services import ServiceMethods
|
|
50
|
+
from pyfect._effect_timing import TimingMethods
|
|
51
|
+
from pyfect._effect_tracing import TracingMethods
|
|
52
|
+
from pyfect.result import Err, Ok
|
|
53
|
+
|
|
54
|
+
if TYPE_CHECKING:
|
|
55
|
+
from pyfect._concurrency import Concurrency
|
|
56
|
+
from pyfect._fiber import Fiber
|
|
57
|
+
from pyfect.errors import NoSuchElementException
|
|
58
|
+
from pyfect.option import Option
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Effect(
|
|
62
|
+
EagerMethods[A_co, E_co, R_co],
|
|
63
|
+
ErrorMethods[A_co, E_co, R_co],
|
|
64
|
+
FoldingMethods[A_co, E_co, R_co],
|
|
65
|
+
ResourceMethods[A_co, E_co, R_co],
|
|
66
|
+
ServiceMethods[A_co, E_co, R_co],
|
|
67
|
+
LoggingMethods[A_co, E_co, R_co],
|
|
68
|
+
TracingMethods[A_co, E_co, R_co],
|
|
69
|
+
MetricMethods[A_co, E_co, R_co],
|
|
70
|
+
ConcurrencyMethods[A_co, E_co, R_co],
|
|
71
|
+
TimingMethods[A_co, E_co, R_co],
|
|
72
|
+
):
|
|
73
|
+
"""A lazy description of a computation.
|
|
74
|
+
|
|
75
|
+
- ``A``: the value it succeeds with
|
|
76
|
+
- ``E``: the typed errors it can fail with (``Never`` if none)
|
|
77
|
+
- ``R``: the services it needs (``Never`` if none)
|
|
78
|
+
|
|
79
|
+
Build effects with the functions in ``pyfect`` (``succeed``, ``gen``,
|
|
80
|
+
...). Inside a generator, ``x = yield from eff`` runs ``eff`` and gives
|
|
81
|
+
back its value; its errors and requirements join the generator's own.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
__slots__ = ()
|
|
85
|
+
|
|
86
|
+
def _step(self, fiber: Fiber[Any, Any]) -> Step:
|
|
87
|
+
raise NotImplementedError(f"{type(self).__name__} is not a primitive")
|
|
88
|
+
|
|
89
|
+
def __iter__(self) -> Generator[Effect[Any, E_co, R_co], Any, A_co]:
|
|
90
|
+
return (yield self)
|
|
91
|
+
|
|
92
|
+
def pipe[B](self, f: Callable[[Self], B], /) -> B:
|
|
93
|
+
"""Apply ``f`` to this effect. One step per call, on purpose:
|
|
94
|
+
pyright only narrows types correctly for a single step, so chain
|
|
95
|
+
``.pipe(a).pipe(b)`` rather than passing several functions."""
|
|
96
|
+
return f(self)
|
|
97
|
+
|
|
98
|
+
# --- sequencing -------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
def map[B](self, f: Callable[[A_co], B]) -> Effect[B, E_co, R_co]:
|
|
101
|
+
return _Map(self, f)
|
|
102
|
+
|
|
103
|
+
def flat_map[B, E2 = Never, R2 = Never](
|
|
104
|
+
self, f: Callable[[A_co], Effect[B, E2, R2]]
|
|
105
|
+
) -> Effect[B, E_co | E2, R_co | R2]:
|
|
106
|
+
return _FlatMap(self, f)
|
|
107
|
+
|
|
108
|
+
def and_then[B, E2 = Never, R2 = Never](
|
|
109
|
+
self, that: Effect[B, E2, R2] | Callable[[A_co], Effect[B, E2, R2]]
|
|
110
|
+
) -> Effect[B, E_co | E2, R_co | R2]:
|
|
111
|
+
"""Run ``that``, or ``that(value)``, after this effect, keeping only
|
|
112
|
+
its value."""
|
|
113
|
+
if callable(that) and not isinstance(that, EffectBase):
|
|
114
|
+
return _FlatMap(self, that)
|
|
115
|
+
effect = cast("Effect[B, E2, R2]", that)
|
|
116
|
+
require_effect(effect, "that")
|
|
117
|
+
return _FlatMap(self, lambda _: effect)
|
|
118
|
+
|
|
119
|
+
def tap[E2 = Never, R2 = Never](
|
|
120
|
+
self, f: Callable[[A_co], Effect[object, E2, R2]]
|
|
121
|
+
) -> Effect[A_co, E_co | E2, R_co | R2]:
|
|
122
|
+
"""Run ``f(value)`` for its effects, keeping the original value."""
|
|
123
|
+
return _FlatMap(self, lambda a: _Map(f(a), lambda _: a))
|
|
124
|
+
|
|
125
|
+
def as_[B](self, value: B) -> Effect[B, E_co, R_co]:
|
|
126
|
+
return _Map(self, lambda _: value)
|
|
127
|
+
|
|
128
|
+
def as_void(self) -> Effect[None, E_co, R_co]:
|
|
129
|
+
return _Map(self, _to_none)
|
|
130
|
+
|
|
131
|
+
def zip[B, E2 = Never, R2 = Never](
|
|
132
|
+
self, that: Effect[B, E2, R2], *, concurrent: bool = False
|
|
133
|
+
) -> Effect[tuple[A_co, B], E_co | E2, R_co | R2]:
|
|
134
|
+
"""Pair the results: in order, or both at once with ``concurrent``."""
|
|
135
|
+
require_effect(that, "that")
|
|
136
|
+
return self.zip_with(that, _pair, concurrent=concurrent)
|
|
137
|
+
|
|
138
|
+
def zip_with[B, C, E2 = Never, R2 = Never](
|
|
139
|
+
self, that: Effect[B, E2, R2], f: Callable[[A_co, B], C], *, concurrent: bool = False
|
|
140
|
+
) -> Effect[C, E_co | E2, R_co | R2]:
|
|
141
|
+
"""Combine the results with ``f``. With ``concurrent``, both run at
|
|
142
|
+
once and the first failure interrupts the other."""
|
|
143
|
+
require_effect(that, "that")
|
|
144
|
+
if not concurrent:
|
|
145
|
+
return _FlatMap(self, lambda a: _Map(that, lambda b: f(a, b)))
|
|
146
|
+
from pyfect._all import all
|
|
147
|
+
|
|
148
|
+
return all(self, that, concurrency=2).map(lambda ab: f(*ab))
|
|
149
|
+
|
|
150
|
+
@overload
|
|
151
|
+
def replicate_effect(
|
|
152
|
+
self, n: int, *, concurrency: Concurrency = None, discard: Literal[False] = False
|
|
153
|
+
) -> Effect[list[A_co], E_co, R_co]: ...
|
|
154
|
+
@overload
|
|
155
|
+
def replicate_effect(
|
|
156
|
+
self, n: int, *, concurrency: Concurrency = None, discard: Literal[True]
|
|
157
|
+
) -> Effect[None, E_co, R_co]: ...
|
|
158
|
+
def replicate_effect(
|
|
159
|
+
self, n: int, *, concurrency: Concurrency = None, discard: bool = False
|
|
160
|
+
) -> Effect[Any, E_co, R_co]:
|
|
161
|
+
"""Run this effect ``n`` times, like ``for_each``, collecting the
|
|
162
|
+
values (or none, with ``discard``)."""
|
|
163
|
+
from pyfect._concurrency import for_each
|
|
164
|
+
|
|
165
|
+
repeated = for_each([self] * n, _identity, concurrency=concurrency)
|
|
166
|
+
return repeated.as_void() if discard else repeated
|
|
167
|
+
|
|
168
|
+
def replicate(self, n: int) -> list[Effect[A_co, E_co, R_co]]:
|
|
169
|
+
"""A list holding this effect ``n`` times."""
|
|
170
|
+
return [self] * n
|
|
171
|
+
|
|
172
|
+
def forever(self, *, disable_yield: bool = False) -> Effect[Never, E_co, R_co]:
|
|
173
|
+
"""Run this effect again and again, until it fails. As in Effect,
|
|
174
|
+
the fiber yields after each run (so a loop of steps that never wait
|
|
175
|
+
lets other fibers in between), unless ``disable_yield``."""
|
|
176
|
+
if disable_yield:
|
|
177
|
+
loop: Effect[Never, E_co, R_co] = _Suspend(lambda: _FlatMap(self, lambda _: loop))
|
|
178
|
+
return loop
|
|
179
|
+
yielding: Effect[Never, E_co, R_co] = _Suspend(
|
|
180
|
+
lambda: _FlatMap(self, lambda _: _FlatMap(_YieldNow(), lambda _: yielding))
|
|
181
|
+
)
|
|
182
|
+
return yielding
|
|
183
|
+
|
|
184
|
+
def flatten[B, E2, R2](
|
|
185
|
+
self: Effect[Effect[B, E2, R2], E_co, R_co],
|
|
186
|
+
) -> Effect[B, E_co | E2, R_co | R2]:
|
|
187
|
+
"""Run the effect this effect succeeds with."""
|
|
188
|
+
return _FlatMap(self, _identity)
|
|
189
|
+
|
|
190
|
+
def head[B](
|
|
191
|
+
self: Effect[Iterable[B], E_co, R_co],
|
|
192
|
+
) -> Effect[B, E_co | NoSuchElementException, R_co]:
|
|
193
|
+
"""The first item of the iterable this effect succeeds with; a
|
|
194
|
+
``NoSuchElementException`` if it is empty."""
|
|
195
|
+
|
|
196
|
+
def first(items: Iterable[B]) -> Effect[B, NoSuchElementException]:
|
|
197
|
+
from pyfect.errors import NoSuchElementException
|
|
198
|
+
|
|
199
|
+
for item in items:
|
|
200
|
+
return _Succeed(item)
|
|
201
|
+
return _fail(NoSuchElementException())
|
|
202
|
+
|
|
203
|
+
return _FlatMap(self, first)
|
|
204
|
+
|
|
205
|
+
def when[E2 = Never, R2 = Never](
|
|
206
|
+
self, condition: Effect[bool, E2, R2]
|
|
207
|
+
) -> Effect[Option[A_co], E_co | E2, R_co | R2]:
|
|
208
|
+
"""Run this effect only if ``condition`` succeeds with ``True``:
|
|
209
|
+
``Some(value)`` if it ran, ``None`` if it did not."""
|
|
210
|
+
require_effect(condition, "condition")
|
|
211
|
+
from pyfect.option import Some
|
|
212
|
+
|
|
213
|
+
return _FlatMap(condition, lambda run: _Map(self, Some) if run else _Succeed(None))
|
|
214
|
+
|
|
215
|
+
@overload
|
|
216
|
+
def filter_or_fail[B](
|
|
217
|
+
self, predicate: Callable[[A_co], TypeIs[B]]
|
|
218
|
+
) -> Effect[B, E_co | NoSuchElementException, R_co]: ...
|
|
219
|
+
@overload
|
|
220
|
+
def filter_or_fail(
|
|
221
|
+
self, predicate: Callable[[A_co], bool]
|
|
222
|
+
) -> Effect[A_co, E_co | NoSuchElementException, R_co]: ...
|
|
223
|
+
@overload
|
|
224
|
+
def filter_or_fail[B, E2](
|
|
225
|
+
self, predicate: Callable[[A_co], TypeIs[B]], or_fail_with: Callable[[A_co], E2]
|
|
226
|
+
) -> Effect[B, E_co | E2, R_co]: ...
|
|
227
|
+
@overload
|
|
228
|
+
def filter_or_fail[E2](
|
|
229
|
+
self, predicate: Callable[[A_co], bool], or_fail_with: Callable[[A_co], E2]
|
|
230
|
+
) -> Effect[A_co, E_co | E2, R_co]: ...
|
|
231
|
+
def filter_or_fail(
|
|
232
|
+
self, predicate: Callable[[Any], bool], or_fail_with: Callable[[Any], Any] | None = None
|
|
233
|
+
) -> Effect[Any, Any, Any]:
|
|
234
|
+
"""Fail with ``or_fail_with(value)`` (by default a
|
|
235
|
+
``NoSuchElementException``) when the value fails ``predicate``."""
|
|
236
|
+
if or_fail_with is None:
|
|
237
|
+
from pyfect.errors import NoSuchElementException
|
|
238
|
+
|
|
239
|
+
return self.filter_or_else(predicate, lambda _: _fail(NoSuchElementException()))
|
|
240
|
+
return self.filter_or_else(predicate, lambda a: _fail(or_fail_with(a)))
|
|
241
|
+
|
|
242
|
+
@overload
|
|
243
|
+
def filter_or_else[B, C, E2 = Never, R2 = Never](
|
|
244
|
+
self, predicate: Callable[[A_co], TypeIs[B]], or_else: Callable[[A_co], Effect[C, E2, R2]]
|
|
245
|
+
) -> Effect[B | C, E_co | E2, R_co | R2]: ...
|
|
246
|
+
@overload
|
|
247
|
+
def filter_or_else[C, E2 = Never, R2 = Never](
|
|
248
|
+
self, predicate: Callable[[A_co], bool], or_else: Callable[[A_co], Effect[C, E2, R2]]
|
|
249
|
+
) -> Effect[A_co | C, E_co | E2, R_co | R2]: ...
|
|
250
|
+
def filter_or_else(
|
|
251
|
+
self, predicate: Callable[[Any], bool], or_else: Callable[[Any], Effect[Any, Any, Any]]
|
|
252
|
+
) -> Effect[Any, Any, Any]:
|
|
253
|
+
"""Continue with ``or_else(value)`` when the value fails ``predicate``."""
|
|
254
|
+
return _FlatMap(self, lambda a: _Succeed(a) if predicate(a) else or_else(a))
|
|
255
|
+
|
|
256
|
+
def filter_map_or_else[B, X, C, E2 = Never, R2 = Never](
|
|
257
|
+
self,
|
|
258
|
+
filter: Callable[[A_co], Ok[B] | Err[X]],
|
|
259
|
+
or_else: Callable[[X], Effect[C, E2, R2]],
|
|
260
|
+
) -> Effect[B | C, E_co | E2, R_co | R2]:
|
|
261
|
+
"""Succeed with what ``filter`` passes; continue with ``or_else`` of
|
|
262
|
+
what it rejects."""
|
|
263
|
+
|
|
264
|
+
def next_step(a: Any) -> Effect[Any, Any, Any]:
|
|
265
|
+
r = filter(a)
|
|
266
|
+
return _Succeed(r.value) if isinstance(r, Ok) else or_else(r.error)
|
|
267
|
+
|
|
268
|
+
return _FlatMap(self, next_step)
|
|
269
|
+
|
|
270
|
+
@overload
|
|
271
|
+
def filter_map_or_fail[B](
|
|
272
|
+
self, filter: Callable[[A_co], Ok[B] | Err[object]]
|
|
273
|
+
) -> Effect[B, E_co | NoSuchElementException, R_co]: ...
|
|
274
|
+
@overload
|
|
275
|
+
def filter_map_or_fail[B, X, E2](
|
|
276
|
+
self, filter: Callable[[A_co], Ok[B] | Err[X]], or_fail_with: Callable[[X], E2]
|
|
277
|
+
) -> Effect[B, E_co | E2, R_co]: ...
|
|
278
|
+
def filter_map_or_fail(
|
|
279
|
+
self, filter: Callable[[Any], Any], or_fail_with: Callable[[Any], Any] | None = None
|
|
280
|
+
) -> Effect[Any, Any, Any]:
|
|
281
|
+
"""Succeed with what ``filter`` passes; fail with ``or_fail_with`` of
|
|
282
|
+
what it rejects (by default a ``NoSuchElementException``)."""
|
|
283
|
+
if or_fail_with is None:
|
|
284
|
+
from pyfect.errors import NoSuchElementException
|
|
285
|
+
|
|
286
|
+
return self.filter_map_or_else(filter, lambda _: _fail(NoSuchElementException()))
|
|
287
|
+
return self.filter_map_or_else(filter, lambda x: _fail(or_fail_with(x)))
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _to_none(_: object) -> None:
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _identity[T](value: T) -> T:
|
|
295
|
+
return value
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _pair[A, B](a: A, b: B) -> tuple[A, B]:
|
|
299
|
+
return (a, b)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# The primitives subclass ``Effect``, so they load after it. The methods above
|
|
303
|
+
# use them only when called, and other modules import them from here.
|
|
304
|
+
from pyfect._primitives import ( # noqa: E402
|
|
305
|
+
Register,
|
|
306
|
+
Restore,
|
|
307
|
+
_AfterStarted,
|
|
308
|
+
_Callback,
|
|
309
|
+
_FailCause,
|
|
310
|
+
_FlatMap,
|
|
311
|
+
_Fork,
|
|
312
|
+
_FromExit,
|
|
313
|
+
_Gen,
|
|
314
|
+
_GenFrame,
|
|
315
|
+
_Locally,
|
|
316
|
+
_Map,
|
|
317
|
+
_MatchCause,
|
|
318
|
+
_RestoreContext,
|
|
319
|
+
_RestoreInterruptible,
|
|
320
|
+
_SetInterruptible,
|
|
321
|
+
_Succeed,
|
|
322
|
+
_Suspend,
|
|
323
|
+
_Sync,
|
|
324
|
+
_WithFiber,
|
|
325
|
+
_YieldNow,
|
|
326
|
+
interruptible_mask,
|
|
327
|
+
run_known,
|
|
328
|
+
uninterruptible_mask,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
__all__ = [
|
|
332
|
+
"SUSPEND",
|
|
333
|
+
"Effect",
|
|
334
|
+
"Frame",
|
|
335
|
+
"Register",
|
|
336
|
+
"Restore",
|
|
337
|
+
"Step",
|
|
338
|
+
"_AfterStarted",
|
|
339
|
+
"_Callback",
|
|
340
|
+
"_FailCause",
|
|
341
|
+
"_FlatMap",
|
|
342
|
+
"_Fork",
|
|
343
|
+
"_FromExit",
|
|
344
|
+
"_Gen",
|
|
345
|
+
"_GenFrame",
|
|
346
|
+
"_Locally",
|
|
347
|
+
"_Map",
|
|
348
|
+
"_MatchCause",
|
|
349
|
+
"_RestoreContext",
|
|
350
|
+
"_RestoreInterruptible",
|
|
351
|
+
"_SetInterruptible",
|
|
352
|
+
"_Succeed",
|
|
353
|
+
"_Suspend",
|
|
354
|
+
"_Sync",
|
|
355
|
+
"_WithFiber",
|
|
356
|
+
"_YieldNow",
|
|
357
|
+
"_call_user",
|
|
358
|
+
"_catching_if",
|
|
359
|
+
"_fail",
|
|
360
|
+
"_ignore_failure",
|
|
361
|
+
"_recovering",
|
|
362
|
+
"_run_finalizer",
|
|
363
|
+
"_succeed_none",
|
|
364
|
+
"_to_none",
|
|
365
|
+
"interruptible_mask",
|
|
366
|
+
"run_known",
|
|
367
|
+
"uninterruptible_mask",
|
|
368
|
+
]
|
pyfect/_digits.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""ASCII decimal numbers in text from outside: headers, URLs, wire tags.
|
|
2
|
+
|
|
3
|
+
``str.isdigit`` also accepts digits like ``"²"`` that ``int`` rejects, and
|
|
4
|
+
``int`` alone accepts signs, spaces, underscores and non-ASCII digits, and
|
|
5
|
+
raises past 4300 digits, so every such parse goes through this module (a
|
|
6
|
+
guard test bans ``.isdigit()`` and hand-written digit-run regexes
|
|
7
|
+
elsewhere).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
MAX_DIGITS = 1000
|
|
13
|
+
"""The most digits ``parse_digits`` reads; longer text is no number."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_digits(value: str) -> bool:
|
|
17
|
+
"""``value`` is one or more ASCII digits, however many."""
|
|
18
|
+
return value != "" and value.isascii() and value.isdigit()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_digits(value: str, *, unpadded: bool = False) -> int | None:
|
|
22
|
+
"""``value`` as an int when it is 1 to ``MAX_DIGITS`` ASCII digits (with
|
|
23
|
+
no leading zero when ``unpadded``), else ``None``."""
|
|
24
|
+
if len(value) > MAX_DIGITS or not is_digits(value):
|
|
25
|
+
return None
|
|
26
|
+
if unpadded and value[0] == "0" and value != "0":
|
|
27
|
+
return None
|
|
28
|
+
return int(value)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def parse_signed_digits(value: str) -> int | None:
|
|
32
|
+
"""``parse_digits`` after an optional ``+`` or ``-``."""
|
|
33
|
+
sign = value[:1]
|
|
34
|
+
if sign in ("+", "-"):
|
|
35
|
+
magnitude = parse_digits(value[1:])
|
|
36
|
+
return None if magnitude is None else (-magnitude if sign == "-" else magnitude)
|
|
37
|
+
return parse_digits(value)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_SHORT_RUN = 4000
|
|
41
|
+
"""Digits ``int`` reads in one call (its limit is 4300)."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def digits_value(value: str) -> int | None:
|
|
45
|
+
"""``value`` as an int when it is ASCII digits, however many (what
|
|
46
|
+
JavaScript's ``BigInt`` reads), else ``None``."""
|
|
47
|
+
return _run_value(value) if is_digits(value) else None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _run_value(run: str) -> int:
|
|
51
|
+
if len(run) <= _SHORT_RUN:
|
|
52
|
+
return int(run)
|
|
53
|
+
half = len(run) // 2
|
|
54
|
+
return _run_value(run[:half]) * 10 ** (len(run) - half) + _run_value(run[half:])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_MAX_ARRAY_INDEX = 2**32 - 2
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def array_index(key: str) -> int | None:
|
|
61
|
+
"""``key`` as a JavaScript array index, which an object lists first."""
|
|
62
|
+
index = parse_digits(key, unpadded=True)
|
|
63
|
+
return index if index is not None and index <= _MAX_ARRAY_INDEX else None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def in_object_order[A](values: dict[str, A]) -> dict[str, A]:
|
|
67
|
+
"""``values`` in ``Object.keys`` order: array indexes ascending, then the
|
|
68
|
+
others as they came."""
|
|
69
|
+
indexes = {key: index for key in values if (index := array_index(key)) is not None}
|
|
70
|
+
ordered = sorted(indexes, key=indexes.__getitem__)
|
|
71
|
+
return {key: values[key] for key in (*ordered, *(k for k in values if k not in indexes))}
|
pyfect/_effect_args.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Validate the effects handed to public combinators, once, when the
|
|
2
|
+
combinator is called. Layers are checked the same way (``require_layer``
|
|
3
|
+
in ``_layer_core``).
|
|
4
|
+
|
|
5
|
+
An effect is a value, so a combinator that takes one can only notice a
|
|
6
|
+
wrong value (a lambda, a coroutine, ``None``) when it runs it, possibly
|
|
7
|
+
much later and inside a finalizer that hides the defect. Every public
|
|
8
|
+
combinator with an ``Effect`` parameter calls ``require_effect`` on it
|
|
9
|
+
first; ``tests/test_effect_arguments.py`` walks the public API to keep it
|
|
10
|
+
that way. The run loop never checks again.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from collections.abc import Iterable
|
|
16
|
+
|
|
17
|
+
from pyfect._effect_base import EffectBase
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def require_effect(value: object, name: str) -> None:
|
|
21
|
+
"""Raise ``TypeError`` naming the parameter ``name`` unless ``value``
|
|
22
|
+
is an ``Effect``."""
|
|
23
|
+
if not isinstance(value, EffectBase):
|
|
24
|
+
raise TypeError(_message(value, name))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def require_effects(values: Iterable[object], name: str) -> None:
|
|
28
|
+
"""``require_effect`` for each of a ``*args`` parameter's values."""
|
|
29
|
+
for i, value in enumerate(values):
|
|
30
|
+
if not isinstance(value, EffectBase):
|
|
31
|
+
raise TypeError(_message(value, f"{name}[{i}]"))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _message(value: object, name: str) -> str:
|
|
35
|
+
return wrong_argument(
|
|
36
|
+
value, name, "an Effect", "to run a function, pass fx.sync(f) or fx.suspend(f)"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def wrong_argument(value: object, name: str, expected: str, hint_for_callable: str) -> str:
|
|
41
|
+
"""The ``TypeError`` message for a parameter ``name`` that must be
|
|
42
|
+
``expected``; a callable gets ``hint_for_callable``."""
|
|
43
|
+
message = f"{name} must be {expected}, got {type(value).__name__}: {value!r}"
|
|
44
|
+
if callable(value):
|
|
45
|
+
message += f" ({hint_for_callable})"
|
|
46
|
+
return message
|
pyfect/_effect_base.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""What every effect and continuation frame shares: the step protocol and
|
|
2
|
+
the base of ``Effect``'s method groups.
|
|
3
|
+
|
|
4
|
+
A step is one of:
|
|
5
|
+
|
|
6
|
+
- another ``Effect``: evaluate it next
|
|
7
|
+
- a ``Success`` / ``Failure``: hand it to the next frame on the stack
|
|
8
|
+
- ``SUSPEND``: the fiber parked itself; a callback will resume it
|
|
9
|
+
|
|
10
|
+
``Effect`` (``pyfect._core``) is assembled from method groups, each a
|
|
11
|
+
subclass of ``EffectBase`` in a module of its own. ``as_effect`` lets a
|
|
12
|
+
group hand ``self`` to the primitives, which take an ``Effect``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Final, Generic, Never, TypeVar, cast, final
|
|
18
|
+
|
|
19
|
+
from pyfect.cause import Cause
|
|
20
|
+
from pyfect.exit import Failure, Success
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from pyfect._core import Effect
|
|
24
|
+
from pyfect._fiber import Fiber
|
|
25
|
+
|
|
26
|
+
A_co = TypeVar("A_co", covariant=True)
|
|
27
|
+
E_co = TypeVar("E_co", covariant=True, default=Never)
|
|
28
|
+
R_co = TypeVar("R_co", covariant=True, default=Never)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@final
|
|
32
|
+
class _SuspendMarker:
|
|
33
|
+
__slots__ = ()
|
|
34
|
+
|
|
35
|
+
def __repr__(self) -> str:
|
|
36
|
+
return "SUSPEND"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
SUSPEND: Final = _SuspendMarker()
|
|
40
|
+
|
|
41
|
+
type Step = Effect[Any, Any, Any] | Success[Any] | Failure[Any] | _SuspendMarker
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Frame:
|
|
45
|
+
"""A continuation on the fiber's stack. Defaults pass results through.
|
|
46
|
+
|
|
47
|
+
A frame that claims state (takes a permit) and returns the result as an
|
|
48
|
+
``Exit`` sets ``claims``: the loop then hands that exit on with no pause
|
|
49
|
+
in between, as it does for a step's exit."""
|
|
50
|
+
|
|
51
|
+
__slots__ = ()
|
|
52
|
+
claims: ClassVar[bool] = False
|
|
53
|
+
|
|
54
|
+
def _on_success(self, value: Any, fiber: Fiber[Any, Any]) -> Step:
|
|
55
|
+
return Success(value)
|
|
56
|
+
|
|
57
|
+
def _on_failure(self, cause: Cause[Any], fiber: Fiber[Any, Any]) -> Step:
|
|
58
|
+
return Failure(cause)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class EffectBase(Frame, Generic[A_co, E_co, R_co]):
|
|
62
|
+
"""The base of each group of ``Effect`` methods. Every instance is an
|
|
63
|
+
``Effect``: only ``Effect`` combines the groups, and nothing else
|
|
64
|
+
subclasses them."""
|
|
65
|
+
|
|
66
|
+
__slots__ = ()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def as_effect[A, E, R](group: EffectBase[A, E, R]) -> Effect[A, E, R]:
|
|
70
|
+
"""``self`` inside a method group, typed as the ``Effect`` it is."""
|
|
71
|
+
return cast("Effect[A, E, R]", group)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""``Effect`` methods that fork, race and time out. The work lives in
|
|
2
|
+
``pyfect._concurrency`` and ``pyfect.scope``; these are the method spellings."""
|
|
3
|
+
# pyright: reportPrivateUsage=false
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import TYPE_CHECKING, Never
|
|
9
|
+
|
|
10
|
+
from pyfect import _core
|
|
11
|
+
from pyfect._effect_args import require_effect
|
|
12
|
+
from pyfect._effect_base import A_co, E_co, EffectBase, R_co, as_effect
|
|
13
|
+
from pyfect.duration import Duration
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from pyfect._core import Effect
|
|
17
|
+
from pyfect._fiber import Fiber, Uninterruptible
|
|
18
|
+
from pyfect.errors import TimeoutException
|
|
19
|
+
from pyfect.option import Option
|
|
20
|
+
from pyfect.scope import CloseableScope, Scope
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ConcurrencyMethods(EffectBase[A_co, E_co, R_co]):
|
|
24
|
+
__slots__ = ()
|
|
25
|
+
|
|
26
|
+
def fork(
|
|
27
|
+
self, *, start_immediately: bool = False, uninterruptible: Uninterruptible = False
|
|
28
|
+
) -> Effect[Fiber[A_co, E_co], Never, R_co]:
|
|
29
|
+
"""Start this effect on a child fiber. The child is interrupted if
|
|
30
|
+
it is still running when the parent fiber finishes.
|
|
31
|
+
|
|
32
|
+
With ``start_immediately`` the child runs at once, up to its first
|
|
33
|
+
wait, before the parent goes on; otherwise it starts on the next
|
|
34
|
+
scheduler turn. ``uninterruptible`` starts it uninterruptible
|
|
35
|
+
(``True``) or as interruptible as the parent is (``"inherit"``)."""
|
|
36
|
+
return _core._Fork(
|
|
37
|
+
as_effect(self),
|
|
38
|
+
daemon=False,
|
|
39
|
+
start_immediately=start_immediately,
|
|
40
|
+
uninterruptible=uninterruptible,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def fork_daemon(
|
|
44
|
+
self, *, start_immediately: bool = False, uninterruptible: Uninterruptible = False
|
|
45
|
+
) -> Effect[Fiber[A_co, E_co], Never, R_co]:
|
|
46
|
+
"""Start this effect on a fiber that outlives its parent. Options as
|
|
47
|
+
for ``fork``."""
|
|
48
|
+
return _core._Fork(
|
|
49
|
+
as_effect(self),
|
|
50
|
+
daemon=True,
|
|
51
|
+
start_immediately=start_immediately,
|
|
52
|
+
uninterruptible=uninterruptible,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def fork_scoped(
|
|
56
|
+
self, *, start_immediately: bool = False, uninterruptible: Uninterruptible = False
|
|
57
|
+
) -> Effect[Fiber[A_co, E_co], Never, R_co | Scope]:
|
|
58
|
+
"""``fork_in`` the enclosing ``Scope``."""
|
|
59
|
+
from pyfect.scope import fork_scoped
|
|
60
|
+
|
|
61
|
+
return fork_scoped(
|
|
62
|
+
as_effect(self), start_immediately=start_immediately, uninterruptible=uninterruptible
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def fork_in(
|
|
66
|
+
self,
|
|
67
|
+
scope: CloseableScope,
|
|
68
|
+
*,
|
|
69
|
+
start_immediately: bool = False,
|
|
70
|
+
uninterruptible: Uninterruptible = False,
|
|
71
|
+
) -> Effect[Fiber[A_co, E_co], Never, R_co]:
|
|
72
|
+
"""Start this effect on a fiber that outlives its parent and is
|
|
73
|
+
interrupted when ``scope`` closes. Into a closed scope it gives an
|
|
74
|
+
interrupted fiber that never ran. Options as for ``fork``."""
|
|
75
|
+
from pyfect.scope import fork_in
|
|
76
|
+
|
|
77
|
+
return fork_in(
|
|
78
|
+
as_effect(self),
|
|
79
|
+
scope,
|
|
80
|
+
start_immediately=start_immediately,
|
|
81
|
+
uninterruptible=uninterruptible,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def await_all_children(self) -> Effect[A_co, E_co, R_co]:
|
|
85
|
+
"""When this effect ends, wait for the child fibers it forked
|
|
86
|
+
(not those forked before it started). The wait is interruptible
|
|
87
|
+
if the fiber was interruptible when this effect started."""
|
|
88
|
+
from pyfect._fiber_all import await_all_children
|
|
89
|
+
|
|
90
|
+
return await_all_children(as_effect(self))
|
|
91
|
+
|
|
92
|
+
def race[B, E2 = Never, R2 = Never](
|
|
93
|
+
self, that: Effect[B, E2, R2]
|
|
94
|
+
) -> Effect[A_co | B, E_co | E2, R_co | R2]:
|
|
95
|
+
"""The first to succeed wins and the other is interrupted. Fails
|
|
96
|
+
only if both fail."""
|
|
97
|
+
require_effect(that, "that")
|
|
98
|
+
from pyfect._concurrency import race
|
|
99
|
+
|
|
100
|
+
return race(as_effect(self), that)
|
|
101
|
+
|
|
102
|
+
def race_first[B, E2 = Never, R2 = Never](
|
|
103
|
+
self, that: Effect[B, E2, R2]
|
|
104
|
+
) -> Effect[A_co | B, E_co | E2, R_co | R2]:
|
|
105
|
+
"""The first to finish wins, success or failure."""
|
|
106
|
+
require_effect(that, "that")
|
|
107
|
+
from pyfect._concurrency import race_first
|
|
108
|
+
|
|
109
|
+
return race_first(as_effect(self), that)
|
|
110
|
+
|
|
111
|
+
def timeout(self, duration: Duration) -> Effect[A_co, E_co | TimeoutException, R_co]:
|
|
112
|
+
"""Fail with ``TimeoutException`` if this takes longer than
|
|
113
|
+
``duration``. The effect is interrupted when time runs out."""
|
|
114
|
+
from pyfect._concurrency import timeout
|
|
115
|
+
|
|
116
|
+
return timeout(as_effect(self), duration)
|
|
117
|
+
|
|
118
|
+
def timeout_option(self, duration: Duration) -> Effect[Option[A_co], E_co, R_co]:
|
|
119
|
+
"""``Some(value)``, or ``None`` if this takes longer than
|
|
120
|
+
``duration``. The effect is interrupted when time runs out."""
|
|
121
|
+
from pyfect._concurrency import timeout_option
|
|
122
|
+
|
|
123
|
+
return timeout_option(as_effect(self), duration)
|
|
124
|
+
|
|
125
|
+
def timeout_or_else[B, E2 = Never, R2 = Never](
|
|
126
|
+
self, duration: Duration, or_else: Callable[[], Effect[B, E2, R2]]
|
|
127
|
+
) -> Effect[A_co | B, E_co | E2, R_co | R2]:
|
|
128
|
+
"""Continue with ``or_else()`` if this takes longer than
|
|
129
|
+
``duration``. ``or_else`` runs once this effect has been
|
|
130
|
+
interrupted and has finished cleaning up."""
|
|
131
|
+
from pyfect._concurrency import timeout_or_else
|
|
132
|
+
|
|
133
|
+
return timeout_or_else(as_effect(self), duration, or_else)
|