pyfect-dev 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Sink constructors: from transforms, channels, effects and stream
|
|
2
|
+
pipelines; values and failures; and sinks writing to queues and pubsubs."""
|
|
3
|
+
# pyright: reportPrivateUsage=false
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Never, overload
|
|
9
|
+
|
|
10
|
+
from pyfect import effect as _effect
|
|
11
|
+
from pyfect._core import Effect, _Locally, _Succeed, _Sync
|
|
12
|
+
from pyfect._effect_args import require_effect
|
|
13
|
+
from pyfect.cause import Cause
|
|
14
|
+
from pyfect.channel import Channel
|
|
15
|
+
from pyfect.channel._base import end_with, require_channel
|
|
16
|
+
from pyfect.channel._loop import PullLoop
|
|
17
|
+
from pyfect.errors import Done
|
|
18
|
+
from pyfect.pull import catch_done
|
|
19
|
+
from pyfect.scope import CloseableScope, Scope
|
|
20
|
+
from pyfect.sink._combinators import map_end, with_duration
|
|
21
|
+
from pyfect.sink._for_each import for_each_array
|
|
22
|
+
from pyfect.sink._sink import AnySink, End, Sink, Upstream, require_sink
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from pyfect.pubsub import PubSub
|
|
26
|
+
from pyfect.queue import Enqueue
|
|
27
|
+
from pyfect.stream import Stream
|
|
28
|
+
|
|
29
|
+
type AnyEffect = Effect[Any, Any, Any]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _ignore(_: object) -> None:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_END_VOID: Effect[End[None]] = _Succeed((None, None))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def from_transform[In, A, L = Never, E = Never, R = Never](
|
|
40
|
+
transform: Callable[[Upstream[In], CloseableScope], Effect[End[A, L], E, R]],
|
|
41
|
+
) -> Sink[A, In, L, E, R]:
|
|
42
|
+
"""A sink from ``transform(upstream, scope)``: an effect that pulls
|
|
43
|
+
non-empty tuples from ``upstream`` (which ends with ``Done()``) and
|
|
44
|
+
succeeds with the sink's ``End``. Resources go in ``scope``."""
|
|
45
|
+
return Sink(transform)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def to_channel[A, In, L, E, R](
|
|
49
|
+
sink: Sink[A, In, L, E, R],
|
|
50
|
+
) -> Channel[Never, E, End[A, L], tuple[In, ...], Never, None, R]:
|
|
51
|
+
"""The channel that reads what the sink reads and ends with its
|
|
52
|
+
``End``."""
|
|
53
|
+
require_sink(sink, "sink")
|
|
54
|
+
|
|
55
|
+
def transform(upstream: Any, scope: CloseableScope) -> AnyEffect:
|
|
56
|
+
return _Succeed(sink._transform(upstream, scope).flat_map(end_with))
|
|
57
|
+
|
|
58
|
+
built: Channel[Any, Any, Any, Any, Any, Any, Any] = Channel(transform)
|
|
59
|
+
return built
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def from_channel[A, In, L = Never, E = Never, R = Never](
|
|
63
|
+
channel: Channel[Never, E, End[A, L], tuple[In, ...], Never, None, R],
|
|
64
|
+
) -> Sink[A, In, L, E, R]:
|
|
65
|
+
"""The sink that feeds its input to ``channel`` and ends with the
|
|
66
|
+
``End`` the channel ends with."""
|
|
67
|
+
require_channel(channel, "channel")
|
|
68
|
+
|
|
69
|
+
def transform(upstream: Upstream[Any], scope: CloseableScope) -> AnyEffect:
|
|
70
|
+
drained = channel._transform(upstream, scope).flat_map(lambda pull: PullLoop(pull, _ignore))
|
|
71
|
+
return catch_done(drained, _Succeed)
|
|
72
|
+
|
|
73
|
+
return Sink(transform)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@overload
|
|
77
|
+
def make[In, B, E = Never](
|
|
78
|
+
f: Callable[[Stream[In]], Effect[B, E, Scope]],
|
|
79
|
+
) -> Sink[B, In, Never, E]: ...
|
|
80
|
+
@overload
|
|
81
|
+
def make[In, B, E = Never, R = Never](
|
|
82
|
+
f: Callable[[Stream[In]], Effect[B, E, Scope | R]],
|
|
83
|
+
) -> Sink[B, In, Never, E, R]: ...
|
|
84
|
+
def make(f: Callable[[Any], AnyEffect]) -> AnySink:
|
|
85
|
+
"""A sink from a stream pipeline: ``f`` gets the sink's input as a
|
|
86
|
+
``Stream`` and returns the effect whose value is the sink's (Effect's
|
|
87
|
+
``Sink.make<In>()(...)``). The effect runs with the sink's ``Scope``::
|
|
88
|
+
|
|
89
|
+
sink.make(lambda s: s.run_sum())
|
|
90
|
+
"""
|
|
91
|
+
from pyfect.stream import from_pull
|
|
92
|
+
|
|
93
|
+
def transform(upstream: Upstream[Any], scope: CloseableScope) -> AnyEffect:
|
|
94
|
+
run = f(from_pull(_Succeed(upstream))).map(lambda value: (value, None))
|
|
95
|
+
return _Locally(run, lambda ctx: ctx.add(Scope, scope))
|
|
96
|
+
|
|
97
|
+
return Sink(transform)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def from_effect_end[A, L = Never, E = Never, R = Never](
|
|
101
|
+
eff: Effect[End[A, L], E, R],
|
|
102
|
+
) -> Sink[A, object, L, E, R]:
|
|
103
|
+
"""A sink that reads nothing and ends with what ``eff`` succeeds
|
|
104
|
+
with."""
|
|
105
|
+
require_effect(eff, "eff")
|
|
106
|
+
return Sink(lambda _upstream, _scope: eff)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def from_effect[A, E = Never, R = Never](eff: Effect[A, E, R]) -> Sink[A, object, Never, E, R]:
|
|
110
|
+
"""A sink that reads nothing and succeeds with ``eff``'s value."""
|
|
111
|
+
require_effect(eff, "eff")
|
|
112
|
+
return from_effect_end(eff.map(lambda value: (value, None)))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def succeed[A, L = Never](value: A, leftover: tuple[L, ...] | None = None) -> Sink[A, object, L]:
|
|
116
|
+
"""A sink that reads nothing and succeeds with ``value``, leaving
|
|
117
|
+
``leftover`` over (an empty tuple is no leftover)."""
|
|
118
|
+
end: End[A, L] = (value, leftover or None)
|
|
119
|
+
return from_effect_end(_Succeed(end))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def sync[A](thunk: Callable[[], A]) -> Sink[A]:
|
|
123
|
+
"""A sink that reads nothing and succeeds with ``thunk()``, called
|
|
124
|
+
each time it runs."""
|
|
125
|
+
return from_effect_end(_Sync(lambda: (thunk(), None)))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def suspend[A, In, L, E, R](thunk: Callable[[], Sink[A, In, L, E, R]]) -> Sink[A, In, L, E, R]:
|
|
129
|
+
"""The sink ``thunk()`` returns, built each time it runs."""
|
|
130
|
+
return Sink(lambda upstream, scope: thunk()._transform(upstream, scope))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def fail[E](error: E) -> Sink[Never, object, Never, E]:
|
|
134
|
+
"""A sink that reads nothing and fails with ``error``."""
|
|
135
|
+
return from_effect_end(_effect.fail(error))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def fail_sync[E](thunk: Callable[[], E]) -> Sink[Never, object, Never, E]:
|
|
139
|
+
"""A sink that reads nothing and fails with ``thunk()``."""
|
|
140
|
+
return from_effect_end(_effect.fail_sync(thunk))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def fail_cause[E](cause: Cause[E]) -> Sink[Never, object, Never, E]:
|
|
144
|
+
"""A sink that reads nothing and fails with ``cause``."""
|
|
145
|
+
return from_effect_end(_effect.fail_cause(cause))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def fail_cause_sync[E](thunk: Callable[[], Cause[E]]) -> Sink[Never, object, Never, E]:
|
|
149
|
+
"""A sink that reads nothing and fails with the cause ``thunk()``."""
|
|
150
|
+
return from_effect_end(_effect.fail_cause_sync(thunk))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def die(defect: BaseException) -> Sink[Never]:
|
|
154
|
+
"""A sink that reads nothing and dies with ``defect``."""
|
|
155
|
+
return from_effect_end(_effect.die(defect))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
never: Sink[Never] = from_effect_end(_effect.never)
|
|
159
|
+
"""A sink that reads nothing and never ends."""
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _drain(upstream: Upstream[object], _scope: CloseableScope) -> Effect[End[None]]:
|
|
163
|
+
return catch_done(PullLoop(upstream, _ignore), lambda _: _END_VOID)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
drain: Sink[None] = Sink(_drain)
|
|
167
|
+
"""A sink that reads all of its input and succeeds with ``None``."""
|
|
168
|
+
|
|
169
|
+
timed: Sink[float] = map_end(with_duration(drain), lambda end: (end[0][1], None))
|
|
170
|
+
"""A sink that reads all of its input and succeeds with how long that
|
|
171
|
+
took, in seconds."""
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def from_queue[A](queue: Enqueue[A, Done]) -> Sink[None, A]:
|
|
175
|
+
"""A sink that offers its input to ``queue`` and ends the queue when the
|
|
176
|
+
input ends."""
|
|
177
|
+
|
|
178
|
+
def transform(upstream: Upstream[A], _scope: CloseableScope) -> AnyEffect:
|
|
179
|
+
offered = PullLoop(upstream.flat_map(queue.offer_all), _ignore)
|
|
180
|
+
return catch_done(offered, lambda _: queue.end().and_then(_END_VOID))
|
|
181
|
+
|
|
182
|
+
return Sink(transform)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def from_pubsub[A](pubsub: PubSub[A]) -> Sink[None, A]:
|
|
186
|
+
"""A sink that publishes its input to ``pubsub``."""
|
|
187
|
+
return for_each_array(pubsub.publish_all)
|
pyfect/sink/_context.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Services for sinks: the curried providers (used with ``.pipe``, since
|
|
2
|
+
they remove what they provide from ``R``) and ``unwrap``."""
|
|
3
|
+
# pyright: reportPrivateUsage=false
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Any, Never, overload
|
|
8
|
+
|
|
9
|
+
from pyfect._core import Effect, _Locally
|
|
10
|
+
from pyfect._effect_args import require_effect
|
|
11
|
+
from pyfect.context import Context, TagLike, tag_class
|
|
12
|
+
from pyfect.scope import CloseableScope, Scope
|
|
13
|
+
from pyfect.sink._combinators import transform_effect
|
|
14
|
+
from pyfect.sink._sink import AnySink, Sink, Upstream, require_sink
|
|
15
|
+
|
|
16
|
+
type Sink_ = Sink[Any, Any, Any, Any, Any]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class _ProvideService[S, T]:
|
|
20
|
+
__slots__ = ("service", "tag")
|
|
21
|
+
|
|
22
|
+
def __init__(self, tag: type[Any], service: S) -> None:
|
|
23
|
+
self.tag = tag
|
|
24
|
+
self.service = service
|
|
25
|
+
|
|
26
|
+
@overload
|
|
27
|
+
def __call__[A, In, L, E](self, sink: Sink[A, In, L, E, T]) -> Sink[A, In, L, E]: ...
|
|
28
|
+
@overload
|
|
29
|
+
def __call__[A, In, L, E, R](self, sink: Sink[A, In, L, E, T | R]) -> Sink[A, In, L, E, R]: ...
|
|
30
|
+
def __call__(self, sink: AnySink) -> Sink_:
|
|
31
|
+
require_sink(sink, "sink")
|
|
32
|
+
tag, service = self.tag, self.service
|
|
33
|
+
return transform_effect(sink, lambda eff: _Locally(eff, lambda ctx: ctx.add(tag, service)))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def provide_service[S, T](tag: TagLike[S, T], service: S) -> _ProvideService[S, T]:
|
|
37
|
+
"""Provide one service to a sink and remove it from ``R``::
|
|
38
|
+
|
|
39
|
+
sink.pipe(provide_service(Database, InMemoryDb()))
|
|
40
|
+
"""
|
|
41
|
+
return _ProvideService(tag_class(tag), service)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class _ProvideContext[RO]:
|
|
45
|
+
__slots__ = ("ctx",)
|
|
46
|
+
|
|
47
|
+
def __init__(self, ctx: Context[RO]) -> None:
|
|
48
|
+
self.ctx = ctx
|
|
49
|
+
|
|
50
|
+
@overload
|
|
51
|
+
def __call__[A, In, L, E](self, sink: Sink[A, In, L, E, RO]) -> Sink[A, In, L, E]: ...
|
|
52
|
+
@overload
|
|
53
|
+
def __call__[A, In, L, E, R](self, sink: Sink[A, In, L, E, RO | R]) -> Sink[A, In, L, E, R]: ...
|
|
54
|
+
def __call__(self, sink: AnySink) -> Sink_:
|
|
55
|
+
require_sink(sink, "sink")
|
|
56
|
+
ctx = self.ctx
|
|
57
|
+
return transform_effect(sink, lambda eff: _Locally(eff, lambda current: current.merge(ctx)))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def provide_context[RO](ctx: Context[RO]) -> _ProvideContext[RO]:
|
|
61
|
+
"""Provide every service of ``ctx`` to a sink and remove them from
|
|
62
|
+
``R``::
|
|
63
|
+
|
|
64
|
+
sink.pipe(provide_context(ctx))
|
|
65
|
+
"""
|
|
66
|
+
return _ProvideContext(ctx)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@overload
|
|
70
|
+
def unwrap[A, In, L, EE = Never, RR = Never](
|
|
71
|
+
eff: Effect[Sink[A, In, L, EE, RR], EE, Scope],
|
|
72
|
+
) -> Sink[A, In, L, EE, RR]: ...
|
|
73
|
+
@overload
|
|
74
|
+
def unwrap[A, In, L, EE = Never, RR = Never](
|
|
75
|
+
eff: Effect[Sink[A, In, L, EE, RR], EE, Scope | RR],
|
|
76
|
+
) -> Sink[A, In, L, EE, RR]: ...
|
|
77
|
+
def unwrap(eff: Effect[AnySink, Any, Any]) -> Sink_:
|
|
78
|
+
"""The sink ``eff`` succeeds with, built each time it runs. ``eff``
|
|
79
|
+
runs with the sink's ``Scope``, so its resources live as long as the
|
|
80
|
+
sink's run."""
|
|
81
|
+
require_effect(eff, "eff")
|
|
82
|
+
|
|
83
|
+
def transform(upstream: Upstream[Any], scope: CloseableScope) -> Effect[Any, Any, Any]:
|
|
84
|
+
built: Effect[AnySink, Any, Any] = _Locally(eff, lambda ctx: ctx.add(Scope, scope))
|
|
85
|
+
return built.flat_map(lambda sink: sink._transform(upstream, scope))
|
|
86
|
+
|
|
87
|
+
return Sink(transform)
|
pyfect/sink/_folds.py
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"""Sinks that fold their input into a state: ``fold``, the ``reduce``
|
|
2
|
+
family, and the folds with a fixed answer (``sum``, ``count``,
|
|
3
|
+
``collect``, ``head``, ``last``, ``find``, ``every``, ``some``).
|
|
4
|
+
|
|
5
|
+
The ``*_while`` folds stop reading as soon as the state fails the
|
|
6
|
+
predicate; the item-by-item ones leave the rest of that chunk over.
|
|
7
|
+
"""
|
|
8
|
+
# pyright: reportPrivateUsage=false
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Callable, Generator
|
|
13
|
+
from typing import Any, Never, TypeIs, overload
|
|
14
|
+
|
|
15
|
+
from pyfect._core import Effect, _Gen, _Succeed, _Sync
|
|
16
|
+
from pyfect.option import Option, Some
|
|
17
|
+
from pyfect.pull import catch_done
|
|
18
|
+
from pyfect.scope import CloseableScope
|
|
19
|
+
from pyfect.sink._constructors import sync
|
|
20
|
+
from pyfect.sink._sink import End, Sink, Upstream
|
|
21
|
+
from pyfect.sink._steps import ENDED, VOID, read_until_done
|
|
22
|
+
|
|
23
|
+
type AnyEffect = Effect[Any, Any, Any]
|
|
24
|
+
type Chunk = tuple[Any, ...]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _rest(chunk: Chunk, start: int) -> Chunk | None:
|
|
28
|
+
return chunk[start:] or None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# --- folds with an effect per item ---------------------------------------------
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def fold[S, In, E = Never, R = Never](
|
|
35
|
+
initial: Callable[[], S],
|
|
36
|
+
cont: Callable[[S], bool],
|
|
37
|
+
f: Callable[[S, In], Effect[S, E, R]],
|
|
38
|
+
) -> Sink[S, In, In, E, R]:
|
|
39
|
+
"""Fold each item into ``initial()`` with ``f`` while ``cont(state)``
|
|
40
|
+
holds; once it fails, end with the state and the rest of the chunk as
|
|
41
|
+
leftovers. ``cont`` is first asked after the first item."""
|
|
42
|
+
|
|
43
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
44
|
+
state = initial()
|
|
45
|
+
|
|
46
|
+
def body() -> Generator[AnyEffect, Any, End[S, In]]:
|
|
47
|
+
nonlocal state
|
|
48
|
+
while True:
|
|
49
|
+
chunk = yield upstream
|
|
50
|
+
for i, item in enumerate(chunk):
|
|
51
|
+
state = yield f(state, item)
|
|
52
|
+
if not cont(state):
|
|
53
|
+
return (state, _rest(chunk, i + 1))
|
|
54
|
+
|
|
55
|
+
return catch_done(_Gen(body), lambda _: _Sync(lambda: (state, None)))
|
|
56
|
+
|
|
57
|
+
return Sink(transform)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def fold_array[S, In, E = Never, R = Never](
|
|
61
|
+
initial: Callable[[], S],
|
|
62
|
+
cont: Callable[[S], bool],
|
|
63
|
+
f: Callable[[S, tuple[In, ...]], Effect[S, E, R]],
|
|
64
|
+
) -> Sink[S, In, Never, E, R]:
|
|
65
|
+
"""``fold`` a chunk at a time; nothing is left over."""
|
|
66
|
+
|
|
67
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
68
|
+
state = initial()
|
|
69
|
+
|
|
70
|
+
def step(chunk: tuple[In, ...]) -> AnyEffect:
|
|
71
|
+
def keep(next_state: S) -> AnyEffect:
|
|
72
|
+
nonlocal state
|
|
73
|
+
state = next_state
|
|
74
|
+
return VOID if cont(state) else ENDED
|
|
75
|
+
|
|
76
|
+
return f(state, chunk).flat_map(keep)
|
|
77
|
+
|
|
78
|
+
return read_until_done(upstream.flat_map(step), lambda: (state, None))
|
|
79
|
+
|
|
80
|
+
return Sink(transform)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def fold_until[S, In, E = Never, R = Never](
|
|
84
|
+
initial: Callable[[], S], max: int, f: Callable[[S, In], Effect[S, E, R]]
|
|
85
|
+
) -> Sink[S, In, In, E, R]:
|
|
86
|
+
"""Fold at most ``max`` items into ``initial()`` with ``f``. With
|
|
87
|
+
``max`` 0 or less the sink reads nothing."""
|
|
88
|
+
if max <= 0:
|
|
89
|
+
return sync(initial)
|
|
90
|
+
|
|
91
|
+
def start() -> tuple[S, int]:
|
|
92
|
+
return (initial(), 0)
|
|
93
|
+
|
|
94
|
+
def below_max(pair: tuple[S, int]) -> bool:
|
|
95
|
+
return pair[1] < max
|
|
96
|
+
|
|
97
|
+
def step(pair: tuple[S, int], item: In) -> Effect[tuple[S, int], E, R]:
|
|
98
|
+
return f(pair[0], item).map(lambda state: (state, pair[1] + 1))
|
|
99
|
+
|
|
100
|
+
return fold(start, below_max, step).map(lambda pair: pair[0])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _fold_bool[In](start: bool, predicate: Callable[[In], bool]) -> Sink[bool, In, In]:
|
|
104
|
+
"""Fold to ``predicate(item)`` until it differs from ``start``."""
|
|
105
|
+
|
|
106
|
+
def step(_: bool, item: In) -> Effect[bool]:
|
|
107
|
+
return _Succeed(predicate(item))
|
|
108
|
+
|
|
109
|
+
return fold(lambda: start, lambda found: found == start, step)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def every[In](predicate: Callable[[In], bool]) -> Sink[bool, In, In]:
|
|
113
|
+
"""``True`` if ``predicate`` holds for every item; stops at the first
|
|
114
|
+
item it fails for."""
|
|
115
|
+
return _fold_bool(True, predicate)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def some[In](predicate: Callable[[In], bool]) -> Sink[bool, In, In]:
|
|
119
|
+
"""``True`` if ``predicate`` holds for some item; stops at the first
|
|
120
|
+
item it holds for."""
|
|
121
|
+
return _fold_bool(False, predicate)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def reduce_while_effect[S, In, E = Never, R = Never](
|
|
125
|
+
initial: Callable[[], S],
|
|
126
|
+
predicate: Callable[[S], bool],
|
|
127
|
+
f: Callable[[S, In], Effect[S, E, R]],
|
|
128
|
+
) -> Sink[S, In, In, E, R]:
|
|
129
|
+
"""Reduce each item into ``initial()`` with ``f`` while
|
|
130
|
+
``predicate(state)`` holds, asking it of ``initial()`` first; the rest
|
|
131
|
+
of the chunk is left over."""
|
|
132
|
+
|
|
133
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
134
|
+
state = initial()
|
|
135
|
+
leftover: Chunk | None = None
|
|
136
|
+
if not predicate(state):
|
|
137
|
+
return _Succeed((state, None))
|
|
138
|
+
|
|
139
|
+
def step(chunk: Chunk) -> AnyEffect:
|
|
140
|
+
def body() -> Generator[AnyEffect, Any, AnyEffect]:
|
|
141
|
+
nonlocal state, leftover
|
|
142
|
+
for i, item in enumerate(chunk):
|
|
143
|
+
state = yield f(state, item)
|
|
144
|
+
if not predicate(state):
|
|
145
|
+
leftover = _rest(chunk, i + 1)
|
|
146
|
+
return ENDED
|
|
147
|
+
return VOID
|
|
148
|
+
|
|
149
|
+
return _Gen(body).flatten()
|
|
150
|
+
|
|
151
|
+
return read_until_done(upstream.flat_map(step), lambda: (state, leftover))
|
|
152
|
+
|
|
153
|
+
return Sink(transform)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def reduce_effect[S, In, E = Never, R = Never](
|
|
157
|
+
initial: Callable[[], S], f: Callable[[S, In], Effect[S, E, R]]
|
|
158
|
+
) -> Sink[S, In, Never, E, R]:
|
|
159
|
+
"""Reduce every item into ``initial()`` with ``f``."""
|
|
160
|
+
reduced: Sink[S, In, Any, E, R] = reduce_while_effect(initial, lambda _: True, f)
|
|
161
|
+
return reduced
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def reduce_while_array_effect[S, In, E = Never, R = Never](
|
|
165
|
+
initial: Callable[[], S],
|
|
166
|
+
predicate: Callable[[S], bool],
|
|
167
|
+
f: Callable[[S, tuple[In, ...]], Effect[S, E, R]],
|
|
168
|
+
) -> Sink[S, In, Never, E, R]:
|
|
169
|
+
"""``reduce_while_effect`` a chunk at a time; nothing is left over."""
|
|
170
|
+
|
|
171
|
+
def transform(upstream: Upstream[In], scope: CloseableScope) -> AnyEffect:
|
|
172
|
+
state = initial()
|
|
173
|
+
if not predicate(state):
|
|
174
|
+
return _Succeed((state, None))
|
|
175
|
+
return fold_array(lambda: state, predicate, f)._transform(upstream, scope)
|
|
176
|
+
|
|
177
|
+
return Sink(transform)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# --- pure folds -------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def reduce_while[S, In](
|
|
184
|
+
initial: Callable[[], S], predicate: Callable[[S], bool], f: Callable[[S, In], S]
|
|
185
|
+
) -> Sink[S, In, In]:
|
|
186
|
+
"""Reduce each item into ``initial()`` with ``f`` while
|
|
187
|
+
``predicate(state)`` holds, asking it of ``initial()`` first; the rest
|
|
188
|
+
of the chunk is left over."""
|
|
189
|
+
|
|
190
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
191
|
+
state = initial()
|
|
192
|
+
leftover: Chunk | None = None
|
|
193
|
+
if not predicate(state):
|
|
194
|
+
return _Succeed((state, None))
|
|
195
|
+
|
|
196
|
+
def step(chunk: Chunk) -> AnyEffect:
|
|
197
|
+
nonlocal state, leftover
|
|
198
|
+
for i, item in enumerate(chunk):
|
|
199
|
+
state = f(state, item)
|
|
200
|
+
if not predicate(state):
|
|
201
|
+
leftover = _rest(chunk, i + 1)
|
|
202
|
+
return ENDED
|
|
203
|
+
return VOID
|
|
204
|
+
|
|
205
|
+
return read_until_done(upstream.flat_map(step), lambda: (state, leftover))
|
|
206
|
+
|
|
207
|
+
return Sink(transform)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def reduce_while_array[S, In](
|
|
211
|
+
initial: Callable[[], S], predicate: Callable[[S], bool], f: Callable[[S, tuple[In, ...]], S]
|
|
212
|
+
) -> Sink[S, In]:
|
|
213
|
+
"""``reduce_while`` a chunk at a time; nothing is left over."""
|
|
214
|
+
|
|
215
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
216
|
+
state = initial()
|
|
217
|
+
if not predicate(state):
|
|
218
|
+
return _Succeed((state, None))
|
|
219
|
+
|
|
220
|
+
def step(chunk: tuple[In, ...]) -> AnyEffect:
|
|
221
|
+
nonlocal state
|
|
222
|
+
state = f(state, chunk)
|
|
223
|
+
return VOID if predicate(state) else ENDED
|
|
224
|
+
|
|
225
|
+
return read_until_done(upstream.flat_map(step), lambda: (state, None))
|
|
226
|
+
|
|
227
|
+
return Sink(transform)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def reduce_array[S, In](
|
|
231
|
+
initial: Callable[[], S], f: Callable[[S, tuple[In, ...]], S]
|
|
232
|
+
) -> Sink[S, In]:
|
|
233
|
+
"""Reduce every chunk into ``initial()`` with ``f``."""
|
|
234
|
+
|
|
235
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
236
|
+
state = initial()
|
|
237
|
+
|
|
238
|
+
def step(chunk: tuple[In, ...]) -> None:
|
|
239
|
+
nonlocal state
|
|
240
|
+
state = f(state, chunk)
|
|
241
|
+
|
|
242
|
+
return read_until_done(upstream.map(step), lambda: (state, None))
|
|
243
|
+
|
|
244
|
+
return Sink(transform)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def reduce[S, In](initial: Callable[[], S], f: Callable[[S, In], S]) -> Sink[S, In]:
|
|
248
|
+
"""Reduce every item into ``initial()`` with ``f``."""
|
|
249
|
+
|
|
250
|
+
def reduce_chunk(state: S, chunk: tuple[In, ...]) -> S:
|
|
251
|
+
for item in chunk:
|
|
252
|
+
state = f(state, item)
|
|
253
|
+
return state
|
|
254
|
+
|
|
255
|
+
return reduce_array(initial, reduce_chunk)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _add_up(total: Any, chunk: Chunk) -> Any:
|
|
259
|
+
return total + sum(chunk)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def sum_[N: (int, float)]() -> Sink[N, N]:
|
|
263
|
+
"""A sink adding up its input (``0`` for none). Effect's ``Sink.sum``,
|
|
264
|
+
a function here so the result has the input's type."""
|
|
265
|
+
summed: Sink[Any, Any] = reduce_array(int, _add_up)
|
|
266
|
+
return summed
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _count(n: int, chunk: Chunk) -> int:
|
|
270
|
+
return n + len(chunk)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
count: Sink[int] = reduce_array(int, _count)
|
|
274
|
+
"""A sink counting its input."""
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _extend(items: list[Any], chunk: Chunk) -> list[Any]:
|
|
278
|
+
items.extend(chunk)
|
|
279
|
+
return items
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def collect[In]() -> Sink[list[In], In]:
|
|
283
|
+
"""A sink collecting its input in a list."""
|
|
284
|
+
return reduce_array(list, _extend)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _nothing() -> Option[Any]:
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _not_found(found: Option[Any]) -> bool:
|
|
292
|
+
return found is None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _first(_: Option[Any], item: Any) -> Option[Any]:
|
|
296
|
+
return Some(item)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _last(_: Option[Any], chunk: Chunk) -> Option[Any]:
|
|
300
|
+
return Some(chunk[-1])
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def head[In]() -> Sink[Option[In], In, In]:
|
|
304
|
+
"""``Some`` of the first item (the rest of its chunk is left over), or
|
|
305
|
+
``None`` if there is none."""
|
|
306
|
+
return reduce_while(_nothing, _not_found, _first)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def last[In]() -> Sink[Option[In], In]:
|
|
310
|
+
"""``Some`` of the last item, or ``None`` if there is none."""
|
|
311
|
+
return reduce_array(_nothing, _last)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@overload
|
|
315
|
+
def find[In, B](refinement: Callable[[In], TypeIs[B]]) -> Sink[Option[B], In, In]: ...
|
|
316
|
+
@overload
|
|
317
|
+
def find[In](refinement: Callable[[In], bool]) -> Sink[Option[In], In, In]: ...
|
|
318
|
+
def find(refinement: Callable[[Any], bool]) -> Sink[Option[Any], Any, Any]:
|
|
319
|
+
"""``Some`` of the first item ``predicate`` holds for (the rest of its
|
|
320
|
+
chunk is left over), or ``None``."""
|
|
321
|
+
|
|
322
|
+
def check(found: Option[Any], item: Any) -> Option[Any]:
|
|
323
|
+
return Some(item) if refinement(item) else found
|
|
324
|
+
|
|
325
|
+
return reduce_while(_nothing, _not_found, check)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def find_effect[In, E = Never, R = Never](
|
|
329
|
+
predicate: Callable[[In], Effect[bool, E, R]],
|
|
330
|
+
) -> Sink[Option[In], In, In, E, R]:
|
|
331
|
+
"""``find`` with an effectful ``predicate``."""
|
|
332
|
+
|
|
333
|
+
def check(found: Option[In], item: In) -> Effect[Option[In], E, R]:
|
|
334
|
+
return predicate(item).map(lambda ok: Some(item) if ok else found)
|
|
335
|
+
|
|
336
|
+
return reduce_while_effect(_nothing, _not_found, check)
|
pyfect/sink/_for_each.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Sinks that run an effect on their input: ``for_each``,
|
|
2
|
+
``for_each_array``, ``for_each_while`` and ``for_each_while_array``."""
|
|
3
|
+
# pyright: reportPrivateUsage=false
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable, Generator
|
|
8
|
+
from typing import Any, Never
|
|
9
|
+
|
|
10
|
+
from pyfect._core import Effect, _Gen
|
|
11
|
+
from pyfect.scope import CloseableScope
|
|
12
|
+
from pyfect.sink._sink import End, Sink, Upstream
|
|
13
|
+
from pyfect.sink._steps import ENDED, VOID, read_until_done
|
|
14
|
+
|
|
15
|
+
type AnyEffect = Effect[Any, Any, Any]
|
|
16
|
+
type Chunk = tuple[Any, ...]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _no_value() -> End[None]:
|
|
20
|
+
return (None, None)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _until_done(step: AnyEffect) -> Effect[End[None], Any, Any]:
|
|
24
|
+
return read_until_done(step, _no_value)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def for_each_array[In, E = Never, R = Never](
|
|
28
|
+
f: Callable[[tuple[In, ...]], Effect[object, E, R]],
|
|
29
|
+
) -> Sink[None, In, Never, E, R]:
|
|
30
|
+
"""A sink that runs ``f`` on each chunk of its input, in order."""
|
|
31
|
+
|
|
32
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
33
|
+
return _until_done(upstream.flat_map(f))
|
|
34
|
+
|
|
35
|
+
return Sink(transform)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def for_each[In, E = Never, R = Never](
|
|
39
|
+
f: Callable[[In], Effect[object, E, R]],
|
|
40
|
+
) -> Sink[None, In, Never, E, R]:
|
|
41
|
+
"""A sink that runs ``f`` on each item of its input, in order."""
|
|
42
|
+
|
|
43
|
+
def each(chunk: Chunk) -> AnyEffect:
|
|
44
|
+
if len(chunk) == 1:
|
|
45
|
+
return f(chunk[0])
|
|
46
|
+
|
|
47
|
+
def body() -> Generator[AnyEffect, Any]:
|
|
48
|
+
for item in chunk:
|
|
49
|
+
yield f(item)
|
|
50
|
+
|
|
51
|
+
return _Gen(body)
|
|
52
|
+
|
|
53
|
+
return for_each_array(each)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _go_on(more: bool) -> AnyEffect:
|
|
57
|
+
return VOID if more else ENDED
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def for_each_while_array[In, E = Never, R = Never](
|
|
61
|
+
f: Callable[[tuple[In, ...]], Effect[bool, E, R]],
|
|
62
|
+
) -> Sink[None, In, Never, E, R]:
|
|
63
|
+
"""A sink that runs ``f`` on each chunk until it succeeds with
|
|
64
|
+
``False``; the sink stops reading there."""
|
|
65
|
+
|
|
66
|
+
def transform(upstream: Upstream[In], _scope: CloseableScope) -> AnyEffect:
|
|
67
|
+
return _until_done(upstream.flat_map(f).flat_map(_go_on))
|
|
68
|
+
|
|
69
|
+
return Sink(transform)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def for_each_while[In, E = Never, R = Never](
|
|
73
|
+
f: Callable[[In], Effect[bool, E, R]],
|
|
74
|
+
) -> Sink[None, In, Never, E, R]:
|
|
75
|
+
"""A sink that runs ``f`` on each item until it succeeds with
|
|
76
|
+
``False``; the sink stops reading there."""
|
|
77
|
+
|
|
78
|
+
def each(chunk: Chunk) -> Effect[bool, Any, Any]:
|
|
79
|
+
def body() -> Generator[AnyEffect, Any, bool]:
|
|
80
|
+
for item in chunk:
|
|
81
|
+
if not (yield f(item)):
|
|
82
|
+
return False
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
return _Gen(body)
|
|
86
|
+
|
|
87
|
+
return for_each_while_array(each)
|