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,168 @@
|
|
|
1
|
+
"""An entity's handlers, built one RPC at a time, and the options it is
|
|
2
|
+
registered with."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable, Mapping
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Literal, Never, Protocol, overload
|
|
9
|
+
|
|
10
|
+
from pyfect._core import Effect
|
|
11
|
+
from pyfect.cluster.entity._request import Request
|
|
12
|
+
from pyfect.deferred import Deferred
|
|
13
|
+
from pyfect.duration import Duration, Seconds, to_seconds
|
|
14
|
+
from pyfect.errors import Done
|
|
15
|
+
from pyfect.queue import Dequeue
|
|
16
|
+
from pyfect.rpc._rpc import AnyRpc, Rpc, StreamRpc
|
|
17
|
+
from pyfect.rpc.middleware import RpcMiddleware
|
|
18
|
+
from pyfect.schedule import Schedule
|
|
19
|
+
from pyfect.scope import Scope
|
|
20
|
+
from pyfect.stream import Stream
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from pyfect.cluster.entity._entity import Entity
|
|
24
|
+
|
|
25
|
+
type Concurrency = int | Literal["unbounded"]
|
|
26
|
+
type EntityHandlerFn = Callable[[Request[Any]], Any]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class EntityHandlers[G, R = Never]:
|
|
30
|
+
"""The handlers of the entity ``G`` so far, needing ``R``. Immutable:
|
|
31
|
+
``handle`` gives a new one."""
|
|
32
|
+
|
|
33
|
+
__slots__ = ("_by_tag", "entity")
|
|
34
|
+
|
|
35
|
+
def __init__(self, entity: type[Entity], by_tag: Mapping[str, EntityHandlerFn]) -> None:
|
|
36
|
+
self.entity = entity
|
|
37
|
+
self._by_tag = dict(by_tag)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def by_tag(self) -> Mapping[str, EntityHandlerFn]:
|
|
41
|
+
return self._by_tag
|
|
42
|
+
|
|
43
|
+
@overload
|
|
44
|
+
def handle[P, S, E](
|
|
45
|
+
self, rpc: Rpc[P, S, E, Never], /
|
|
46
|
+
) -> Bound[G, Request[P], S, E, Never, R]: ...
|
|
47
|
+
@overload
|
|
48
|
+
def handle[P, S, E, MP, ME, MT](
|
|
49
|
+
self, rpc: Rpc[P, S, E, RpcMiddleware[MP, ME, Any], MT], /
|
|
50
|
+
) -> Bound[G, Request[P], S, E | ME, MP, R | MT]: ...
|
|
51
|
+
@overload
|
|
52
|
+
def handle[P, A, E](
|
|
53
|
+
self, rpc: StreamRpc[P, A, E, Never], /
|
|
54
|
+
) -> StreamBound[G, Request[P], A, E, Never, R]: ...
|
|
55
|
+
@overload
|
|
56
|
+
def handle[P, A, E, MP, ME, MT](
|
|
57
|
+
self, rpc: StreamRpc[P, A, E, RpcMiddleware[MP, ME, Any], MT], /
|
|
58
|
+
) -> StreamBound[G, Request[P], A, E | ME, MP, R | MT]: ...
|
|
59
|
+
def handle(self, rpc: AnyRpc, /) -> Any:
|
|
60
|
+
"""The slot of ``rpc``'s handler: call it with the handler, which
|
|
61
|
+
gets the entity ``Request`` and returns the effect to run (for a
|
|
62
|
+
stream RPC, a stream or a scoped queue)."""
|
|
63
|
+
if rpc.tag not in self.entity.rpcs:
|
|
64
|
+
raise ValueError(f"{rpc.tag!r} is not an RPC of the entity {self.entity.type}")
|
|
65
|
+
return _Slot(self, rpc.tag)
|
|
66
|
+
|
|
67
|
+
def missing(self) -> list[str]:
|
|
68
|
+
"""The tags of the entity's RPCs without a handler."""
|
|
69
|
+
return [tag for tag in self.entity.rpcs if tag not in self._by_tag]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Bound[G, X, S, E, MP, RR](Protocol):
|
|
73
|
+
"""The handler slot of a unary RPC of the entity ``G``: request ``X``,
|
|
74
|
+
success ``S``, errors ``E``; its middleware provides ``MP``; ``RR`` is
|
|
75
|
+
what the handlers need so far."""
|
|
76
|
+
|
|
77
|
+
@overload
|
|
78
|
+
def __call__(
|
|
79
|
+
self, handler: Callable[[X], Effect[S | Deferred[S, E], E, MP]], /
|
|
80
|
+
) -> EntityHandlers[G, RR]: ...
|
|
81
|
+
@overload
|
|
82
|
+
def __call__[R1](
|
|
83
|
+
self, handler: Callable[[X], Effect[S | Deferred[S, E], E, R1 | MP]], /
|
|
84
|
+
) -> EntityHandlers[G, RR | R1]: ...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class StreamBound[G, X, A, E, MP, RR](Protocol):
|
|
88
|
+
"""The handler slot of a stream RPC of the entity ``G`` (see
|
|
89
|
+
``Bound``): the handler gives a stream, or a scoped queue that ends
|
|
90
|
+
with ``Done``."""
|
|
91
|
+
|
|
92
|
+
@overload
|
|
93
|
+
def __call__(self, handler: Callable[[X], Stream[A, E, MP]], /) -> EntityHandlers[G, RR]: ...
|
|
94
|
+
@overload
|
|
95
|
+
def __call__[R1](
|
|
96
|
+
self, handler: Callable[[X], Stream[A, E, R1 | MP]], /
|
|
97
|
+
) -> EntityHandlers[G, RR | R1]: ...
|
|
98
|
+
@overload
|
|
99
|
+
def __call__(
|
|
100
|
+
self, handler: Callable[[X], Effect[Dequeue[A, E | Done], E, MP | Scope]], /
|
|
101
|
+
) -> EntityHandlers[G, RR]: ...
|
|
102
|
+
@overload
|
|
103
|
+
def __call__[R1](
|
|
104
|
+
self, handler: Callable[[X], Effect[Dequeue[A, E | Done], E, R1 | MP | Scope]], /
|
|
105
|
+
) -> EntityHandlers[G, RR | R1]: ...
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class _Slot:
|
|
109
|
+
__slots__ = ("handlers", "tag")
|
|
110
|
+
|
|
111
|
+
def __init__(self, handlers: EntityHandlers[Any, Any], tag: str) -> None:
|
|
112
|
+
self.handlers = handlers
|
|
113
|
+
self.tag = tag
|
|
114
|
+
|
|
115
|
+
def __call__(self, handler: EntityHandlerFn, /) -> EntityHandlers[Any, Any]:
|
|
116
|
+
handlers = self.handlers
|
|
117
|
+
return EntityHandlers(handlers.entity, {**handlers.by_tag, self.tag: handler})
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
121
|
+
class EntityOptions:
|
|
122
|
+
"""How an entity runs: an idle entity stops after ``max_idle_time``
|
|
123
|
+
(the sharding default when ``None``), at most ``concurrency`` requests
|
|
124
|
+
at once, ``mailbox_capacity`` requests waiting (the sharding default
|
|
125
|
+
when ``None``); a defect ends every request of the entity unless
|
|
126
|
+
``disable_fatal_defects``, and the entity restarts on the
|
|
127
|
+
``defect_retry_policy``."""
|
|
128
|
+
|
|
129
|
+
max_idle_time: Seconds | None = None
|
|
130
|
+
concurrency: Concurrency = 1
|
|
131
|
+
mailbox_capacity: Concurrency | None = None
|
|
132
|
+
mailbox_metrics: bool | None = None
|
|
133
|
+
disable_fatal_defects: bool = False
|
|
134
|
+
defect_retry_policy: Schedule[Any, Any, Any, Any] | None = None
|
|
135
|
+
span_attributes: Mapping[str, str] = field(default_factory=dict[str, str])
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def parse_options(
|
|
139
|
+
*,
|
|
140
|
+
max_idle_time: Duration | None,
|
|
141
|
+
concurrency: Concurrency,
|
|
142
|
+
mailbox_capacity: Concurrency | None,
|
|
143
|
+
mailbox_metrics: bool | None,
|
|
144
|
+
disable_fatal_defects: bool,
|
|
145
|
+
defect_retry_policy: Schedule[Any, Any, Any, Any] | None,
|
|
146
|
+
span_attributes: Mapping[str, str] | None,
|
|
147
|
+
) -> EntityOptions:
|
|
148
|
+
"""The options of ``to_layer``, checked and parsed once."""
|
|
149
|
+
return EntityOptions(
|
|
150
|
+
max_idle_time=None if max_idle_time is None else to_seconds(max_idle_time),
|
|
151
|
+
concurrency=concurrency,
|
|
152
|
+
mailbox_capacity=mailbox_capacity,
|
|
153
|
+
mailbox_metrics=mailbox_metrics,
|
|
154
|
+
disable_fatal_defects=disable_fatal_defects,
|
|
155
|
+
defect_retry_policy=defect_retry_policy,
|
|
156
|
+
span_attributes=dict(span_attributes or {}),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
__all__ = [
|
|
161
|
+
"Bound",
|
|
162
|
+
"Concurrency",
|
|
163
|
+
"EntityHandlerFn",
|
|
164
|
+
"EntityHandlers",
|
|
165
|
+
"EntityOptions",
|
|
166
|
+
"StreamBound",
|
|
167
|
+
"parse_options",
|
|
168
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Keeping an entity alive while it holds a resource (Effect's
|
|
2
|
+
``Entity.keepAlive``)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Never
|
|
7
|
+
|
|
8
|
+
from pyfect._core import Effect
|
|
9
|
+
from pyfect.cluster.cluster_schema import Persisted, Uninterruptible
|
|
10
|
+
from pyfect.cluster.entity._services import CurrentAddress, KeepAliveLatch
|
|
11
|
+
from pyfect.cluster.envelope import make_request
|
|
12
|
+
from pyfect.cluster.message import OutgoingRequest
|
|
13
|
+
from pyfect.cluster.reply import Reply
|
|
14
|
+
from pyfect.cluster.sharding import Sharding
|
|
15
|
+
from pyfect.context import Context
|
|
16
|
+
from pyfect.effect import gen, service_option, succeed
|
|
17
|
+
from pyfect.rpc import make as make_rpc
|
|
18
|
+
from pyfect.tracer import current_span
|
|
19
|
+
|
|
20
|
+
KeepAliveRpc = (
|
|
21
|
+
make_rpc("Cluster/Entity/keepAlive").annotate(Persisted, True).annotate(Uninterruptible, True)
|
|
22
|
+
)
|
|
23
|
+
"""The persisted, uninterruptible RPC an entity sends itself to stay
|
|
24
|
+
alive."""
|
|
25
|
+
|
|
26
|
+
_VOID: Effect[None] = succeed(None)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _ignore(_: Reply) -> Effect[None]:
|
|
30
|
+
return _VOID
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def keep_alive(enabled: bool) -> Effect[None, Never, Sharding | CurrentAddress]:
|
|
34
|
+
"""Keep the current entity alive (``True``) until called with
|
|
35
|
+
``False``; outside an entity that can be kept alive it does nothing."""
|
|
36
|
+
|
|
37
|
+
@gen
|
|
38
|
+
def run():
|
|
39
|
+
found = yield from service_option(KeepAliveLatch)
|
|
40
|
+
if found is None:
|
|
41
|
+
return
|
|
42
|
+
latch = found.value
|
|
43
|
+
if not enabled:
|
|
44
|
+
yield from latch.open()
|
|
45
|
+
return
|
|
46
|
+
sharding = yield from Sharding()
|
|
47
|
+
address = yield from CurrentAddress()
|
|
48
|
+
request_id = yield from sharding.get_snowflake
|
|
49
|
+
span = yield from current_span().or_die()
|
|
50
|
+
latch.close_unsafe()
|
|
51
|
+
message = OutgoingRequest(
|
|
52
|
+
annotations=KeepAliveRpc.annotations,
|
|
53
|
+
rpc=KeepAliveRpc,
|
|
54
|
+
context=Context.empty(),
|
|
55
|
+
envelope=make_request(
|
|
56
|
+
request_id=request_id,
|
|
57
|
+
address=address,
|
|
58
|
+
tag=KeepAliveRpc.tag,
|
|
59
|
+
payload=None,
|
|
60
|
+
trace_id=span.trace_id,
|
|
61
|
+
span_id=span.span_id,
|
|
62
|
+
sampled=span.sampled,
|
|
63
|
+
),
|
|
64
|
+
last_received_reply=None,
|
|
65
|
+
respond=_ignore,
|
|
66
|
+
)
|
|
67
|
+
yield from sharding.send_outgoing(message, True).or_die()
|
|
68
|
+
|
|
69
|
+
return run.with_span(
|
|
70
|
+
"Entity/keepAlive", attributes={"enabled": enabled}, capture_stack_trace=False
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
__all__ = ["KeepAliveRpc", "keep_alive"]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""An entity served from a mailbox queue (Effect's ``toLayerQueue``): its
|
|
2
|
+
behaviour takes requests from the queue and answers them with a
|
|
3
|
+
``Replier``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Never, cast
|
|
9
|
+
|
|
10
|
+
from pyfect import exit as Exit
|
|
11
|
+
from pyfect._core import Effect
|
|
12
|
+
from pyfect.cause import Cause
|
|
13
|
+
from pyfect.cluster.entity._handlers import EntityHandlers
|
|
14
|
+
from pyfect.cluster.entity._request import Request
|
|
15
|
+
from pyfect.effect import callback, from_exit, gen, sync
|
|
16
|
+
from pyfect.queue import Dequeue, Queue
|
|
17
|
+
from pyfect.scope import Scope, fork_scoped
|
|
18
|
+
from pyfect.stream import Stream, from_queue, unwrap
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from pyfect.cluster.entity._entity import Entity
|
|
22
|
+
|
|
23
|
+
type _Resume = Callable[[Effect[Any, Any]], None]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Replier:
|
|
27
|
+
"""Answers the requests a queue-based entity takes. A streamed RPC is
|
|
28
|
+
answered with a ``Stream`` or a ``Dequeue`` of its items."""
|
|
29
|
+
|
|
30
|
+
__slots__ = ("_resumes",)
|
|
31
|
+
|
|
32
|
+
def __init__(self, resumes: dict[int, tuple[Request[Any], _Resume]]) -> None:
|
|
33
|
+
self._resumes = resumes
|
|
34
|
+
|
|
35
|
+
def complete(self, request: Request[Any], exit: Exit.Exit[Any, Any]) -> Effect[None]:
|
|
36
|
+
def run() -> None:
|
|
37
|
+
found = self._resumes.pop(id(request), None)
|
|
38
|
+
if found is not None:
|
|
39
|
+
found[1](from_exit(exit))
|
|
40
|
+
|
|
41
|
+
return sync(run)
|
|
42
|
+
|
|
43
|
+
def succeed(self, request: Request[Any], value: object) -> Effect[None]:
|
|
44
|
+
return self.complete(request, Exit.succeed(value))
|
|
45
|
+
|
|
46
|
+
def fail(self, request: Request[Any], error: object) -> Effect[None]:
|
|
47
|
+
return self.complete(request, Exit.fail(error))
|
|
48
|
+
|
|
49
|
+
def fail_cause(self, request: Request[Any], cause: Cause[Any]) -> Effect[None]:
|
|
50
|
+
return self.complete(request, Exit.Failure(cause))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
type Behaviour[R] = Callable[[Dequeue[Request[Any], Never], Replier], Effect[Never, Never, R]]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def queue_handlers[R](
|
|
57
|
+
entity: type[Entity], behaviour: Behaviour[R]
|
|
58
|
+
) -> Effect[EntityHandlers[Any, Any], Never, R | Scope]:
|
|
59
|
+
"""Handlers putting each request in a mailbox, with ``behaviour``
|
|
60
|
+
forked into the current scope to answer them."""
|
|
61
|
+
|
|
62
|
+
@gen
|
|
63
|
+
def make():
|
|
64
|
+
mailbox = yield from Queue[Request[Any], Never].unbounded()
|
|
65
|
+
resumes: dict[int, tuple[Request[Any], _Resume]] = {}
|
|
66
|
+
|
|
67
|
+
def enqueue(request: Request[Any]) -> Effect[Any, Any]:
|
|
68
|
+
def register(resume: _Resume) -> None:
|
|
69
|
+
mailbox.offer_unsafe(request)
|
|
70
|
+
resumes[id(request)] = (request, resume)
|
|
71
|
+
|
|
72
|
+
return callback(register)
|
|
73
|
+
|
|
74
|
+
def stream_of(value: Any) -> Stream[Any, Any, Any]:
|
|
75
|
+
if isinstance(value, Stream):
|
|
76
|
+
return cast("Stream[Any, Any, Any]", value)
|
|
77
|
+
return from_queue(cast("Dequeue[Any, Any]", value))
|
|
78
|
+
|
|
79
|
+
def enqueue_stream(request: Request[Any]) -> Stream[Any, Any, Any]:
|
|
80
|
+
return unwrap(enqueue(request).map(stream_of))
|
|
81
|
+
|
|
82
|
+
by_tag = {
|
|
83
|
+
tag: enqueue_stream if rpc.is_stream else enqueue for tag, rpc in entity.rpcs.items()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def fail_all(cause: Cause[Any]) -> Effect[None]:
|
|
87
|
+
def run() -> None:
|
|
88
|
+
waiting = list(resumes.values())
|
|
89
|
+
resumes.clear()
|
|
90
|
+
for _, resume in waiting:
|
|
91
|
+
resume(from_exit(Exit.Failure(cause)))
|
|
92
|
+
|
|
93
|
+
return sync(run)
|
|
94
|
+
|
|
95
|
+
yield from fork_scoped(
|
|
96
|
+
behaviour(mailbox, Replier(resumes)).catch_cause(fail_all).interruptible()
|
|
97
|
+
)
|
|
98
|
+
return EntityHandlers[Any, Any](entity, by_tag)
|
|
99
|
+
|
|
100
|
+
return make
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
__all__ = ["Behaviour", "Replier", "queue_handlers"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""What an entity handler receives (Effect's ``Entity.Request``)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pyfect.cluster import envelope as envelope_
|
|
9
|
+
from pyfect.cluster.reply import Chunk
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, slots=True, kw_only=True)
|
|
13
|
+
class Request[P](envelope_.Request[P]):
|
|
14
|
+
"""A request for one of the entity's RPCs, with its decoded ``payload``
|
|
15
|
+
and the last chunk of a streamed reply already sent
|
|
16
|
+
(``last_sent_chunk``), so a restarted handler can resume its
|
|
17
|
+
sequence."""
|
|
18
|
+
|
|
19
|
+
last_sent_chunk: Chunk[Any] | None = None
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def last_sent_chunk_value(self) -> Any | None:
|
|
23
|
+
"""The last item of the last chunk sent, if a chunk was sent."""
|
|
24
|
+
chunk = self.last_sent_chunk
|
|
25
|
+
return None if chunk is None else chunk.values[-1]
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def next_sequence(self) -> int:
|
|
29
|
+
"""The sequence number of the next chunk to send."""
|
|
30
|
+
chunk = self.last_sent_chunk
|
|
31
|
+
return 0 if chunk is None else chunk.sequence + 1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__all__ = ["Request"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""The services an entity handler can read."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pyfect.cluster.entity_address import EntityAddress
|
|
6
|
+
from pyfect.cluster.runner_address import RunnerAddress
|
|
7
|
+
from pyfect.context import Tag
|
|
8
|
+
from pyfect.latch import Latch
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CurrentAddress(Tag[EntityAddress], key="effect/cluster/Entity/EntityAddress"):
|
|
12
|
+
"""The address of the entity handling the current request."""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CurrentRunnerAddress(Tag[RunnerAddress], key="effect/cluster/Entity/RunnerAddress"):
|
|
16
|
+
"""The address of the runner the entity runs on."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class KeepAliveLatch(Tag[Latch], key="effect/cluster/Entity/KeepAliveLatch"):
|
|
20
|
+
"""Closed while ``keep_alive(True)`` keeps the entity alive."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__all__ = ["CurrentAddress", "CurrentRunnerAddress", "KeepAliveLatch"]
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""In-memory clients of an entity layer (Effect's
|
|
2
|
+
``Entity.makeTestClient``): no sharding, no serialization, one server per
|
|
3
|
+
entity id."""
|
|
4
|
+
# pyright: reportPrivateUsage=false
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from dataclasses import dataclass, replace
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Never, overload
|
|
11
|
+
|
|
12
|
+
from pyfect import layer as layer_
|
|
13
|
+
from pyfect._core import Effect, _Locally
|
|
14
|
+
from pyfect._layer_core import require_layer
|
|
15
|
+
from pyfect.cluster._entity_activation import CurrentActivationScope
|
|
16
|
+
from pyfect.cluster._hash import shard_number
|
|
17
|
+
from pyfect.cluster._resource_map import ResourceMap
|
|
18
|
+
from pyfect.cluster.entity._request import Request
|
|
19
|
+
from pyfect.cluster.entity._services import CurrentAddress, CurrentRunnerAddress
|
|
20
|
+
from pyfect.cluster.entity_address import EntityAddress
|
|
21
|
+
from pyfect.cluster.entity_id import EntityId
|
|
22
|
+
from pyfect.cluster.message import Incoming, Outgoing
|
|
23
|
+
from pyfect.cluster.runner_address import RunnerAddress
|
|
24
|
+
from pyfect.cluster.shard_id import ShardId
|
|
25
|
+
from pyfect.cluster.sharding import Sharding
|
|
26
|
+
from pyfect.cluster.sharding_config import ShardingConfig
|
|
27
|
+
from pyfect.cluster.snowflake import Snowflake, make_generator
|
|
28
|
+
from pyfect.context import Context
|
|
29
|
+
from pyfect.effect import context, die, fn_untraced, gen
|
|
30
|
+
from pyfect.layer import Layer
|
|
31
|
+
from pyfect.rpc import message as rpc_message
|
|
32
|
+
from pyfect.rpc.client._core import ClientCore, ClientOptions, OnFromClient
|
|
33
|
+
from pyfect.rpc.client._core import make_no_serialization as make_client_core
|
|
34
|
+
from pyfect.rpc.message import FromServer
|
|
35
|
+
from pyfect.rpc.server._core import ServerOptions
|
|
36
|
+
from pyfect.rpc.server._core import make_no_serialization as make_server_core
|
|
37
|
+
from pyfect.scope import Scope
|
|
38
|
+
from pyfect.stream import Stream
|
|
39
|
+
from pyfect.stream import die as stream_die
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from pyfect.cluster.entity._entity import Entity
|
|
43
|
+
from pyfect.cluster.entity._handlers import Concurrency, EntityHandlers, EntityOptions
|
|
44
|
+
from pyfect.cluster.sharding_registration_event import ShardingRegistrationEvent
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True, slots=True)
|
|
48
|
+
class _Registered:
|
|
49
|
+
context: Context[Any]
|
|
50
|
+
concurrency: Concurrency
|
|
51
|
+
disable_fatal_defects: bool
|
|
52
|
+
build: Effect[Context[Any], Never, Any]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _not_in_test(name: str) -> Effect[Never]:
|
|
56
|
+
return die(RuntimeError(f"Sharding.{name} is not available to Entity.make_test_client"))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class _TestSharding:
|
|
60
|
+
"""A sharding that only records entity registrations."""
|
|
61
|
+
|
|
62
|
+
__slots__ = ("registered",)
|
|
63
|
+
|
|
64
|
+
def __init__(self) -> None:
|
|
65
|
+
self.registered: dict[str, _Registered] = {}
|
|
66
|
+
|
|
67
|
+
def register_entity(
|
|
68
|
+
self,
|
|
69
|
+
entity: type[Entity],
|
|
70
|
+
build: Effect[EntityHandlers[Any, Any], Never, Any],
|
|
71
|
+
options: EntityOptions,
|
|
72
|
+
/,
|
|
73
|
+
) -> Effect[None, Never, Any]:
|
|
74
|
+
def record(services: Context[Any]) -> None:
|
|
75
|
+
self.registered[entity.type] = _Registered(
|
|
76
|
+
services,
|
|
77
|
+
options.concurrency,
|
|
78
|
+
options.disable_fatal_defects,
|
|
79
|
+
entity.to_handlers(build),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return context().map(record)
|
|
83
|
+
|
|
84
|
+
def get_shard_id(self, entity_id: EntityId, group: str, /) -> ShardId:
|
|
85
|
+
raise RuntimeError("Sharding.get_shard_id is not available to Entity.make_test_client")
|
|
86
|
+
|
|
87
|
+
def has_shard_id(self, shard_id: ShardId, /) -> bool:
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def get_registration_events(self) -> Stream[ShardingRegistrationEvent]:
|
|
92
|
+
return stream_die(
|
|
93
|
+
RuntimeError(
|
|
94
|
+
"Sharding.get_registration_events is not available to Entity.make_test_client"
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def get_snowflake(self) -> Effect[Snowflake]:
|
|
100
|
+
return _not_in_test("get_snowflake")
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def is_shutdown(self) -> Effect[bool]:
|
|
104
|
+
return _not_in_test("is_shutdown")
|
|
105
|
+
|
|
106
|
+
def make_client[T: Entity](self, entity: type[T], /) -> Effect[Callable[[str], T]]:
|
|
107
|
+
return _not_in_test("make_client")
|
|
108
|
+
|
|
109
|
+
def register_singleton(
|
|
110
|
+
self, name: str, run: Effect[None, Any, Any], shard_group: str | None, /
|
|
111
|
+
) -> Effect[None, Never, Any]:
|
|
112
|
+
return _not_in_test("register_singleton")
|
|
113
|
+
|
|
114
|
+
def send(self, message: Incoming, /) -> Effect[None, Any]:
|
|
115
|
+
return _not_in_test("send")
|
|
116
|
+
|
|
117
|
+
def send_outgoing(self, message: Outgoing, discard: bool, /) -> Effect[None, Any]:
|
|
118
|
+
return _not_in_test("send_outgoing")
|
|
119
|
+
|
|
120
|
+
def notify(self, message: Incoming, /, *, wait_until_read: bool = False) -> Effect[None, Any]:
|
|
121
|
+
return _not_in_test("notify")
|
|
122
|
+
|
|
123
|
+
def reset(
|
|
124
|
+
self, request_id: Snowflake, /, *, expected_reply_id: Snowflake | None = None
|
|
125
|
+
) -> Effect[bool]:
|
|
126
|
+
return _not_in_test("reset")
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def poll_storage(self) -> Effect[None]:
|
|
130
|
+
return _not_in_test("poll_storage")
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def active_entity_count(self) -> Effect[int]:
|
|
134
|
+
return _not_in_test("active_entity_count")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
_RUNNER_ADDRESS = RunnerAddress(host="localhost", port=3000)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@overload
|
|
141
|
+
def make_test_client[T: Entity, E](
|
|
142
|
+
entity: type[T], layer: Layer[Any, E, Sharding], /
|
|
143
|
+
) -> Effect[Callable[[str], Effect[T]], E, Scope | ShardingConfig]: ...
|
|
144
|
+
@overload
|
|
145
|
+
def make_test_client[T: Entity, E, R](
|
|
146
|
+
entity: type[T], layer: Layer[Any, E, R | Sharding], /
|
|
147
|
+
) -> Effect[Callable[[str], Effect[T]], E, Scope | ShardingConfig | R]: ...
|
|
148
|
+
def make_test_client(entity: type[Entity], layer: Layer[Any, Any, Any], /) -> Effect[Any, Any, Any]:
|
|
149
|
+
"""Build ``layer`` against a sharding that records the entity's
|
|
150
|
+
registration, and give a function making the client of an entity id:
|
|
151
|
+
each id gets its own entity, started on first use and stopped when the
|
|
152
|
+
current scope closes."""
|
|
153
|
+
require_layer(layer, "layer")
|
|
154
|
+
|
|
155
|
+
@gen
|
|
156
|
+
def make():
|
|
157
|
+
config = yield from ShardingConfig()
|
|
158
|
+
snowflakes = yield from make_generator()
|
|
159
|
+
sharding = _TestSharding()
|
|
160
|
+
yield from layer.pipe(layer_.provide(layer_.succeed(Sharding, sharding))).build()
|
|
161
|
+
entry = sharding.registered.get(entity.type)
|
|
162
|
+
if entry is None:
|
|
163
|
+
yield from die(
|
|
164
|
+
RuntimeError(f"Entity.make_test_client: {entity.type} was not registered by layer")
|
|
165
|
+
)
|
|
166
|
+
return None
|
|
167
|
+
registered = entry
|
|
168
|
+
|
|
169
|
+
@fn_untraced
|
|
170
|
+
def lookup(entity_id: str):
|
|
171
|
+
id = EntityId(entity_id)
|
|
172
|
+
address = EntityAddress(
|
|
173
|
+
entity_type=entity.type,
|
|
174
|
+
entity_id=id,
|
|
175
|
+
shard_id=ShardId(
|
|
176
|
+
group=entity.get_shard_group(id),
|
|
177
|
+
id=shard_number(entity_id, config.shards_per_group),
|
|
178
|
+
),
|
|
179
|
+
)
|
|
180
|
+
scope = yield from Scope()
|
|
181
|
+
handler_context = (
|
|
182
|
+
registered.context.add(CurrentRunnerAddress, _RUNNER_ADDRESS)
|
|
183
|
+
.add(CurrentAddress, address)
|
|
184
|
+
.add(CurrentActivationScope, scope)
|
|
185
|
+
.add(Scope, scope)
|
|
186
|
+
)
|
|
187
|
+
handlers = yield from _Locally(registered.build, lambda _: handler_context)
|
|
188
|
+
cores: list[ClientCore] = []
|
|
189
|
+
|
|
190
|
+
def on_from_server(response: FromServer) -> Effect[None]:
|
|
191
|
+
return cores[0].write(response)
|
|
192
|
+
|
|
193
|
+
server = yield from _Locally(
|
|
194
|
+
make_server_core(
|
|
195
|
+
entity.protocol,
|
|
196
|
+
on_from_server,
|
|
197
|
+
ServerOptions(
|
|
198
|
+
concurrency=registered.concurrency,
|
|
199
|
+
disable_fatal_defects=registered.disable_fatal_defects,
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
lambda _: handler_context.merge(handlers),
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def on_from_client(sent: OnFromClient) -> Effect[None, Any]:
|
|
206
|
+
message = sent.message
|
|
207
|
+
if isinstance(message, rpc_message.Request):
|
|
208
|
+
request = Request[Any](
|
|
209
|
+
request_id=Snowflake(int(message.id)),
|
|
210
|
+
address=address,
|
|
211
|
+
tag=message.tag,
|
|
212
|
+
payload=message.payload,
|
|
213
|
+
headers=message.headers,
|
|
214
|
+
trace_id=message.trace_id,
|
|
215
|
+
span_id=message.span_id,
|
|
216
|
+
sampled=message.sampled,
|
|
217
|
+
)
|
|
218
|
+
return server.write(0, replace(message, payload=request))
|
|
219
|
+
return server.write(0, message)
|
|
220
|
+
|
|
221
|
+
_, core = yield from make_client_core(
|
|
222
|
+
entity.protocol,
|
|
223
|
+
on_from_client,
|
|
224
|
+
ClientOptions(supports_ack=True, generate_request_id=snowflakes.next_unsafe),
|
|
225
|
+
)
|
|
226
|
+
cores.append(core)
|
|
227
|
+
client = entity()
|
|
228
|
+
client._rpc_methods = core.methods(entity.protocol)
|
|
229
|
+
return client
|
|
230
|
+
|
|
231
|
+
clients = yield from ResourceMap.make(lookup)
|
|
232
|
+
return clients.get
|
|
233
|
+
|
|
234
|
+
return make
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
__all__ = ["make_test_client"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""``EntityAddress``: an entity's type, id and shard (Effect's
|
|
2
|
+
``EntityAddress``)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from pyfect import schema as S
|
|
7
|
+
from pyfect.cluster.entity_id import EntityId, EntityIdSchema
|
|
8
|
+
from pyfect.cluster.entity_type import EntityType, EntityTypeSchema
|
|
9
|
+
from pyfect.cluster.shard_id import ShardId
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EntityAddress(S.Class, identifier="~effect/cluster/EntityAddress"):
|
|
13
|
+
shard_id: ShardId = S.field(ShardId, key="shardId")
|
|
14
|
+
entity_type: EntityType = S.field(EntityTypeSchema, key="entityType")
|
|
15
|
+
entity_id: EntityId = S.field(EntityIdSchema, key="entityId")
|
|
16
|
+
|
|
17
|
+
def __str__(self) -> str:
|
|
18
|
+
return f"EntityAddress({self.entity_type}, {self.entity_id}, {self.shard_id})"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def make(*, shard_id: ShardId, entity_type: EntityType, entity_id: EntityId) -> EntityAddress:
|
|
22
|
+
return EntityAddress(shard_id=shard_id, entity_type=entity_type, entity_id=entity_id)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ["EntityAddress", "make"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""``EntityId``: the id of one entity of a type (Effect's ``EntityId``)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import NewType
|
|
6
|
+
|
|
7
|
+
from pyfect import schema as S
|
|
8
|
+
from pyfect.brand import Brand
|
|
9
|
+
|
|
10
|
+
EntityId = NewType("EntityId", str)
|
|
11
|
+
|
|
12
|
+
EntityIdSchema = S.String.pipe(S.from_brand(Brand[str, EntityId].nominal()))
|
|
13
|
+
"""Entity ids (Effect's ``EntityId`` schema)."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def make(id: str) -> EntityId:
|
|
17
|
+
return EntityId(id)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
__all__ = ["EntityId", "EntityIdSchema", "make"]
|