pyfect-dev 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"""``TxQueue[A, E]`` itself. See the package docstring for its behavior.
|
|
2
|
+
|
|
3
|
+
Every operation is one transactional step: it reads the state and items
|
|
4
|
+
from the transaction's journal, writes what changes, and returns its
|
|
5
|
+
outcome (a value, the queue's failure, or ``tx_retry`` to wait).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Iterable
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any, Literal, Never, overload
|
|
13
|
+
|
|
14
|
+
from pyfect import _tx_ops
|
|
15
|
+
from pyfect._core import Effect
|
|
16
|
+
from pyfect.cause import Cause
|
|
17
|
+
from pyfect.cause import fail as fail_cause
|
|
18
|
+
from pyfect.cause import interrupt as interrupt_cause
|
|
19
|
+
from pyfect.effect import fail_cause as fail_with
|
|
20
|
+
from pyfect.effect import fiber_id, succeed, sync
|
|
21
|
+
from pyfect.errors import Done
|
|
22
|
+
from pyfect.option import Option, Some
|
|
23
|
+
from pyfect.pull import is_done_cause
|
|
24
|
+
from pyfect.transaction import Transaction, TransactionMeta, tx, tx_retry
|
|
25
|
+
from pyfect.tx_queue._views import TxDequeue, TxEnqueue
|
|
26
|
+
from pyfect.tx_ref import TxRef
|
|
27
|
+
|
|
28
|
+
type Strategy = Literal["bounded", "unbounded", "dropping", "sliding"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True, slots=True)
|
|
32
|
+
class _Open:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True, slots=True)
|
|
37
|
+
class _Closing[E]:
|
|
38
|
+
"""Failed or ended with items left: takers drain them first."""
|
|
39
|
+
|
|
40
|
+
cause: Cause[E]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True, slots=True)
|
|
44
|
+
class _Done[E]:
|
|
45
|
+
cause: Cause[E]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
type _State[E] = _Open | _Closing[E] | _Done[E]
|
|
49
|
+
|
|
50
|
+
_OPEN = _Open()
|
|
51
|
+
|
|
52
|
+
type _Step[T, E] = Effect[T, E, Transaction]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TxQueue[A, E = Never](TxEnqueue[A, E], TxDequeue[A, E]):
|
|
56
|
+
"""Build one with ``TxQueue[int].bounded(8)`` (or ``unbounded``,
|
|
57
|
+
``dropping``, ``sliding``); ``TxQueue[int, Done]`` can be ended."""
|
|
58
|
+
|
|
59
|
+
__slots__ = ("_capacity", "_items", "_state", "_strategy")
|
|
60
|
+
|
|
61
|
+
def __init__(self, strategy: Strategy, capacity: int | None) -> None:
|
|
62
|
+
self._strategy: Strategy = strategy
|
|
63
|
+
self._capacity = capacity
|
|
64
|
+
self._items: TxRef[tuple[A, ...]] = TxRef(())
|
|
65
|
+
self._state: TxRef[_State[E]] = TxRef(_OPEN)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def bounded(cls, capacity: int) -> Effect[TxQueue[A, E]]:
|
|
69
|
+
"""``offer`` waits while the queue holds ``capacity`` items."""
|
|
70
|
+
return cls._make("bounded", capacity)
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def unbounded(cls) -> Effect[TxQueue[A, E]]:
|
|
74
|
+
return cls._make("unbounded", None)
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def dropping(cls, capacity: int) -> Effect[TxQueue[A, E]]:
|
|
78
|
+
"""``offer`` on a full queue drops the new item."""
|
|
79
|
+
return cls._make("dropping", capacity)
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def sliding(cls, capacity: int) -> Effect[TxQueue[A, E]]:
|
|
83
|
+
"""``offer`` on a full queue drops the oldest item."""
|
|
84
|
+
return cls._make("sliding", capacity)
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def _make(cls, strategy: Strategy, capacity: int | None) -> Effect[TxQueue[A, E]]:
|
|
88
|
+
return sync(lambda: cls(strategy, capacity))
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def strategy(self) -> Strategy:
|
|
92
|
+
return self._strategy
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def capacity(self) -> int | None:
|
|
96
|
+
"""How many items the queue holds; ``None`` if unbounded."""
|
|
97
|
+
return self._capacity
|
|
98
|
+
|
|
99
|
+
# --- producing --------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
def offer(self, item: A) -> Effect[bool]:
|
|
102
|
+
"""Add an item. ``False`` if the queue is closing or done, or full and
|
|
103
|
+
dropping. A full bounded queue waits; a full sliding one drops its
|
|
104
|
+
oldest item."""
|
|
105
|
+
|
|
106
|
+
def decide(meta: TransactionMeta) -> _Step[bool, Never]:
|
|
107
|
+
accepted = self._offer(meta, item)
|
|
108
|
+
return tx_retry if accepted is None else succeed(accepted)
|
|
109
|
+
|
|
110
|
+
return _tx_ops.decide(decide)
|
|
111
|
+
|
|
112
|
+
def offer_all[T](self: TxQueue[T, Any], items: Iterable[T]) -> Effect[list[T]]:
|
|
113
|
+
"""Offer each item in one transaction; succeeds with those not added.
|
|
114
|
+
On a bounded queue, waits until all of them fit. ``items`` is read
|
|
115
|
+
once, here, so a one-shot iterator survives transaction retries."""
|
|
116
|
+
batch = list(items)
|
|
117
|
+
|
|
118
|
+
def decide(meta: TransactionMeta) -> _Step[list[T], Never]:
|
|
119
|
+
rejected: list[T] = []
|
|
120
|
+
for item in batch:
|
|
121
|
+
accepted = self._offer(meta, item)
|
|
122
|
+
if accepted is None:
|
|
123
|
+
return tx_retry
|
|
124
|
+
if not accepted:
|
|
125
|
+
rejected.append(item)
|
|
126
|
+
return succeed(rejected)
|
|
127
|
+
|
|
128
|
+
return _tx_ops.decide(decide)
|
|
129
|
+
|
|
130
|
+
def fail(self, error: E) -> Effect[bool]:
|
|
131
|
+
"""Drop the items and finish the queue with ``error`` at once: takers
|
|
132
|
+
fail with it. ``False`` if the queue was already closing or done."""
|
|
133
|
+
|
|
134
|
+
def decide(meta: TransactionMeta) -> _Step[bool, Never]:
|
|
135
|
+
if not isinstance(_tx_ops.value(meta, self._state), _Open):
|
|
136
|
+
return succeed(False)
|
|
137
|
+
_tx_ops.write(meta, self._items, ())
|
|
138
|
+
_tx_ops.write(meta, self._state, _Done(fail_cause(error)))
|
|
139
|
+
return succeed(True)
|
|
140
|
+
|
|
141
|
+
return _tx_ops.decide(decide)
|
|
142
|
+
|
|
143
|
+
def fail_cause(self, cause: Cause[E]) -> Effect[bool]:
|
|
144
|
+
"""Finish the queue with ``cause`` once its items are taken. ``False``
|
|
145
|
+
if the queue was already closing or done."""
|
|
146
|
+
|
|
147
|
+
def decide(meta: TransactionMeta) -> _Step[bool, Never]:
|
|
148
|
+
if not isinstance(_tx_ops.value(meta, self._state), _Open):
|
|
149
|
+
return succeed(False)
|
|
150
|
+
items = _tx_ops.value(meta, self._items)
|
|
151
|
+
_tx_ops.write(meta, self._state, _Closing(cause) if items else _Done(cause))
|
|
152
|
+
return succeed(True)
|
|
153
|
+
|
|
154
|
+
return _tx_ops.decide(decide)
|
|
155
|
+
|
|
156
|
+
def end(self: TxEnqueue[Any, Done]) -> Effect[bool]:
|
|
157
|
+
"""No more items: takers fail with ``Done()`` once the queue drains.
|
|
158
|
+
Only on queues whose error type is ``Done``."""
|
|
159
|
+
return self.fail_cause(fail_cause(Done()))
|
|
160
|
+
|
|
161
|
+
def interrupt(self) -> Effect[bool]:
|
|
162
|
+
"""Takers are interrupted (by the calling fiber) once the queue
|
|
163
|
+
drains."""
|
|
164
|
+
return fiber_id().flat_map(lambda fid: self.fail_cause(interrupt_cause(fid)))
|
|
165
|
+
|
|
166
|
+
def shutdown(self) -> Effect[bool]:
|
|
167
|
+
"""Drop the items and interrupt the queue; ``False`` if it was
|
|
168
|
+
already closing or done."""
|
|
169
|
+
return tx(self.clear().ignore_cause().and_then(self.interrupt()))
|
|
170
|
+
|
|
171
|
+
# --- consuming --------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
def take(self) -> Effect[A, E]:
|
|
174
|
+
"""Wait for an item. Once the queue is done, fails with its cause."""
|
|
175
|
+
|
|
176
|
+
def decide(meta: TransactionMeta) -> _Step[A, E]:
|
|
177
|
+
state = _tx_ops.value(meta, self._state)
|
|
178
|
+
if isinstance(state, _Done):
|
|
179
|
+
return fail_with(state.cause)
|
|
180
|
+
items = _tx_ops.value(meta, self._items)
|
|
181
|
+
if not items:
|
|
182
|
+
return tx_retry
|
|
183
|
+
self._remove(meta, state, items, 1)
|
|
184
|
+
return succeed(items[0])
|
|
185
|
+
|
|
186
|
+
return _tx_ops.decide(decide)
|
|
187
|
+
|
|
188
|
+
def poll(self) -> Effect[Option[A]]:
|
|
189
|
+
"""An item if one is there now; ``None`` if not, or if the queue is
|
|
190
|
+
done."""
|
|
191
|
+
|
|
192
|
+
def decide(meta: TransactionMeta) -> _Step[Option[A], Never]:
|
|
193
|
+
state = _tx_ops.value(meta, self._state)
|
|
194
|
+
items = _tx_ops.value(meta, self._items)
|
|
195
|
+
if isinstance(state, _Done) or not items:
|
|
196
|
+
return succeed(None)
|
|
197
|
+
self._remove(meta, state, items, 1)
|
|
198
|
+
return succeed(Some(items[0]))
|
|
199
|
+
|
|
200
|
+
return _tx_ops.decide(decide)
|
|
201
|
+
|
|
202
|
+
def peek(self) -> Effect[A, E]:
|
|
203
|
+
"""Wait for an item and succeed with it, leaving it in the queue."""
|
|
204
|
+
|
|
205
|
+
def decide(meta: TransactionMeta) -> _Step[A, E]:
|
|
206
|
+
state = _tx_ops.value(meta, self._state)
|
|
207
|
+
if isinstance(state, _Done):
|
|
208
|
+
return fail_with(state.cause)
|
|
209
|
+
items = _tx_ops.value(meta, self._items)
|
|
210
|
+
return succeed(items[0]) if items else tx_retry
|
|
211
|
+
|
|
212
|
+
return _tx_ops.decide(decide)
|
|
213
|
+
|
|
214
|
+
def take_all(self) -> Effect[list[A], E]:
|
|
215
|
+
"""Wait for at least one item, then take them all."""
|
|
216
|
+
|
|
217
|
+
def decide(meta: TransactionMeta) -> _Step[list[A], E]:
|
|
218
|
+
state = _tx_ops.value(meta, self._state)
|
|
219
|
+
if isinstance(state, _Done):
|
|
220
|
+
return fail_with(state.cause)
|
|
221
|
+
items = _tx_ops.value(meta, self._items)
|
|
222
|
+
if not items:
|
|
223
|
+
return tx_retry
|
|
224
|
+
self._remove(meta, state, items, len(items))
|
|
225
|
+
return succeed(list(items))
|
|
226
|
+
|
|
227
|
+
return _tx_ops.decide(decide)
|
|
228
|
+
|
|
229
|
+
def take_n(self, n: int) -> Effect[list[A], E]:
|
|
230
|
+
"""Wait for ``n`` items and take them. A bounded queue never holds
|
|
231
|
+
more than its capacity, so asking for more takes that many; a closing
|
|
232
|
+
queue gives what is left. A count of 0 or less takes nothing."""
|
|
233
|
+
wanted = max(n, 0)
|
|
234
|
+
minimum = wanted if self._capacity is None else min(wanted, self._capacity)
|
|
235
|
+
return self._take_at_least(minimum, minimum)
|
|
236
|
+
|
|
237
|
+
def take_between(self, minimum: int, maximum: int) -> Effect[list[A], E]:
|
|
238
|
+
"""Wait for ``minimum`` items, then take up to ``maximum``; a closing
|
|
239
|
+
queue gives what is left. Takes nothing if either count is 0 or less,
|
|
240
|
+
or ``minimum > maximum``."""
|
|
241
|
+
if minimum <= 0 or maximum <= 0 or minimum > maximum:
|
|
242
|
+
return self._take_at_least(0, 0)
|
|
243
|
+
return self._take_at_least(minimum, maximum)
|
|
244
|
+
|
|
245
|
+
@overload
|
|
246
|
+
def clear[AA](self: TxQueue[AA, Done[Any]]) -> Effect[list[AA], Never]: ...
|
|
247
|
+
@overload
|
|
248
|
+
def clear[AA, EE](self: TxQueue[AA, EE | Done[Any]]) -> Effect[list[AA], EE]: ...
|
|
249
|
+
@overload
|
|
250
|
+
def clear(self) -> Effect[list[A], E]: ...
|
|
251
|
+
def clear(self: TxQueue[Any, Any]) -> Effect[list[Any], Any]:
|
|
252
|
+
"""Take every item there is now, without waiting. On a queue that is
|
|
253
|
+
done: ``[]`` if it was ended, otherwise fails with its cause."""
|
|
254
|
+
|
|
255
|
+
def decide(meta: TransactionMeta) -> _Step[list[Any], Any]:
|
|
256
|
+
state = _tx_ops.value(meta, self._state)
|
|
257
|
+
if isinstance(state, _Done):
|
|
258
|
+
return succeed([]) if is_done_cause(state.cause) else fail_with(state.cause)
|
|
259
|
+
items = _tx_ops.value(meta, self._items)
|
|
260
|
+
_tx_ops.write(meta, self._items, ())
|
|
261
|
+
if isinstance(state, _Closing):
|
|
262
|
+
_tx_ops.write(meta, self._state, _Done(state.cause))
|
|
263
|
+
return succeed(list(items))
|
|
264
|
+
|
|
265
|
+
return _tx_ops.decide(decide)
|
|
266
|
+
|
|
267
|
+
# --- state ------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
def size(self) -> Effect[int]:
|
|
270
|
+
return _tx_ops.read(self._items, len)
|
|
271
|
+
|
|
272
|
+
def is_empty(self) -> Effect[bool]:
|
|
273
|
+
return _tx_ops.read(self._items, lambda items: not items)
|
|
274
|
+
|
|
275
|
+
def is_non_empty(self) -> Effect[bool]:
|
|
276
|
+
return _tx_ops.read(self._items, bool)
|
|
277
|
+
|
|
278
|
+
def is_full(self) -> Effect[bool]:
|
|
279
|
+
capacity = self._capacity
|
|
280
|
+
if capacity is None:
|
|
281
|
+
return succeed(False)
|
|
282
|
+
return _tx_ops.read(self._items, lambda items: len(items) >= capacity)
|
|
283
|
+
|
|
284
|
+
def is_open(self) -> Effect[bool]:
|
|
285
|
+
return self._state_is(_Open)
|
|
286
|
+
|
|
287
|
+
def is_closing(self) -> Effect[bool]:
|
|
288
|
+
return self._state_is(_Closing)
|
|
289
|
+
|
|
290
|
+
def is_done(self) -> Effect[bool]:
|
|
291
|
+
return self._state_is(_Done)
|
|
292
|
+
|
|
293
|
+
def is_shutdown(self) -> Effect[bool]:
|
|
294
|
+
"""``is_done``."""
|
|
295
|
+
return self.is_done()
|
|
296
|
+
|
|
297
|
+
def await_completion(self) -> Effect[None]:
|
|
298
|
+
"""Wait until the queue is done, however it finished."""
|
|
299
|
+
|
|
300
|
+
def decide(meta: TransactionMeta) -> _Step[None, Never]:
|
|
301
|
+
done = isinstance(_tx_ops.value(meta, self._state), _Done)
|
|
302
|
+
return succeed(None) if done else tx_retry
|
|
303
|
+
|
|
304
|
+
return _tx_ops.decide(decide)
|
|
305
|
+
|
|
306
|
+
# --- helpers ----------------------------------------------------------
|
|
307
|
+
|
|
308
|
+
def _state_is(self, kind: type[_State[Any]]) -> Effect[bool]:
|
|
309
|
+
return _tx_ops.read(self._state, lambda state: isinstance(state, kind))
|
|
310
|
+
|
|
311
|
+
def _offer(self, meta: TransactionMeta, item: A) -> bool | None:
|
|
312
|
+
"""Whether ``item`` was added; ``None`` if the offer has to wait."""
|
|
313
|
+
if not isinstance(_tx_ops.value(meta, self._state), _Open):
|
|
314
|
+
return False
|
|
315
|
+
items = _tx_ops.value(meta, self._items)
|
|
316
|
+
if self._capacity is None or len(items) < self._capacity:
|
|
317
|
+
_tx_ops.write(meta, self._items, (*items, item))
|
|
318
|
+
return True
|
|
319
|
+
match self._strategy:
|
|
320
|
+
case "dropping":
|
|
321
|
+
return False
|
|
322
|
+
case "sliding":
|
|
323
|
+
_tx_ops.write(meta, self._items, (*items[1:], item))
|
|
324
|
+
return True
|
|
325
|
+
case "bounded" | "unbounded":
|
|
326
|
+
return None
|
|
327
|
+
|
|
328
|
+
def _remove(
|
|
329
|
+
self, meta: TransactionMeta, state: _State[E], items: tuple[A, ...], n: int
|
|
330
|
+
) -> None:
|
|
331
|
+
"""Drop the first ``n`` items, finishing a closing queue they empty."""
|
|
332
|
+
rest = items[n:]
|
|
333
|
+
_tx_ops.write(meta, self._items, rest)
|
|
334
|
+
if isinstance(state, _Closing) and not rest:
|
|
335
|
+
_tx_ops.write(meta, self._state, _Done(state.cause))
|
|
336
|
+
|
|
337
|
+
def _take_at_least(self, minimum: int, maximum: int) -> Effect[list[A], E]:
|
|
338
|
+
def decide(meta: TransactionMeta) -> _Step[list[A], E]:
|
|
339
|
+
state = _tx_ops.value(meta, self._state)
|
|
340
|
+
if isinstance(state, _Done):
|
|
341
|
+
return fail_with(state.cause)
|
|
342
|
+
items = _tx_ops.value(meta, self._items)
|
|
343
|
+
if len(items) < minimum:
|
|
344
|
+
if isinstance(state, _Open):
|
|
345
|
+
return tx_retry
|
|
346
|
+
self._remove(meta, state, items, len(items))
|
|
347
|
+
_tx_ops.write(meta, self._state, _Done(state.cause))
|
|
348
|
+
return succeed(list(items))
|
|
349
|
+
count = min(len(items), maximum)
|
|
350
|
+
self._remove(meta, state, items, count)
|
|
351
|
+
return succeed(list(items[:count]))
|
|
352
|
+
|
|
353
|
+
return _tx_ops.decide(decide)
|
|
354
|
+
|
|
355
|
+
def __repr__(self) -> str:
|
|
356
|
+
return f"TxQueue({self._strategy}, {self._capacity})"
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
__all__ = ["Strategy", "TxQueue"]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Write-only and read-only views of a ``TxQueue``, for variance.
|
|
2
|
+
|
|
3
|
+
A ``TxQueue[A, E]`` is invariant. Code that only offers can take a
|
|
4
|
+
``TxEnqueue[A, E]`` (contravariant); code that only takes can take a
|
|
5
|
+
``TxDequeue[A, E]`` (covariant), which gives batches as ``Sequence`` and
|
|
6
|
+
has ``clear`` (Effect puts it on ``TxEnqueue``, but it gives back items).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections.abc import Iterable, Sequence
|
|
12
|
+
from typing import Any, Never, Protocol, overload
|
|
13
|
+
|
|
14
|
+
from pyfect._core import Effect
|
|
15
|
+
from pyfect.cause import Cause
|
|
16
|
+
from pyfect.errors import Done
|
|
17
|
+
from pyfect.option import Option
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TxQueueState(Protocol):
|
|
21
|
+
"""What every view can ask about the queue."""
|
|
22
|
+
|
|
23
|
+
def size(self) -> Effect[int]: ...
|
|
24
|
+
def is_empty(self) -> Effect[bool]: ...
|
|
25
|
+
def is_non_empty(self) -> Effect[bool]: ...
|
|
26
|
+
def is_full(self) -> Effect[bool]: ...
|
|
27
|
+
def is_open(self) -> Effect[bool]: ...
|
|
28
|
+
def is_closing(self) -> Effect[bool]: ...
|
|
29
|
+
def is_done(self) -> Effect[bool]: ...
|
|
30
|
+
def is_shutdown(self) -> Effect[bool]: ...
|
|
31
|
+
def await_completion(self) -> Effect[None]: ...
|
|
32
|
+
@property
|
|
33
|
+
def capacity(self) -> int | None: ...
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TxEnqueue[A, E](TxQueueState, Protocol):
|
|
37
|
+
"""The producing side of a transactional queue."""
|
|
38
|
+
|
|
39
|
+
def offer(self, item: A) -> Effect[bool]: ...
|
|
40
|
+
def offer_all[T](self: TxEnqueue[T, Any], items: Iterable[T]) -> Effect[list[T]]: ...
|
|
41
|
+
def fail(self, error: E) -> Effect[bool]: ...
|
|
42
|
+
def fail_cause(self, cause: Cause[E]) -> Effect[bool]: ...
|
|
43
|
+
def end(self: TxEnqueue[Any, Done]) -> Effect[bool]: ...
|
|
44
|
+
def interrupt(self) -> Effect[bool]: ...
|
|
45
|
+
def shutdown(self) -> Effect[bool]: ...
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TxDequeue[A, E](TxQueueState, Protocol):
|
|
49
|
+
"""The consuming side of a transactional queue."""
|
|
50
|
+
|
|
51
|
+
def take(self) -> Effect[A, E]: ...
|
|
52
|
+
def poll(self) -> Effect[Option[A]]: ...
|
|
53
|
+
def peek(self) -> Effect[A, E]: ...
|
|
54
|
+
def take_all(self) -> Effect[Sequence[A], E]: ...
|
|
55
|
+
def take_n(self, n: int) -> Effect[Sequence[A], E]: ...
|
|
56
|
+
def take_between(self, minimum: int, maximum: int) -> Effect[Sequence[A], E]: ...
|
|
57
|
+
@overload
|
|
58
|
+
def clear[AA](self: TxDequeue[AA, Done[Any]]) -> Effect[Sequence[AA], Never]: ...
|
|
59
|
+
@overload
|
|
60
|
+
def clear[AA, EE](self: TxDequeue[AA, EE | Done[Any]]) -> Effect[Sequence[AA], EE]: ...
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""``TxReentrantLock``: a transactional reentrant read/write lock (Effect's
|
|
2
|
+
``TxReentrantLock``).
|
|
3
|
+
|
|
4
|
+
Locks are held per fiber. Any number of fibers can hold read locks while no
|
|
5
|
+
other fiber holds the write lock; one fiber can hold the write lock while
|
|
6
|
+
no other fiber holds a read lock. A fiber can take either lock again while
|
|
7
|
+
it holds it (each take counts and needs its own release), and the write
|
|
8
|
+
lock's holder can also take read locks.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Callable, Mapping
|
|
14
|
+
from dataclasses import dataclass, field, replace
|
|
15
|
+
from types import MappingProxyType
|
|
16
|
+
from typing import Never
|
|
17
|
+
|
|
18
|
+
from pyfect import _tx_ops
|
|
19
|
+
from pyfect._core import Effect, Restore, uninterruptible_mask
|
|
20
|
+
from pyfect._effect_args import require_effect
|
|
21
|
+
from pyfect.effect import fiber_id, succeed
|
|
22
|
+
from pyfect.option import Option, Some
|
|
23
|
+
from pyfect.scope import Scope, add_finalizer
|
|
24
|
+
from pyfect.transaction import Transaction, TransactionMeta, tx_restoring, tx_retry
|
|
25
|
+
from pyfect.tx_ref import TxRef
|
|
26
|
+
|
|
27
|
+
_NO_READERS: Mapping[int, int] = MappingProxyType({})
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True, slots=True)
|
|
31
|
+
class LockState:
|
|
32
|
+
"""Read-lock counts by fiber id, and the write lock's holder and count."""
|
|
33
|
+
|
|
34
|
+
readers: Mapping[int, int] = field(default=_NO_READERS)
|
|
35
|
+
writer: tuple[int, int] | None = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TxReentrantLock:
|
|
39
|
+
"""``TxReentrantLock(ref)`` wraps an existing ``TxRef`` (Effect's
|
|
40
|
+
``makeUnsafe``); ``make`` is the effect."""
|
|
41
|
+
|
|
42
|
+
__slots__ = ("_state",)
|
|
43
|
+
|
|
44
|
+
def __init__(self, state: TxRef[LockState]) -> None:
|
|
45
|
+
self._state = state
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def make() -> Effect[TxReentrantLock]:
|
|
49
|
+
return TxRef.make(LockState()).map(TxReentrantLock)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def ref(self) -> TxRef[LockState]:
|
|
53
|
+
return self._state
|
|
54
|
+
|
|
55
|
+
def acquire_read(self) -> Effect[int]:
|
|
56
|
+
"""Take a read lock, waiting while another fiber holds the write
|
|
57
|
+
lock; succeeds with this fiber's read-lock count."""
|
|
58
|
+
return self._as_fiber(self._acquire_read)
|
|
59
|
+
|
|
60
|
+
def acquire_write(self) -> Effect[int]:
|
|
61
|
+
"""Take the write lock, waiting while another fiber holds any lock;
|
|
62
|
+
succeeds with this fiber's write-lock count."""
|
|
63
|
+
return self._as_fiber(self._acquire_write)
|
|
64
|
+
|
|
65
|
+
def release_read(self) -> Effect[int]:
|
|
66
|
+
"""Give back one of this fiber's read locks; succeeds with how many it
|
|
67
|
+
still holds (0 if it held none)."""
|
|
68
|
+
return self._as_fiber(self._release_read)
|
|
69
|
+
|
|
70
|
+
def release_write(self) -> Effect[int]:
|
|
71
|
+
"""Give back one take of the write lock if this fiber holds it;
|
|
72
|
+
succeeds with how many it still holds (0 if it held none)."""
|
|
73
|
+
return self._as_fiber(self._release_write)
|
|
74
|
+
|
|
75
|
+
def read_lock(self) -> Effect[int, Never, Scope]:
|
|
76
|
+
"""Take a read lock until the current scope closes. Only the wait is
|
|
77
|
+
interruptible; whoever closes the scope releases this fiber's lock."""
|
|
78
|
+
return self._scoped(self._acquire_read, self._release_read)
|
|
79
|
+
|
|
80
|
+
def write_lock(self) -> Effect[int, Never, Scope]:
|
|
81
|
+
"""Take the write lock until the current scope closes."""
|
|
82
|
+
return self._scoped(self._acquire_write, self._release_write)
|
|
83
|
+
|
|
84
|
+
def with_read_lock[A, E, R](self, eff: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
85
|
+
"""Run ``eff`` holding a read lock, releasing it however it ends.
|
|
86
|
+
Only the wait for the lock is interruptible."""
|
|
87
|
+
require_effect(eff, "eff")
|
|
88
|
+
return self._with_acquired(self._acquire_read, eff, self._release_read)
|
|
89
|
+
|
|
90
|
+
def with_write_lock[A, E, R](self, eff: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
91
|
+
"""Run ``eff`` holding the write lock, releasing it however it ends."""
|
|
92
|
+
require_effect(eff, "eff")
|
|
93
|
+
return self._with_acquired(self._acquire_write, eff, self._release_write)
|
|
94
|
+
|
|
95
|
+
def with_lock[A, E, R](self, eff: Effect[A, E, R]) -> Effect[A, E, R]:
|
|
96
|
+
"""``with_write_lock``."""
|
|
97
|
+
require_effect(eff, "eff")
|
|
98
|
+
return self.with_write_lock(eff)
|
|
99
|
+
|
|
100
|
+
def read_locks(self) -> Effect[int]:
|
|
101
|
+
"""Read locks held, summed over fibers."""
|
|
102
|
+
return _tx_ops.read(self._state, lambda state: sum(state.readers.values()))
|
|
103
|
+
|
|
104
|
+
def write_locks(self) -> Effect[int]:
|
|
105
|
+
"""How many times the write lock is held."""
|
|
106
|
+
return _tx_ops.read(self._state, lambda state: state.writer[1] if state.writer else 0)
|
|
107
|
+
|
|
108
|
+
def locked(self) -> Effect[bool]:
|
|
109
|
+
return _tx_ops.read(self._state, lambda state: bool(state.readers) or bool(state.writer))
|
|
110
|
+
|
|
111
|
+
def read_locked(self) -> Effect[bool]:
|
|
112
|
+
return _tx_ops.read(self._state, lambda state: bool(state.readers))
|
|
113
|
+
|
|
114
|
+
def write_locked(self) -> Effect[bool]:
|
|
115
|
+
return _tx_ops.read(self._state, lambda state: state.writer is not None)
|
|
116
|
+
|
|
117
|
+
# --- one-step transactions for a given fiber ------------------------------
|
|
118
|
+
|
|
119
|
+
def _acquire_read(self, owner: int) -> Effect[int]:
|
|
120
|
+
def take(meta: TransactionMeta) -> Effect[int, Never, Transaction]:
|
|
121
|
+
state = _tx_ops.value(meta, self._state)
|
|
122
|
+
if state.writer is not None and state.writer[0] != owner:
|
|
123
|
+
return tx_retry
|
|
124
|
+
count = state.readers.get(owner, 0) + 1
|
|
125
|
+
readers = MappingProxyType({**state.readers, owner: count})
|
|
126
|
+
_tx_ops.write(meta, self._state, replace(state, readers=readers))
|
|
127
|
+
return succeed(count)
|
|
128
|
+
|
|
129
|
+
return _tx_ops.decide(take)
|
|
130
|
+
|
|
131
|
+
def _acquire_write(self, owner: int) -> Effect[int]:
|
|
132
|
+
def take(meta: TransactionMeta) -> Effect[int, Never, Transaction]:
|
|
133
|
+
state = _tx_ops.value(meta, self._state)
|
|
134
|
+
if state.writer is not None and state.writer[0] != owner:
|
|
135
|
+
return tx_retry
|
|
136
|
+
if any(reader != owner for reader in state.readers):
|
|
137
|
+
return tx_retry
|
|
138
|
+
count = state.writer[1] + 1 if state.writer is not None else 1
|
|
139
|
+
_tx_ops.write(meta, self._state, replace(state, writer=(owner, count)))
|
|
140
|
+
return succeed(count)
|
|
141
|
+
|
|
142
|
+
return _tx_ops.decide(take)
|
|
143
|
+
|
|
144
|
+
def _release_read(self, owner: int) -> Effect[int]:
|
|
145
|
+
def give_back(state: LockState) -> tuple[int, Option[LockState]]:
|
|
146
|
+
count = state.readers.get(owner, 0)
|
|
147
|
+
if count <= 0:
|
|
148
|
+
return 0, None
|
|
149
|
+
readers = {**state.readers, owner: count - 1}
|
|
150
|
+
if count == 1:
|
|
151
|
+
del readers[owner]
|
|
152
|
+
return count - 1, Some(replace(state, readers=MappingProxyType(readers)))
|
|
153
|
+
|
|
154
|
+
return _tx_ops.modify_or_skip(self._state, give_back)
|
|
155
|
+
|
|
156
|
+
def _release_write(self, owner: int) -> Effect[int]:
|
|
157
|
+
def give_back(state: LockState) -> tuple[int, Option[LockState]]:
|
|
158
|
+
if state.writer is None or state.writer[0] != owner:
|
|
159
|
+
return 0, None
|
|
160
|
+
count = state.writer[1] - 1
|
|
161
|
+
return count, Some(replace(state, writer=(owner, count) if count > 0 else None))
|
|
162
|
+
|
|
163
|
+
return _tx_ops.modify_or_skip(self._state, give_back)
|
|
164
|
+
|
|
165
|
+
# --- combinators ----------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def _as_fiber[A](f: Callable[[int], Effect[A]]) -> Effect[A]:
|
|
169
|
+
return fiber_id().flat_map(f)
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
def _scoped(
|
|
173
|
+
acquire: Callable[[int], Effect[int]], release: Callable[[int], Effect[int]]
|
|
174
|
+
) -> Effect[int, Never, Scope]:
|
|
175
|
+
def body(fid: int, restore: Restore) -> Effect[int, Never, Scope]:
|
|
176
|
+
return tx_restoring(restore, acquire(fid)).tap(
|
|
177
|
+
lambda _: add_finalizer(lambda _: release(fid))
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return fiber_id().flat_map(
|
|
181
|
+
lambda fid: uninterruptible_mask(lambda restore: body(fid, restore))
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def _with_acquired[A, E, R](
|
|
186
|
+
acquire: Callable[[int], Effect[int]],
|
|
187
|
+
eff: Effect[A, E, R],
|
|
188
|
+
release: Callable[[int], Effect[int]],
|
|
189
|
+
) -> Effect[A, E, R]:
|
|
190
|
+
def body(fid: int, restore: Restore) -> Effect[A, E, R]:
|
|
191
|
+
return tx_restoring(restore, acquire(fid)).and_then(restore(eff).ensuring(release(fid)))
|
|
192
|
+
|
|
193
|
+
return fiber_id().flat_map(
|
|
194
|
+
lambda fid: uninterruptible_mask(lambda restore: body(fid, restore))
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def __repr__(self) -> str:
|
|
198
|
+
return "TxReentrantLock()"
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
__all__ = ["LockState", "TxReentrantLock"]
|