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/_round_robin.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Keyed queues served in turn, one item per turn.
|
|
2
|
+
|
|
3
|
+
The cursor survives additions and removals the way a JavaScript ``Map``
|
|
4
|
+
iterator does (Effect's ``PartitionedSemaphore`` relies on it): a key added
|
|
5
|
+
later is reached in this round, and a key removed and added again moves to
|
|
6
|
+
the end.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections.abc import Hashable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _Queue[K, V]:
|
|
15
|
+
__slots__ = ("items", "key", "slot")
|
|
16
|
+
|
|
17
|
+
def __init__(self, key: K, slot: int) -> None:
|
|
18
|
+
self.key = key
|
|
19
|
+
self.slot = slot
|
|
20
|
+
# A dict as an insertion-ordered set: removal from the middle is O(1).
|
|
21
|
+
self.items: dict[V, None] = {}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RoundRobin[K: Hashable, V: Hashable]:
|
|
25
|
+
__slots__ = ("_cursor", "_holes", "_queues", "_ring")
|
|
26
|
+
|
|
27
|
+
def __init__(self) -> None:
|
|
28
|
+
self._queues: dict[K, _Queue[K, V]] = {}
|
|
29
|
+
self._ring: list[_Queue[K, V] | None] = []
|
|
30
|
+
self._cursor = 0
|
|
31
|
+
self._holes = 0
|
|
32
|
+
|
|
33
|
+
def add(self, key: K, item: V) -> None:
|
|
34
|
+
queue = self._queues.get(key)
|
|
35
|
+
if queue is None:
|
|
36
|
+
queue = self._queues[key] = _Queue(key, len(self._ring))
|
|
37
|
+
self._ring.append(queue)
|
|
38
|
+
queue.items[item] = None
|
|
39
|
+
|
|
40
|
+
def remove(self, key: K, item: V) -> None:
|
|
41
|
+
queue = self._queues.get(key)
|
|
42
|
+
if queue is None or item not in queue.items:
|
|
43
|
+
return
|
|
44
|
+
del queue.items[item]
|
|
45
|
+
if queue.items:
|
|
46
|
+
return
|
|
47
|
+
del self._queues[key]
|
|
48
|
+
self._ring[queue.slot] = None
|
|
49
|
+
self._holes += 1
|
|
50
|
+
if self._holes * 2 > len(self._ring):
|
|
51
|
+
self._compact()
|
|
52
|
+
|
|
53
|
+
def next(self) -> tuple[K, V] | None:
|
|
54
|
+
"""The next key and the head of its queue, or ``None`` if all are
|
|
55
|
+
empty. Does not remove it."""
|
|
56
|
+
for _ in range(2):
|
|
57
|
+
while self._cursor < len(self._ring):
|
|
58
|
+
queue = self._ring[self._cursor]
|
|
59
|
+
self._cursor += 1
|
|
60
|
+
if queue is not None:
|
|
61
|
+
return queue.key, next(iter(queue.items))
|
|
62
|
+
self._cursor = 0
|
|
63
|
+
self._compact()
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
def _compact(self) -> None:
|
|
67
|
+
live_before_cursor = sum(q is not None for q in self._ring[: self._cursor])
|
|
68
|
+
live = [q for q in self._ring if q is not None]
|
|
69
|
+
for slot, queue in enumerate(live):
|
|
70
|
+
queue.slot = slot
|
|
71
|
+
self._ring = list[_Queue[K, V] | None](live)
|
|
72
|
+
self._cursor = live_before_cursor
|
|
73
|
+
self._holes = 0
|
pyfect/_some.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""``Some``, on its own so ``pyfect.result`` and ``pyfect.option`` can both
|
|
2
|
+
use it without importing each other."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Generic, TypeVar
|
|
8
|
+
|
|
9
|
+
A_co = TypeVar("A_co", covariant=True)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, slots=True)
|
|
13
|
+
class Some(Generic[A_co]):
|
|
14
|
+
value: A_co
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
type Option[A] = Some[A] | None
|
pyfect/_stack.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Recursion that goes as deep as its data: ``with_fresh_stack`` runs a step
|
|
2
|
+
of a recursive walk on a new thread once the caller's stack is nearly full.
|
|
3
|
+
|
|
4
|
+
Python counts frames per thread and stops at ``sys.getrecursionlimit()``
|
|
5
|
+
(about 1000), where JavaScript's stack takes about ten times as many. A
|
|
6
|
+
walk whose depth comes from its input (a recursive schema decoding a deep
|
|
7
|
+
value, an issue tree from one) calls ``with_fresh_stack`` at the node that
|
|
8
|
+
recurses (``Suspend``), so each thread holds a bounded slice of the walk.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import contextvars
|
|
14
|
+
import sys
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
17
|
+
|
|
18
|
+
_HEADROOM = 400
|
|
19
|
+
"""Frames left to the caller when the walk moves to a new thread: room for
|
|
20
|
+
one level of a recursive structure between two calls."""
|
|
21
|
+
|
|
22
|
+
MAX_FRESH_STACKS = 300
|
|
23
|
+
"""Fresh stacks one walk may nest (each about ``getrecursionlimit() -
|
|
24
|
+
_HEADROOM`` frames, so far deeper than JavaScript's stack). Past it the walk
|
|
25
|
+
raises ``RecursionError``: a walk that never ends (a cycle) fails instead
|
|
26
|
+
of starting threads until the process runs out of them."""
|
|
27
|
+
|
|
28
|
+
_depth: contextvars.ContextVar[int] = contextvars.ContextVar("pyfect_fresh_stacks", default=0)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def stack_nearly_full() -> bool:
|
|
32
|
+
"""Whether the next level of a walk belongs on a fresh stack."""
|
|
33
|
+
try:
|
|
34
|
+
sys._getframe(max(sys.getrecursionlimit() - _HEADROOM, 1)) # pyright: ignore[reportPrivateUsage]
|
|
35
|
+
except ValueError:
|
|
36
|
+
return False
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def with_fresh_stack[A](f: Callable[[], A]) -> A:
|
|
41
|
+
"""``f()``, on a new thread (with the caller's context variables) when
|
|
42
|
+
the stack is too deep for another level of the walk. Its result or
|
|
43
|
+
exception is the caller's."""
|
|
44
|
+
if not stack_nearly_full():
|
|
45
|
+
return f()
|
|
46
|
+
depth = _depth.get() + 1
|
|
47
|
+
if depth > MAX_FRESH_STACKS:
|
|
48
|
+
raise RecursionError("maximum recursion depth exceeded (pyfect fresh stacks)")
|
|
49
|
+
context = contextvars.copy_context()
|
|
50
|
+
context.run(_depth.set, depth)
|
|
51
|
+
with ThreadPoolExecutor(1, thread_name_prefix="pyfect-deep-stack") as executor:
|
|
52
|
+
return executor.submit(context.run, f).result()
|
pyfect/_stack_frame.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Stack frames: where spans and traced functions were written, recorded on
|
|
2
|
+
the causes of failures inside them (Effect's ``References.CurrentStackFrame``
|
|
3
|
+
and ``Cause.StackTrace`` / ``InterruptorStackTrace``).
|
|
4
|
+
|
|
5
|
+
Apart from ``pyfect.tracer`` so the fiber runtime can read the current frame
|
|
6
|
+
without importing the span functions.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from types import CodeType
|
|
16
|
+
from typing import final
|
|
17
|
+
|
|
18
|
+
from pyfect.context import Reference, Tag
|
|
19
|
+
|
|
20
|
+
type CaptureStackTrace = bool | Callable[[], str | None]
|
|
21
|
+
"""Effect's ``captureStackTrace``: ``True`` records the call site, ``False``
|
|
22
|
+
records nothing, a function gives the location when it is first read."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
26
|
+
class StackFrame:
|
|
27
|
+
"""One frame: a name, its location (read lazily, ``None`` if unknown, in
|
|
28
|
+
the form ``"at function (file:line)"``) and the frame it runs inside."""
|
|
29
|
+
|
|
30
|
+
name: str
|
|
31
|
+
stack: Callable[[], str | None] = field(repr=False)
|
|
32
|
+
parent: StackFrame | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CurrentStackFrame(Reference[StackFrame | None]):
|
|
36
|
+
"""The innermost frame the fiber runs in: a span from ``with_span`` or a
|
|
37
|
+
call of an ``@fx.fn`` function. ``None`` outside every frame."""
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def default(cls) -> StackFrame | None:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class StackTrace(Tag[StackFrame]):
|
|
45
|
+
"""The annotation a failure's reasons get: the frame it happened in."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class InterruptorStackTrace(Tag[StackFrame]):
|
|
49
|
+
"""The annotation an interruption gets: the frame of the fiber that
|
|
50
|
+
interrupted."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _no_location() -> None:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@final
|
|
58
|
+
class _CallSite:
|
|
59
|
+
"""A call site, rendered when first read. Keeps the code object and the
|
|
60
|
+
line, never the frame, so the caller's locals are not kept alive."""
|
|
61
|
+
|
|
62
|
+
__slots__ = ("_code", "_line", "_text")
|
|
63
|
+
|
|
64
|
+
def __init__(self, code: CodeType, line: int) -> None:
|
|
65
|
+
self._code = code
|
|
66
|
+
self._line = line
|
|
67
|
+
self._text: str | None = None
|
|
68
|
+
|
|
69
|
+
def __call__(self) -> str:
|
|
70
|
+
if self._text is None:
|
|
71
|
+
code = self._code
|
|
72
|
+
self._text = f"at {code.co_qualname} ({code.co_filename}:{self._line})"
|
|
73
|
+
return self._text
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def call_site(depth: int) -> Callable[[], str | None]:
|
|
77
|
+
"""The location of the caller ``depth`` frames above the function
|
|
78
|
+
calling this one (1 is that function's caller)."""
|
|
79
|
+
frame = sys._getframe(depth + 1) # pyright: ignore[reportPrivateUsage]
|
|
80
|
+
return _CallSite(frame.f_code, frame.f_lineno)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def stack_of(capture: CaptureStackTrace, depth: int) -> Callable[[], str | None]:
|
|
84
|
+
"""The location a frame records for ``capture``: the call site ``depth``
|
|
85
|
+
frames above the caller when ``True``."""
|
|
86
|
+
if capture is True:
|
|
87
|
+
return call_site(depth + 1)
|
|
88
|
+
if capture is False:
|
|
89
|
+
return _no_location
|
|
90
|
+
return capture
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def push(name: str, stack: Callable[[], str | None]) -> Callable[[StackFrame | None], StackFrame]:
|
|
94
|
+
"""The update that runs inside a new frame ``name``."""
|
|
95
|
+
return lambda parent: StackFrame(name, stack, parent)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
_LOCATION = re.compile(r"\((.*)\)")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def render(frame: StackFrame | None) -> list[str]:
|
|
102
|
+
"""Effect's trace lines for ``frame`` and its parents (ten at most):
|
|
103
|
+
`` at name (location)``."""
|
|
104
|
+
lines: list[str] = []
|
|
105
|
+
while frame is not None and len(lines) < 10:
|
|
106
|
+
location = frame.stack()
|
|
107
|
+
if location is None:
|
|
108
|
+
lines.append(f" at {frame.name}")
|
|
109
|
+
else:
|
|
110
|
+
inner = _LOCATION.search(location)
|
|
111
|
+
where = inner.group(1) if inner else location.removeprefix("at ")
|
|
112
|
+
lines.append(f" at {frame.name} ({where})")
|
|
113
|
+
frame = frame.parent
|
|
114
|
+
return lines
|
pyfect/_string_hash.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Effect's ``Hash.string``: the djb2 hash of a string's UTF-16 code units,
|
|
2
|
+
with JavaScript's 32-bit integer arithmetic and ``Hash.optimize``. Hash
|
|
3
|
+
rings and shard ids depend on the exact values, so they match Effect's."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from array import array
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
_UTF16_NATIVE = "utf-16-le" if sys.byteorder == "little" else "utf-16-be"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _int32(n: int) -> int:
|
|
15
|
+
n &= 0xFFFFFFFF
|
|
16
|
+
return n - 0x1_0000_0000 if n & 0x8000_0000 else n
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def optimize(n: int) -> int:
|
|
20
|
+
"""``(n & 0xbfffffff) | ((n >>> 1) & 0x40000000)`` as a signed int32."""
|
|
21
|
+
u = n & 0xFFFFFFFF
|
|
22
|
+
return _int32((u & 0xBFFFFFFF) | ((u >> 1) & 0x40000000))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def string_hash(text: str) -> int:
|
|
26
|
+
"""Effect's ``Hash.string(text)``."""
|
|
27
|
+
# Unsigned 32-bit arithmetic: the same bits as JavaScript's int32 ops.
|
|
28
|
+
if text.isascii():
|
|
29
|
+
units: Sequence[int] = text.encode("ascii")
|
|
30
|
+
else:
|
|
31
|
+
units = array("H", text.encode(_UTF16_NATIVE, "surrogatepass"))
|
|
32
|
+
h = 5381
|
|
33
|
+
for unit in reversed(units):
|
|
34
|
+
h = ((h * 33) & 0xFFFFFFFF) ^ unit
|
|
35
|
+
return optimize(h)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""The queue a subscription to an outside publisher (a PostgreSQL
|
|
2
|
+
``LISTEN``, a Redis ``SUBSCRIBE``) hands the program.
|
|
3
|
+
|
|
4
|
+
It holds at most ``capacity`` messages the program has not taken. A
|
|
5
|
+
publisher that gets further ahead fails the subscription with a
|
|
6
|
+
``FellBehind`` cause (the messages already held are taken first), the way
|
|
7
|
+
Redis drops a subscriber whose output buffer is full: memory stays bounded
|
|
8
|
+
and nothing is lost silently.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import Callable, Sequence
|
|
14
|
+
from typing import Final
|
|
15
|
+
|
|
16
|
+
from pyfect.cause import fail as fail_cause
|
|
17
|
+
from pyfect.queue import Queue
|
|
18
|
+
|
|
19
|
+
DEFAULT_CAPACITY: Final = 1024
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FellBehind(Exception):
|
|
23
|
+
"""The program left ``capacity`` messages untaken while more arrived."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, what: str, capacity: int) -> None:
|
|
26
|
+
super().__init__(
|
|
27
|
+
f"{what}: the subscriber fell behind, holding {capacity} messages it had not "
|
|
28
|
+
"taken; take faster or pass a larger capacity"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def check_capacity(capacity: int) -> int:
|
|
33
|
+
"""``capacity`` when it can hold a message; raises ``ValueError``."""
|
|
34
|
+
if isinstance(capacity, bool) or capacity < 1:
|
|
35
|
+
raise ValueError(f"capacity must be a positive int, got {capacity!r}")
|
|
36
|
+
return capacity
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def offer[A, E](queue: Queue[A, E], items: Sequence[A], behind: Callable[[], E]) -> bool:
|
|
40
|
+
"""Add ``items`` to ``queue`` (a ``Queue.dropping(capacity)``) without
|
|
41
|
+
waiting; when they do not all fit, fail
|
|
42
|
+
``queue`` with ``behind()`` and give ``False``: stop delivering. Call on
|
|
43
|
+
the queue's fibers' scheduler thread."""
|
|
44
|
+
if not queue.offer_all_unsafe(items):
|
|
45
|
+
return True
|
|
46
|
+
queue.fail_cause_unsafe(fail_cause(behind()))
|
|
47
|
+
return False
|
pyfect/_tags.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""``Tags[T]``: the classes a ``catch_tag`` or ``tap_error_tag`` matches.
|
|
2
|
+
|
|
3
|
+
One class, or a tuple of any number of them (Effect's ``catchTag`` takes
|
|
4
|
+
any number of tags). ``T`` is solved as the union of the classes::
|
|
5
|
+
|
|
6
|
+
eff.pipe(fx.catch_tag((A, B, C, D), lambda e: ...)) # e: A | B | C | D
|
|
7
|
+
|
|
8
|
+
Every combinator taking classes to match uses this alias, so none caps
|
|
9
|
+
how many it takes (``tests/test_tag_tuples.py``).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
type Tags[T] = type[T] | tuple[type[T], ...]
|
|
13
|
+
|
|
14
|
+
__all__ = ["Tags"]
|
pyfect/_tracer_ref.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""``CurrentTracer``, apart from ``pyfect.tracer`` so the fiber runtime can
|
|
2
|
+
read a tracer's context hook without importing the span functions."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from pyfect.context import Reference
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pyfect.tracer import Tracer
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CurrentTracer(Reference["Tracer"]):
|
|
15
|
+
"""The tracer spans are made with. ``native_tracer`` by default."""
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def default(cls) -> Tracer:
|
|
19
|
+
from pyfect.tracer._native import native_tracer
|
|
20
|
+
|
|
21
|
+
return native_tracer
|
pyfect/_tx_ops.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Single-step transactional reads and writes over ``TxRef`` values, the
|
|
2
|
+
building blocks of the transactional collections.
|
|
3
|
+
|
|
4
|
+
Each runs in the running transaction, or as a transaction of its own, in one
|
|
5
|
+
fiber step, so a read-then-write needs no ``tx`` wrapper to be atomic.
|
|
6
|
+
"""
|
|
7
|
+
# pyright: reportPrivateUsage=false
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from pyfect._core import Effect
|
|
15
|
+
from pyfect.option import Option
|
|
16
|
+
from pyfect.transaction import Transaction, TransactionMeta, transactional, tx
|
|
17
|
+
from pyfect.tx_ref import TxRef
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def read[A, B](ref: TxRef[A], f: Callable[[A], B]) -> Effect[B]:
|
|
21
|
+
"""``f`` of the ref's value in the transaction."""
|
|
22
|
+
return transactional(lambda state: f(state._entry(ref)._value))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def read2[A, B, C](first: TxRef[A], second: TxRef[B], f: Callable[[A, B], C]) -> Effect[C]:
|
|
26
|
+
"""``f`` of two refs' values, read in one transaction."""
|
|
27
|
+
return transactional(lambda state: f(state._entry(first)._value, state._entry(second)._value))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def update_reading[A, B](ref: TxRef[A], other: TxRef[B], f: Callable[[A, B], A]) -> Effect[None]:
|
|
31
|
+
"""Replace ``ref``'s value with ``f(value, other's value)``, both read
|
|
32
|
+
in one transaction."""
|
|
33
|
+
|
|
34
|
+
def run(state: TransactionMeta) -> None:
|
|
35
|
+
source = state._entry(other)._value
|
|
36
|
+
entry = state._entry(ref)
|
|
37
|
+
entry._value = f(entry._value, source)
|
|
38
|
+
entry._written = True
|
|
39
|
+
|
|
40
|
+
return transactional(run)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def modify_or_skip[A, B](ref: TxRef[A], f: Callable[[A], tuple[B, Option[A]]]) -> Effect[B]:
|
|
44
|
+
"""Like ``TxRef.modify``, but ``f`` says whether to write: ``Some(value)``
|
|
45
|
+
writes ``value`` (waking the ref's waiters even when it is the current
|
|
46
|
+
value), ``None`` leaves the ref unwritten (Effect's ``remove`` /
|
|
47
|
+
``modify`` on a missing key)."""
|
|
48
|
+
|
|
49
|
+
def run(state: TransactionMeta) -> B:
|
|
50
|
+
entry = state._entry(ref)
|
|
51
|
+
result, value = f(entry._value)
|
|
52
|
+
if value is not None:
|
|
53
|
+
entry._value = value.value
|
|
54
|
+
entry._written = True
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
return transactional(run)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def decide[A, E](f: Callable[[TransactionMeta], Effect[A, E, Transaction]]) -> Effect[A, E]:
|
|
61
|
+
"""``f``'s verdict, in the running transaction or one of its own. ``f``
|
|
62
|
+
reads and writes refs with ``value`` and ``write`` in one step and
|
|
63
|
+
returns the outcome: a success, a failure, or ``tx_retry``."""
|
|
64
|
+
return tx(Transaction().flat_map(f))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def value[A](state: TransactionMeta, ref: TxRef[A]) -> A:
|
|
68
|
+
"""The ref's value in the transaction."""
|
|
69
|
+
entry: Any = state._entry(ref)
|
|
70
|
+
return entry._value
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def write[A](state: TransactionMeta, ref: TxRef[A], new: A) -> None:
|
|
74
|
+
"""Write ``new`` to the ref in the transaction."""
|
|
75
|
+
entry = state._entry(ref)
|
|
76
|
+
entry._value = new
|
|
77
|
+
entry._written = True
|
pyfect/_unsubscripted.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""``Unsubscripted``: the default type argument of a class whose constructors
|
|
2
|
+
honour a subscript.
|
|
3
|
+
|
|
4
|
+
``Ref[int | None].make(None)`` makes a ``Ref[int | None]``, and ``Ref.make(None)``
|
|
5
|
+
infers a ``Ref[None]``. Pyright gives a class written without type arguments
|
|
6
|
+
``Unknown`` ones, which no overload can tell from a subscript, so the class's
|
|
7
|
+
type parameters default to this marker: an overload for ``type[Ref[Unsubscripted]]``
|
|
8
|
+
infers from the arguments, the next takes the subscript
|
|
9
|
+
(research/typing/ctor_1_subscript.py). A bare annotation (``r: Ref``) gets the marker
|
|
10
|
+
too, so using it is a type error naming ``Unsubscripted``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Never, final
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@final
|
|
17
|
+
class Unsubscripted:
|
|
18
|
+
"""Never instantiated: it only stands for a missing type argument."""
|
|
19
|
+
|
|
20
|
+
__slots__ = ()
|
|
21
|
+
|
|
22
|
+
def __new__(cls) -> Never:
|
|
23
|
+
raise TypeError("Unsubscripted only stands for a missing type argument")
|
pyfect/_url.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""The URL value every pyfect API gives back, and URL parsing as WHATWG's
|
|
2
|
+
``URL.canParse`` sees it, for ``config.url`` and the schema codecs."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
from urllib.parse import SplitResult, urlsplit
|
|
8
|
+
|
|
9
|
+
_SCHEME = re.compile(r"[A-Za-z][A-Za-z0-9+.\-]*")
|
|
10
|
+
_SPECIAL_SCHEMES = frozenset({"http", "https", "ws", "wss", "ftp"})
|
|
11
|
+
# Schemes whose URLs always have an authority (``//``), even an empty one.
|
|
12
|
+
_AUTHORITY_SCHEMES = _SPECIAL_SCHEMES | {"file"}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Url(SplitResult):
|
|
16
|
+
"""A URL: a ``urllib.parse.SplitResult`` whose ``str()`` is the URL
|
|
17
|
+
written out (``f"{url}/items"`` builds a URL), as JavaScript's ``URL``
|
|
18
|
+
writes its ``href``. ``Url(*urlsplit(text))`` makes one from text that
|
|
19
|
+
is not checked; ``http.url.from_string`` parses and normalizes it."""
|
|
20
|
+
|
|
21
|
+
__slots__ = ()
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return write(self)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def write(url: SplitResult) -> str:
|
|
28
|
+
"""``url`` written out, as ``URL.href``."""
|
|
29
|
+
authority = f"//{url.netloc}" if url.netloc or url.scheme in _AUTHORITY_SCHEMES else ""
|
|
30
|
+
query = f"?{url.query}" if url.query else ""
|
|
31
|
+
fragment = f"#{url.fragment}" if url.fragment else ""
|
|
32
|
+
return f"{url.scheme}:{authority}{url.path}{query}{fragment}"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def as_url(url: SplitResult) -> Url:
|
|
36
|
+
"""``url`` as a ``Url`` (itself when it is one)."""
|
|
37
|
+
return url if isinstance(url, Url) else Url._make(url)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def parse_url(text: str) -> Url | None:
|
|
41
|
+
"""``text`` as a URL: an absolute one with a valid scheme, a host for
|
|
42
|
+
the special schemes and a numeric port in range; ``None`` otherwise."""
|
|
43
|
+
if any(ch <= " " or ch == "\x7f" for ch in text):
|
|
44
|
+
return None
|
|
45
|
+
try:
|
|
46
|
+
url = urlsplit(text)
|
|
47
|
+
_ = url.port # raises ValueError for a port that is not a number in range
|
|
48
|
+
except ValueError:
|
|
49
|
+
return None
|
|
50
|
+
if not _SCHEME.fullmatch(url.scheme):
|
|
51
|
+
return None
|
|
52
|
+
if url.scheme.lower() in _SPECIAL_SCHEMES and not url.hostname:
|
|
53
|
+
return None
|
|
54
|
+
return Url._make(url)
|
pyfect/_waiters.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""A list of parked fibers waiting for a state change.
|
|
2
|
+
|
|
3
|
+
Waiters are woken to *retry*, never handed a value: whatever they wait
|
|
4
|
+
for is taken in their own step. That way an interrupt that lands between
|
|
5
|
+
the wake-up and the retry cannot lose anything. Spurious wake-ups are
|
|
6
|
+
harmless: a woken fiber that finds nothing simply waits again.
|
|
7
|
+
|
|
8
|
+
``wait`` takes the condition the caller is waiting for and checks it again as
|
|
9
|
+
the fiber parks: a fiber can pause between its own check and the park, and a
|
|
10
|
+
notify landing in that gap would otherwise find nobody to wake. The list keeps
|
|
11
|
+
the condition, and ``notify_ready`` wakes every waiter whose condition holds,
|
|
12
|
+
as Effect's ``releaseTakers`` does. There is no "wake exactly one": a woken
|
|
13
|
+
fiber can pause for its step budget before it retries and be interrupted
|
|
14
|
+
there, and a wake-up spent on it would leave the waiter behind it parked next
|
|
15
|
+
to the item it was waiting for.
|
|
16
|
+
|
|
17
|
+
A wake-up resumes the fiber at once (fibers resume synchronously, as in
|
|
18
|
+
Effect). Where Effect defers it to a scheduled task instead (a queue's
|
|
19
|
+
``scheduleReleaseTaker``, a semaphore release), use ``notify_ready_soon``
|
|
20
|
+
or ``wake_soon``, so the notifier finishes its step first.
|
|
21
|
+
"""
|
|
22
|
+
# pyright: reportPrivateUsage=false
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import contextlib
|
|
27
|
+
from collections import deque
|
|
28
|
+
from collections.abc import Callable
|
|
29
|
+
from typing import TYPE_CHECKING
|
|
30
|
+
|
|
31
|
+
from pyfect._core import Effect, _Callback, _Succeed, _Sync
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from pyfect.scheduler import Scheduler
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _Waiter:
|
|
38
|
+
__slots__ = ("ready", "wake")
|
|
39
|
+
|
|
40
|
+
def __init__(self, ready: Callable[[], bool], wake: Callable[[], None]) -> None:
|
|
41
|
+
self.ready = ready
|
|
42
|
+
self.wake = wake
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def wake_soon(task: Callable[[], None]) -> None:
|
|
46
|
+
"""Run ``task`` in a task scheduled on the running fiber's scheduler
|
|
47
|
+
(Effect's ``fiber.currentDispatcher.scheduleTask``), or now when no
|
|
48
|
+
fiber is running here."""
|
|
49
|
+
from pyfect._fiber import current_fiber
|
|
50
|
+
|
|
51
|
+
fiber = current_fiber()
|
|
52
|
+
if fiber is None:
|
|
53
|
+
task()
|
|
54
|
+
else:
|
|
55
|
+
fiber._scheduler.schedule(task)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class WaitList:
|
|
59
|
+
__slots__ = ("_soon", "_waiters")
|
|
60
|
+
|
|
61
|
+
def __init__(self) -> None:
|
|
62
|
+
self._waiters: deque[_Waiter] = deque()
|
|
63
|
+
# Whether a ``notify_ready_soon`` task is pending.
|
|
64
|
+
self._soon = False
|
|
65
|
+
|
|
66
|
+
def wait(self, ready: Callable[[], bool]) -> Effect[None]:
|
|
67
|
+
"""Park until notified, or not at all if ``ready()`` already holds."""
|
|
68
|
+
|
|
69
|
+
def register(resume: Callable[[Effect[None]], None]) -> Effect[None] | None:
|
|
70
|
+
if ready():
|
|
71
|
+
resume(_Succeed(None))
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
waiter = _Waiter(ready, lambda: resume(_Succeed(None)))
|
|
75
|
+
self._waiters.append(waiter)
|
|
76
|
+
return _Sync(lambda: self._discard(waiter))
|
|
77
|
+
|
|
78
|
+
return _Callback(register)
|
|
79
|
+
|
|
80
|
+
def notify_ready(self) -> None:
|
|
81
|
+
"""Wake every waiter whose condition holds now."""
|
|
82
|
+
ready: list[_Waiter] = []
|
|
83
|
+
parked: deque[_Waiter] = deque()
|
|
84
|
+
for waiter in self._waiters:
|
|
85
|
+
(ready if waiter.ready() else parked).append(waiter)
|
|
86
|
+
self._waiters = parked
|
|
87
|
+
for waiter in ready:
|
|
88
|
+
waiter.wake()
|
|
89
|
+
|
|
90
|
+
def notify_ready_soon(self, scheduler: Scheduler | None = None) -> None:
|
|
91
|
+
"""``notify_ready`` in a task on ``scheduler``, else as ``wake_soon``;
|
|
92
|
+
several calls before it runs share it, as Effect's
|
|
93
|
+
``scheduleReleaseTaker``."""
|
|
94
|
+
if self._soon or not self._waiters:
|
|
95
|
+
return
|
|
96
|
+
self._soon = True
|
|
97
|
+
|
|
98
|
+
def flush() -> None:
|
|
99
|
+
self._soon = False
|
|
100
|
+
self.notify_ready()
|
|
101
|
+
|
|
102
|
+
if scheduler is not None:
|
|
103
|
+
scheduler.schedule(flush)
|
|
104
|
+
return
|
|
105
|
+
from pyfect._fiber import current_fiber
|
|
106
|
+
from pyfect.scheduler import SyncOnlyScheduler
|
|
107
|
+
|
|
108
|
+
# Effect's queue holds the dispatcher of the fiber that made it, whichever
|
|
109
|
+
# fiber offers: under a ``run_sync`` the root flushes it.
|
|
110
|
+
fiber = current_fiber()
|
|
111
|
+
run = None if fiber is None else fiber._scheduler
|
|
112
|
+
if isinstance(run, SyncOnlyScheduler):
|
|
113
|
+
run.schedule_as_root(flush)
|
|
114
|
+
else:
|
|
115
|
+
wake_soon(flush)
|
|
116
|
+
|
|
117
|
+
def notify_all(self) -> None:
|
|
118
|
+
waiters, self._waiters = self._waiters, deque()
|
|
119
|
+
for waiter in waiters:
|
|
120
|
+
waiter.wake()
|
|
121
|
+
|
|
122
|
+
def __len__(self) -> int:
|
|
123
|
+
return len(self._waiters)
|
|
124
|
+
|
|
125
|
+
def _discard(self, waiter: _Waiter) -> None:
|
|
126
|
+
with contextlib.suppress(ValueError):
|
|
127
|
+
self._waiters.remove(waiter)
|