pyfect-dev 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
pyfect/pubsub/_hub.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""The state a ``PubSub`` shares with its subscriptions.
|
|
2
|
+
|
|
3
|
+
A parked publish owns its surplus items. The hub admits them into the store
|
|
4
|
+
as space frees, oldest publish first, and resumes the publisher once all of
|
|
5
|
+
them are in (Effect's ``BackPressureStrategy``). An interrupted publisher
|
|
6
|
+
withdraws what was not admitted and has delivered the rest.
|
|
7
|
+
|
|
8
|
+
As in Effect, the takes waiting on a subscription are served in the order
|
|
9
|
+
they parked, and a message published while one waits is read for it at
|
|
10
|
+
once, so it no longer takes room in the store. It is not handed to the
|
|
11
|
+
waiting fiber: it moves to the subscription's ``claimed`` buffer and the
|
|
12
|
+
fiber is woken to collect it. A take that does not wait never reads a
|
|
13
|
+
claimed message meant for a woken one. If the woken fiber is interrupted
|
|
14
|
+
before it collects, the message passes to the next waiting take (or the
|
|
15
|
+
next take), so nothing is lost.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from collections import deque
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
|
|
24
|
+
from pyfect._core import Effect, _Succeed, _Sync # pyright: ignore[reportPrivateUsage]
|
|
25
|
+
from pyfect._waiters import WaitList
|
|
26
|
+
from pyfect.option import Option
|
|
27
|
+
from pyfect.pubsub._atomic import Atomic, BackingSubscription
|
|
28
|
+
from pyfect.pubsub._replay import ReplayWindow
|
|
29
|
+
from pyfect.queue import Strategy
|
|
30
|
+
from pyfect.scope import CloseableScope
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(eq=False, slots=True)
|
|
34
|
+
class Publish[A]:
|
|
35
|
+
"""A parked publish. ``items[admitted:]`` are still waiting."""
|
|
36
|
+
|
|
37
|
+
items: list[A]
|
|
38
|
+
resume: Callable[[Effect[bool]], None]
|
|
39
|
+
admitted: int = 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(eq=False, slots=True)
|
|
43
|
+
class Ticket:
|
|
44
|
+
"""A parked take. ``claimed`` once a message was read for it; it is
|
|
45
|
+
woken without one when the hub ends or the subscription closes."""
|
|
46
|
+
|
|
47
|
+
wake: Callable[[], None]
|
|
48
|
+
claimed: bool = False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(eq=False, slots=True)
|
|
52
|
+
class Reader[A]:
|
|
53
|
+
"""One subscription's state. A take that does not wait reads ``replay``,
|
|
54
|
+
then the spare ``claimed`` messages (those no woken take is owed), then
|
|
55
|
+
``backing``."""
|
|
56
|
+
|
|
57
|
+
backing: BackingSubscription[A]
|
|
58
|
+
replay: ReplayWindow[A]
|
|
59
|
+
waiting: deque[Ticket] = field(default_factory=deque[Ticket])
|
|
60
|
+
claimed: deque[A] = field(default_factory=deque[A])
|
|
61
|
+
# Woken tickets that have not collected their message yet.
|
|
62
|
+
owed: int = 0
|
|
63
|
+
unsubscribed: bool = False
|
|
64
|
+
# The fiber that unsubscribed; it interrupts the takes then waiting.
|
|
65
|
+
closed_by: int | None = None
|
|
66
|
+
|
|
67
|
+
def size(self) -> int:
|
|
68
|
+
"""What a take that does not wait can read now."""
|
|
69
|
+
return len(self.claimed) - self.owed + self.backing.size()
|
|
70
|
+
|
|
71
|
+
def read_up_to(self, n: int) -> list[A]:
|
|
72
|
+
"""Read up to ``n`` messages for a take that does not wait. Messages
|
|
73
|
+
owed to woken takes are not read; while takes wait there is nothing
|
|
74
|
+
else (``dispatch`` gave it to them)."""
|
|
75
|
+
if n <= 0:
|
|
76
|
+
return []
|
|
77
|
+
spare = min(n, len(self.claimed) - self.owed)
|
|
78
|
+
out = [self.claimed.popleft() for _ in range(spare)]
|
|
79
|
+
return out + self.backing.poll_up_to(n - spare)
|
|
80
|
+
|
|
81
|
+
def collect(self, ticket: Ticket) -> A:
|
|
82
|
+
"""The message read for a woken ``ticket``."""
|
|
83
|
+
ticket.claimed = False
|
|
84
|
+
self.owed -= 1
|
|
85
|
+
return self.claimed.popleft()
|
|
86
|
+
|
|
87
|
+
def release(self, ticket: Ticket) -> bool:
|
|
88
|
+
"""A take ended without collecting: its message is spare again.
|
|
89
|
+
Whether there was one."""
|
|
90
|
+
if not ticket.claimed or self.unsubscribed:
|
|
91
|
+
return False
|
|
92
|
+
ticket.claimed = False
|
|
93
|
+
self.owed -= 1
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
def discard(self, ticket: Ticket) -> None:
|
|
97
|
+
if ticket in self.waiting:
|
|
98
|
+
self.waiting.remove(ticket)
|
|
99
|
+
|
|
100
|
+
def dispatch(self) -> None:
|
|
101
|
+
"""Give each waiting take, oldest first, a message to collect."""
|
|
102
|
+
while self.waiting:
|
|
103
|
+
if len(self.claimed) == self.owed:
|
|
104
|
+
polled = self.backing.poll()
|
|
105
|
+
if polled is None:
|
|
106
|
+
return
|
|
107
|
+
self.claimed.append(polled.value)
|
|
108
|
+
ticket = self.waiting.popleft()
|
|
109
|
+
ticket.claimed = True
|
|
110
|
+
self.owed += 1
|
|
111
|
+
ticket.wake()
|
|
112
|
+
|
|
113
|
+
def wake_all(self) -> None:
|
|
114
|
+
"""Wake every waiting take without a message, to look again."""
|
|
115
|
+
tickets, self.waiting = self.waiting, deque[Ticket]()
|
|
116
|
+
for ticket in tickets:
|
|
117
|
+
ticket.wake()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Hub[A]:
|
|
121
|
+
__slots__ = (
|
|
122
|
+
"atomic",
|
|
123
|
+
"ended",
|
|
124
|
+
"is_shutdown",
|
|
125
|
+
"publishers",
|
|
126
|
+
"readers",
|
|
127
|
+
"scope",
|
|
128
|
+
"shutdown_waiters",
|
|
129
|
+
"strategy",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def __init__(self, atomic: Atomic[A], strategy: Strategy) -> None:
|
|
133
|
+
self.atomic = atomic
|
|
134
|
+
self.strategy: Strategy = strategy
|
|
135
|
+
self.publishers: deque[Publish[A]] = deque()
|
|
136
|
+
self.readers: dict[Reader[A], None] = {}
|
|
137
|
+
self.shutdown_waiters = WaitList()
|
|
138
|
+
# Subscriptions are closed with this scope.
|
|
139
|
+
self.scope = CloseableScope()
|
|
140
|
+
self.ended: Option[A] = None
|
|
141
|
+
self.is_shutdown = False
|
|
142
|
+
|
|
143
|
+
def accepts(self) -> bool:
|
|
144
|
+
"""Whether a publish can still deliver."""
|
|
145
|
+
return not self.is_shutdown and self.ended is None
|
|
146
|
+
|
|
147
|
+
def subscribe(self) -> Reader[A]:
|
|
148
|
+
reader = Reader(self.atomic.subscribe(), self.atomic.replay_window())
|
|
149
|
+
self.readers[reader] = None
|
|
150
|
+
return reader
|
|
151
|
+
|
|
152
|
+
def unsubscribe(self, reader: Reader[A], by: int) -> None:
|
|
153
|
+
if reader.unsubscribed:
|
|
154
|
+
return
|
|
155
|
+
reader.unsubscribed = True
|
|
156
|
+
reader.closed_by = by
|
|
157
|
+
del self.readers[reader]
|
|
158
|
+
if reader.owed:
|
|
159
|
+
# Effect runs a woken take before the unsubscribe goes on, so
|
|
160
|
+
# it still gets its message and, for a batch, what is left.
|
|
161
|
+
reader.claimed.extend(reader.backing.poll_up_to(reader.backing.size()))
|
|
162
|
+
else:
|
|
163
|
+
reader.claimed.clear()
|
|
164
|
+
reader.backing.unsubscribe()
|
|
165
|
+
reader.replay.close()
|
|
166
|
+
reader.wake_all()
|
|
167
|
+
self.deliver()
|
|
168
|
+
|
|
169
|
+
def deliver(self) -> None:
|
|
170
|
+
"""Read new messages for waiting takes and admit parked publishes
|
|
171
|
+
into the room that frees, until neither makes progress (Effect's
|
|
172
|
+
``completeSubscribers`` and ``onPubSubEmptySpace``); then resume the
|
|
173
|
+
publishers that are done."""
|
|
174
|
+
done: list[Publish[A]] = []
|
|
175
|
+
while True:
|
|
176
|
+
# A woken take runs at once and may subscribe or unsubscribe.
|
|
177
|
+
for reader in list(self.readers):
|
|
178
|
+
if not reader.unsubscribed:
|
|
179
|
+
reader.dispatch()
|
|
180
|
+
if not self._admit(done):
|
|
181
|
+
break
|
|
182
|
+
for entry in done:
|
|
183
|
+
entry.resume(_Succeed(True))
|
|
184
|
+
|
|
185
|
+
def wake_takers(self) -> None:
|
|
186
|
+
"""The hub ended: every waiting take looks again."""
|
|
187
|
+
for reader in list(self.readers):
|
|
188
|
+
reader.wake_all()
|
|
189
|
+
|
|
190
|
+
def _admit(self, done: list[Publish[A]]) -> bool:
|
|
191
|
+
admitted = False
|
|
192
|
+
while self.publishers and not self.atomic.is_full():
|
|
193
|
+
entry = self.publishers[0]
|
|
194
|
+
self.atomic.publish(entry.items[entry.admitted])
|
|
195
|
+
entry.admitted += 1
|
|
196
|
+
admitted = True
|
|
197
|
+
if entry.admitted == len(entry.items):
|
|
198
|
+
done.append(self.publishers.popleft())
|
|
199
|
+
return admitted
|
|
200
|
+
|
|
201
|
+
def park(self, entry: Publish[A]) -> Effect[None]:
|
|
202
|
+
"""Queue a publish's surplus; the canceler withdraws it."""
|
|
203
|
+
self.publishers.append(entry)
|
|
204
|
+
return _Sync(lambda: self._withdraw(entry))
|
|
205
|
+
|
|
206
|
+
def _withdraw(self, entry: Publish[A]) -> None:
|
|
207
|
+
if entry in self.publishers:
|
|
208
|
+
self.publishers.remove(entry)
|
|
209
|
+
|
|
210
|
+
def reject_publishers(self) -> None:
|
|
211
|
+
"""The hub ended: every parked publish returns ``False``."""
|
|
212
|
+
entries, self.publishers = self.publishers, deque[Publish[A]]()
|
|
213
|
+
for entry in entries:
|
|
214
|
+
entry.resume(_Succeed(False))
|
pyfect/pubsub/_pubsub.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""``PubSub[A]`` itself. See the package docstring for its behavior."""
|
|
2
|
+
# pyright: reportPrivateUsage=false
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable, Iterable
|
|
7
|
+
from typing import Any, Never, overload
|
|
8
|
+
|
|
9
|
+
from pyfect._core import Effect, _Callback, _Gen, _Succeed, _Suspend, _Sync, _WithFiber
|
|
10
|
+
from pyfect._fiber import Fiber
|
|
11
|
+
from pyfect._unsubscripted import Unsubscripted
|
|
12
|
+
from pyfect.cause import interrupt as interrupt_cause
|
|
13
|
+
from pyfect.exit import Failure
|
|
14
|
+
from pyfect.option import Some
|
|
15
|
+
from pyfect.pubsub._atomic import Atomic
|
|
16
|
+
from pyfect.pubsub._hub import Hub, Publish
|
|
17
|
+
from pyfect.pubsub._subscription import Subscription
|
|
18
|
+
from pyfect.queue import Strategy
|
|
19
|
+
from pyfect.scope import Scope
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PubSub[A = Unsubscripted]:
|
|
23
|
+
__slots__ = ("_hub",)
|
|
24
|
+
|
|
25
|
+
def __init__(self, atomic: Atomic[A], strategy: Strategy) -> None:
|
|
26
|
+
self._hub = Hub(atomic, strategy)
|
|
27
|
+
|
|
28
|
+
@overload
|
|
29
|
+
@classmethod
|
|
30
|
+
def make[T](
|
|
31
|
+
cls: type[PubSub[Unsubscripted]], atomic: Callable[[], Atomic[T]], strategy: Strategy
|
|
32
|
+
) -> Effect[PubSub[T]]: ...
|
|
33
|
+
@overload
|
|
34
|
+
@classmethod
|
|
35
|
+
def make(cls, atomic: Callable[[], Atomic[A]], strategy: Strategy) -> Effect[PubSub[A]]: ...
|
|
36
|
+
@classmethod
|
|
37
|
+
def make[T](cls, atomic: Callable[[], Atomic[T]], strategy: Strategy) -> Effect[PubSub[T]]:
|
|
38
|
+
"""A hub over the store ``atomic()`` builds, handling a full store by
|
|
39
|
+
``strategy`` (Effect's ``make``, with the built-in strategies)."""
|
|
40
|
+
return _Sync(lambda: PubSub(atomic(), strategy))
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def bounded(cls, capacity: int | None, *, replay: int | None = None) -> Effect[PubSub[A]]:
|
|
44
|
+
"""Publishers wait while ``capacity`` messages are unread. ``replay``
|
|
45
|
+
messages are kept for new subscribers. A capacity below 1 is a
|
|
46
|
+
defect; ``None`` is unbounded."""
|
|
47
|
+
return _Sync(lambda: cls(Atomic(capacity, replay), "suspend"))
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def dropping(cls, capacity: int | None, *, replay: int | None = None) -> Effect[PubSub[A]]:
|
|
51
|
+
"""A publish to a full hub drops the message and returns ``False``."""
|
|
52
|
+
return _Sync(lambda: cls(Atomic(capacity, replay), "dropping"))
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def sliding(cls, capacity: int | None, *, replay: int | None = None) -> Effect[PubSub[A]]:
|
|
56
|
+
"""A publish to a full hub drops the oldest unread message."""
|
|
57
|
+
return _Sync(lambda: cls(Atomic(capacity, replay), "sliding"))
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def unbounded(cls, *, replay: int | None = None) -> Effect[PubSub[A]]:
|
|
61
|
+
return _Sync(lambda: cls(Atomic(None, replay), "dropping"))
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def capacity(self) -> int | None:
|
|
65
|
+
"""How many unread messages the hub holds; ``None`` if unbounded."""
|
|
66
|
+
return self._hub.atomic.capacity
|
|
67
|
+
|
|
68
|
+
# --- publishing -------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
def publish(self, value: A) -> Effect[bool]:
|
|
71
|
+
"""Publish to every current subscriber. ``False`` if the hub ended or
|
|
72
|
+
shut down, or a dropping hub is full; a bounded hub waits for room."""
|
|
73
|
+
|
|
74
|
+
def register(resume: Callable[[Effect[bool]], None]) -> Effect[None] | None:
|
|
75
|
+
hub = self._hub
|
|
76
|
+
if self.publish_unsafe(value):
|
|
77
|
+
resume(_Succeed(True))
|
|
78
|
+
elif not hub.accepts() or hub.strategy == "dropping":
|
|
79
|
+
resume(_Succeed(False))
|
|
80
|
+
elif hub.strategy == "sliding":
|
|
81
|
+
resume(_Succeed(self._slide_in([value])))
|
|
82
|
+
else:
|
|
83
|
+
return self._park([value], resume)
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
return _Callback(register)
|
|
87
|
+
|
|
88
|
+
def publish_unsafe(self, value: A) -> bool:
|
|
89
|
+
"""Publish without waiting, sliding or dropping: ``False`` if the
|
|
90
|
+
store is full. Call on the scheduler thread of the hub's fibers."""
|
|
91
|
+
hub = self._hub
|
|
92
|
+
if not hub.accepts() or not hub.atomic.publish(value):
|
|
93
|
+
return False
|
|
94
|
+
hub.deliver()
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
def publish_all(self, items: Iterable[A]) -> Effect[bool]:
|
|
98
|
+
"""Publish ``items`` in order; ``True`` if every one was published.
|
|
99
|
+
A bounded hub publishes what fits and waits to publish the rest."""
|
|
100
|
+
|
|
101
|
+
def register(resume: Callable[[Effect[bool]], None]) -> Effect[None] | None:
|
|
102
|
+
hub = self._hub
|
|
103
|
+
if not hub.accepts():
|
|
104
|
+
resume(_Succeed(False))
|
|
105
|
+
return None
|
|
106
|
+
surplus = hub.atomic.publish_all(items)
|
|
107
|
+
hub.deliver()
|
|
108
|
+
if not surplus:
|
|
109
|
+
resume(_Succeed(True))
|
|
110
|
+
elif hub.strategy == "dropping":
|
|
111
|
+
resume(_Succeed(False))
|
|
112
|
+
elif hub.strategy == "sliding":
|
|
113
|
+
resume(_Succeed(self._slide_in(surplus)))
|
|
114
|
+
else:
|
|
115
|
+
return self._park(surplus, resume)
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
return _Callback(register)
|
|
119
|
+
|
|
120
|
+
def end(self, value: A) -> Effect[bool]:
|
|
121
|
+
"""Publish nothing more. Subscribers take what they have not read,
|
|
122
|
+
then ``value`` from every take; waiting publishes return ``False``.
|
|
123
|
+
``False`` if the hub already ended or shut down."""
|
|
124
|
+
return _Sync(lambda: self.end_unsafe(value))
|
|
125
|
+
|
|
126
|
+
def end_unsafe(self, value: A) -> bool:
|
|
127
|
+
hub = self._hub
|
|
128
|
+
if not hub.accepts():
|
|
129
|
+
return False
|
|
130
|
+
hub.ended = Some(value)
|
|
131
|
+
hub.reject_publishers()
|
|
132
|
+
hub.wake_takers()
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
# --- subscribing ------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
def subscribe(self) -> Effect[Subscription[A], Never, Scope]:
|
|
138
|
+
"""Subscribe until the current scope closes (or the hub shuts down).
|
|
139
|
+
The subscription reads the replayed messages, then everything
|
|
140
|
+
published from now on."""
|
|
141
|
+
hub = self._hub
|
|
142
|
+
|
|
143
|
+
def body():
|
|
144
|
+
local = yield from Scope()
|
|
145
|
+
scope = yield from hub.scope.fork()
|
|
146
|
+
reader = hub.subscribe()
|
|
147
|
+
yield from scope.add_finalizer(
|
|
148
|
+
lambda _: _WithFiber(lambda fiber: _Sync(lambda: hub.unsubscribe(reader, fiber.id)))
|
|
149
|
+
)
|
|
150
|
+
yield from local.add_finalizer(scope.close)
|
|
151
|
+
return Subscription[A](hub, reader)
|
|
152
|
+
|
|
153
|
+
return _Gen(body).uninterruptible()
|
|
154
|
+
|
|
155
|
+
# --- lifecycle --------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
def shutdown(self) -> Effect[None]:
|
|
158
|
+
"""Unsubscribe everyone, interrupting their takes. Later publishes
|
|
159
|
+
return ``False``. Waiting publishes complete: with nobody subscribed
|
|
160
|
+
the store empties and admits them (as in Effect, whose strategy
|
|
161
|
+
shutdown then finds no publisher left to interrupt)."""
|
|
162
|
+
|
|
163
|
+
def body(fiber: Fiber[Any, Any]) -> Effect[None]:
|
|
164
|
+
hub = self._hub
|
|
165
|
+
if hub.is_shutdown:
|
|
166
|
+
return _Succeed(None)
|
|
167
|
+
hub.is_shutdown = True
|
|
168
|
+
hub.shutdown_waiters.notify_all()
|
|
169
|
+
return hub.scope.close(Failure(interrupt_cause(fiber.id)))
|
|
170
|
+
|
|
171
|
+
return _WithFiber(body).uninterruptible()
|
|
172
|
+
|
|
173
|
+
def is_shutdown(self) -> Effect[bool]:
|
|
174
|
+
return _Sync(self.is_shutdown_unsafe)
|
|
175
|
+
|
|
176
|
+
def is_shutdown_unsafe(self) -> bool:
|
|
177
|
+
return self._hub.is_shutdown
|
|
178
|
+
|
|
179
|
+
def await_shutdown(self) -> Effect[None]:
|
|
180
|
+
"""Wait until the hub is shut down."""
|
|
181
|
+
return _Suspend(lambda: self._hub.shutdown_waiters.wait(self.is_shutdown_unsafe))
|
|
182
|
+
|
|
183
|
+
def size(self) -> Effect[int]:
|
|
184
|
+
"""How many messages are stored unread (0 once shut down)."""
|
|
185
|
+
return _Sync(self.size_unsafe)
|
|
186
|
+
|
|
187
|
+
def size_unsafe(self) -> int:
|
|
188
|
+
return 0 if self._hub.is_shutdown else self._hub.atomic.size()
|
|
189
|
+
|
|
190
|
+
def is_full(self) -> Effect[bool]:
|
|
191
|
+
return _Sync(lambda: self.size_unsafe() == self.capacity)
|
|
192
|
+
|
|
193
|
+
def is_empty(self) -> Effect[bool]:
|
|
194
|
+
return _Sync(lambda: self.size_unsafe() == 0)
|
|
195
|
+
|
|
196
|
+
# --- internals --------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
def _park(self, surplus: list[A], resume: Callable[[Effect[bool]], None]) -> Effect[None]:
|
|
199
|
+
"""Wait to publish ``surplus``. Room a waiting subscriber freed is
|
|
200
|
+
filled at once, as Effect's ``handleSurplus`` does."""
|
|
201
|
+
canceler = self._hub.park(Publish(surplus, resume))
|
|
202
|
+
self._hub.deliver()
|
|
203
|
+
return canceler
|
|
204
|
+
|
|
205
|
+
def _slide_in(self, items: list[A]) -> bool:
|
|
206
|
+
"""Publish each item after dropping the oldest stored message, as
|
|
207
|
+
Effect's ``SlidingStrategy`` does (also when a waiting subscriber
|
|
208
|
+
has just read one and made room)."""
|
|
209
|
+
atomic = self._hub.atomic
|
|
210
|
+
for item in items:
|
|
211
|
+
atomic.slide()
|
|
212
|
+
atomic.publish(item)
|
|
213
|
+
self._hub.deliver()
|
|
214
|
+
return True
|
pyfect/pubsub/_replay.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""The replay buffer of a ``PubSub`` and the window a new subscriber reads.
|
|
2
|
+
|
|
3
|
+
The buffer keeps the last ``capacity`` published messages, whether or not
|
|
4
|
+
anyone is subscribed. A subscriber starts with a ``ReplayWindow``: a
|
|
5
|
+
snapshot of those messages, read before anything published after it
|
|
6
|
+
subscribed.
|
|
7
|
+
|
|
8
|
+
While the window is not drained, the subscriber has read nothing else, so a
|
|
9
|
+
message slid out of the hub before the subscriber read it would leave a gap
|
|
10
|
+
between the window and the subscription. The buffer logs each slid message
|
|
11
|
+
with its publish index, and the window shifts forward over the ones newer
|
|
12
|
+
than its own newest message (Effect's ``ReplayWindowImpl.sync``): the
|
|
13
|
+
subscriber still reads one unbroken run of messages.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections import deque
|
|
19
|
+
|
|
20
|
+
from pyfect.option import Option, Some
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ReplayBuffer[A]:
|
|
24
|
+
__slots__ = ("_next_index", "capacity", "items", "slide_count", "slides")
|
|
25
|
+
|
|
26
|
+
def __init__(self, capacity: int) -> None:
|
|
27
|
+
self.capacity = capacity
|
|
28
|
+
self.items: deque[tuple[int, A]] = deque(maxlen=capacity)
|
|
29
|
+
self._next_index = 0
|
|
30
|
+
self.slides: deque[tuple[int, A]] = deque(maxlen=capacity)
|
|
31
|
+
self.slide_count = 0
|
|
32
|
+
|
|
33
|
+
def offer(self, value: A) -> int:
|
|
34
|
+
"""Keep ``value`` (dropping the oldest past capacity); its index."""
|
|
35
|
+
index = self._next_index
|
|
36
|
+
self._next_index += 1
|
|
37
|
+
self.items.append((index, value))
|
|
38
|
+
return index
|
|
39
|
+
|
|
40
|
+
def slide(self, value: A, index: int) -> None:
|
|
41
|
+
"""Log that the message published at ``index`` slid out of the hub."""
|
|
42
|
+
self.slides.append((index, value))
|
|
43
|
+
self.slide_count += 1
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ReplayWindow[A]:
|
|
47
|
+
"""The replayed messages a subscription reads first."""
|
|
48
|
+
|
|
49
|
+
__slots__ = ("_buffer", "_newest", "_slides_seen", "_values")
|
|
50
|
+
|
|
51
|
+
def __init__(self, buffer: ReplayBuffer[A] | None) -> None:
|
|
52
|
+
self._buffer = buffer
|
|
53
|
+
self._values: deque[A] = deque()
|
|
54
|
+
self._newest = -1
|
|
55
|
+
self._slides_seen = 0
|
|
56
|
+
if buffer is not None:
|
|
57
|
+
for index, value in buffer.items:
|
|
58
|
+
self._values.append(value)
|
|
59
|
+
self._newest = index
|
|
60
|
+
self._slides_seen = buffer.slide_count
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def remaining(self) -> int:
|
|
64
|
+
return len(self._values)
|
|
65
|
+
|
|
66
|
+
def take(self) -> Option[A]:
|
|
67
|
+
"""The next replayed message; ``None`` once drained."""
|
|
68
|
+
if not self._values:
|
|
69
|
+
return None
|
|
70
|
+
self._sync()
|
|
71
|
+
value = self._values.popleft()
|
|
72
|
+
if not self._values:
|
|
73
|
+
self.close()
|
|
74
|
+
return Some(value)
|
|
75
|
+
|
|
76
|
+
def take_n(self, n: int) -> list[A]:
|
|
77
|
+
"""Up to ``n`` replayed messages (none if ``n`` is 0 or less)."""
|
|
78
|
+
if not self._values or n <= 0:
|
|
79
|
+
return []
|
|
80
|
+
self._sync()
|
|
81
|
+
values = [self._values.popleft() for _ in range(min(n, len(self._values)))]
|
|
82
|
+
if not self._values:
|
|
83
|
+
self.close()
|
|
84
|
+
return values
|
|
85
|
+
|
|
86
|
+
def take_all(self) -> list[A]:
|
|
87
|
+
return self.take_n(len(self._values))
|
|
88
|
+
|
|
89
|
+
def close(self) -> None:
|
|
90
|
+
"""Drop what is left; a drained window keeps nothing alive."""
|
|
91
|
+
self._values.clear()
|
|
92
|
+
self._buffer = None
|
|
93
|
+
|
|
94
|
+
def _sync(self) -> None:
|
|
95
|
+
buffer = self._buffer
|
|
96
|
+
if buffer is None:
|
|
97
|
+
return
|
|
98
|
+
slides = buffer.slide_count - self._slides_seen
|
|
99
|
+
self._slides_seen = buffer.slide_count
|
|
100
|
+
if slides == 0:
|
|
101
|
+
return
|
|
102
|
+
logged = list(buffer.slides)
|
|
103
|
+
for index, value in logged[len(logged) - min(slides, len(logged)) :]:
|
|
104
|
+
if index > self._newest:
|
|
105
|
+
self._values.popleft()
|
|
106
|
+
self._values.append(value)
|
|
107
|
+
self._newest = index
|