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/tx_ref.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""``TxRef[A]``: a mutable cell read and written in transactions.
|
|
2
|
+
|
|
3
|
+
Every operation joins the running transaction (``fx.tx``), or runs as a
|
|
4
|
+
transaction of its own when there is none. Inside a transaction, reads and
|
|
5
|
+
writes go to its journal; other fibers see the writes only once the
|
|
6
|
+
outermost transaction commits.
|
|
7
|
+
"""
|
|
8
|
+
# pyright: reportPrivateUsage=false
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from typing import overload
|
|
14
|
+
|
|
15
|
+
from pyfect._core import Effect, _Sync
|
|
16
|
+
from pyfect._unsubscripted import Unsubscripted
|
|
17
|
+
from pyfect.transaction import TransactionMeta, transactional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TxRef[A = Unsubscripted]:
|
|
21
|
+
"""``TxRef(value)`` is Effect's ``makeUnsafe``; ``TxRef.make`` is the
|
|
22
|
+
effect. A committed write that changes the value (by JavaScript's
|
|
23
|
+
``Object.is``: ``-0.0`` replaces ``0.0``) bumps ``version``; every
|
|
24
|
+
committed write, changed or not, wakes the transactions waiting in
|
|
25
|
+
``tx_retry`` on this ref."""
|
|
26
|
+
|
|
27
|
+
__slots__ = ("_pending", "_value", "_version")
|
|
28
|
+
|
|
29
|
+
def __init__(self, value: A) -> None:
|
|
30
|
+
self._value = value
|
|
31
|
+
self._version = 0
|
|
32
|
+
# Wake callbacks of transactions parked in ``tx_retry`` after reading
|
|
33
|
+
# this ref, keyed by waiter.
|
|
34
|
+
self._pending: dict[object, Callable[[], None]] = {}
|
|
35
|
+
|
|
36
|
+
@overload
|
|
37
|
+
@classmethod
|
|
38
|
+
def make[T](cls: type[TxRef[Unsubscripted]], value: T) -> Effect[TxRef[T]]: ...
|
|
39
|
+
@overload
|
|
40
|
+
@classmethod
|
|
41
|
+
def make(cls, value: A) -> Effect[TxRef[A]]: ...
|
|
42
|
+
@classmethod
|
|
43
|
+
def make[T](cls, value: T) -> Effect[TxRef[T]]:
|
|
44
|
+
return _Sync(lambda: TxRef(value))
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def version(self) -> int:
|
|
48
|
+
"""How many committed writes changed the value."""
|
|
49
|
+
return self._version
|
|
50
|
+
|
|
51
|
+
def get(self) -> Effect[A]:
|
|
52
|
+
return transactional(self._get)
|
|
53
|
+
|
|
54
|
+
def set(self, value: A) -> Effect[None]:
|
|
55
|
+
return self.modify(lambda _: (None, value))
|
|
56
|
+
|
|
57
|
+
def update(self, f: Callable[[A], A]) -> Effect[None]:
|
|
58
|
+
return self.modify(lambda current: (None, f(current)))
|
|
59
|
+
|
|
60
|
+
def modify[B](self, f: Callable[[A], tuple[B, A]]) -> Effect[B]:
|
|
61
|
+
"""Replace the value with the second item of ``f(value)``; succeed
|
|
62
|
+
with the first."""
|
|
63
|
+
|
|
64
|
+
def run(state: TransactionMeta) -> B:
|
|
65
|
+
entry = state._entry(self)
|
|
66
|
+
result, entry._value = f(entry._value)
|
|
67
|
+
entry._written = True
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
return transactional(run)
|
|
71
|
+
|
|
72
|
+
def _get(self, state: TransactionMeta) -> A:
|
|
73
|
+
return state._entry(self)._value
|
|
74
|
+
|
|
75
|
+
def __repr__(self) -> str:
|
|
76
|
+
return f"TxRef({self._value!r})"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
__all__ = ["TxRef"]
|
pyfect/tx_semaphore.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""``TxSemaphore``: a transactional counting semaphore (Effect's
|
|
2
|
+
``TxSemaphore``).
|
|
3
|
+
|
|
4
|
+
The free permits live in a ``TxRef``. ``acquire`` waits (``tx_retry``)
|
|
5
|
+
until enough permits are free; ``release`` never raises the count above the
|
|
6
|
+
capacity it was made with. Inside a transaction, acquiring and releasing
|
|
7
|
+
take effect only when it commits.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Never
|
|
13
|
+
|
|
14
|
+
from pyfect import _tx_ops
|
|
15
|
+
from pyfect._core import Effect, Restore, uninterruptible_mask
|
|
16
|
+
from pyfect._effect_args import require_effect
|
|
17
|
+
from pyfect.effect import die, void
|
|
18
|
+
from pyfect.scope import Scope, add_finalizer
|
|
19
|
+
from pyfect.transaction import Transaction, TransactionMeta, tx_restoring, tx_retry
|
|
20
|
+
from pyfect.tx_ref import TxRef
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _positive(n: int) -> Effect[None]:
|
|
24
|
+
"""Effect dies, rather than raising, on a count that is not positive."""
|
|
25
|
+
return void if n > 0 else die(ValueError("Number of permits must be positive"))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TxSemaphore:
|
|
29
|
+
"""``TxSemaphore(ref, capacity)`` wraps an existing ``TxRef`` (Effect's
|
|
30
|
+
``makeUnsafe``); ``make`` is the effect."""
|
|
31
|
+
|
|
32
|
+
__slots__ = ("_capacity", "_permits")
|
|
33
|
+
|
|
34
|
+
def __init__(self, permits: TxRef[int], capacity: int) -> None:
|
|
35
|
+
self._permits = permits
|
|
36
|
+
self._capacity = capacity
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def make(permits: int) -> Effect[TxSemaphore]:
|
|
40
|
+
"""A semaphore with ``permits`` free permits, also its capacity. Dies
|
|
41
|
+
if ``permits`` is negative."""
|
|
42
|
+
if permits < 0:
|
|
43
|
+
return die(ValueError("Permits must be non-negative"))
|
|
44
|
+
return TxRef.make(permits).map(lambda ref: TxSemaphore(ref, permits))
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def capacity(self) -> int:
|
|
48
|
+
return self._capacity
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def ref(self) -> TxRef[int]:
|
|
52
|
+
"""The free permits."""
|
|
53
|
+
return self._permits
|
|
54
|
+
|
|
55
|
+
def available(self) -> Effect[int]:
|
|
56
|
+
return self._permits.get()
|
|
57
|
+
|
|
58
|
+
def acquire(self) -> Effect[None]:
|
|
59
|
+
"""Take one permit, waiting until one is free."""
|
|
60
|
+
return self._acquire(1)
|
|
61
|
+
|
|
62
|
+
def acquire_n(self, n: int) -> Effect[None]:
|
|
63
|
+
"""Take ``n`` permits at once, waiting until that many are free.
|
|
64
|
+
Dies if ``n`` is not positive."""
|
|
65
|
+
return _positive(n).and_then(self._acquire(n))
|
|
66
|
+
|
|
67
|
+
def try_acquire(self) -> Effect[bool]:
|
|
68
|
+
"""Take one permit if one is free now."""
|
|
69
|
+
return self._try_acquire(1)
|
|
70
|
+
|
|
71
|
+
def try_acquire_n(self, n: int) -> Effect[bool]:
|
|
72
|
+
"""Take ``n`` permits if that many are free now. Dies if ``n`` is not
|
|
73
|
+
positive."""
|
|
74
|
+
return _positive(n).and_then(self._try_acquire(n))
|
|
75
|
+
|
|
76
|
+
def release(self) -> Effect[None]:
|
|
77
|
+
"""Give back one permit, up to the capacity."""
|
|
78
|
+
return self._release(1)
|
|
79
|
+
|
|
80
|
+
def release_n(self, n: int) -> Effect[None]:
|
|
81
|
+
"""Give back ``n`` permits, up to the capacity. Dies if ``n`` is not
|
|
82
|
+
positive."""
|
|
83
|
+
return _positive(n).and_then(self._release(n))
|
|
84
|
+
|
|
85
|
+
def with_permit[A, E, R](self, eff: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
86
|
+
"""Run ``eff`` holding one permit, giving it back however it ends.
|
|
87
|
+
Only the wait for the permit is interruptible."""
|
|
88
|
+
require_effect(eff, "eff")
|
|
89
|
+
return self._with_acquired(self.acquire(), eff, self.release())
|
|
90
|
+
|
|
91
|
+
def with_permits[A, E, R](self, eff: Effect[A, E, R], n: int) -> Effect[A, E, R]:
|
|
92
|
+
"""``with_permit`` for ``n`` permits."""
|
|
93
|
+
require_effect(eff, "eff")
|
|
94
|
+
return self._with_acquired(self.acquire_n(n), eff, self.release_n(n))
|
|
95
|
+
|
|
96
|
+
def with_permit_scoped(self) -> Effect[None, Never, Scope]:
|
|
97
|
+
"""Take one permit until the current scope closes. Only the wait for
|
|
98
|
+
the permit is interruptible."""
|
|
99
|
+
release = self.release()
|
|
100
|
+
return uninterruptible_mask(
|
|
101
|
+
lambda restore: tx_restoring(restore, self.acquire()).and_then(
|
|
102
|
+
add_finalizer(lambda _: release)
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def _acquire(self, n: int) -> Effect[None]:
|
|
107
|
+
def take(state: TransactionMeta) -> Effect[None, Never, Transaction]:
|
|
108
|
+
permits = _tx_ops.value(state, self._permits)
|
|
109
|
+
if permits < n:
|
|
110
|
+
return tx_retry
|
|
111
|
+
_tx_ops.write(state, self._permits, permits - n)
|
|
112
|
+
return void
|
|
113
|
+
|
|
114
|
+
return _tx_ops.decide(take)
|
|
115
|
+
|
|
116
|
+
def _try_acquire(self, n: int) -> Effect[bool]:
|
|
117
|
+
return self._permits.modify(
|
|
118
|
+
lambda permits: (True, permits - n) if permits >= n else (False, permits)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def _release(self, n: int) -> Effect[None]:
|
|
122
|
+
return self._permits.update(lambda permits: min(permits + n, self._capacity))
|
|
123
|
+
|
|
124
|
+
def _with_acquired[A, E, R](
|
|
125
|
+
self, acquire: Effect[None], eff: Effect[A, E, R], release: Effect[None]
|
|
126
|
+
) -> Effect[A, E, R]:
|
|
127
|
+
def body(restore: Restore) -> Effect[A, E, R]:
|
|
128
|
+
return tx_restoring(restore, acquire).and_then(restore(eff).ensuring(release))
|
|
129
|
+
|
|
130
|
+
return uninterruptible_mask(body)
|
|
131
|
+
|
|
132
|
+
def __repr__(self) -> str:
|
|
133
|
+
return f"TxSemaphore(capacity={self._capacity})"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
__all__ = ["TxSemaphore"]
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""``TxSubscriptionRef[A]``: a ``TxRef`` that publishes every value it
|
|
2
|
+
stores to an unbounded ``TxPubSub`` (Effect's ``TxSubscriptionRef``).
|
|
3
|
+
|
|
4
|
+
``changes()`` subscribes for the current scope: the queue's first value is
|
|
5
|
+
the current one, then every value stored after it, in commit order.
|
|
6
|
+
``changes_stream()`` is the same as a ``Stream``, subscribing when it
|
|
7
|
+
starts. A write publishes in the same transaction, so a transaction that
|
|
8
|
+
rolls back publishes nothing.
|
|
9
|
+
"""
|
|
10
|
+
# pyright: reportPrivateUsage=false
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections.abc import Callable
|
|
15
|
+
from typing import TYPE_CHECKING, Never, overload
|
|
16
|
+
|
|
17
|
+
from pyfect._core import Effect
|
|
18
|
+
from pyfect._unsubscripted import Unsubscripted
|
|
19
|
+
from pyfect.effect import gen
|
|
20
|
+
from pyfect.scope import Scope, acquire_release
|
|
21
|
+
from pyfect.transaction import tx
|
|
22
|
+
from pyfect.tx_pubsub import TxPubSub
|
|
23
|
+
from pyfect.tx_queue import TxDequeue
|
|
24
|
+
from pyfect.tx_ref import TxRef
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from pyfect.stream import Stream
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TxSubscriptionRef[A = Unsubscripted]:
|
|
31
|
+
"""``make`` is the effect; ``isinstance(x, TxSubscriptionRef)`` is
|
|
32
|
+
Effect's ``isTxSubscriptionRef``."""
|
|
33
|
+
|
|
34
|
+
__slots__ = ("_pubsub", "_ref")
|
|
35
|
+
|
|
36
|
+
def __init__(self, ref: TxRef[A], pubsub: TxPubSub[A]) -> None:
|
|
37
|
+
self._ref = ref
|
|
38
|
+
self._pubsub = pubsub
|
|
39
|
+
|
|
40
|
+
@overload
|
|
41
|
+
@classmethod
|
|
42
|
+
def make[T](
|
|
43
|
+
cls: type[TxSubscriptionRef[Unsubscripted]], value: T
|
|
44
|
+
) -> Effect[TxSubscriptionRef[T]]: ...
|
|
45
|
+
@overload
|
|
46
|
+
@classmethod
|
|
47
|
+
def make(cls, value: A) -> Effect[TxSubscriptionRef[A]]: ...
|
|
48
|
+
@classmethod
|
|
49
|
+
def make[T](cls, value: T) -> Effect[TxSubscriptionRef[T]]:
|
|
50
|
+
return TxPubSub[T].unbounded().map(lambda pubsub: TxSubscriptionRef(TxRef(value), pubsub))
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def ref(self) -> TxRef[A]:
|
|
54
|
+
return self._ref
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def pubsub(self) -> TxPubSub[A]:
|
|
58
|
+
return self._pubsub
|
|
59
|
+
|
|
60
|
+
def get(self) -> Effect[A]:
|
|
61
|
+
return self._ref.get()
|
|
62
|
+
|
|
63
|
+
def modify[B](self, f: Callable[[A], tuple[B, A]]) -> Effect[B]:
|
|
64
|
+
"""Store the second item of ``f(value)`` and publish it; succeed with
|
|
65
|
+
the first."""
|
|
66
|
+
|
|
67
|
+
@gen
|
|
68
|
+
def body():
|
|
69
|
+
result, value = f((yield from self._ref.get()))
|
|
70
|
+
yield from self._ref.set(value)
|
|
71
|
+
yield from self._pubsub.publish(value)
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
return tx(body)
|
|
75
|
+
|
|
76
|
+
def set(self, value: A) -> Effect[None]:
|
|
77
|
+
return self.modify(lambda _: (None, value))
|
|
78
|
+
|
|
79
|
+
def update(self, f: Callable[[A], A]) -> Effect[None]:
|
|
80
|
+
return self.modify(lambda current: (None, f(current)))
|
|
81
|
+
|
|
82
|
+
def get_and_set(self, value: A) -> Effect[A]:
|
|
83
|
+
return self.modify(lambda current: (current, value))
|
|
84
|
+
|
|
85
|
+
def get_and_update(self, f: Callable[[A], A]) -> Effect[A]:
|
|
86
|
+
return self.modify(lambda current: (current, f(current)))
|
|
87
|
+
|
|
88
|
+
def update_and_get(self, f: Callable[[A], A]) -> Effect[A]:
|
|
89
|
+
def step(current: A) -> tuple[A, A]:
|
|
90
|
+
new = f(current)
|
|
91
|
+
return new, new
|
|
92
|
+
|
|
93
|
+
return self.modify(step)
|
|
94
|
+
|
|
95
|
+
def changes(self) -> Effect[TxDequeue[A, Never], Never, Scope]:
|
|
96
|
+
"""A queue of the current value, then every later one, until the
|
|
97
|
+
current scope closes."""
|
|
98
|
+
|
|
99
|
+
@gen
|
|
100
|
+
def acquire():
|
|
101
|
+
queue = yield from self._pubsub._acquire_queue()
|
|
102
|
+
yield from queue.offer((yield from self._ref.get()))
|
|
103
|
+
return queue
|
|
104
|
+
|
|
105
|
+
return acquire_release(tx(acquire), lambda queue, _: self._pubsub.release_subscriber(queue))
|
|
106
|
+
|
|
107
|
+
def changes_stream(self) -> Stream[A]:
|
|
108
|
+
"""A stream of the current value, then every later one (Effect's
|
|
109
|
+
``changesStream``). Each run subscribes anew."""
|
|
110
|
+
from pyfect.stream import from_effect_repeat, unwrap
|
|
111
|
+
|
|
112
|
+
return unwrap(self.changes().map(lambda queue: from_effect_repeat(queue.take())))
|
|
113
|
+
|
|
114
|
+
def __repr__(self) -> str:
|
|
115
|
+
return "TxSubscriptionRef()"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
__all__ = ["TxSubscriptionRef"]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Workers (Effect's ``effect/workers``): run code on other threads or in
|
|
2
|
+
other processes and talk to it with messages.
|
|
3
|
+
|
|
4
|
+
- ``worker``: the parent side, ``Worker``, ``WorkerPlatform`` and
|
|
5
|
+
``Spawner``, and ``make_platform`` for transports.
|
|
6
|
+
- ``worker_runner``: the worker side, ``WorkerRunner`` and
|
|
7
|
+
``WorkerRunnerPlatform``.
|
|
8
|
+
- ``worker_error``: ``WorkerError`` and its reasons.
|
|
9
|
+
- ``transferable``: ``Collector`` and transferable schemas.
|
|
10
|
+
- ``python_worker`` / ``python_worker_runner``: the platform for Python
|
|
11
|
+
threads (``ThreadWorker``) and child processes (``ProcessWorker``),
|
|
12
|
+
Effect's ``NodeWorker`` / ``NodeWorkerRunner``.
|
|
13
|
+
|
|
14
|
+
``pyfect.rpc`` serves and calls RPC groups over workers
|
|
15
|
+
(``rpc.client.layer_protocol_worker``,
|
|
16
|
+
``rpc.server.layer_protocol_worker_runner``).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from pyfect.workers import (
|
|
20
|
+
python_worker,
|
|
21
|
+
python_worker_runner,
|
|
22
|
+
transferable,
|
|
23
|
+
worker,
|
|
24
|
+
worker_error,
|
|
25
|
+
worker_runner,
|
|
26
|
+
)
|
|
27
|
+
from pyfect.workers.python_worker import ProcessWorker, ThreadWorker, WorkerHandle
|
|
28
|
+
from pyfect.workers.worker import Spawner, Worker, WorkerPlatform, layer_spawner, make_platform
|
|
29
|
+
from pyfect.workers.worker_error import (
|
|
30
|
+
WorkerError,
|
|
31
|
+
WorkerReceiveError,
|
|
32
|
+
WorkerSendError,
|
|
33
|
+
WorkerSpawnError,
|
|
34
|
+
WorkerUnknownError,
|
|
35
|
+
is_worker_error,
|
|
36
|
+
)
|
|
37
|
+
from pyfect.workers.worker_runner import WorkerRunner, WorkerRunnerPlatform
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"ProcessWorker",
|
|
41
|
+
"Spawner",
|
|
42
|
+
"ThreadWorker",
|
|
43
|
+
"Worker",
|
|
44
|
+
"WorkerError",
|
|
45
|
+
"WorkerHandle",
|
|
46
|
+
"WorkerPlatform",
|
|
47
|
+
"WorkerReceiveError",
|
|
48
|
+
"WorkerRunner",
|
|
49
|
+
"WorkerRunnerPlatform",
|
|
50
|
+
"WorkerSendError",
|
|
51
|
+
"WorkerSpawnError",
|
|
52
|
+
"WorkerUnknownError",
|
|
53
|
+
"is_worker_error",
|
|
54
|
+
"layer_spawner",
|
|
55
|
+
"make_platform",
|
|
56
|
+
"python_worker",
|
|
57
|
+
"python_worker_runner",
|
|
58
|
+
"transferable",
|
|
59
|
+
"worker",
|
|
60
|
+
"worker_error",
|
|
61
|
+
"worker_runner",
|
|
62
|
+
]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Messages over a socket between a parent and its worker process.
|
|
2
|
+
|
|
3
|
+
Each message is a pickle behind its four-byte length. A ``FrameWriter``
|
|
4
|
+
writes on a thread of its own, so posting never waits on the socket;
|
|
5
|
+
``read_frames`` reads on the calling thread (a reader thread) until the
|
|
6
|
+
other end closes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import pickle
|
|
12
|
+
import queue
|
|
13
|
+
import socket
|
|
14
|
+
import struct
|
|
15
|
+
import threading
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
|
|
18
|
+
_HEADER = struct.Struct(">I")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def encode(message: object) -> bytes:
|
|
22
|
+
payload = pickle.dumps(message, protocol=pickle.HIGHEST_PROTOCOL)
|
|
23
|
+
return _HEADER.pack(len(payload)) + payload
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def read_frames(
|
|
27
|
+
sock: socket.socket,
|
|
28
|
+
on_message: Callable[[object], None],
|
|
29
|
+
on_unreadable: Callable[[BaseException], None],
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Read messages until the socket ends or fails."""
|
|
32
|
+
with sock.makefile("rb") as stream:
|
|
33
|
+
while True:
|
|
34
|
+
try:
|
|
35
|
+
header = stream.read(_HEADER.size)
|
|
36
|
+
if len(header) < _HEADER.size:
|
|
37
|
+
return
|
|
38
|
+
(size,) = _HEADER.unpack(header)
|
|
39
|
+
payload = stream.read(size)
|
|
40
|
+
except OSError:
|
|
41
|
+
return
|
|
42
|
+
if len(payload) < size:
|
|
43
|
+
return
|
|
44
|
+
try:
|
|
45
|
+
message = pickle.loads(payload) # our own child or parent
|
|
46
|
+
except Exception as cause: # reported as an unreadable message
|
|
47
|
+
on_unreadable(cause)
|
|
48
|
+
continue
|
|
49
|
+
on_message(message)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class FrameWriter:
|
|
53
|
+
"""Writes frames in order on its own thread; once the socket fails,
|
|
54
|
+
frames are dropped."""
|
|
55
|
+
|
|
56
|
+
__slots__ = ("_frames", "_sock", "_thread")
|
|
57
|
+
|
|
58
|
+
def __init__(self, sock: socket.socket, name: str) -> None:
|
|
59
|
+
self._sock = sock
|
|
60
|
+
self._frames: queue.SimpleQueue[bytes | None] = queue.SimpleQueue()
|
|
61
|
+
self._thread = threading.Thread(target=self._run, name=name, daemon=True)
|
|
62
|
+
self._thread.start()
|
|
63
|
+
|
|
64
|
+
def write(self, frame: bytes) -> None:
|
|
65
|
+
self._frames.put(frame)
|
|
66
|
+
|
|
67
|
+
def close(self, timeout: float | None = None) -> None:
|
|
68
|
+
"""Stop once what was written is sent, waiting at most ``timeout``
|
|
69
|
+
for it."""
|
|
70
|
+
self._frames.put(None)
|
|
71
|
+
if timeout is not None and threading.current_thread() is not self._thread:
|
|
72
|
+
self._thread.join(timeout)
|
|
73
|
+
|
|
74
|
+
def _run(self) -> None:
|
|
75
|
+
while True:
|
|
76
|
+
frame = self._frames.get()
|
|
77
|
+
if frame is None:
|
|
78
|
+
return
|
|
79
|
+
try:
|
|
80
|
+
self._sock.sendall(frame)
|
|
81
|
+
except OSError:
|
|
82
|
+
return
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""A ``Mailbox``: items put from any thread, taken in order by a fiber.
|
|
2
|
+
|
|
3
|
+
The threads of a worker's transport (a reader, another runtime's
|
|
4
|
+
scheduler) put what arrives; one fiber takes it, so everything a worker
|
|
5
|
+
receives is handled on its runtime's own thread. A waiting take suspends
|
|
6
|
+
in ``os_wait``; a put wakes it to retry, never handing it the item.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import threading
|
|
12
|
+
from collections import deque
|
|
13
|
+
from typing import Never
|
|
14
|
+
|
|
15
|
+
from pyfect._core import Effect
|
|
16
|
+
from pyfect._os_wait import Cancel, Resume, os_wait
|
|
17
|
+
from pyfect.effect import succeed, suspend
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Mailbox[A]:
|
|
21
|
+
__slots__ = ("_items", "_lock", "_waiters")
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
self._lock = threading.Lock()
|
|
25
|
+
self._items: deque[A] = deque()
|
|
26
|
+
self._waiters: list[Resume[A, Never]] = []
|
|
27
|
+
|
|
28
|
+
def put(self, item: A) -> None:
|
|
29
|
+
with self._lock:
|
|
30
|
+
self._items.append(item)
|
|
31
|
+
waiters, self._waiters = self._waiters, []
|
|
32
|
+
for resume in waiters:
|
|
33
|
+
resume(suspend(self.take))
|
|
34
|
+
|
|
35
|
+
def take(self) -> Effect[A]:
|
|
36
|
+
def register(resume: Resume[A, Never]) -> Cancel | None:
|
|
37
|
+
with self._lock:
|
|
38
|
+
taken = (self._items.popleft(),) if self._items else None
|
|
39
|
+
if taken is None:
|
|
40
|
+
self._waiters.append(resume)
|
|
41
|
+
if taken is not None:
|
|
42
|
+
resume(succeed(taken[0]))
|
|
43
|
+
return None
|
|
44
|
+
return lambda: self._forget(resume)
|
|
45
|
+
|
|
46
|
+
return os_wait(register, waits_for="event")
|
|
47
|
+
|
|
48
|
+
def _forget(self, resume: Resume[A, Never]) -> None:
|
|
49
|
+
with self._lock:
|
|
50
|
+
if resume in self._waiters:
|
|
51
|
+
self._waiters.remove(resume)
|
pyfect/workers/_port.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""The two ends of a Python worker's transport, as the platforms see them.
|
|
2
|
+
|
|
3
|
+
The parent holds a ``WorkerPort``: it posts messages to the worker, and the
|
|
4
|
+
worker's messages, receive errors and exit arrive in ``events``. The worker
|
|
5
|
+
holds a ``ParentPort``: it posts to the parent, and the parent's messages
|
|
6
|
+
arrive in ``inbox``. Each backend (a thread, a child process) implements
|
|
7
|
+
both; the threads moving messages only ever put into a ``Mailbox``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import threading
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
from pyfect.process._os import Latch
|
|
17
|
+
from pyfect.workers._mailbox import Mailbox
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True, slots=True)
|
|
21
|
+
class Received:
|
|
22
|
+
"""A message arrived."""
|
|
23
|
+
|
|
24
|
+
data: object
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True, slots=True)
|
|
28
|
+
class Unreadable:
|
|
29
|
+
"""A message arrived that could not be read (Node's ``messageerror``)."""
|
|
30
|
+
|
|
31
|
+
cause: BaseException
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True, slots=True)
|
|
35
|
+
class Raised:
|
|
36
|
+
"""The worker raised (Node's ``error`` event)."""
|
|
37
|
+
|
|
38
|
+
cause: BaseException
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class Exited:
|
|
43
|
+
"""The worker has exited."""
|
|
44
|
+
|
|
45
|
+
code: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True, slots=True)
|
|
49
|
+
class Disconnected:
|
|
50
|
+
"""The parent went away."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
type WorkerEvent = Received | Unreadable | Raised | Exited
|
|
54
|
+
type ParentEvent = Received | Unreadable | Disconnected
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class WorkerPort(ABC):
|
|
58
|
+
"""The parent's end of one worker."""
|
|
59
|
+
|
|
60
|
+
__slots__ = ("_finished", "events", "exited")
|
|
61
|
+
|
|
62
|
+
def __init__(self) -> None:
|
|
63
|
+
self.events: Mailbox[WorkerEvent] = Mailbox()
|
|
64
|
+
self.exited: Latch[int] = Latch(waits_for="event")
|
|
65
|
+
self._finished = threading.Lock()
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def post(self, message: object) -> None:
|
|
69
|
+
"""Send ``message`` without waiting; raises if it cannot be sent."""
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def kill(self) -> None:
|
|
73
|
+
"""Stop the worker now."""
|
|
74
|
+
|
|
75
|
+
def finish(self, code: int) -> None:
|
|
76
|
+
"""Report the exit, once."""
|
|
77
|
+
if self._finished.acquire(blocking=False):
|
|
78
|
+
self.events.put(Exited(code))
|
|
79
|
+
self.exited.set(code)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ParentPort(ABC):
|
|
83
|
+
"""The worker's end of its parent."""
|
|
84
|
+
|
|
85
|
+
__slots__ = ("inbox",)
|
|
86
|
+
|
|
87
|
+
def __init__(self) -> None:
|
|
88
|
+
self.inbox: Mailbox[ParentEvent] = Mailbox()
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def post(self, message: object) -> None:
|
|
92
|
+
"""Send ``message`` to the parent without waiting."""
|
|
93
|
+
|
|
94
|
+
@abstractmethod
|
|
95
|
+
def close(self) -> None:
|
|
96
|
+
"""Stop receiving: the parent closed the worker."""
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class WorkerHandle(ABC):
|
|
100
|
+
"""A worker that has not started yet: what a ``Spawner`` gives the
|
|
101
|
+
Python platform. ``start`` runs it."""
|
|
102
|
+
|
|
103
|
+
__slots__ = ()
|
|
104
|
+
|
|
105
|
+
@abstractmethod
|
|
106
|
+
def start(self) -> WorkerPort: ...
|