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/process/_pipes.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""The parent's ends of a child's pipes, as byte streams and sinks.
|
|
2
|
+
|
|
3
|
+
A ``Reader`` has a thread that reads only while a fiber waits for data, so
|
|
4
|
+
an unread pipe fills up and the child waits for it, as a Node stream's
|
|
5
|
+
backpressure makes it. Chunks it has read wait in the reader until taken,
|
|
6
|
+
so an interrupted pull loses nothing. A ``Writer`` has a thread, started
|
|
7
|
+
by its first write, that writes in short waits on a non-blocking
|
|
8
|
+
descriptor, so an interrupted write stops within one of those waits. Each
|
|
9
|
+
pipe has threads of its own, never a shared pool, so a pipe waiting on its
|
|
10
|
+
child never holds up another pipe. Both close their descriptor themselves,
|
|
11
|
+
once no thread is using it.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import errno
|
|
17
|
+
import os
|
|
18
|
+
import select
|
|
19
|
+
import threading
|
|
20
|
+
from collections import deque
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
from typing import Never
|
|
23
|
+
|
|
24
|
+
from pyfect import sink as sink_
|
|
25
|
+
from pyfect import stream as stream_
|
|
26
|
+
from pyfect._core import Effect
|
|
27
|
+
from pyfect._os_wait import Cancel, Resume, os_wait
|
|
28
|
+
from pyfect.effect import fail, succeed, suspend, sync
|
|
29
|
+
from pyfect.errors import Done
|
|
30
|
+
from pyfect.platform_error import PlatformError
|
|
31
|
+
from pyfect.sink import Sink
|
|
32
|
+
from pyfect.stream import Stream
|
|
33
|
+
|
|
34
|
+
CHUNK_SIZE = 64 * 1024
|
|
35
|
+
_POLL = 0.05
|
|
36
|
+
"""Seconds a pipe thread waits for its descriptor before checking whether
|
|
37
|
+
it was closed."""
|
|
38
|
+
|
|
39
|
+
type OnError = Callable[[OSError], PlatformError]
|
|
40
|
+
type _Pull = Effect[tuple[bytes, ...], PlatformError | Done]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _ready(fd: int, *, write: bool) -> bool:
|
|
44
|
+
if os.name == "nt":
|
|
45
|
+
return True
|
|
46
|
+
ready = select.select([], [fd], [], _POLL) if write else select.select([fd], [], [], _POLL)
|
|
47
|
+
return bool(ready[0] or ready[1])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Reader:
|
|
51
|
+
"""Reads the pipe ``fd`` on demand; ``stream`` is its bytes."""
|
|
52
|
+
|
|
53
|
+
__slots__ = (
|
|
54
|
+
"_chunks",
|
|
55
|
+
"_closing",
|
|
56
|
+
"_cond",
|
|
57
|
+
"_ended",
|
|
58
|
+
"_error",
|
|
59
|
+
"_fd",
|
|
60
|
+
"_on_error",
|
|
61
|
+
"_waiters",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def __init__(self, fd: int, on_error: OnError) -> None:
|
|
65
|
+
self._fd = fd
|
|
66
|
+
self._on_error = on_error
|
|
67
|
+
self._cond = threading.Condition()
|
|
68
|
+
self._chunks: deque[bytes] = deque()
|
|
69
|
+
self._ended = False
|
|
70
|
+
self._closing = False
|
|
71
|
+
self._error: OSError | None = None
|
|
72
|
+
self._waiters: list[Resume[tuple[bytes, ...], PlatformError | Done]] = []
|
|
73
|
+
threading.Thread(target=self._run, name="pyfect-process-reader", daemon=True).start()
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def stream(self) -> Stream[bytes, PlatformError]:
|
|
77
|
+
return stream_.from_pull(succeed(self._pull()))
|
|
78
|
+
|
|
79
|
+
def close(self) -> None:
|
|
80
|
+
"""Stop reading once what the pipe holds now is read (all a child
|
|
81
|
+
that has exited wrote); then pulls end the stream."""
|
|
82
|
+
with self._cond:
|
|
83
|
+
self._closing = True
|
|
84
|
+
self._cond.notify()
|
|
85
|
+
|
|
86
|
+
def _pull(self) -> _Pull:
|
|
87
|
+
def register(resume: Resume[tuple[bytes, ...], PlatformError | Done]) -> Cancel | None:
|
|
88
|
+
with self._cond:
|
|
89
|
+
taken = self._take()
|
|
90
|
+
if taken is None:
|
|
91
|
+
self._waiters.append(resume)
|
|
92
|
+
self._cond.notify()
|
|
93
|
+
if taken is not None:
|
|
94
|
+
resume(taken)
|
|
95
|
+
return None
|
|
96
|
+
return lambda: self._forget(resume)
|
|
97
|
+
|
|
98
|
+
return os_wait(register, waits_for="event")
|
|
99
|
+
|
|
100
|
+
def _take(self) -> _Pull | None:
|
|
101
|
+
if self._chunks:
|
|
102
|
+
return succeed((self._chunks.popleft(),))
|
|
103
|
+
if self._error is not None:
|
|
104
|
+
return fail(self._on_error(self._error))
|
|
105
|
+
if self._ended:
|
|
106
|
+
return fail(Done())
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
def _forget(self, resume: Resume[tuple[bytes, ...], PlatformError | Done]) -> None:
|
|
110
|
+
with self._cond:
|
|
111
|
+
if resume in self._waiters:
|
|
112
|
+
self._waiters.remove(resume)
|
|
113
|
+
|
|
114
|
+
def _wake(self, waiters: list[Resume[tuple[bytes, ...], PlatformError | Done]]) -> None:
|
|
115
|
+
# Woken pulls run again rather than being handed a chunk: one that
|
|
116
|
+
# was interrupted meanwhile takes nothing.
|
|
117
|
+
for resume in waiters:
|
|
118
|
+
resume(suspend(self._pull))
|
|
119
|
+
|
|
120
|
+
def _wanted(self) -> bool:
|
|
121
|
+
return bool(self._waiters) and not self._chunks
|
|
122
|
+
|
|
123
|
+
def _run(self) -> None:
|
|
124
|
+
try:
|
|
125
|
+
while self._read_once():
|
|
126
|
+
pass
|
|
127
|
+
finally:
|
|
128
|
+
os.close(self._fd)
|
|
129
|
+
|
|
130
|
+
def _read_once(self) -> bool:
|
|
131
|
+
with self._cond:
|
|
132
|
+
while not self._ended and not self._closing and not self._wanted():
|
|
133
|
+
self._cond.wait()
|
|
134
|
+
if self._ended:
|
|
135
|
+
return False
|
|
136
|
+
closing = self._closing
|
|
137
|
+
if closing:
|
|
138
|
+
self._end(*self._drain())
|
|
139
|
+
return False
|
|
140
|
+
data = b""
|
|
141
|
+
error: OSError | None = None
|
|
142
|
+
try:
|
|
143
|
+
if not _ready(self._fd, write=False):
|
|
144
|
+
return True
|
|
145
|
+
data = os.read(self._fd, CHUNK_SIZE)
|
|
146
|
+
except OSError as e:
|
|
147
|
+
error = e
|
|
148
|
+
if not data:
|
|
149
|
+
self._end([], error)
|
|
150
|
+
return False
|
|
151
|
+
with self._cond:
|
|
152
|
+
self._chunks.append(data)
|
|
153
|
+
waiters, self._waiters = self._waiters, []
|
|
154
|
+
self._wake(waiters)
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
def _drain(self) -> tuple[list[bytes], OSError | None]:
|
|
158
|
+
"""What the pipe holds now, without waiting for more."""
|
|
159
|
+
chunks: list[bytes] = []
|
|
160
|
+
if os.name == "nt":
|
|
161
|
+
return chunks, None
|
|
162
|
+
try:
|
|
163
|
+
while select.select([self._fd], [], [], 0)[0]:
|
|
164
|
+
data = os.read(self._fd, CHUNK_SIZE)
|
|
165
|
+
if not data:
|
|
166
|
+
break
|
|
167
|
+
chunks.append(data)
|
|
168
|
+
except OSError as error:
|
|
169
|
+
return chunks, error
|
|
170
|
+
return chunks, None
|
|
171
|
+
|
|
172
|
+
def _end(self, chunks: list[bytes], error: OSError | None) -> None:
|
|
173
|
+
with self._cond:
|
|
174
|
+
self._chunks.extend(chunks)
|
|
175
|
+
self._error = error
|
|
176
|
+
self._ended = True
|
|
177
|
+
waiters, self._waiters = self._waiters, []
|
|
178
|
+
self._wake(waiters)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class _Write:
|
|
182
|
+
"""One write waiting for, or held by, a writer's thread."""
|
|
183
|
+
|
|
184
|
+
__slots__ = ("cancelled", "resume", "view")
|
|
185
|
+
|
|
186
|
+
def __init__(self, data: bytes, resume: Resume[None, PlatformError]) -> None:
|
|
187
|
+
self.view = memoryview(data)
|
|
188
|
+
self.resume = resume
|
|
189
|
+
self.cancelled = False
|
|
190
|
+
|
|
191
|
+
def cancel(self) -> None:
|
|
192
|
+
"""Skip the rest of the write: its thread stops at its next wait or
|
|
193
|
+
never starts it."""
|
|
194
|
+
self.cancelled = True
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class Writer:
|
|
198
|
+
"""Writes to the pipe ``fd``; ``sink`` writes its input."""
|
|
199
|
+
|
|
200
|
+
__slots__ = ("_closed", "_cond", "_fd", "_on_error", "_queue", "_started")
|
|
201
|
+
|
|
202
|
+
def __init__(self, fd: int, on_error: OnError) -> None:
|
|
203
|
+
self._fd = fd
|
|
204
|
+
self._on_error = on_error
|
|
205
|
+
self._cond = threading.Condition()
|
|
206
|
+
self._queue: deque[_Write] = deque()
|
|
207
|
+
self._closed = False
|
|
208
|
+
self._started = False
|
|
209
|
+
if os.name != "nt":
|
|
210
|
+
os.set_blocking(fd, False)
|
|
211
|
+
|
|
212
|
+
def sink(self, *, end_on_done: bool) -> Sink[None, bytes, Never, PlatformError]:
|
|
213
|
+
"""Write each chunk; with ``end_on_done``, close the pipe however the
|
|
214
|
+
input ends."""
|
|
215
|
+
write: Sink[None, bytes, Never, PlatformError] = sink_.for_each(self.write)
|
|
216
|
+
return write.ensuring(sync(self.close)) if end_on_done else write
|
|
217
|
+
|
|
218
|
+
def write(self, data: bytes) -> Effect[None, PlatformError]:
|
|
219
|
+
def register(resume: Resume[None, PlatformError]) -> Cancel | None:
|
|
220
|
+
request = _Write(data, resume)
|
|
221
|
+
with self._cond:
|
|
222
|
+
closed = self._closed
|
|
223
|
+
if not closed:
|
|
224
|
+
self._queue.append(request)
|
|
225
|
+
self._start()
|
|
226
|
+
self._cond.notify()
|
|
227
|
+
if closed:
|
|
228
|
+
resume(fail(self._on_error(BrokenPipeError(errno.EPIPE, "write after end"))))
|
|
229
|
+
return None
|
|
230
|
+
return request.cancel
|
|
231
|
+
|
|
232
|
+
return os_wait(register, waits_for="work")
|
|
233
|
+
|
|
234
|
+
def close(self) -> None:
|
|
235
|
+
"""End the pipe: a write in progress stops, queued ones fail."""
|
|
236
|
+
with self._cond:
|
|
237
|
+
if self._closed:
|
|
238
|
+
return
|
|
239
|
+
self._closed = True
|
|
240
|
+
started = self._started
|
|
241
|
+
self._cond.notify()
|
|
242
|
+
if not started:
|
|
243
|
+
os.close(self._fd)
|
|
244
|
+
|
|
245
|
+
def _start(self) -> None:
|
|
246
|
+
if not self._started:
|
|
247
|
+
self._started = True
|
|
248
|
+
threading.Thread(target=self._run, name="pyfect-process-writer", daemon=True).start()
|
|
249
|
+
|
|
250
|
+
def _cancel(self, request: _Write) -> None:
|
|
251
|
+
with self._cond:
|
|
252
|
+
request.cancelled = True
|
|
253
|
+
# The request in progress stops itself; a queued one leaves.
|
|
254
|
+
if request in self._queue and request is not self._queue[0]:
|
|
255
|
+
self._queue.remove(request)
|
|
256
|
+
|
|
257
|
+
def _run(self) -> None:
|
|
258
|
+
try:
|
|
259
|
+
while (request := self._next()) is not None:
|
|
260
|
+
self._serve(request)
|
|
261
|
+
finally:
|
|
262
|
+
os.close(self._fd)
|
|
263
|
+
|
|
264
|
+
def _next(self) -> _Write | None:
|
|
265
|
+
with self._cond:
|
|
266
|
+
while not self._queue and not self._closed:
|
|
267
|
+
self._cond.wait()
|
|
268
|
+
if not self._closed:
|
|
269
|
+
return self._queue[0]
|
|
270
|
+
queued, self._queue = list(self._queue), deque()
|
|
271
|
+
for request in queued:
|
|
272
|
+
request.resume(fail(self._on_error(BrokenPipeError(errno.EPIPE, "write after end"))))
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
def _serve(self, request: _Write) -> None:
|
|
276
|
+
"""Write ``request`` in short waits, until done, cancelled or
|
|
277
|
+
closed."""
|
|
278
|
+
result: Effect[None, PlatformError] = succeed(None)
|
|
279
|
+
try:
|
|
280
|
+
while request.view and not request.cancelled and not self._closed:
|
|
281
|
+
if not _ready(self._fd, write=True):
|
|
282
|
+
continue
|
|
283
|
+
try:
|
|
284
|
+
request.view = request.view[os.write(self._fd, request.view) :]
|
|
285
|
+
except BlockingIOError:
|
|
286
|
+
continue
|
|
287
|
+
except OSError as error:
|
|
288
|
+
result = fail(self._on_error(error))
|
|
289
|
+
with self._cond:
|
|
290
|
+
self._queue.popleft()
|
|
291
|
+
request.resume(result)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""The service that starts child processes (Effect's
|
|
2
|
+
``ChildProcessSpawner``)."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
from pyfect._core import Effect
|
|
10
|
+
from pyfect.context import Tag
|
|
11
|
+
from pyfect.effect import never
|
|
12
|
+
from pyfect.platform_error import PlatformError
|
|
13
|
+
from pyfect.process._command import Command
|
|
14
|
+
from pyfect.process._handle import ChildProcessHandle, ExitCode
|
|
15
|
+
from pyfect.scope import Scope, scoped
|
|
16
|
+
from pyfect.stream import Stream
|
|
17
|
+
from pyfect.stream import unwrap as unwrap_stream
|
|
18
|
+
|
|
19
|
+
type Spawn = Callable[[Command], Effect[ChildProcessHandle, PlatformError, Scope]]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class ChildProcessSpawnerService:
|
|
24
|
+
"""Starts commands; the constructor is Effect's
|
|
25
|
+
``ChildProcessSpawner.make``: given ``spawn``, the rest is built on it.
|
|
26
|
+
|
|
27
|
+
The helpers own the handles they spawn, so they read and drop the output
|
|
28
|
+
they do not return, keeping a full pipe from stopping the child."""
|
|
29
|
+
|
|
30
|
+
spawn: Spawn
|
|
31
|
+
|
|
32
|
+
def exit_code(self, command: Command) -> Effect[ExitCode, PlatformError]:
|
|
33
|
+
"""Run ``command`` and give back its exit code, reading and dropping
|
|
34
|
+
its output meanwhile. Returns once the process exits, even if
|
|
35
|
+
descendants keep its pipes open."""
|
|
36
|
+
|
|
37
|
+
def wait(handle: ChildProcessHandle) -> Effect[ExitCode, PlatformError]:
|
|
38
|
+
return handle.exit_code.race_first(handle.all.run_drain().flat_map(lambda _: never))
|
|
39
|
+
|
|
40
|
+
return scoped(self.spawn(command).flat_map(wait))
|
|
41
|
+
|
|
42
|
+
def stream_string(
|
|
43
|
+
self, command: Command, *, include_stderr: bool = False
|
|
44
|
+
) -> Stream[str, PlatformError]:
|
|
45
|
+
"""The standard output of ``command`` as text; with
|
|
46
|
+
``include_stderr``, interleaved with its standard error, else that
|
|
47
|
+
is read and dropped until standard output ends."""
|
|
48
|
+
|
|
49
|
+
def text(handle: ChildProcessHandle) -> Stream[str, PlatformError]:
|
|
50
|
+
output = handle.all if include_stderr else handle.stdout.drain_fork(handle.stderr)
|
|
51
|
+
return output.decode_text()
|
|
52
|
+
|
|
53
|
+
return unwrap_stream(self.spawn(command).map(text))
|
|
54
|
+
|
|
55
|
+
def stream_lines(
|
|
56
|
+
self, command: Command, *, include_stderr: bool = False
|
|
57
|
+
) -> Stream[str, PlatformError]:
|
|
58
|
+
"""``stream_string`` split into lines."""
|
|
59
|
+
return self.stream_string(command, include_stderr=include_stderr).split_lines()
|
|
60
|
+
|
|
61
|
+
def lines(
|
|
62
|
+
self, command: Command, *, include_stderr: bool = False
|
|
63
|
+
) -> Effect[list[str], PlatformError]:
|
|
64
|
+
"""Run ``command`` and collect the lines of its output."""
|
|
65
|
+
return self.stream_lines(command, include_stderr=include_stderr).run_collect()
|
|
66
|
+
|
|
67
|
+
def string(
|
|
68
|
+
self, command: Command, *, include_stderr: bool = False
|
|
69
|
+
) -> Effect[str, PlatformError]:
|
|
70
|
+
"""Run ``command`` and give back its output."""
|
|
71
|
+
return self.stream_string(command, include_stderr=include_stderr).run_string()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ChildProcessSpawner(Tag[ChildProcessSpawnerService]):
|
|
75
|
+
"""The service commands run with."""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""``PubSub[A]``: a hub that delivers every published message to every
|
|
2
|
+
subscriber.
|
|
3
|
+
|
|
4
|
+
A subscriber reads the messages published after it subscribed (plus up to
|
|
5
|
+
``replay`` earlier ones), in order, through a ``Subscription`` that lasts
|
|
6
|
+
until its scope closes. A message is stored once and dropped when every
|
|
7
|
+
subscriber that owed it has read it. What happens when ``capacity``
|
|
8
|
+
messages are unread is fixed at creation:
|
|
9
|
+
|
|
10
|
+
- ``bounded``: publishers wait until the slowest subscriber reads.
|
|
11
|
+
- ``dropping``: the new message is dropped and the publish returns ``False``.
|
|
12
|
+
- ``sliding``: the oldest unread message is dropped for everyone.
|
|
13
|
+
- ``unbounded``: the hub is never full.
|
|
14
|
+
|
|
15
|
+
A message published while nobody is subscribed goes only to the replay
|
|
16
|
+
buffer. ``end(value)`` stops publishing: subscribers read what is left, and
|
|
17
|
+
then every ``take`` succeeds with ``value``. ``shutdown()`` unsubscribes
|
|
18
|
+
everyone, interrupting their takes; later publishes return ``False``.
|
|
19
|
+
|
|
20
|
+
Takes waiting on one subscription are served in the order they started.
|
|
21
|
+
|
|
22
|
+
``Atomic`` is the store under a hub, with no waiting, for building a hub
|
|
23
|
+
with ``PubSub.make``. The ``unsafe_*`` methods act at once, outside an
|
|
24
|
+
effect; call them on the scheduler thread of the hub's fibers.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from pyfect.pubsub._atomic import Atomic, BackingSubscription
|
|
28
|
+
from pyfect.pubsub._pubsub import PubSub
|
|
29
|
+
from pyfect.pubsub._replay import ReplayWindow
|
|
30
|
+
from pyfect.pubsub._subscription import Subscription
|
|
31
|
+
from pyfect.queue import Strategy
|
|
32
|
+
|
|
33
|
+
__all__ = ["Atomic", "BackingSubscription", "PubSub", "ReplayWindow", "Strategy", "Subscription"]
|
pyfect/pubsub/_atomic.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""``Atomic``: the message store under a ``PubSub``, with no waiting.
|
|
2
|
+
|
|
3
|
+
Each published message is stored once with a count of the subscribers that
|
|
4
|
+
still have to read it, and dropped when the count reaches 0. A message
|
|
5
|
+
published while nobody is subscribed is not stored (only replayed). A
|
|
6
|
+
subscriber reads messages published after it subscribed, in order.
|
|
7
|
+
|
|
8
|
+
Counts reach 0 oldest first: every subscriber still owing a message also
|
|
9
|
+
owes every later one. So the store is a deque whose head is the oldest
|
|
10
|
+
message anyone still has to read, and ``slide`` drops that head for
|
|
11
|
+
everyone.
|
|
12
|
+
|
|
13
|
+
Effect's bounded variants (single slot, power of two, arbitrary ring) and
|
|
14
|
+
its unbounded linked list behave alike; one deque with an optional capacity
|
|
15
|
+
does for all of them.
|
|
16
|
+
"""
|
|
17
|
+
# pyright: reportPrivateUsage=false
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from collections import deque
|
|
22
|
+
from collections.abc import Iterable
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
|
|
25
|
+
from pyfect.option import Option, Some
|
|
26
|
+
from pyfect.pubsub._replay import ReplayBuffer, ReplayWindow
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(eq=False, slots=True)
|
|
30
|
+
class _Slot[A]:
|
|
31
|
+
value: A
|
|
32
|
+
unread: int
|
|
33
|
+
replay_index: int
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Atomic[A]:
|
|
37
|
+
"""A store of messages for a set of subscribers. Not thread-safe: use it
|
|
38
|
+
from one scheduler thread, as a ``PubSub`` does."""
|
|
39
|
+
|
|
40
|
+
__slots__ = ("_capacity", "_replay", "_slots", "_start", "_subscribers")
|
|
41
|
+
|
|
42
|
+
def __init__(self, capacity: int | None, replay: int | None = None) -> None:
|
|
43
|
+
if capacity is not None and capacity <= 0:
|
|
44
|
+
raise ValueError(f"Cannot construct PubSub with capacity of {capacity}")
|
|
45
|
+
self._capacity = capacity
|
|
46
|
+
self._replay = ReplayBuffer[A](replay) if replay is not None and replay > 0 else None
|
|
47
|
+
self._slots: deque[_Slot[A]] = deque()
|
|
48
|
+
# The publish index of the oldest stored message.
|
|
49
|
+
self._start = 0
|
|
50
|
+
self._subscribers = 0
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def bounded(cls, capacity: int | None, *, replay: int | None = None) -> Atomic[A]:
|
|
54
|
+
"""A store of at most ``capacity`` messages (``None``: unbounded),
|
|
55
|
+
replaying the last ``replay`` to new subscribers (Effect's
|
|
56
|
+
``makeAtomicBounded``). A capacity below 1 is a ``ValueError``."""
|
|
57
|
+
return cls(capacity, replay)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def unbounded(cls, *, replay: int | None = None) -> Atomic[A]:
|
|
61
|
+
"""Effect's ``makeAtomicUnbounded``."""
|
|
62
|
+
return cls(None, replay)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def capacity(self) -> int | None:
|
|
66
|
+
return self._capacity
|
|
67
|
+
|
|
68
|
+
def size(self) -> int:
|
|
69
|
+
"""How many messages are stored (each once, however many readers)."""
|
|
70
|
+
return len(self._slots)
|
|
71
|
+
|
|
72
|
+
def is_empty(self) -> bool:
|
|
73
|
+
return not self._slots
|
|
74
|
+
|
|
75
|
+
def is_full(self) -> bool:
|
|
76
|
+
return len(self._slots) == self._capacity
|
|
77
|
+
|
|
78
|
+
def publish(self, value: A) -> bool:
|
|
79
|
+
"""Store ``value`` for the current subscribers; ``False`` if full."""
|
|
80
|
+
if self.is_full():
|
|
81
|
+
return False
|
|
82
|
+
replay_index = 0 if self._replay is None else self._replay.offer(value)
|
|
83
|
+
if self._subscribers:
|
|
84
|
+
self._slots.append(_Slot(value, self._subscribers, replay_index))
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
def publish_all(self, items: Iterable[A]) -> list[A]:
|
|
88
|
+
"""Store what fits, in order; the items that did not."""
|
|
89
|
+
if not self._subscribers:
|
|
90
|
+
if self._replay is not None:
|
|
91
|
+
for item in items:
|
|
92
|
+
self._replay.offer(item)
|
|
93
|
+
return []
|
|
94
|
+
batch = list(items)
|
|
95
|
+
fits = len(batch) if self._capacity is None else self._capacity - len(self._slots)
|
|
96
|
+
for item in batch[:fits]:
|
|
97
|
+
self.publish(item)
|
|
98
|
+
return batch[fits:]
|
|
99
|
+
|
|
100
|
+
def slide(self) -> None:
|
|
101
|
+
"""Drop the oldest stored message for every subscriber."""
|
|
102
|
+
if self._slots:
|
|
103
|
+
slot = self._slots.popleft()
|
|
104
|
+
self._start += 1
|
|
105
|
+
if self._replay is not None:
|
|
106
|
+
self._replay.slide(slot.value, slot.replay_index)
|
|
107
|
+
|
|
108
|
+
def subscribe(self) -> BackingSubscription[A]:
|
|
109
|
+
"""A reader of the messages published from now on."""
|
|
110
|
+
self._subscribers += 1
|
|
111
|
+
return BackingSubscription(self)
|
|
112
|
+
|
|
113
|
+
def replay_window(self) -> ReplayWindow[A]:
|
|
114
|
+
"""The messages a subscriber that joins now replays."""
|
|
115
|
+
return ReplayWindow(self._replay)
|
|
116
|
+
|
|
117
|
+
# --- for BackingSubscription ------------------------------------------
|
|
118
|
+
|
|
119
|
+
def _end(self) -> int:
|
|
120
|
+
"""The publish index the next stored message gets."""
|
|
121
|
+
return self._start + len(self._slots)
|
|
122
|
+
|
|
123
|
+
def _read(self, index: int) -> A:
|
|
124
|
+
"""Read the message at publish ``index`` for one subscriber."""
|
|
125
|
+
slot = self._slots[index - self._start]
|
|
126
|
+
slot.unread -= 1
|
|
127
|
+
if slot.unread == 0:
|
|
128
|
+
self._slots.popleft()
|
|
129
|
+
self._start += 1
|
|
130
|
+
return slot.value
|
|
131
|
+
|
|
132
|
+
def _leave(self, index: int) -> None:
|
|
133
|
+
"""A subscriber at ``index`` leaves without reading the rest."""
|
|
134
|
+
self._subscribers -= 1
|
|
135
|
+
for i in range(max(index, self._start), self._end()):
|
|
136
|
+
self._read(i)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class BackingSubscription[A]:
|
|
140
|
+
"""One subscriber's position in an ``Atomic``."""
|
|
141
|
+
|
|
142
|
+
__slots__ = ("_atomic", "_index", "_unsubscribed")
|
|
143
|
+
|
|
144
|
+
def __init__(self, atomic: Atomic[A]) -> None:
|
|
145
|
+
self._atomic = atomic
|
|
146
|
+
self._index = atomic._end()
|
|
147
|
+
self._unsubscribed = False
|
|
148
|
+
|
|
149
|
+
def size(self) -> int:
|
|
150
|
+
"""How many messages are there to read."""
|
|
151
|
+
if self._unsubscribed:
|
|
152
|
+
return 0
|
|
153
|
+
atomic = self._atomic
|
|
154
|
+
return atomic._end() - max(self._index, atomic._start)
|
|
155
|
+
|
|
156
|
+
def is_empty(self) -> bool:
|
|
157
|
+
return self.size() == 0
|
|
158
|
+
|
|
159
|
+
def poll(self) -> Option[A]:
|
|
160
|
+
"""The next message, if there is one."""
|
|
161
|
+
items = self.poll_up_to(1)
|
|
162
|
+
return Some(items[0]) if items else None
|
|
163
|
+
|
|
164
|
+
def poll_up_to(self, n: int) -> list[A]:
|
|
165
|
+
"""Up to ``n`` messages (none if ``n`` is 0 or less)."""
|
|
166
|
+
count = min(n, self.size())
|
|
167
|
+
if count <= 0:
|
|
168
|
+
return []
|
|
169
|
+
atomic = self._atomic
|
|
170
|
+
start = max(self._index, atomic._start)
|
|
171
|
+
self._index = start + count
|
|
172
|
+
return [atomic._read(i) for i in range(start, start + count)]
|
|
173
|
+
|
|
174
|
+
def poll_all(self) -> list[A]:
|
|
175
|
+
return self.poll_up_to(self.size())
|
|
176
|
+
|
|
177
|
+
def unsubscribe(self) -> None:
|
|
178
|
+
"""Stop reading; messages only this subscriber owed are dropped."""
|
|
179
|
+
if not self._unsubscribed:
|
|
180
|
+
self._unsubscribed = True
|
|
181
|
+
self._atomic._leave(self._index)
|