pyfect-dev 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""What an entity manager's entities share: the manager's services and
|
|
2
|
+
settings, the resident and draining entities, and the helpers that write
|
|
3
|
+
requests to an entity's server."""
|
|
4
|
+
# pyright: reportPrivateUsage=false
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Never
|
|
11
|
+
|
|
12
|
+
from pyfect._core import Effect, Restore, _Locally, _WithFiber, uninterruptible_mask
|
|
13
|
+
from pyfect._fiber import Fiber
|
|
14
|
+
from pyfect.clock import ClockService, now_millis_unsafe
|
|
15
|
+
from pyfect.cluster.cluster_schema import WithTransaction
|
|
16
|
+
from pyfect.cluster.entity._handlers import Concurrency
|
|
17
|
+
from pyfect.cluster.entity._request import Request as EntityRequest
|
|
18
|
+
from pyfect.cluster.entity_address import EntityAddress
|
|
19
|
+
from pyfect.cluster.entity_id import EntityId
|
|
20
|
+
from pyfect.cluster.message_storage import MessageStorageService
|
|
21
|
+
from pyfect.cluster.sharding_config import ShardingConfigService
|
|
22
|
+
from pyfect.cluster.snowflake import Snowflake, SnowflakeGenerator
|
|
23
|
+
from pyfect.context import Context
|
|
24
|
+
from pyfect.effect import interrupt, sync
|
|
25
|
+
from pyfect.exit import Exit
|
|
26
|
+
from pyfect.latch import Latch
|
|
27
|
+
from pyfect.rpc import AnyRpc
|
|
28
|
+
from pyfect.rpc import message as rpc_message
|
|
29
|
+
from pyfect.rpc.server._core import OnRequest
|
|
30
|
+
from pyfect.schedule import Decision
|
|
31
|
+
from pyfect.scope import CloseableScope
|
|
32
|
+
|
|
33
|
+
from ._state import EntityState, ManagerOptions, RequestEntry, RequestTransaction
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from pyfect.cluster.entity import Entity, EntityHandlers
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(eq=False, slots=True)
|
|
40
|
+
class CloseLatches:
|
|
41
|
+
"""Opened when an entity's scope has closed (``closed``), or to stop
|
|
42
|
+
waiting for its handlers on a forced release (``force``)."""
|
|
43
|
+
|
|
44
|
+
closed: Latch = field(default_factory=Latch)
|
|
45
|
+
force: Latch = field(default_factory=Latch)
|
|
46
|
+
closing: bool = False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(eq=False, slots=True, kw_only=True)
|
|
50
|
+
class ManagerCore:
|
|
51
|
+
entity: type[Entity]
|
|
52
|
+
build: Effect[EntityHandlers[Any, Any], Never, Any]
|
|
53
|
+
rpcs: dict[str, AnyRpc]
|
|
54
|
+
options: ManagerOptions
|
|
55
|
+
config: ShardingConfigService
|
|
56
|
+
snowflakes: SnowflakeGenerator
|
|
57
|
+
clock: ClockService
|
|
58
|
+
context: Context[Any]
|
|
59
|
+
manager_scope: CloseableScope
|
|
60
|
+
storage_enabled: bool
|
|
61
|
+
mailbox_capacity: Concurrency
|
|
62
|
+
mailbox_metrics: bool
|
|
63
|
+
defect_retry_policy: Any
|
|
64
|
+
retry_driver: Callable[[None], Effect[Decision[Any], Any, Any]]
|
|
65
|
+
active_servers: dict[EntityId, EntityState] = field(default_factory=dict[EntityId, EntityState])
|
|
66
|
+
draining_servers: dict[EntityId, EntityState] = field(
|
|
67
|
+
default_factory=dict[EntityId, EntityState]
|
|
68
|
+
)
|
|
69
|
+
resident_mailboxes: dict[int, dict[Snowflake, RequestEntry]] = field(
|
|
70
|
+
default_factory=dict[int, dict[Snowflake, RequestEntry]]
|
|
71
|
+
)
|
|
72
|
+
"""Mailboxes of every resident entity, draining ones too, by ``id``."""
|
|
73
|
+
server_close_latches: dict[EntityAddress, CloseLatches] = field(
|
|
74
|
+
default_factory=dict[EntityAddress, CloseLatches]
|
|
75
|
+
)
|
|
76
|
+
processed_request_ids: set[Snowflake] = field(default_factory=set[Snowflake])
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def storage(self) -> MessageStorageService:
|
|
80
|
+
return self.options.storage
|
|
81
|
+
|
|
82
|
+
def now_millis(self) -> int:
|
|
83
|
+
return now_millis_unsafe(self.clock)
|
|
84
|
+
|
|
85
|
+
def request_write_options(self, entry: RequestEntry) -> OnRequest | None:
|
|
86
|
+
"""Runs the request in a storage transaction (``WithTransaction``)
|
|
87
|
+
and bound to its caller (``caller_scope``)."""
|
|
88
|
+
on_transaction: OnRequest | None = None
|
|
89
|
+
if entry.message.annotations.get(WithTransaction):
|
|
90
|
+
storage = self.storage
|
|
91
|
+
|
|
92
|
+
def transaction(effect: Effect[Any, Any, Any]) -> Effect[Any, Any, Any]:
|
|
93
|
+
# Interrupts during commit must not hide the outcome from settle.
|
|
94
|
+
def body(restore: Restore) -> Effect[Any, Any, Any]:
|
|
95
|
+
current = RequestTransaction()
|
|
96
|
+
entry.transaction = current
|
|
97
|
+
|
|
98
|
+
def settled(outcome: Exit[Any, Any]) -> Effect[None]:
|
|
99
|
+
if entry.transaction is current:
|
|
100
|
+
entry.transaction = None
|
|
101
|
+
settle = current.settle
|
|
102
|
+
return sync(lambda: None) if settle is None else settle(outcome)
|
|
103
|
+
|
|
104
|
+
return storage.with_transaction(restore(effect)).on_exit(settled)
|
|
105
|
+
|
|
106
|
+
return uninterruptible_mask(body)
|
|
107
|
+
|
|
108
|
+
on_transaction = transaction
|
|
109
|
+
caller = entry.caller_scope
|
|
110
|
+
if caller is None:
|
|
111
|
+
return on_transaction
|
|
112
|
+
on_caller = bind_to_caller(caller)
|
|
113
|
+
if on_transaction is None:
|
|
114
|
+
return on_caller
|
|
115
|
+
inner = on_transaction
|
|
116
|
+
return lambda effect: on_caller(inner(effect))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def bind_to_caller(caller_scope: CloseableScope) -> OnRequest:
|
|
120
|
+
"""Bind each handler fiber, replays too, to its caller's scope before
|
|
121
|
+
its body runs."""
|
|
122
|
+
|
|
123
|
+
def bind(effect: Effect[Any, Any, Any]) -> Effect[Any, Any, Any]:
|
|
124
|
+
def body(fiber: Fiber[Any, Any]) -> Effect[Any, Any, Any]:
|
|
125
|
+
if caller_scope.is_closed:
|
|
126
|
+
return interrupt()
|
|
127
|
+
key = object()
|
|
128
|
+
|
|
129
|
+
def interrupt_handler(_: Exit[Any, Any]) -> Effect[None]:
|
|
130
|
+
return sync(lambda: fiber.interrupt_as_unsafe(fiber.id))
|
|
131
|
+
|
|
132
|
+
caller_scope._add(key, interrupt_handler)
|
|
133
|
+
return effect.ensuring(sync(lambda: caller_scope._remove(key)))
|
|
134
|
+
|
|
135
|
+
return _WithFiber(body)
|
|
136
|
+
|
|
137
|
+
return bind
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def request_envelope(entry: RequestEntry) -> rpc_message.Request:
|
|
141
|
+
"""The request as the entity's RPC server takes it, its payload the
|
|
142
|
+
entity ``Request``."""
|
|
143
|
+
envelope = entry.message.envelope
|
|
144
|
+
payload = EntityRequest[Any](
|
|
145
|
+
request_id=envelope.request_id,
|
|
146
|
+
address=envelope.address,
|
|
147
|
+
tag=envelope.tag,
|
|
148
|
+
payload=envelope.payload,
|
|
149
|
+
headers=envelope.headers,
|
|
150
|
+
trace_id=envelope.trace_id,
|
|
151
|
+
span_id=envelope.span_id,
|
|
152
|
+
sampled=envelope.sampled,
|
|
153
|
+
last_sent_chunk=entry.last_sent_chunk,
|
|
154
|
+
)
|
|
155
|
+
return rpc_message.Request(
|
|
156
|
+
envelope.request_id,
|
|
157
|
+
envelope.tag,
|
|
158
|
+
payload,
|
|
159
|
+
envelope.headers,
|
|
160
|
+
envelope.trace_id,
|
|
161
|
+
envelope.span_id,
|
|
162
|
+
envelope.sampled,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def set_context[A, E](effect: Effect[A, E, Any], services: Context[Any]) -> Effect[A, E]:
|
|
167
|
+
"""``effect`` run with exactly ``services`` (Effect's ``setContext``)."""
|
|
168
|
+
located: Effect[A, E] = _Locally(effect, lambda _: services)
|
|
169
|
+
return located
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def retry_respond[A, E, R](times: int, effect: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
173
|
+
"""``effect``, retried ``times`` times 200ms apart on a typed failure."""
|
|
174
|
+
if times == 0:
|
|
175
|
+
return effect
|
|
176
|
+
return effect.catch_all(lambda _: retry_respond(times - 1, effect).delay(0.2))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
__all__ = [
|
|
180
|
+
"CloseLatches",
|
|
181
|
+
"ManagerCore",
|
|
182
|
+
"bind_to_caller",
|
|
183
|
+
"request_envelope",
|
|
184
|
+
"retry_respond",
|
|
185
|
+
"set_context",
|
|
186
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Decoding a message read from storage or another runner, with the
|
|
2
|
+
entity's RPC schemas (Effect's ``makeMessageDecode``)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import dataclass, replace
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from pyfect import schema as S
|
|
11
|
+
from pyfect._core import Effect
|
|
12
|
+
from pyfect.cluster.envelope import Request
|
|
13
|
+
from pyfect.cluster.message import Incoming, IncomingEnvelope, IncomingRequest
|
|
14
|
+
from pyfect.cluster.reply import Reply, reply_schema
|
|
15
|
+
from pyfect.effect import fail, gen, succeed
|
|
16
|
+
from pyfect.rpc import AnyRpc
|
|
17
|
+
from pyfect.schema.issue import InvalidValue
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True, slots=True)
|
|
21
|
+
class DecodedRequest:
|
|
22
|
+
envelope: Request[Any]
|
|
23
|
+
last_sent_reply: Reply | None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _unknown_tag() -> S.SchemaError:
|
|
27
|
+
return S.SchemaError(InvalidValue(annotations={"message": "Expected a known entity RPC tag"}))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _decode_request(message: IncomingRequest, rpc: AnyRpc) -> Effect[DecodedRequest, S.SchemaError]:
|
|
31
|
+
codec_for = message.codec_for
|
|
32
|
+
|
|
33
|
+
@gen
|
|
34
|
+
def run():
|
|
35
|
+
payload = yield from S.decode_unknown_effect(codec_for(rpc.payload_schema))(
|
|
36
|
+
message.envelope.payload
|
|
37
|
+
)
|
|
38
|
+
last = message.last_sent_reply
|
|
39
|
+
decoded_last: Reply | None = None
|
|
40
|
+
if last is not None:
|
|
41
|
+
decoded_last = yield from S.decode_unknown_effect(reply_schema(rpc, codec_for))(last)
|
|
42
|
+
return DecodedRequest(replace(message.envelope, payload=payload), decoded_last)
|
|
43
|
+
|
|
44
|
+
return run
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def decode_message(
|
|
48
|
+
rpcs: Mapping[str, AnyRpc], message: Incoming
|
|
49
|
+
) -> Effect[DecodedRequest | IncomingEnvelope, S.SchemaError, Any]:
|
|
50
|
+
"""The request with its payload (and last sent reply) decoded; an
|
|
51
|
+
envelope as it is. An unknown tag is a ``SchemaError``."""
|
|
52
|
+
if isinstance(message, IncomingEnvelope):
|
|
53
|
+
return succeed(message)
|
|
54
|
+
rpc = rpcs.get(message.envelope.tag)
|
|
55
|
+
if rpc is None:
|
|
56
|
+
return fail(_unknown_tag())
|
|
57
|
+
return _decode_request(message, rpc)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = ["DecodedRequest", "decode_message"]
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"""An entity type's manager (Effect's ``internal/entityManager``): starts
|
|
2
|
+
its entities on demand, delivers requests and envelopes to them, keeps
|
|
3
|
+
them alive, and stops those of a shard."""
|
|
4
|
+
# pyright: reportPrivateUsage=false
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Never
|
|
10
|
+
|
|
11
|
+
from pyfect import exit as Exit
|
|
12
|
+
from pyfect import schema as S
|
|
13
|
+
from pyfect._core import Effect, _Locally
|
|
14
|
+
from pyfect._fiber import Fiber
|
|
15
|
+
from pyfect.clock import Clock
|
|
16
|
+
from pyfect.cluster import cluster_metrics
|
|
17
|
+
from pyfect.cluster._entity_reaper import EntityReaper
|
|
18
|
+
from pyfect.cluster._resource_map import ResourceMap
|
|
19
|
+
from pyfect.cluster.cluster_error import (
|
|
20
|
+
AlreadyProcessingMessage,
|
|
21
|
+
EntityNotAssignedToRunner,
|
|
22
|
+
MailboxFull,
|
|
23
|
+
MalformedMessage,
|
|
24
|
+
)
|
|
25
|
+
from pyfect.cluster.cluster_schema import Persisted, Uninterruptible
|
|
26
|
+
from pyfect.cluster.entity._keep_alive import KeepAliveRpc
|
|
27
|
+
from pyfect.cluster.entity_address import EntityAddress
|
|
28
|
+
from pyfect.cluster.envelope import AckChunk
|
|
29
|
+
from pyfect.cluster.message import (
|
|
30
|
+
Incoming,
|
|
31
|
+
IncomingEnvelope,
|
|
32
|
+
IncomingLocal,
|
|
33
|
+
IncomingRequest,
|
|
34
|
+
IncomingRequestLocal,
|
|
35
|
+
dynamic_annotations,
|
|
36
|
+
)
|
|
37
|
+
from pyfect.cluster.message_storage import NOOP
|
|
38
|
+
from pyfect.cluster.reply import Chunk, Reply, ReplyWithContext, WithExit
|
|
39
|
+
from pyfect.cluster.shard_id import ShardId
|
|
40
|
+
from pyfect.cluster.sharding_config import ShardingConfig
|
|
41
|
+
from pyfect.cluster.snowflake import Generator
|
|
42
|
+
from pyfect.context import Context
|
|
43
|
+
from pyfect.duration import to_millis
|
|
44
|
+
from pyfect.effect import context, die, fail, gen, succeed, suspend, sync
|
|
45
|
+
from pyfect.exit import Exit as ExitT
|
|
46
|
+
from pyfect.logger import CurrentLogAnnotations
|
|
47
|
+
from pyfect.metric import register_unsafe
|
|
48
|
+
from pyfect.metric._state import GaugeState
|
|
49
|
+
from pyfect.rpc import message as rpc_message
|
|
50
|
+
from pyfect.schedule import exponential, spaced, to_step_with_sleep
|
|
51
|
+
from pyfect.schedule import min as schedule_min
|
|
52
|
+
from pyfect.scope import Scope, acquire_release, fork_in
|
|
53
|
+
|
|
54
|
+
from ._activation import make_entity
|
|
55
|
+
from ._core import ManagerCore, request_envelope, retry_respond
|
|
56
|
+
from ._decode import DecodedRequest, decode_message
|
|
57
|
+
from ._state import EntityState, ManagerOptions, RequestEntry
|
|
58
|
+
|
|
59
|
+
if TYPE_CHECKING:
|
|
60
|
+
from pyfect.cluster.entity import Entity, EntityHandlers
|
|
61
|
+
|
|
62
|
+
type LocalSendError = EntityNotAssignedToRunner | MailboxFull | AlreadyProcessingMessage
|
|
63
|
+
|
|
64
|
+
_VOID: Effect[None] = succeed(None)
|
|
65
|
+
|
|
66
|
+
DEFAULT_RETRY_POLICY = schedule_min(exponential(0.5, 1.5), spaced(10))
|
|
67
|
+
"""Restarts after a defect wait 500ms, growing by 1.5, at most 10s."""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _run_fork(services: Context[Any], effect: Effect[Any, Any, Any]) -> Fiber[Any, Any]:
|
|
71
|
+
"""Effect's ``runForkWith(services)``: a detached fiber, started now."""
|
|
72
|
+
parent = Fiber.get_current()
|
|
73
|
+
assert parent is not None, "an entity manager runs on a fiber"
|
|
74
|
+
return parent._fork(_Locally(effect, lambda _: services), daemon=True, start_immediately=True)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class EntityManager:
|
|
78
|
+
"""The entities of one entity type on this runner."""
|
|
79
|
+
|
|
80
|
+
__slots__ = ("_core", "_entities")
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self, core: ManagerCore, entities: ResourceMap[EntityAddress, EntityState, Any]
|
|
84
|
+
) -> None:
|
|
85
|
+
self._core = core
|
|
86
|
+
self._entities = entities
|
|
87
|
+
|
|
88
|
+
# --- delivery -----------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
def _send_envelope(self, server: EntityState, message: IncomingEnvelope) -> Effect[None]:
|
|
91
|
+
envelope = message.envelope
|
|
92
|
+
entry = server.active_requests.get(envelope.request_id)
|
|
93
|
+
if entry is None:
|
|
94
|
+
return _VOID
|
|
95
|
+
if isinstance(envelope, AckChunk):
|
|
96
|
+
last = entry.last_sent_chunk
|
|
97
|
+
if last is not None and envelope.reply_id != last.id:
|
|
98
|
+
return _VOID
|
|
99
|
+
return server.write(0, rpc_message.Ack(envelope.request_id), None)
|
|
100
|
+
return server.write(0, rpc_message.Interrupt(envelope.request_id), None)
|
|
101
|
+
|
|
102
|
+
def _keep_alive(self, server: EntityState, message: IncomingRequestLocal) -> Effect[None]:
|
|
103
|
+
core = self._core
|
|
104
|
+
|
|
105
|
+
def respond() -> Effect[None]:
|
|
106
|
+
reply = WithExit(
|
|
107
|
+
request_id=message.envelope.request_id,
|
|
108
|
+
id=core.snowflakes.next_unsafe(),
|
|
109
|
+
exit=Exit.succeed(None),
|
|
110
|
+
)
|
|
111
|
+
return retry_respond(4, message.respond(reply)).or_die()
|
|
112
|
+
|
|
113
|
+
reply = suspend(respond)
|
|
114
|
+
if server.keep_alive_enabled:
|
|
115
|
+
return reply
|
|
116
|
+
server.keep_alive_enabled = True
|
|
117
|
+
|
|
118
|
+
def disabled() -> Effect[None]:
|
|
119
|
+
server.keep_alive_enabled = False
|
|
120
|
+
return reply
|
|
121
|
+
|
|
122
|
+
waiting = server.keep_alive_latch.when_open(suspend(disabled))
|
|
123
|
+
return fork_in(waiting, server.scope, start_immediately=True).as_void()
|
|
124
|
+
|
|
125
|
+
def _send_request(
|
|
126
|
+
self, server: EntityState, message: IncomingRequestLocal
|
|
127
|
+
) -> Effect[None, LocalSendError]:
|
|
128
|
+
core = self._core
|
|
129
|
+
request_id = message.envelope.request_id
|
|
130
|
+
address = message.envelope.address
|
|
131
|
+
# A request already running may have more than one sender; the
|
|
132
|
+
# others resume from storage only.
|
|
133
|
+
if request_id in server.active_requests or request_id in core.processed_request_ids:
|
|
134
|
+
return fail(AlreadyProcessingMessage(envelope_id=request_id, address=address))
|
|
135
|
+
rpc = core.rpcs[message.envelope.tag]
|
|
136
|
+
if not core.storage_enabled and message.annotations.get(Persisted):
|
|
137
|
+
return die(
|
|
138
|
+
RuntimeError(
|
|
139
|
+
"EntityManager.sendLocal: Cannot process a persisted message without "
|
|
140
|
+
"MessageStorage"
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
if rpc.tag == KeepAliveRpc.tag:
|
|
144
|
+
return self._keep_alive(server, message)
|
|
145
|
+
capacity = core.mailbox_capacity
|
|
146
|
+
if capacity != "unbounded" and len(server.active_requests) >= capacity:
|
|
147
|
+
return fail(MailboxFull(address=address))
|
|
148
|
+
caller_scope = (
|
|
149
|
+
message.caller_scope
|
|
150
|
+
if message.caller_scope is not None
|
|
151
|
+
and not message.annotations.get(Persisted)
|
|
152
|
+
and message.annotations.get(Uninterruptible) is False
|
|
153
|
+
else None
|
|
154
|
+
)
|
|
155
|
+
if caller_scope is not None and caller_scope.is_closed:
|
|
156
|
+
return _VOID
|
|
157
|
+
last = message.last_sent_reply
|
|
158
|
+
entry = RequestEntry(
|
|
159
|
+
rpc=rpc,
|
|
160
|
+
message=message,
|
|
161
|
+
last_sent_chunk=last if isinstance(last, Chunk) else None,
|
|
162
|
+
sequence=last.sequence + 1 if isinstance(last, Chunk) else 0,
|
|
163
|
+
caller_scope=caller_scope,
|
|
164
|
+
)
|
|
165
|
+
server.active_requests[request_id] = entry
|
|
166
|
+
if caller_scope is not None:
|
|
167
|
+
# Register cleanup atomically with admission, including requests
|
|
168
|
+
# awaiting delivery.
|
|
169
|
+
def forget(_: ExitT[Any, Any]) -> Effect[None]:
|
|
170
|
+
def run() -> None:
|
|
171
|
+
if server.active_requests.pop(request_id, None) is not None and (
|
|
172
|
+
not server.active_requests
|
|
173
|
+
):
|
|
174
|
+
server.last_active_check = core.now_millis()
|
|
175
|
+
|
|
176
|
+
return sync(run)
|
|
177
|
+
|
|
178
|
+
caller_scope._add(object(), forget)
|
|
179
|
+
return server.write(0, request_envelope(entry), core.request_write_options(entry))
|
|
180
|
+
|
|
181
|
+
def send_local(self, message: IncomingLocal) -> Effect[None, LocalSendError]:
|
|
182
|
+
"""Deliver a decoded message to its entity, starting it if needed."""
|
|
183
|
+
core = self._core
|
|
184
|
+
if isinstance(message, IncomingEnvelope):
|
|
185
|
+
draining = core.draining_servers.get(message.envelope.address.entity_id)
|
|
186
|
+
if draining is not None and message.envelope.request_id in draining.active_requests:
|
|
187
|
+
return self._send_envelope(draining, message)
|
|
188
|
+
|
|
189
|
+
def deliver(server: EntityState) -> Effect[None, LocalSendError]:
|
|
190
|
+
if isinstance(message, IncomingRequestLocal):
|
|
191
|
+
return self._send_request(server, message)
|
|
192
|
+
return self._send_envelope(server, message)
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
self._entities.get(message.envelope.address)
|
|
196
|
+
.flat_map(deliver)
|
|
197
|
+
.pipe(_without_log_annotations)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def send(self, message: Incoming) -> Effect[None, LocalSendError]:
|
|
201
|
+
"""Decode a message read from storage or another runner, then
|
|
202
|
+
deliver it; a request that does not decode is answered with a
|
|
203
|
+
``MalformedMessage`` defect."""
|
|
204
|
+
core = self._core
|
|
205
|
+
|
|
206
|
+
def malformed(error: S.SchemaError) -> Effect[None, LocalSendError]:
|
|
207
|
+
if isinstance(message, IncomingEnvelope):
|
|
208
|
+
return die(MalformedMessage(cause=error))
|
|
209
|
+
rpc = core.rpcs.get(message.envelope.tag)
|
|
210
|
+
reply = WithExit(
|
|
211
|
+
id=core.snowflakes.next_unsafe(),
|
|
212
|
+
request_id=message.envelope.request_id,
|
|
213
|
+
exit=Exit.die(MalformedMessage(cause=error)),
|
|
214
|
+
)
|
|
215
|
+
replied = (
|
|
216
|
+
ReplyWithContext(reply=reply, context=core.context)
|
|
217
|
+
if rpc is None
|
|
218
|
+
else ReplyWithContext(reply=reply, rpc=rpc, context=core.context)
|
|
219
|
+
)
|
|
220
|
+
return message.respond(replied).or_die()
|
|
221
|
+
|
|
222
|
+
def decoded(
|
|
223
|
+
result: DecodedRequest | IncomingEnvelope,
|
|
224
|
+
) -> Effect[None, LocalSendError]:
|
|
225
|
+
if isinstance(result, IncomingEnvelope):
|
|
226
|
+
return self.send_local(result)
|
|
227
|
+
request = message
|
|
228
|
+
assert isinstance(request, IncomingRequest)
|
|
229
|
+
rpc = core.rpcs[result.envelope.tag]
|
|
230
|
+
|
|
231
|
+
def respond(reply: Reply) -> Effect[None, Any]:
|
|
232
|
+
return request.respond(ReplyWithContext(reply=reply, rpc=rpc, context=core.context))
|
|
233
|
+
|
|
234
|
+
return self.send_local(
|
|
235
|
+
IncomingRequestLocal(
|
|
236
|
+
annotations=dynamic_annotations(rpc, result.envelope),
|
|
237
|
+
envelope=result.envelope,
|
|
238
|
+
last_sent_reply=result.last_sent_reply,
|
|
239
|
+
caller_scope=request.caller_scope,
|
|
240
|
+
respond=respond,
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
decode_message(core.rpcs, message)
|
|
246
|
+
.match_effect(on_failure=malformed, on_success=decoded)
|
|
247
|
+
.pipe(_provide(core.context))
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# --- state ----------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
def is_processing_for(
|
|
253
|
+
self, message: Incoming, *, exclude_replies: bool = False, exclude_completed: bool = False
|
|
254
|
+
) -> bool:
|
|
255
|
+
core = self._core
|
|
256
|
+
request_id = message.envelope.request_id
|
|
257
|
+
if (
|
|
258
|
+
not exclude_replies
|
|
259
|
+
and not exclude_completed
|
|
260
|
+
and request_id in core.processed_request_ids
|
|
261
|
+
):
|
|
262
|
+
return True
|
|
263
|
+
state = core.active_servers.get(message.envelope.address.entity_id)
|
|
264
|
+
if state is None:
|
|
265
|
+
return False
|
|
266
|
+
request = state.active_requests.get(request_id)
|
|
267
|
+
if request is None:
|
|
268
|
+
return False
|
|
269
|
+
if exclude_replies and request.sent_reply:
|
|
270
|
+
return False
|
|
271
|
+
return not (exclude_completed and request.sent_exit)
|
|
272
|
+
|
|
273
|
+
def clear_processed(self) -> None:
|
|
274
|
+
self._core.processed_request_ids.clear()
|
|
275
|
+
|
|
276
|
+
def has_servers_unsafe(self, shard_id: ShardId) -> bool:
|
|
277
|
+
"""Whether any entity of the shard is still open, those
|
|
278
|
+
``interrupt_shard`` waits on too."""
|
|
279
|
+
return any(address.shard_id == shard_id for address in self._core.server_close_latches)
|
|
280
|
+
|
|
281
|
+
def is_resident_unsafe(self, address: EntityAddress) -> bool:
|
|
282
|
+
return self._entities.has_unsafe(address)
|
|
283
|
+
|
|
284
|
+
def resident_addresses_unsafe(self) -> list[EntityAddress]:
|
|
285
|
+
return self._entities.keys_unsafe()
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def active_entity_count(self) -> Effect[int]:
|
|
289
|
+
return sync(lambda: len(self._core.active_servers))
|
|
290
|
+
|
|
291
|
+
def interrupt_shard(self, shard_id: ShardId, *, force: bool = False) -> Effect[None]:
|
|
292
|
+
"""Stop every entity of ``shard_id`` and wait until each has closed;
|
|
293
|
+
with ``force``, without waiting for their handlers."""
|
|
294
|
+
core = self._core
|
|
295
|
+
entities = self._entities
|
|
296
|
+
|
|
297
|
+
def loop() -> Effect[None]:
|
|
298
|
+
latches = [
|
|
299
|
+
(address, found)
|
|
300
|
+
for address, found in list(core.server_close_latches.items())
|
|
301
|
+
if address.shard_id == shard_id
|
|
302
|
+
]
|
|
303
|
+
if force:
|
|
304
|
+
for _, found in latches:
|
|
305
|
+
found.force.open_unsafe()
|
|
306
|
+
# Look entities up by address: ``active_servers`` is keyed by
|
|
307
|
+
# entity id, so an entity with the same id on another shard
|
|
308
|
+
# can hide this one.
|
|
309
|
+
fibers: list[Fiber[Any, Any]] = []
|
|
310
|
+
for address, found in latches:
|
|
311
|
+
if not found.closing:
|
|
312
|
+
fibers.append(_run_fork(core.context, entities.remove_ignore(address)))
|
|
313
|
+
fibers.append(_run_fork(core.context, found.closed.wait()))
|
|
314
|
+
if not fibers:
|
|
315
|
+
return _VOID
|
|
316
|
+
return Fiber.join_all(fibers).flat_map(lambda _: suspend(loop))
|
|
317
|
+
|
|
318
|
+
return suspend(loop)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _without_log_annotations[A, E, R](effect: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
322
|
+
return _Locally(effect, lambda current: current.add(CurrentLogAnnotations, {}))
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _provide(services: Context[Any]) -> Any:
|
|
326
|
+
def apply[A, E](effect: Effect[A, E, Any]) -> Effect[A, E]:
|
|
327
|
+
return _Locally(effect, lambda current: current.merge(services))
|
|
328
|
+
|
|
329
|
+
return apply
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def make(
|
|
333
|
+
entity: type[Entity],
|
|
334
|
+
build: Effect[EntityHandlers[Any, Any], Never, Any],
|
|
335
|
+
options: ManagerOptions,
|
|
336
|
+
) -> Effect[EntityManager, Never, Any]:
|
|
337
|
+
"""The manager of ``entity``'s entities on this runner, closed with
|
|
338
|
+
the current scope."""
|
|
339
|
+
|
|
340
|
+
@gen
|
|
341
|
+
def run():
|
|
342
|
+
config = yield from ShardingConfig()
|
|
343
|
+
snowflakes = yield from Generator()
|
|
344
|
+
manager_scope = yield from Scope()
|
|
345
|
+
clock = yield from Clock()
|
|
346
|
+
services = yield from context()
|
|
347
|
+
policy = (
|
|
348
|
+
DEFAULT_RETRY_POLICY
|
|
349
|
+
if options.defect_retry_policy is None
|
|
350
|
+
else options.defect_retry_policy.concat(DEFAULT_RETRY_POLICY)
|
|
351
|
+
)
|
|
352
|
+
retry_driver = yield from to_step_with_sleep(policy)
|
|
353
|
+
rpcs = dict(entity.rpcs)
|
|
354
|
+
rpcs[KeepAliveRpc.tag] = KeepAliveRpc
|
|
355
|
+
core = ManagerCore(
|
|
356
|
+
entity=entity,
|
|
357
|
+
build=build,
|
|
358
|
+
rpcs=rpcs,
|
|
359
|
+
options=options,
|
|
360
|
+
config=config,
|
|
361
|
+
snowflakes=snowflakes,
|
|
362
|
+
clock=clock,
|
|
363
|
+
context=services,
|
|
364
|
+
manager_scope=manager_scope,
|
|
365
|
+
storage_enabled=options.storage is not NOOP,
|
|
366
|
+
mailbox_capacity=(
|
|
367
|
+
config.entity_mailbox_capacity
|
|
368
|
+
if options.mailbox_capacity is None
|
|
369
|
+
else options.mailbox_capacity
|
|
370
|
+
),
|
|
371
|
+
mailbox_metrics=(
|
|
372
|
+
config.entity_mailbox_metrics
|
|
373
|
+
if options.mailbox_metrics is None
|
|
374
|
+
else options.mailbox_metrics
|
|
375
|
+
),
|
|
376
|
+
defect_retry_policy=policy,
|
|
377
|
+
retry_driver=retry_driver,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
# Registered before the entities resource map, so the series is
|
|
381
|
+
# removed only after every entity has drained on shutdown.
|
|
382
|
+
def largest(_: Any) -> GaugeState[int]:
|
|
383
|
+
return GaugeState(max((len(m) for m in core.resident_mailboxes.values()), default=0))
|
|
384
|
+
|
|
385
|
+
yield from acquire_release(
|
|
386
|
+
sync(
|
|
387
|
+
lambda: register_unsafe(
|
|
388
|
+
cluster_metrics.entity_mailbox_size_max.with_attributes({"type": entity.type}),
|
|
389
|
+
services,
|
|
390
|
+
largest,
|
|
391
|
+
)
|
|
392
|
+
),
|
|
393
|
+
lambda registration, _: sync(registration.remove),
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
def lookup(address: EntityAddress) -> Effect[EntityState, Any, Scope]:
|
|
397
|
+
return make_entity(core, address).pipe(_without_log_annotations)
|
|
398
|
+
|
|
399
|
+
entities = yield from ResourceMap.make(lookup)
|
|
400
|
+
reaper = yield from EntityReaper()
|
|
401
|
+
max_idle = (
|
|
402
|
+
config.entity_max_idle_time if options.max_idle_time is None else options.max_idle_time
|
|
403
|
+
)
|
|
404
|
+
if math.isfinite(max_idle):
|
|
405
|
+
yield from reaper.register(
|
|
406
|
+
max_idle_millis=to_millis(max_idle),
|
|
407
|
+
servers=core.active_servers,
|
|
408
|
+
entities=entities,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
def count(_: Any) -> GaugeState[int]:
|
|
412
|
+
return GaugeState(len(core.active_servers))
|
|
413
|
+
|
|
414
|
+
series = register_unsafe(
|
|
415
|
+
cluster_metrics.entities.with_attributes({"type": entity.type}), services, count
|
|
416
|
+
)
|
|
417
|
+
yield from manager_scope.add_finalizer(lambda _: sync(series.remove))
|
|
418
|
+
return EntityManager(core, entities)
|
|
419
|
+
|
|
420
|
+
return run
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
__all__ = ["DEFAULT_RETRY_POLICY", "EntityManager", "make"]
|