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/queue/_queue.py
ADDED
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
"""``Queue[A, E]`` itself. See the package docstring for its behavior."""
|
|
2
|
+
# pyright: reportPrivateUsage=false
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections import deque
|
|
7
|
+
from collections.abc import Callable, Generator, Iterable
|
|
8
|
+
from typing import Any, Literal, Never, overload
|
|
9
|
+
|
|
10
|
+
from pyfect._core import (
|
|
11
|
+
Effect,
|
|
12
|
+
_Callback,
|
|
13
|
+
_FailCause,
|
|
14
|
+
_FromExit,
|
|
15
|
+
_Gen,
|
|
16
|
+
_Succeed,
|
|
17
|
+
_Suspend,
|
|
18
|
+
_Sync,
|
|
19
|
+
_WithFiber,
|
|
20
|
+
uninterruptible_mask,
|
|
21
|
+
)
|
|
22
|
+
from pyfect._effect_args import require_effect
|
|
23
|
+
from pyfect._fiber import current_fiber
|
|
24
|
+
from pyfect._waiters import WaitList
|
|
25
|
+
from pyfect.cause import Cause
|
|
26
|
+
from pyfect.cause import fail as fail_cause
|
|
27
|
+
from pyfect.cause import interrupt as interrupt_cause
|
|
28
|
+
from pyfect.errors import Done, filter_done
|
|
29
|
+
from pyfect.exit import Exit, Failure, Success
|
|
30
|
+
from pyfect.option import Option, Some
|
|
31
|
+
from pyfect.queue import _state as st
|
|
32
|
+
from pyfect.queue._views import Dequeue, Enqueue
|
|
33
|
+
from pyfect.result import Ok
|
|
34
|
+
from pyfect.scheduler import Scheduler
|
|
35
|
+
|
|
36
|
+
type Strategy = Literal["suspend", "dropping", "sliding"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Queue[A, E = Never](Enqueue[A, E], Dequeue[A, E]):
|
|
40
|
+
__slots__ = ("_capacity", "_items", "_scheduler", "_state", "_strategy")
|
|
41
|
+
|
|
42
|
+
def __init__(self, capacity: int | None = None, strategy: Strategy = "suspend") -> None:
|
|
43
|
+
self._capacity = capacity
|
|
44
|
+
# A scheduler given to the fiber making the queue also holds its
|
|
45
|
+
# wakes (Effect keeps that fiber's dispatcher); pyfect's own run
|
|
46
|
+
# schedulers do not outlive their run, so offers wake on their own.
|
|
47
|
+
fiber = current_fiber()
|
|
48
|
+
self._scheduler: Scheduler | None = (
|
|
49
|
+
None if fiber is None or fiber._scheduler is fiber._run_scheduler else fiber._scheduler
|
|
50
|
+
)
|
|
51
|
+
self._strategy: Strategy = strategy
|
|
52
|
+
self._items: deque[A] = deque()
|
|
53
|
+
self._state: st.State[A, E] = st.Open()
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def make(
|
|
57
|
+
cls, capacity: int | None = None, *, strategy: Strategy = "suspend"
|
|
58
|
+
) -> Effect[Queue[A, E]]:
|
|
59
|
+
"""A queue of ``capacity`` items (``None``: unbounded) that handles a
|
|
60
|
+
full queue by ``strategy``."""
|
|
61
|
+
return _Sync(lambda: cls(capacity, strategy))
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def unbounded(cls) -> Effect[Queue[A, E]]:
|
|
65
|
+
return _Sync(cls)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def bounded(cls, capacity: int) -> Effect[Queue[A, E]]:
|
|
69
|
+
"""``offer`` waits while the queue holds ``capacity`` items."""
|
|
70
|
+
return _Sync(lambda: cls(capacity, "suspend"))
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def dropping(cls, capacity: int) -> Effect[Queue[A, E]]:
|
|
74
|
+
return _Sync(lambda: cls(capacity, "dropping"))
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def sliding(cls, capacity: int) -> Effect[Queue[A, E]]:
|
|
78
|
+
return _Sync(lambda: cls(capacity, "sliding"))
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def capacity(self) -> int | None:
|
|
82
|
+
"""How many items the queue holds; ``None`` if unbounded."""
|
|
83
|
+
return self._capacity
|
|
84
|
+
|
|
85
|
+
# --- producing --------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
def offer(self, item: A) -> Effect[bool]:
|
|
88
|
+
"""Add an item. ``False`` if the queue is finished, or if it is full
|
|
89
|
+
and drops new items."""
|
|
90
|
+
|
|
91
|
+
def register(resume: Callable[[Effect[bool]], None]) -> Effect[None] | None:
|
|
92
|
+
if self.offer_unsafe(item):
|
|
93
|
+
resume(_Succeed(True))
|
|
94
|
+
elif isinstance(self._state, st.Open) and self._strategy == "suspend":
|
|
95
|
+
return self._park(self._state.offers, st.OfferOne(item, resume))
|
|
96
|
+
else:
|
|
97
|
+
resume(_Succeed(False))
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
return _Callback(register)
|
|
101
|
+
|
|
102
|
+
def flush(self) -> Effect[None]:
|
|
103
|
+
"""Wake the takers that can go on now, not in a scheduled task."""
|
|
104
|
+
return _Sync(self.flush_unsafe)
|
|
105
|
+
|
|
106
|
+
def flush_unsafe(self) -> None:
|
|
107
|
+
if isinstance(self._state, st.Open | st.Closing):
|
|
108
|
+
self._state.takers.notify_ready()
|
|
109
|
+
|
|
110
|
+
def offer_unsafe(self, item: A) -> bool:
|
|
111
|
+
"""``offer`` without waiting, from outside an effect: ``False`` if it
|
|
112
|
+
would have to wait. Call on the queue's fibers' scheduler thread."""
|
|
113
|
+
match self._state:
|
|
114
|
+
case st.Open(takers=takers):
|
|
115
|
+
if self._strategy == "sliding" and self._is_full():
|
|
116
|
+
# As Effect's ``offerUnsafe``: a full sliding queue swaps
|
|
117
|
+
# its oldest item for this one, even at capacity 0, and
|
|
118
|
+
# releases no taker.
|
|
119
|
+
if self._items:
|
|
120
|
+
self._items.popleft()
|
|
121
|
+
self._items.append(item)
|
|
122
|
+
return True
|
|
123
|
+
if self._free(takers) > 0:
|
|
124
|
+
# A rendezvous queue hands the item to its waiting takers
|
|
125
|
+
# at once.
|
|
126
|
+
self._push(
|
|
127
|
+
[item], takers, now=self._capacity is not None and self._capacity <= 0
|
|
128
|
+
)
|
|
129
|
+
return True
|
|
130
|
+
return False
|
|
131
|
+
case st.Closing() | st.Done():
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
def offer_all[T](self: Queue[T, Any], items: Iterable[T]) -> Effect[list[T]]:
|
|
135
|
+
"""Add items in order; succeeds with those not added. What fits is
|
|
136
|
+
added at once. A suspending queue waits to add the rest, and returns
|
|
137
|
+
what is left if the queue is finished meanwhile."""
|
|
138
|
+
|
|
139
|
+
def register(resume: Callable[[Effect[list[T]]], None]) -> Effect[None] | None:
|
|
140
|
+
rest = self.offer_all_unsafe(items)
|
|
141
|
+
if rest and isinstance(self._state, st.Open) and self._strategy == "suspend":
|
|
142
|
+
return self._park(self._state.offers, st.OfferMany(rest, resume))
|
|
143
|
+
resume(_Succeed(rest))
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
return _Callback(register)
|
|
147
|
+
|
|
148
|
+
def offer_all_unsafe[T](self: Queue[T, Any], items: Iterable[T]) -> list[T]:
|
|
149
|
+
"""``offer_all`` without waiting: the items that did not fit."""
|
|
150
|
+
batch = list(items)
|
|
151
|
+
match self._state:
|
|
152
|
+
case st.Open(takers=takers):
|
|
153
|
+
fits = len(batch) if self._strategy == "sliding" else self._free(takers)
|
|
154
|
+
self._push(batch[:fits], takers)
|
|
155
|
+
return batch[fits:]
|
|
156
|
+
case st.Closing() | st.Done():
|
|
157
|
+
return batch
|
|
158
|
+
|
|
159
|
+
def end(self: Enqueue[Any, Done]) -> Effect[bool]:
|
|
160
|
+
"""No more items. Takers fail with ``Done()`` once the queue drains.
|
|
161
|
+
Only available on queues whose error type includes ``Done``."""
|
|
162
|
+
return self.fail_cause(fail_cause(Done()))
|
|
163
|
+
|
|
164
|
+
def end_unsafe(self: Queue[Any, Done]) -> bool:
|
|
165
|
+
return self.fail_cause_unsafe(fail_cause(Done()))
|
|
166
|
+
|
|
167
|
+
def fail(self, error: E) -> Effect[bool]:
|
|
168
|
+
"""Takers fail with ``error`` once the queue drains."""
|
|
169
|
+
return self.fail_cause(fail_cause(error))
|
|
170
|
+
|
|
171
|
+
def interrupt(self) -> Effect[bool]:
|
|
172
|
+
"""Takers are interrupted (by the calling fiber) once the queue
|
|
173
|
+
drains."""
|
|
174
|
+
return _WithFiber(lambda fiber: self.fail_cause(interrupt_cause(fiber.id)))
|
|
175
|
+
|
|
176
|
+
def fail_cause(self, cause: Cause[E]) -> Effect[bool]:
|
|
177
|
+
"""``False`` if the queue was already ended, failed or shut down."""
|
|
178
|
+
return _Sync(lambda: self.fail_cause_unsafe(cause))
|
|
179
|
+
|
|
180
|
+
def fail_cause_unsafe(self, cause: Cause[E]) -> bool:
|
|
181
|
+
match self._state:
|
|
182
|
+
case st.Open(takers=takers, offers=offers, awaiters=awaiters):
|
|
183
|
+
self._state = st.Closing(takers, offers, awaiters, cause)
|
|
184
|
+
# A batch take waiting for its minimum can now drain the rest.
|
|
185
|
+
takers.notify_ready_soon(self._scheduler)
|
|
186
|
+
self._finish_if_drained()
|
|
187
|
+
return True
|
|
188
|
+
case st.Closing() | st.Done():
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
def shutdown(self) -> Effect[bool]:
|
|
192
|
+
"""Drop all items and waiting offers; takers are interrupted (or see
|
|
193
|
+
the failure of a queue that was closing). ``False`` if the queue was
|
|
194
|
+
already done."""
|
|
195
|
+
return _Sync(self.shutdown_unsafe)
|
|
196
|
+
|
|
197
|
+
def shutdown_unsafe(self) -> bool:
|
|
198
|
+
match self._state:
|
|
199
|
+
case st.Done():
|
|
200
|
+
return False
|
|
201
|
+
case st.Open(offers=offers):
|
|
202
|
+
self._shut(interrupt_cause(), offers)
|
|
203
|
+
case st.Closing(offers=offers, cause=cause):
|
|
204
|
+
self._shut(cause, offers)
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
def into[R, EE](
|
|
208
|
+
self: Queue[Any, EE | Done], eff: Effect[object, EE | Done, R]
|
|
209
|
+
) -> Effect[bool, Never, R]:
|
|
210
|
+
"""Run ``eff``, then end the queue, or fail it with ``eff``'s cause.
|
|
211
|
+
The queue is finished even if the running fiber is interrupted."""
|
|
212
|
+
require_effect(eff, "eff")
|
|
213
|
+
return uninterruptible_mask(
|
|
214
|
+
lambda restore: restore(eff).match_cause_effect(
|
|
215
|
+
on_failure=self.fail_cause, on_success=lambda _: self.end()
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# --- consuming --------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
def take(self) -> Effect[A, E]:
|
|
222
|
+
"""Wait for an item."""
|
|
223
|
+
|
|
224
|
+
def attempt() -> Effect[A, E]:
|
|
225
|
+
ex = self.take_unsafe()
|
|
226
|
+
return self._wait_then(1, attempt) if ex is None else _FromExit(ex)
|
|
227
|
+
|
|
228
|
+
return _Suspend(attempt)
|
|
229
|
+
|
|
230
|
+
def take_unsafe(self) -> Exit[A, E] | None:
|
|
231
|
+
"""An item, the failure of a finished queue, or ``None`` if ``take``
|
|
232
|
+
would wait."""
|
|
233
|
+
match self._state:
|
|
234
|
+
case st.Done(cause=cause):
|
|
235
|
+
return Failure(cause)
|
|
236
|
+
case st.Open(offers=offers) | st.Closing(offers=offers):
|
|
237
|
+
if not self._items and not self._takes_from(offers):
|
|
238
|
+
return None
|
|
239
|
+
item = self._take_one(offers)
|
|
240
|
+
self._release()
|
|
241
|
+
return Success(item)
|
|
242
|
+
|
|
243
|
+
def take_between(self, minimum: int, maximum: int) -> Effect[list[A], E]:
|
|
244
|
+
"""Wait until ``minimum`` items are there (or the queue is finishing),
|
|
245
|
+
then take up to ``maximum``. A bounded queue never holds more than
|
|
246
|
+
its capacity, so it waits for at most that many. A count of 0 or less
|
|
247
|
+
takes nothing and does not wait (but still fails on a finished
|
|
248
|
+
queue)."""
|
|
249
|
+
return self._take_between(minimum, maximum)
|
|
250
|
+
|
|
251
|
+
def take_n(self, n: int) -> Effect[list[A], E]:
|
|
252
|
+
"""``take_between(n, n)``."""
|
|
253
|
+
return self._take_between(n, n)
|
|
254
|
+
|
|
255
|
+
def take_all(self) -> Effect[list[A], E]:
|
|
256
|
+
"""Wait for at least one item, then take everything."""
|
|
257
|
+
return self._take_between(1, None)
|
|
258
|
+
|
|
259
|
+
def poll(self) -> Effect[Option[A]]:
|
|
260
|
+
"""An item if one is there now; ``None`` if not, or if the queue is
|
|
261
|
+
finished."""
|
|
262
|
+
|
|
263
|
+
def attempt() -> Effect[Option[A]]:
|
|
264
|
+
ex = self.take_unsafe()
|
|
265
|
+
return _Succeed(Some(ex.value) if isinstance(ex, Success) else None)
|
|
266
|
+
|
|
267
|
+
return _Suspend(attempt)
|
|
268
|
+
|
|
269
|
+
def peek(self) -> Effect[A, E]:
|
|
270
|
+
"""Wait for an item and succeed with it, leaving it in the queue.
|
|
271
|
+
Offers waiting on a rendezvous queue are not visible to ``peek``."""
|
|
272
|
+
|
|
273
|
+
def attempt() -> Effect[A, E]:
|
|
274
|
+
match self._state:
|
|
275
|
+
case st.Done(cause=cause):
|
|
276
|
+
return _FailCause(cause)
|
|
277
|
+
case st.Open(takers=takers) | st.Closing(takers=takers):
|
|
278
|
+
if not self._items:
|
|
279
|
+
ready = self._finished_or(lambda: bool(self._items))
|
|
280
|
+
return takers.wait(ready).and_then(_Suspend(attempt))
|
|
281
|
+
return _Succeed(self._items[0])
|
|
282
|
+
|
|
283
|
+
return _Suspend(attempt)
|
|
284
|
+
|
|
285
|
+
@overload
|
|
286
|
+
def clear[AA](self: Queue[AA, Done[Any]]) -> Effect[list[AA], Never]: ...
|
|
287
|
+
@overload
|
|
288
|
+
def clear[AA, EE](self: Queue[AA, EE | Done[Any]]) -> Effect[list[AA], EE]: ...
|
|
289
|
+
@overload
|
|
290
|
+
def clear(self) -> Effect[list[A], E]: ...
|
|
291
|
+
def clear(self: Queue[Any, Any]) -> Effect[list[Any], Any]:
|
|
292
|
+
"""Take every item there is now, without waiting. On a queue that
|
|
293
|
+
was ended, succeeds with ``[]``; on one that failed, fails with that
|
|
294
|
+
failure (without ``Done``)."""
|
|
295
|
+
|
|
296
|
+
def attempt() -> Effect[list[Any], Any]:
|
|
297
|
+
match self._state:
|
|
298
|
+
case st.Done(cause=cause):
|
|
299
|
+
return _catch_done(cause, [])
|
|
300
|
+
case st.Open(offers=offers) | st.Closing(offers=offers):
|
|
301
|
+
if self._items:
|
|
302
|
+
items = self._take_buffered(len(self._items))
|
|
303
|
+
elif self._takes_from(offers):
|
|
304
|
+
items = [st.take_offer(offers)]
|
|
305
|
+
else:
|
|
306
|
+
return _Succeed([])
|
|
307
|
+
self._release()
|
|
308
|
+
return _Succeed(items)
|
|
309
|
+
|
|
310
|
+
return _Suspend(attempt)
|
|
311
|
+
|
|
312
|
+
@overload
|
|
313
|
+
def collect[AA](self: Queue[AA, Done[Any]]) -> Effect[list[AA], Never]: ...
|
|
314
|
+
@overload
|
|
315
|
+
def collect[AA, EE](self: Queue[AA, EE | Done[Any]]) -> Effect[list[AA], EE]: ...
|
|
316
|
+
@overload
|
|
317
|
+
def collect(self) -> Effect[list[A], E]: ...
|
|
318
|
+
def collect(self: Queue[Any, Any]) -> Effect[list[Any], Any]:
|
|
319
|
+
"""Take items until the queue is ended, and succeed with all of them.
|
|
320
|
+
If the queue fails instead, fail with that failure."""
|
|
321
|
+
|
|
322
|
+
def body() -> Generator[Effect[Any, Any, Any], Any, list[Any]]:
|
|
323
|
+
out: list[Any] = []
|
|
324
|
+
while True:
|
|
325
|
+
taken: Exit[list[Any], Any] = yield self.take_all().exit()
|
|
326
|
+
match taken:
|
|
327
|
+
case Success(value=items):
|
|
328
|
+
out.extend(items)
|
|
329
|
+
case Failure(cause=cause):
|
|
330
|
+
return (yield _catch_done(cause, out))
|
|
331
|
+
|
|
332
|
+
return _Gen(body)
|
|
333
|
+
|
|
334
|
+
@overload
|
|
335
|
+
def await_done(self: Queue[Any, Done[Any]]) -> Effect[None]: ...
|
|
336
|
+
@overload
|
|
337
|
+
def await_done[EE](self: Queue[Any, EE | Done[Any]]) -> Effect[None, EE]: ...
|
|
338
|
+
@overload
|
|
339
|
+
def await_done(self) -> Effect[None, E]: ...
|
|
340
|
+
def await_done(self: Queue[Any, Any]) -> Effect[None, Any]:
|
|
341
|
+
"""Wait until the queue is finished: ended, failed or shut down, and
|
|
342
|
+
drained. Succeeds if it was ended; otherwise fails as its takers do
|
|
343
|
+
(Effect's ``Queue.await``)."""
|
|
344
|
+
|
|
345
|
+
def attempt() -> Effect[None, Any]:
|
|
346
|
+
match self._state:
|
|
347
|
+
case st.Done(cause=cause):
|
|
348
|
+
return _catch_done(cause, None)
|
|
349
|
+
case st.Open(awaiters=awaiters) | st.Closing(awaiters=awaiters):
|
|
350
|
+
return awaiters.wait(self._finished_or(lambda: False)).and_then(
|
|
351
|
+
_Suspend(attempt)
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
return _Suspend(attempt)
|
|
355
|
+
|
|
356
|
+
def size(self) -> Effect[int]:
|
|
357
|
+
"""How many items the queue holds (0 once it is finished)."""
|
|
358
|
+
return _Sync(self.size_unsafe)
|
|
359
|
+
|
|
360
|
+
def size_unsafe(self) -> int:
|
|
361
|
+
return 0 if isinstance(self._state, st.Done) else len(self._items)
|
|
362
|
+
|
|
363
|
+
def is_full(self) -> Effect[bool]:
|
|
364
|
+
"""Whether the queue holds ``capacity`` items."""
|
|
365
|
+
return _Sync(self.is_full_unsafe)
|
|
366
|
+
|
|
367
|
+
def is_full_unsafe(self) -> bool:
|
|
368
|
+
return self.size_unsafe() == self._capacity
|
|
369
|
+
|
|
370
|
+
def is_done_unsafe(self) -> bool:
|
|
371
|
+
"""Whether the queue has ended, failed or shut down and has no items
|
|
372
|
+
left (Effect's ``queue.state._tag === "Done"``)."""
|
|
373
|
+
return isinstance(self._state, st.Done)
|
|
374
|
+
|
|
375
|
+
# --- internals --------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
def _is_full(self) -> bool:
|
|
378
|
+
return self._capacity is not None and len(self._items) >= self._capacity
|
|
379
|
+
|
|
380
|
+
def _free(self, takers: WaitList) -> int:
|
|
381
|
+
"""How many items can be added now. A rendezvous queue admits one per
|
|
382
|
+
parked taker."""
|
|
383
|
+
if self._capacity is None:
|
|
384
|
+
return 2**62
|
|
385
|
+
if self._capacity <= 0:
|
|
386
|
+
return len(takers)
|
|
387
|
+
return max(0, self._capacity - len(self._items))
|
|
388
|
+
|
|
389
|
+
def _push(self, batch: list[A], takers: WaitList, *, now: bool = False) -> None:
|
|
390
|
+
"""Add ``batch``; a sliding queue then drops its oldest items down to
|
|
391
|
+
its capacity. Takers are woken in a
|
|
392
|
+
scheduled task (Effect's ``scheduleReleaseTaker``), so an offerer
|
|
393
|
+
parks what did not fit before a taker makes room; ``now`` wakes
|
|
394
|
+
them at once (Effect's rendezvous offer)."""
|
|
395
|
+
self._items.extend(batch)
|
|
396
|
+
if self._strategy == "sliding" and self._capacity is not None:
|
|
397
|
+
for _ in range(len(self._items) - max(self._capacity, 0)):
|
|
398
|
+
self._items.popleft()
|
|
399
|
+
if now:
|
|
400
|
+
takers.notify_ready()
|
|
401
|
+
else:
|
|
402
|
+
takers.notify_ready_soon(self._scheduler)
|
|
403
|
+
|
|
404
|
+
def _shut(self, cause: Cause[E], offers: deque[st.Offer[A]]) -> None:
|
|
405
|
+
self._items.clear()
|
|
406
|
+
self._finish(cause)
|
|
407
|
+
# A rejected offerer runs at once and may touch the queue again.
|
|
408
|
+
rejected = list(offers)
|
|
409
|
+
offers.clear()
|
|
410
|
+
for entry in rejected:
|
|
411
|
+
st.reject(entry)
|
|
412
|
+
|
|
413
|
+
def _park(self, offers: deque[st.Offer[A]], entry: st.Offer[A]) -> Effect[None]:
|
|
414
|
+
offers.append(entry)
|
|
415
|
+
return _Sync(lambda: self._withdraw(entry))
|
|
416
|
+
|
|
417
|
+
def _withdraw(self, entry: st.Offer[A]) -> None:
|
|
418
|
+
"""An interrupted offer gives up the items not yet admitted."""
|
|
419
|
+
match self._state:
|
|
420
|
+
case st.Open(offers=offers) | st.Closing(offers=offers):
|
|
421
|
+
if entry in offers:
|
|
422
|
+
offers.remove(entry)
|
|
423
|
+
self._finish_if_drained()
|
|
424
|
+
case st.Done():
|
|
425
|
+
pass
|
|
426
|
+
|
|
427
|
+
def _take_between(self, minimum: int, maximum: int | None) -> Effect[list[A], E]:
|
|
428
|
+
"""``maximum=None`` takes every buffered item. The items are taken in
|
|
429
|
+
the taking fiber's own step, so none is handed to a parked fiber."""
|
|
430
|
+
|
|
431
|
+
def attempt() -> Effect[list[A], E]:
|
|
432
|
+
match self._state:
|
|
433
|
+
case st.Done(cause=cause):
|
|
434
|
+
return _FailCause(cause)
|
|
435
|
+
case st.Open(offers=offers) | st.Closing(offers=offers):
|
|
436
|
+
if minimum <= 0 or (maximum is not None and maximum <= 0):
|
|
437
|
+
return _Succeed([])
|
|
438
|
+
if not self._can_take(minimum):
|
|
439
|
+
return self._wait_then(minimum, attempt)
|
|
440
|
+
if self._items:
|
|
441
|
+
items = self._take_buffered(maximum or len(self._items))
|
|
442
|
+
else:
|
|
443
|
+
items = [st.take_offer(offers)]
|
|
444
|
+
self._release()
|
|
445
|
+
return _Succeed(items)
|
|
446
|
+
|
|
447
|
+
return _Suspend(attempt)
|
|
448
|
+
|
|
449
|
+
def _wait_then[T](self, minimum: int, retry: Callable[[], Effect[T, E]]) -> Effect[T, E]:
|
|
450
|
+
"""Park until a take of ``minimum`` items can complete (or the queue
|
|
451
|
+
is done), then ``retry``."""
|
|
452
|
+
match self._state:
|
|
453
|
+
case st.Done(cause=cause):
|
|
454
|
+
return _FailCause(cause)
|
|
455
|
+
case st.Open(takers=takers) | st.Closing(takers=takers):
|
|
456
|
+
ready = self._finished_or(lambda: self._can_take(minimum))
|
|
457
|
+
return takers.wait(ready).and_then(_Suspend(retry))
|
|
458
|
+
|
|
459
|
+
def _finished_or(self, ready: Callable[[], bool]) -> Callable[[], bool]:
|
|
460
|
+
return lambda: isinstance(self._state, st.Done) or ready()
|
|
461
|
+
|
|
462
|
+
def _can_take(self, minimum: int) -> bool:
|
|
463
|
+
"""Whether a take of at least ``minimum`` items can complete now. A
|
|
464
|
+
closing queue gets no more items, so whatever is left will do."""
|
|
465
|
+
match self._state:
|
|
466
|
+
case st.Done():
|
|
467
|
+
return True
|
|
468
|
+
case st.Closing(offers=offers):
|
|
469
|
+
return bool(self._items) or self._takes_from(offers)
|
|
470
|
+
case st.Open(offers=offers):
|
|
471
|
+
limit = minimum if self._capacity is None else (self._capacity or 1)
|
|
472
|
+
return len(self._items) >= min(minimum, limit) or self._takes_from(offers)
|
|
473
|
+
|
|
474
|
+
def _takes_from(self, offers: deque[st.Offer[A]]) -> bool:
|
|
475
|
+
"""Whether a take goes straight to a waiting offer: on a rendezvous
|
|
476
|
+
queue with nothing buffered."""
|
|
477
|
+
return self._capacity is not None and self._capacity <= 0 and bool(offers)
|
|
478
|
+
|
|
479
|
+
def _take_one(self, offers: deque[st.Offer[A]]) -> A:
|
|
480
|
+
"""Buffered items come first; a rendezvous queue then takes straight
|
|
481
|
+
from the oldest waiting offer."""
|
|
482
|
+
return self._items.popleft() if self._items else st.take_offer(offers)
|
|
483
|
+
|
|
484
|
+
def _take_buffered(self, n: int) -> list[A]:
|
|
485
|
+
return [self._items.popleft() for _ in range(min(n, len(self._items)))]
|
|
486
|
+
|
|
487
|
+
def _release(self) -> None:
|
|
488
|
+
"""Admit waiting offers into the space a take freed, and finish a
|
|
489
|
+
closing queue that is now empty. No taker needs waking: offers wait
|
|
490
|
+
only on a full queue, where every taker is ready (as in Effect's
|
|
491
|
+
``releaseCapacity``)."""
|
|
492
|
+
match self._state:
|
|
493
|
+
case st.Open(offers=offers) | st.Closing(offers=offers):
|
|
494
|
+
if self._capacity is not None:
|
|
495
|
+
st.admit(offers, self._items, self._capacity)
|
|
496
|
+
self._finish_if_drained()
|
|
497
|
+
case st.Done():
|
|
498
|
+
pass
|
|
499
|
+
|
|
500
|
+
def _finish_if_drained(self) -> None:
|
|
501
|
+
state = self._state
|
|
502
|
+
if isinstance(state, st.Closing) and not self._items and not state.offers:
|
|
503
|
+
self._finish(state.cause)
|
|
504
|
+
|
|
505
|
+
def _finish(self, cause: Cause[E]) -> None:
|
|
506
|
+
match self._state:
|
|
507
|
+
case (
|
|
508
|
+
st.Open(takers=takers, awaiters=awaiters)
|
|
509
|
+
| st.Closing(takers=takers, awaiters=awaiters)
|
|
510
|
+
):
|
|
511
|
+
self._state = st.Done(cause)
|
|
512
|
+
takers.notify_all()
|
|
513
|
+
awaiters.notify_all()
|
|
514
|
+
case st.Done():
|
|
515
|
+
pass
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _catch_done[T](cause: Cause[Any], value: T) -> Effect[T, Any]:
|
|
519
|
+
"""Succeed with ``value`` if the queue was ended; otherwise fail with
|
|
520
|
+
``cause`` without its ``Done``."""
|
|
521
|
+
match filter_done(cause):
|
|
522
|
+
case Ok():
|
|
523
|
+
return _Succeed(value)
|
|
524
|
+
case rest:
|
|
525
|
+
return _FailCause(rest.error)
|
pyfect/queue/_state.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""The states of a ``Queue``, as a closed union.
|
|
2
|
+
|
|
3
|
+
``Open`` accepts offers. ``Closing(cause)`` was ended or failed but still
|
|
4
|
+
holds items or parked offers, which takers drain. ``Done(cause)`` is
|
|
5
|
+
finished: nothing is parked, so there is nowhere to drop an offer, a taker
|
|
6
|
+
or an ``await_done`` waiter. Matches over ``State`` are checked for
|
|
7
|
+
exhaustiveness by pyright.
|
|
8
|
+
|
|
9
|
+
A parked offer owns its items. The queue admits them as space frees (or
|
|
10
|
+
hands them straight to a taker when the capacity is 0) and only then
|
|
11
|
+
resumes the offerer. Items move *into* the queue, never out to a parked
|
|
12
|
+
fiber, so an interrupt can't lose one: an offerer interrupted before
|
|
13
|
+
admission withdraws its entry, and one interrupted after has delivered.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections import deque
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
|
|
22
|
+
from pyfect._core import Effect, _Succeed # pyright: ignore[reportPrivateUsage]
|
|
23
|
+
from pyfect._waiters import WaitList
|
|
24
|
+
from pyfect.cause import Cause
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(eq=False, slots=True)
|
|
28
|
+
class OfferOne[A]:
|
|
29
|
+
item: A
|
|
30
|
+
resume: Callable[[Effect[bool]], None]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(eq=False, slots=True)
|
|
34
|
+
class OfferMany[A]:
|
|
35
|
+
"""A batch offer. ``items[admitted:]`` are still waiting."""
|
|
36
|
+
|
|
37
|
+
items: list[A]
|
|
38
|
+
resume: Callable[[Effect[list[A]]], None]
|
|
39
|
+
admitted: int = 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
type Offer[A] = OfferOne[A] | OfferMany[A]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(eq=False, slots=True)
|
|
46
|
+
class Open[A]:
|
|
47
|
+
takers: WaitList = field(default_factory=WaitList)
|
|
48
|
+
offers: deque[Offer[A]] = field(default_factory=deque[Offer[A]])
|
|
49
|
+
awaiters: WaitList = field(default_factory=WaitList)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(eq=False, slots=True)
|
|
53
|
+
class Closing[A, E]:
|
|
54
|
+
takers: WaitList
|
|
55
|
+
offers: deque[Offer[A]]
|
|
56
|
+
awaiters: WaitList
|
|
57
|
+
cause: Cause[E]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True, slots=True)
|
|
61
|
+
class Done[E]:
|
|
62
|
+
cause: Cause[E]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
type State[A, E] = Open[A] | Closing[A, E] | Done[E]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def take_offer[A](offers: deque[Offer[A]]) -> A:
|
|
69
|
+
"""Take the next item straight from the oldest parked offer."""
|
|
70
|
+
match offers[0]:
|
|
71
|
+
case OfferOne(item=item, resume=resume):
|
|
72
|
+
offers.popleft()
|
|
73
|
+
resume(_Succeed(True))
|
|
74
|
+
return item
|
|
75
|
+
case OfferMany() as entry:
|
|
76
|
+
item = entry.items[entry.admitted]
|
|
77
|
+
entry.admitted += 1
|
|
78
|
+
if entry.admitted == len(entry.items):
|
|
79
|
+
offers.popleft()
|
|
80
|
+
entry.resume(_Succeed([]))
|
|
81
|
+
return item
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def admit[A](offers: deque[Offer[A]], buffer: deque[A], capacity: int) -> None:
|
|
85
|
+
"""Move parked items into ``buffer`` while it holds fewer than
|
|
86
|
+
``capacity``, oldest first, resuming each offer once all of its items
|
|
87
|
+
are in. A resumed offerer runs at once and may offer again, so the free
|
|
88
|
+
space is read again after each resume (Effect's ``releaseCapacity``)."""
|
|
89
|
+
while offers and len(buffer) < capacity:
|
|
90
|
+
match offers[0]:
|
|
91
|
+
case OfferOne(item=item, resume=resume):
|
|
92
|
+
buffer.append(item)
|
|
93
|
+
offers.popleft()
|
|
94
|
+
resume(_Succeed(True))
|
|
95
|
+
case OfferMany() as entry:
|
|
96
|
+
n = min(capacity - len(buffer), len(entry.items) - entry.admitted)
|
|
97
|
+
buffer.extend(entry.items[entry.admitted : entry.admitted + n])
|
|
98
|
+
entry.admitted += n
|
|
99
|
+
if entry.admitted < len(entry.items):
|
|
100
|
+
return
|
|
101
|
+
offers.popleft()
|
|
102
|
+
entry.resume(_Succeed([]))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def reject[A](entry: Offer[A]) -> None:
|
|
106
|
+
"""Resume a parked offer the queue will never admit."""
|
|
107
|
+
match entry:
|
|
108
|
+
case OfferOne(resume=resume):
|
|
109
|
+
resume(_Succeed(False))
|
|
110
|
+
case OfferMany(items=items, admitted=admitted, resume=resume):
|
|
111
|
+
resume(_Succeed(items[admitted:]))
|
pyfect/queue/_views.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Read-only and write-only views of a ``Queue``, for variance.
|
|
2
|
+
|
|
3
|
+
A ``Queue[A, E]`` both takes and gives ``A``s, so it is invariant. A function
|
|
4
|
+
that only offers can ask for an ``Enqueue[A, E]``, which is contravariant: a
|
|
5
|
+
``Queue[int]`` is an ``Enqueue[bool]``. A function that only takes can ask
|
|
6
|
+
for a ``Dequeue[A, E]``, which is covariant: a ``Queue[int, Done]`` is a
|
|
7
|
+
``Dequeue[float, Exception]``. Effect's ``asEnqueue`` / ``asDequeue`` are an
|
|
8
|
+
annotation; ``isinstance(x, Queue)`` is its ``isQueue``.
|
|
9
|
+
|
|
10
|
+
A ``Dequeue`` gives batches as ``Sequence`` (``Queue`` gives ``list``), and
|
|
11
|
+
its ``clear`` / ``collect`` / ``await_done`` keep ``Done`` in the error type:
|
|
12
|
+
only the invariant ``Queue`` can take ``Done`` out of ``E``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Iterable, Sequence
|
|
18
|
+
from typing import Any, Never, Protocol
|
|
19
|
+
|
|
20
|
+
from pyfect._core import Effect
|
|
21
|
+
from pyfect.cause import Cause
|
|
22
|
+
from pyfect.errors import Done
|
|
23
|
+
from pyfect.option import Option
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Enqueue[A, E](Protocol):
|
|
27
|
+
"""The producing side of a queue."""
|
|
28
|
+
|
|
29
|
+
def offer(self, item: A) -> Effect[bool]: ...
|
|
30
|
+
def offer_all[T](self: Enqueue[T, Any], items: Iterable[T]) -> Effect[list[T]]: ...
|
|
31
|
+
def fail(self, error: E) -> Effect[bool]: ...
|
|
32
|
+
def fail_cause(self, cause: Cause[E]) -> Effect[bool]: ...
|
|
33
|
+
def end(self: Enqueue[Any, Done]) -> Effect[bool]: ...
|
|
34
|
+
def interrupt(self) -> Effect[bool]: ...
|
|
35
|
+
def shutdown(self) -> Effect[bool]: ...
|
|
36
|
+
def flush(self) -> Effect[None]: ...
|
|
37
|
+
def into[R](self: Enqueue[Any, Done], eff: Effect[object, E, R]) -> Effect[bool, Never, R]: ...
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Dequeue[A, E](Protocol):
|
|
41
|
+
"""The consuming side of a queue."""
|
|
42
|
+
|
|
43
|
+
def take(self) -> Effect[A, E]: ...
|
|
44
|
+
def take_all(self) -> Effect[Sequence[A], E]: ...
|
|
45
|
+
def take_n(self, n: int) -> Effect[Sequence[A], E]: ...
|
|
46
|
+
def take_between(self, minimum: int, maximum: int) -> Effect[Sequence[A], E]: ...
|
|
47
|
+
def poll(self) -> Effect[Option[A]]: ...
|
|
48
|
+
def peek(self) -> Effect[A, E]: ...
|
|
49
|
+
def clear(self) -> Effect[Sequence[A], E]: ...
|
|
50
|
+
def collect(self) -> Effect[Sequence[A], E]: ...
|
|
51
|
+
def await_done(self) -> Effect[None, E]: ...
|
|
52
|
+
def size(self) -> Effect[int]: ...
|
|
53
|
+
def is_full(self) -> Effect[bool]: ...
|
|
54
|
+
@property
|
|
55
|
+
def capacity(self) -> int | None: ...
|