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/_layer_core.py
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""``Layer`` itself, the ``MemoMap`` that shares builds, and building.
|
|
2
|
+
|
|
3
|
+
A layer is a build function ``(memo_map, scope) -> Effect[Context]``.
|
|
4
|
+
``from_build`` gives each build a child of ``scope`` that closes if the
|
|
5
|
+
build fails; ``from_build_memo`` also shares the build through the memo map.
|
|
6
|
+
|
|
7
|
+
A memo map entry owns a scope of its own and counts its users: every scope
|
|
8
|
+
that builds or reuses the layer holds one count, and gives it back when it
|
|
9
|
+
closes. The last one to leave closes the entry, releasing the layer, and a
|
|
10
|
+
later build starts afresh. A forked map reuses its parent's entries but
|
|
11
|
+
keeps the layers first built through it to itself.
|
|
12
|
+
"""
|
|
13
|
+
# pyright: reportPrivateUsage=false
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Callable, Iterable
|
|
18
|
+
from typing import TYPE_CHECKING, Any, Generic, Never, Self, TypeVar, Unpack, overload
|
|
19
|
+
|
|
20
|
+
from pyfect._core import Effect, _FailCause, _Locally, _Succeed, _Suspend, _WithFiber
|
|
21
|
+
from pyfect._effect_args import require_effect, wrong_argument
|
|
22
|
+
from pyfect.cause import Cause
|
|
23
|
+
from pyfect.cause import fail as fail_cause
|
|
24
|
+
from pyfect.context import Context, Tag, TagLike
|
|
25
|
+
from pyfect.deferred import Deferred
|
|
26
|
+
from pyfect.effect import never
|
|
27
|
+
from pyfect.exit import Exit, Failure
|
|
28
|
+
from pyfect.scope import CloseableScope, Scope, scoped
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from pyfect._layer_tracing import OnEnd
|
|
32
|
+
from pyfect._stack_frame import CaptureStackTrace
|
|
33
|
+
from pyfect.tracer import AnySpan, TracedSpanOptions
|
|
34
|
+
|
|
35
|
+
ROut_contra = TypeVar("ROut_contra", contravariant=True)
|
|
36
|
+
E_co = TypeVar("E_co", covariant=True, default=Never)
|
|
37
|
+
RIn_co = TypeVar("RIn_co", covariant=True, default=Never)
|
|
38
|
+
|
|
39
|
+
type Builder = Callable[[MemoMap, CloseableScope], Effect[Context, Any, Any]]
|
|
40
|
+
|
|
41
|
+
_VOID: Effect[None] = _Succeed(None)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_LAYER_HINT = "to build a layer lazily, pass fx.layer.suspend(f)"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def require_layer(value: object, name: str, expected: str = "a Layer") -> None:
|
|
48
|
+
"""``require_effect`` for a ``Layer`` parameter."""
|
|
49
|
+
if not isinstance(value, Layer):
|
|
50
|
+
raise TypeError(wrong_argument(value, name, expected, _LAYER_HINT))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def require_layers(values: Iterable[object], name: str) -> None:
|
|
54
|
+
"""``require_layer`` for each of a ``*args`` parameter's values."""
|
|
55
|
+
for i, value in enumerate(values):
|
|
56
|
+
require_layer(value, f"{name}[{i}]")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class _Entry:
|
|
60
|
+
"""A shared build: its result, its scope, and how many scopes use it."""
|
|
61
|
+
|
|
62
|
+
__slots__ = ("deferred", "finalizer", "observers", "scope")
|
|
63
|
+
|
|
64
|
+
def __init__(self, memo: MemoMap, layer: Layer[Any, Any, Any]) -> None:
|
|
65
|
+
self.observers = 0
|
|
66
|
+
self.deferred: Deferred[Context, Any] = Deferred()
|
|
67
|
+
self.scope = CloseableScope()
|
|
68
|
+
|
|
69
|
+
def leave(ex: Exit[Any, Any]) -> Effect[None]:
|
|
70
|
+
self.observers -= 1
|
|
71
|
+
if self.observers > 0:
|
|
72
|
+
return _VOID
|
|
73
|
+
memo._entries.pop(layer, None)
|
|
74
|
+
return self.scope.close(ex)
|
|
75
|
+
|
|
76
|
+
def finalizer(ex: Exit[Any, Any]) -> Effect[None]:
|
|
77
|
+
return _Suspend(lambda: leave(ex))
|
|
78
|
+
|
|
79
|
+
self.finalizer = finalizer
|
|
80
|
+
|
|
81
|
+
def observe(self, scope: CloseableScope) -> bool:
|
|
82
|
+
"""Count ``scope`` as a user until it closes, at once, so no
|
|
83
|
+
interruption can lose the count. A closed scope can't use it."""
|
|
84
|
+
if scope._closed is not None:
|
|
85
|
+
return False
|
|
86
|
+
self.observers += 1
|
|
87
|
+
scope._add(object(), self.finalizer)
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class MemoMap:
|
|
92
|
+
"""Shares layer builds by layer identity, releasing each layer when the
|
|
93
|
+
last scope using it closes. ``fork`` gives a map that reuses this one's
|
|
94
|
+
layers and keeps its own builds to itself."""
|
|
95
|
+
|
|
96
|
+
__slots__ = ("_entries", "_parent")
|
|
97
|
+
|
|
98
|
+
def __init__(self, parent: MemoMap | None = None) -> None:
|
|
99
|
+
self._parent = parent
|
|
100
|
+
self._entries: dict[Layer[Any, Any, Any], _Entry] = {}
|
|
101
|
+
|
|
102
|
+
def fork(self) -> MemoMap:
|
|
103
|
+
return MemoMap(self)
|
|
104
|
+
|
|
105
|
+
def get[O, E, R](
|
|
106
|
+
self, layer: Layer[O, E, R], scope: CloseableScope
|
|
107
|
+
) -> Effect[Context[O], E, R] | None:
|
|
108
|
+
"""The shared build of ``layer``, if this map or a parent has one.
|
|
109
|
+
``scope`` uses it from now on, whether or not the effect runs."""
|
|
110
|
+
require_layer(layer, "layer")
|
|
111
|
+
entry = self._entries.get(layer)
|
|
112
|
+
if entry is not None:
|
|
113
|
+
entry.observe(scope)
|
|
114
|
+
return entry.deferred.wait()
|
|
115
|
+
return self._parent.get(layer, scope) if self._parent is not None else None
|
|
116
|
+
|
|
117
|
+
def get_or_else_memoize[O, E, R](
|
|
118
|
+
self,
|
|
119
|
+
layer: Layer[O, E, R],
|
|
120
|
+
scope: CloseableScope,
|
|
121
|
+
build: Callable[[MemoMap, CloseableScope], Effect[Context[O], E, R]],
|
|
122
|
+
) -> Effect[Context[O], E, R]:
|
|
123
|
+
"""Reuse ``layer``'s shared build, or run ``build`` as it."""
|
|
124
|
+
require_layer(layer, "layer")
|
|
125
|
+
|
|
126
|
+
def run() -> Effect[Context[O], E, R]:
|
|
127
|
+
published: list[Deferred[Context, Any]] = []
|
|
128
|
+
|
|
129
|
+
def reuse_or_build() -> Effect[Context[O], E, R]:
|
|
130
|
+
existing = self.get(layer, scope)
|
|
131
|
+
if existing is not None:
|
|
132
|
+
return existing
|
|
133
|
+
entry = _Entry(self, layer)
|
|
134
|
+
if not entry.observe(scope):
|
|
135
|
+
return build(self, scope)
|
|
136
|
+
published.append(entry.deferred)
|
|
137
|
+
self._entries[layer] = entry
|
|
138
|
+
return build(self, entry.scope)
|
|
139
|
+
|
|
140
|
+
def settle(ex: Exit[Any, Any]) -> Effect[None]:
|
|
141
|
+
for deferred in published:
|
|
142
|
+
deferred.done_unsafe(ex)
|
|
143
|
+
return _VOID
|
|
144
|
+
|
|
145
|
+
# ``settle`` is in place before the entry is published, so even
|
|
146
|
+
# an interrupted first requester completes it for the others.
|
|
147
|
+
return _Suspend(reuse_or_build).on_exit(settle)
|
|
148
|
+
|
|
149
|
+
return _Suspend(run)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class CurrentMemoMap(Tag[MemoMap]):
|
|
153
|
+
"""The memo map of the enclosing build. ``provide`` and ``build`` fork
|
|
154
|
+
it, so layers built further out are reused."""
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def fork_or_create(ctx: Context) -> MemoMap:
|
|
158
|
+
"""A fork of ``ctx``'s memo map, or a new map if it has none."""
|
|
159
|
+
found, current = ctx.lookup(CurrentMemoMap)
|
|
160
|
+
return current.fork() if found else MemoMap()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class Layer(Generic[ROut_contra, E_co, RIn_co]):
|
|
164
|
+
"""Builds the services ``ROut``, may fail with ``E``, needs ``RIn``.
|
|
165
|
+
|
|
166
|
+
``Layer(build)`` is Effect's ``fromBuildUnsafe``: no child scope and no
|
|
167
|
+
sharing. Use ``from_build`` / ``from_build_memo`` or the constructors in
|
|
168
|
+
``pyfect.layer``."""
|
|
169
|
+
|
|
170
|
+
__slots__ = ("_build",)
|
|
171
|
+
|
|
172
|
+
def __init__(self, build: Builder) -> None:
|
|
173
|
+
self._build = build
|
|
174
|
+
|
|
175
|
+
def pipe[B](self, f: Callable[[Self], B], /) -> B:
|
|
176
|
+
return f(self)
|
|
177
|
+
|
|
178
|
+
# --- building ---------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
def build_with_memo_map(
|
|
181
|
+
self, memo_map: MemoMap, scope: CloseableScope
|
|
182
|
+
) -> Effect[Context[ROut_contra], E_co, RIn_co]:
|
|
183
|
+
"""Build with ``memo_map``, releasing into ``scope``. The memo map is
|
|
184
|
+
``CurrentMemoMap`` during the build and in the built context."""
|
|
185
|
+
building: Effect[Context, Any, Any] = _Locally(
|
|
186
|
+
_Suspend(lambda: self._build(memo_map, scope)),
|
|
187
|
+
lambda ctx: ctx.add(CurrentMemoMap, memo_map),
|
|
188
|
+
)
|
|
189
|
+
return building.map(lambda built: built.add(CurrentMemoMap, memo_map))
|
|
190
|
+
|
|
191
|
+
def build_with_scope(self, scope: CloseableScope) -> Effect[Context[ROut_contra], E_co, RIn_co]:
|
|
192
|
+
"""Build into ``scope``, reusing what an enclosing build built."""
|
|
193
|
+
return _WithFiber(
|
|
194
|
+
lambda fiber: self.build_with_memo_map(fork_or_create(fiber._context), scope)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def build(self) -> Effect[Context[ROut_contra], E_co, RIn_co | Scope]:
|
|
198
|
+
"""Build into the current ``Scope``, reusing what an enclosing build
|
|
199
|
+
built."""
|
|
200
|
+
return _WithFiber(
|
|
201
|
+
lambda fiber: self.build_with_memo_map(
|
|
202
|
+
fork_or_create(fiber._context), fiber._context.get_unsafe(Scope)
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def launch(self) -> Effect[Never, E_co, RIn_co]:
|
|
207
|
+
"""Build the layer and keep it until interrupted, as for a server."""
|
|
208
|
+
return scoped(self.build().and_then(never))
|
|
209
|
+
|
|
210
|
+
# --- sequencing -------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
def flat_map[O2, E2 = Never, R2 = Never](
|
|
213
|
+
self, f: Callable[[Context[ROut_contra]], Layer[O2, E2, R2]]
|
|
214
|
+
) -> Layer[O2, E_co | E2, RIn_co | R2]:
|
|
215
|
+
"""Build the layer ``f`` makes from this one's services."""
|
|
216
|
+
return from_build(
|
|
217
|
+
lambda memo, scope: self._build(memo, scope).flat_map(
|
|
218
|
+
lambda ctx: f(ctx)._build(memo, scope)
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
@overload
|
|
223
|
+
def tap[E2 = Never](
|
|
224
|
+
self, f: Callable[[Context[ROut_contra]], Effect[object, E2, Scope]]
|
|
225
|
+
) -> Layer[ROut_contra, E_co | E2, RIn_co]: ...
|
|
226
|
+
@overload
|
|
227
|
+
def tap[E2 = Never, R2 = Never](
|
|
228
|
+
self, f: Callable[[Context[ROut_contra]], Effect[object, E2, Scope | R2]]
|
|
229
|
+
) -> Layer[ROut_contra, E_co | E2, RIn_co | R2]: ...
|
|
230
|
+
def tap(self, f: Callable[[Context], Effect[object, Any, Any]]) -> Layer[Any, Any, Any]:
|
|
231
|
+
"""Run ``f`` on the built services, in the layer's scope."""
|
|
232
|
+
return from_build(
|
|
233
|
+
lambda memo, scope: self._build(memo, scope).flat_map(
|
|
234
|
+
lambda ctx: _in_scope(f(ctx), scope).as_(ctx)
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
@overload
|
|
239
|
+
def tap_error[E2 = Never](
|
|
240
|
+
self, f: Callable[[E_co], Effect[object, E2, Scope]]
|
|
241
|
+
) -> Layer[ROut_contra, E_co | E2, RIn_co]: ...
|
|
242
|
+
@overload
|
|
243
|
+
def tap_error[E2 = Never, R2 = Never](
|
|
244
|
+
self, f: Callable[[E_co], Effect[object, E2, Scope | R2]]
|
|
245
|
+
) -> Layer[ROut_contra, E_co | E2, RIn_co | R2]: ...
|
|
246
|
+
def tap_error(self, f: Callable[[Any], Effect[object, Any, Any]]) -> Layer[Any, Any, Any]:
|
|
247
|
+
"""Run ``f`` on a typed build error, in the layer's scope; the build
|
|
248
|
+
still fails with it."""
|
|
249
|
+
return from_build(
|
|
250
|
+
lambda memo, scope: self._build(memo, scope).catch_all(
|
|
251
|
+
lambda e: _in_scope(f(e), scope).and_then(_FailCause(fail_cause(e)))
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
@overload
|
|
256
|
+
def tap_cause[E2 = Never](
|
|
257
|
+
self, f: Callable[[Cause[E_co]], Effect[object, E2, Scope]]
|
|
258
|
+
) -> Layer[ROut_contra, E_co | E2, RIn_co]: ...
|
|
259
|
+
@overload
|
|
260
|
+
def tap_cause[E2 = Never, R2 = Never](
|
|
261
|
+
self, f: Callable[[Cause[E_co]], Effect[object, E2, Scope | R2]]
|
|
262
|
+
) -> Layer[ROut_contra, E_co | E2, RIn_co | R2]: ...
|
|
263
|
+
def tap_cause(
|
|
264
|
+
self, f: Callable[[Cause[Any]], Effect[object, Any, Any]]
|
|
265
|
+
) -> Layer[Any, Any, Any]:
|
|
266
|
+
"""Run ``f`` on the cause of a failed build, in the layer's scope;
|
|
267
|
+
the build still fails with it."""
|
|
268
|
+
return from_build(
|
|
269
|
+
lambda memo, scope: self._build(memo, scope).catch_cause(
|
|
270
|
+
lambda c: _in_scope(f(c), scope).and_then(_FailCause(c))
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# --- errors -----------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
def or_die(self) -> Layer[ROut_contra, Never, RIn_co]:
|
|
277
|
+
"""Die with a typed build error instead of failing with it."""
|
|
278
|
+
return Layer(lambda memo, scope: self._build(memo, scope).or_die())
|
|
279
|
+
|
|
280
|
+
def catch_all[O, E, R, E2 = Never, R2 = Never](
|
|
281
|
+
self: Layer[O, E, R], f: Callable[[E], Layer[O, E2, R2]]
|
|
282
|
+
) -> Layer[O, E2, R | R2]:
|
|
283
|
+
"""Build ``f(error)`` instead when the build fails with a typed
|
|
284
|
+
error. The fallback must build at least what this layer builds."""
|
|
285
|
+
return Layer(
|
|
286
|
+
lambda memo, scope: self._build(memo, scope).catch_all(
|
|
287
|
+
lambda e: f(e)._build(memo, scope)
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
def catch_cause[O, E, R, E2 = Never, R2 = Never](
|
|
292
|
+
self: Layer[O, E, R], f: Callable[[Cause[E]], Layer[O, E2, R2]]
|
|
293
|
+
) -> Layer[O, E2, R | R2]:
|
|
294
|
+
"""Build ``f(cause)`` instead when the build fails, defects
|
|
295
|
+
included. The fallback must build at least what this layer builds."""
|
|
296
|
+
return Layer(
|
|
297
|
+
lambda memo, scope: self._build(memo, scope).catch_cause(
|
|
298
|
+
lambda c: f(c)._build(memo, scope)
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# --- services ---------------------------------------------------------------
|
|
303
|
+
|
|
304
|
+
def update_service[S, T](
|
|
305
|
+
self, tag: TagLike[S, T], f: Callable[[S], S]
|
|
306
|
+
) -> Layer[ROut_contra, E_co, RIn_co | T]:
|
|
307
|
+
"""Build with ``f`` applied to the service of ``tag`` that this
|
|
308
|
+
layer needs (which it then needs from outside)."""
|
|
309
|
+
updated = effect_context(tag().map(lambda service: Context.make(tag, f(service))))
|
|
310
|
+
return feed(updated, self, keep_dep=False)
|
|
311
|
+
|
|
312
|
+
# --- tracing ----------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
def with_span(
|
|
315
|
+
self,
|
|
316
|
+
name: str,
|
|
317
|
+
*,
|
|
318
|
+
on_end: OnEnd | None = None,
|
|
319
|
+
**options: Unpack[TracedSpanOptions],
|
|
320
|
+
) -> Layer[ROut_contra, E_co, RIn_co]:
|
|
321
|
+
"""Build inside a new span, the parent of the spans made while
|
|
322
|
+
building, and inside its stack frame (this call site unless
|
|
323
|
+
``capture_stack_trace`` says otherwise). The span ends, and
|
|
324
|
+
``on_end`` runs, when the layer's scope closes."""
|
|
325
|
+
from pyfect._layer_tracing import with_span
|
|
326
|
+
from pyfect._stack_frame import stack_of
|
|
327
|
+
|
|
328
|
+
stack = stack_of(options.get("capture_stack_trace", True), 1)
|
|
329
|
+
return with_span(self, name, on_end, options, stack)
|
|
330
|
+
|
|
331
|
+
def with_parent_span(
|
|
332
|
+
self, span: AnySpan, *, capture_stack_trace: CaptureStackTrace = True
|
|
333
|
+
) -> Layer[ROut_contra, E_co, RIn_co]:
|
|
334
|
+
"""Build with ``span`` as the parent of the spans made while
|
|
335
|
+
building, and, unless it is external, inside its stack frame."""
|
|
336
|
+
from pyfect._layer_tracing import with_parent_span
|
|
337
|
+
from pyfect._stack_frame import stack_of
|
|
338
|
+
|
|
339
|
+
return with_parent_span(self, span, stack_of(capture_stack_trace, 1))
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _in_scope[A, E](eff: Effect[A, E, Any], scope: CloseableScope) -> Effect[A, E, Any]:
|
|
343
|
+
return _Locally(eff, lambda ctx: ctx.add(Scope, scope))
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def from_build[O, E = Never, R = Never](
|
|
347
|
+
build: Callable[[MemoMap, CloseableScope], Effect[Context[O], E, R]],
|
|
348
|
+
) -> Layer[O, E, R]:
|
|
349
|
+
"""A layer from its build function. Each build gets a child of the
|
|
350
|
+
scope it is given, closed at once if the build fails."""
|
|
351
|
+
|
|
352
|
+
def child_scoped(memo: MemoMap, scope: CloseableScope) -> Effect[Context, Any, Any]:
|
|
353
|
+
def run() -> Effect[Context, Any, Any]:
|
|
354
|
+
layer_scope = scope.fork_unsafe()
|
|
355
|
+
return build(memo, layer_scope).on_exit(
|
|
356
|
+
lambda ex: layer_scope.close(ex) if isinstance(ex, Failure) else _VOID
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return _Suspend(run)
|
|
360
|
+
|
|
361
|
+
return Layer(child_scoped)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def from_build_memo[O, E = Never, R = Never](
|
|
365
|
+
build: Callable[[MemoMap, CloseableScope], Effect[Context[O], E, R]],
|
|
366
|
+
) -> Layer[O, E, R]:
|
|
367
|
+
"""``from_build``, shared through the memo map: within one build, and
|
|
368
|
+
across builds with the same memo map while a scope still uses it."""
|
|
369
|
+
layer: Layer[O, E, R] = from_build(
|
|
370
|
+
lambda memo, scope: memo.get_or_else_memoize(layer, scope, build)
|
|
371
|
+
)
|
|
372
|
+
return layer
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@overload
|
|
376
|
+
def effect_context[A, E](eff: Effect[Context[A], E, Scope]) -> Layer[A, E]: ...
|
|
377
|
+
@overload
|
|
378
|
+
def effect_context[A, E = Never, R = Never](
|
|
379
|
+
eff: Effect[Context[A], E, Scope | R],
|
|
380
|
+
) -> Layer[A, E, R]: ...
|
|
381
|
+
def effect_context(eff: Effect[Context, Any, Any]) -> Layer[Any, Any, Any]:
|
|
382
|
+
"""Build every service of the context ``eff`` makes. ``eff`` may acquire
|
|
383
|
+
resources (it runs with a ``Scope``); they are released with the layer."""
|
|
384
|
+
require_effect(eff, "eff")
|
|
385
|
+
return from_build_memo(lambda _memo, scope: _in_scope(eff, scope))
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def feed(
|
|
389
|
+
dep: Layer[Any, Any, Any], target: Layer[Any, Any, Any], *, keep_dep: bool
|
|
390
|
+
) -> Layer[Any, Any, Any]:
|
|
391
|
+
"""Build ``target`` with ``dep``'s services (both in one scope), giving
|
|
392
|
+
``target``'s services, plus ``dep``'s when ``keep_dep``."""
|
|
393
|
+
|
|
394
|
+
def build(memo: MemoMap, scope: CloseableScope) -> Effect[Context, Any, Any]:
|
|
395
|
+
def with_dep(dep_ctx: Context) -> Effect[Context, Any, Any]:
|
|
396
|
+
built: Effect[Context, Any, Any] = _Locally(
|
|
397
|
+
target._build(memo, scope), lambda ctx: ctx.merge(dep_ctx)
|
|
398
|
+
)
|
|
399
|
+
return built.map(dep_ctx.merge) if keep_dep else built
|
|
400
|
+
|
|
401
|
+
return dep._build(memo, scope).flat_map(with_dep)
|
|
402
|
+
|
|
403
|
+
return from_build_memo(build)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""``make(..., layer=...)``: a value made with a layer that lives as long as
|
|
2
|
+
the value does.
|
|
3
|
+
|
|
4
|
+
``made.pipe(provide(layer))`` releases the layer once ``made`` returns, so a
|
|
5
|
+
client made that way outlives its protocol or handlers. ``with_layer(make,
|
|
6
|
+
layer)`` builds ``layer`` into the caller's ``Scope`` instead, the scope the
|
|
7
|
+
value is valid in, and is what every ``layer=`` parameter uses.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from pyfect._core import Effect
|
|
16
|
+
from pyfect._layer_core import Layer, require_layer
|
|
17
|
+
from pyfect.effect import provide_context
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def with_layer(
|
|
21
|
+
make: Callable[[], Effect[Any, Any, Any]], layer: Layer[Any, Any, Any] | None
|
|
22
|
+
) -> Effect[Any, Any, Any]:
|
|
23
|
+
"""``make()``, with the services of ``layer`` built into the caller's
|
|
24
|
+
``Scope`` (``layer`` is checked first); without ``layer``, ``make()``
|
|
25
|
+
alone."""
|
|
26
|
+
if layer is None:
|
|
27
|
+
return make()
|
|
28
|
+
require_layer(layer, "layer")
|
|
29
|
+
made = make()
|
|
30
|
+
return layer.build().flat_map(lambda built: made.pipe(provide_context(built)))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = ["with_layer"]
|
pyfect/_layer_tracing.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Layers and spans: a layer providing a parent span, and spans around a
|
|
2
|
+
layer's construction (Effect's ``Layer.span`` / ``parentSpan`` /
|
|
3
|
+
``withSpan`` / ``withParentSpan``)."""
|
|
4
|
+
# pyright: reportPrivateUsage=false
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from typing import Any, Never, Unpack
|
|
10
|
+
|
|
11
|
+
from pyfect._core import Effect, _Succeed
|
|
12
|
+
from pyfect._layer_core import Layer, feed
|
|
13
|
+
from pyfect._stack_frame import CurrentStackFrame, push
|
|
14
|
+
from pyfect.context import Context
|
|
15
|
+
from pyfect.exit import Exit
|
|
16
|
+
from pyfect.scope import Scope, add_finalizer
|
|
17
|
+
from pyfect.tracer import AnySpan, ExternalSpan, ParentSpan, Span, SpanOptions
|
|
18
|
+
from pyfect.tracer._spans import span_scoped
|
|
19
|
+
|
|
20
|
+
type OnEnd = Callable[[Span, Exit[Any, Any]], Effect[object]]
|
|
21
|
+
"""Runs when a layer's span ends (when the layer's scope closes)."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def parent_span(span: AnySpan) -> Layer[ParentSpan]:
|
|
25
|
+
"""Provide ``span`` as the parent of the spans made with it."""
|
|
26
|
+
built: Effect[Context] = _Succeed(Context.make(ParentSpan, span))
|
|
27
|
+
return Layer(lambda _memo, _scope: built)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _scoped_span(
|
|
31
|
+
name: str, on_end: OnEnd | None, options: SpanOptions
|
|
32
|
+
) -> Effect[Span, Never, Scope]:
|
|
33
|
+
made = span_scoped(name, options)
|
|
34
|
+
if on_end is None:
|
|
35
|
+
return made
|
|
36
|
+
return made.tap(lambda span: add_finalizer(lambda ex: on_end(span, ex)))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def span(
|
|
40
|
+
name: str, *, on_end: OnEnd | None = None, **options: Unpack[SpanOptions]
|
|
41
|
+
) -> Layer[ParentSpan]:
|
|
42
|
+
"""Provide a new span as the parent of the spans made with it. The span
|
|
43
|
+
ends, and ``on_end`` runs, when the layer's scope closes."""
|
|
44
|
+
from pyfect.layer import effect
|
|
45
|
+
|
|
46
|
+
return effect(ParentSpan, _scoped_span(name, on_end, options))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def with_parent_span[O, E, R](
|
|
50
|
+
layer: Layer[O, E, R], span: AnySpan, stack: Callable[[], str | None]
|
|
51
|
+
) -> Layer[O, E, R]:
|
|
52
|
+
"""``layer`` built with ``span`` as the parent span and, unless it is
|
|
53
|
+
external, inside a stack frame named after it at ``stack``."""
|
|
54
|
+
if not isinstance(span, ExternalSpan):
|
|
55
|
+
layer = layer.update_service(CurrentStackFrame, push(span.name, stack))
|
|
56
|
+
return feed(parent_span(span), layer, keep_dep=False)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def with_span[O, E, R](
|
|
60
|
+
layer: Layer[O, E, R],
|
|
61
|
+
name: str,
|
|
62
|
+
on_end: OnEnd | None,
|
|
63
|
+
options: SpanOptions,
|
|
64
|
+
stack: Callable[[], str | None],
|
|
65
|
+
) -> Layer[O, E, R]:
|
|
66
|
+
from pyfect.layer import unwrap
|
|
67
|
+
|
|
68
|
+
return unwrap(
|
|
69
|
+
_scoped_span(name, on_end, options).map(lambda s: with_parent_span(layer, s, stack))
|
|
70
|
+
)
|
pyfect/_locked_ref.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""``LockedRef[A]``: the operations ``SynchronizedRef`` and
|
|
2
|
+
``SubscriptionRef`` share.
|
|
3
|
+
|
|
4
|
+
Every update holds a one-permit semaphore, so an effectful update (the
|
|
5
|
+
``*_effect`` methods) sees no other update between reading the value and
|
|
6
|
+
storing its result. ``get`` takes no permit: it reads the last stored
|
|
7
|
+
value. A subclass decides what storing does by overriding ``_store``.
|
|
8
|
+
|
|
9
|
+
Each operation is written as a transition from the current value to a
|
|
10
|
+
result and an optional new value (``None`` keeps the current one), which
|
|
11
|
+
``_locked`` / ``_locked_effect`` run under the semaphore.
|
|
12
|
+
"""
|
|
13
|
+
# pyright: reportPrivateUsage=false
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from typing import Never
|
|
19
|
+
|
|
20
|
+
from pyfect._core import Effect, _Suspend, _Sync
|
|
21
|
+
from pyfect.option import Option, Some
|
|
22
|
+
from pyfect.semaphore import Semaphore
|
|
23
|
+
|
|
24
|
+
type _Transition[A, B] = tuple[B, Option[A]]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LockedRef[A]:
|
|
28
|
+
__slots__ = ("_lock", "_value")
|
|
29
|
+
|
|
30
|
+
def __init__(self, value: A) -> None:
|
|
31
|
+
self._value = value
|
|
32
|
+
self._lock = Semaphore(1)
|
|
33
|
+
|
|
34
|
+
def _store(self, value: A) -> None:
|
|
35
|
+
self._value = value
|
|
36
|
+
|
|
37
|
+
def _commit[B](self, transition: _Transition[A, B]) -> B:
|
|
38
|
+
result, new = transition
|
|
39
|
+
if new is not None:
|
|
40
|
+
self._store(new.value)
|
|
41
|
+
return result
|
|
42
|
+
|
|
43
|
+
def _locked[B](self, f: Callable[[A], _Transition[A, B]]) -> Effect[B]:
|
|
44
|
+
return self._lock.with_permits(_Sync(lambda: self._commit(f(self._value))))
|
|
45
|
+
|
|
46
|
+
def _locked_effect[B, E, R](
|
|
47
|
+
self, f: Callable[[A], Effect[_Transition[A, B], E, R]]
|
|
48
|
+
) -> Effect[B, E, R]:
|
|
49
|
+
return self._lock.with_permits(_Suspend(lambda: f(self._value).map(self._commit)))
|
|
50
|
+
|
|
51
|
+
# --- reading ----------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
def get_unsafe(self) -> A:
|
|
54
|
+
return self._value
|
|
55
|
+
|
|
56
|
+
def get(self) -> Effect[A]:
|
|
57
|
+
return _Sync(lambda: self._value)
|
|
58
|
+
|
|
59
|
+
# --- plain updates ----------------------------------------------------
|
|
60
|
+
|
|
61
|
+
def set(self, value: A) -> Effect[None]:
|
|
62
|
+
return self._locked(lambda _: (None, Some(value)))
|
|
63
|
+
|
|
64
|
+
def set_and_get(self, value: A) -> Effect[A]:
|
|
65
|
+
return self._locked(lambda _: (value, Some(value)))
|
|
66
|
+
|
|
67
|
+
def get_and_set(self, value: A) -> Effect[A]:
|
|
68
|
+
return self._locked(lambda old: (old, Some(value)))
|
|
69
|
+
|
|
70
|
+
def update(self, f: Callable[[A], A]) -> Effect[None]:
|
|
71
|
+
return self._locked(lambda old: (None, Some(f(old))))
|
|
72
|
+
|
|
73
|
+
def update_and_get(self, f: Callable[[A], A]) -> Effect[A]:
|
|
74
|
+
return self._locked(lambda old: _new_and_get(f(old)))
|
|
75
|
+
|
|
76
|
+
def get_and_update(self, f: Callable[[A], A]) -> Effect[A]:
|
|
77
|
+
return self._locked(lambda old: (old, Some(f(old))))
|
|
78
|
+
|
|
79
|
+
def update_some(self, f: Callable[[A], Option[A]]) -> Effect[None]:
|
|
80
|
+
return self._locked(lambda old: (None, f(old)))
|
|
81
|
+
|
|
82
|
+
def update_some_and_get(self, f: Callable[[A], Option[A]]) -> Effect[A]:
|
|
83
|
+
return self._locked(lambda old: _some_and_get(old, f(old)))
|
|
84
|
+
|
|
85
|
+
def get_and_update_some(self, f: Callable[[A], Option[A]]) -> Effect[A]:
|
|
86
|
+
return self._locked(lambda old: (old, f(old)))
|
|
87
|
+
|
|
88
|
+
def modify[B](self, f: Callable[[A], tuple[B, A]]) -> Effect[B]:
|
|
89
|
+
return self._locked(lambda old: _modified(f(old)))
|
|
90
|
+
|
|
91
|
+
def modify_some[B](self, f: Callable[[A], tuple[B, Option[A]]]) -> Effect[B]:
|
|
92
|
+
return self._locked(f)
|
|
93
|
+
|
|
94
|
+
# --- effectful updates ------------------------------------------------
|
|
95
|
+
|
|
96
|
+
def update_effect[E = Never, R = Never](
|
|
97
|
+
self, f: Callable[[A], Effect[A, E, R]]
|
|
98
|
+
) -> Effect[None, E, R]:
|
|
99
|
+
return self._locked_effect(lambda old: f(old).map(lambda new: (None, Some(new))))
|
|
100
|
+
|
|
101
|
+
def update_and_get_effect[E = Never, R = Never](
|
|
102
|
+
self, f: Callable[[A], Effect[A, E, R]]
|
|
103
|
+
) -> Effect[A, E, R]:
|
|
104
|
+
return self._locked_effect(lambda old: f(old).map(_new_and_get))
|
|
105
|
+
|
|
106
|
+
def get_and_update_effect[E = Never, R = Never](
|
|
107
|
+
self, f: Callable[[A], Effect[A, E, R]]
|
|
108
|
+
) -> Effect[A, E, R]:
|
|
109
|
+
return self._locked_effect(lambda old: f(old).map(lambda new: (old, Some(new))))
|
|
110
|
+
|
|
111
|
+
def update_some_effect[E = Never, R = Never](
|
|
112
|
+
self, f: Callable[[A], Effect[Option[A], E, R]]
|
|
113
|
+
) -> Effect[None, E, R]:
|
|
114
|
+
return self._locked_effect(lambda old: f(old).map(lambda new: (None, new)))
|
|
115
|
+
|
|
116
|
+
def update_some_and_get_effect[E = Never, R = Never](
|
|
117
|
+
self, f: Callable[[A], Effect[Option[A], E, R]]
|
|
118
|
+
) -> Effect[A, E, R]:
|
|
119
|
+
return self._locked_effect(lambda old: f(old).map(lambda new: _some_and_get(old, new)))
|
|
120
|
+
|
|
121
|
+
def get_and_update_some_effect[E = Never, R = Never](
|
|
122
|
+
self, f: Callable[[A], Effect[Option[A], E, R]]
|
|
123
|
+
) -> Effect[A, E, R]:
|
|
124
|
+
return self._locked_effect(lambda old: f(old).map(lambda new: (old, new)))
|
|
125
|
+
|
|
126
|
+
def modify_effect[B, E = Never, R = Never](
|
|
127
|
+
self, f: Callable[[A], Effect[tuple[B, A], E, R]]
|
|
128
|
+
) -> Effect[B, E, R]:
|
|
129
|
+
return self._locked_effect(lambda old: f(old).map(_modified))
|
|
130
|
+
|
|
131
|
+
def modify_some_effect[B, E = Never, R = Never](
|
|
132
|
+
self, f: Callable[[A], Effect[tuple[B, Option[A]], E, R]]
|
|
133
|
+
) -> Effect[B, E, R]:
|
|
134
|
+
return self._locked_effect(f)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _new_and_get[A](new: A) -> _Transition[A, A]:
|
|
138
|
+
return new, Some(new)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _some_and_get[A](old: A, new: Option[A]) -> _Transition[A, A]:
|
|
142
|
+
return (old if new is None else new.value), new
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _modified[A, B](pair: tuple[B, A]) -> _Transition[A, B]:
|
|
146
|
+
result, new = pair
|
|
147
|
+
return result, Some(new)
|