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,161 @@
|
|
|
1
|
+
"""RPC and HTTP API definitions for workflows (Effect's
|
|
2
|
+
``effect/workflow/WorkflowProxy``)::
|
|
3
|
+
|
|
4
|
+
WorkflowRpcs = workflow_proxy.to_rpc_group([EmailWorkflow])
|
|
5
|
+
Workflows = workflow_proxy.to_http_api_group("workflows", [EmailWorkflow])
|
|
6
|
+
|
|
7
|
+
Each workflow gets three operations: ``<Tag>`` executes it (its payload,
|
|
8
|
+
success and error), ``<Tag>Discard`` starts it and answers with the
|
|
9
|
+
execution id, and ``<Tag>Resume`` resumes a suspended execution by
|
|
10
|
+
``executionId``. Over HTTP they are ``POST /<tag>``, ``/<tag>/discard``
|
|
11
|
+
and ``/<tag>/resume`` (the tag lowercased). ``workflow_proxy_server``
|
|
12
|
+
serves them.
|
|
13
|
+
|
|
14
|
+
The groups above are built at run time, so a client reads their
|
|
15
|
+
operations by name (``.method("<Tag>")``, untyped). To type a client,
|
|
16
|
+
declare the operations in your own group (Effect types its groups by the
|
|
17
|
+
workflows' tags, which Python cannot)::
|
|
18
|
+
|
|
19
|
+
class Expenses(HttpApiGroup, identifier="expenses"):
|
|
20
|
+
submit, submit_discard, submit_resume = workflow_proxy.http_endpoints(SubmitExpense)
|
|
21
|
+
|
|
22
|
+
class ExpenseRpcs(RpcGroup):
|
|
23
|
+
submit, submit_discard, submit_resume = workflow_proxy.rpcs(SubmitExpense)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import types
|
|
29
|
+
from collections.abc import Sequence
|
|
30
|
+
from typing import Any, NamedTuple, Never, TypedDict
|
|
31
|
+
|
|
32
|
+
from pyfect.http_api import endpoint as http_endpoint
|
|
33
|
+
from pyfect.http_api.endpoint import HttpApiEndpoint
|
|
34
|
+
from pyfect.http_api.group import HttpApiGroup
|
|
35
|
+
from pyfect.rpc import Rpc
|
|
36
|
+
from pyfect.rpc import make as make_rpc
|
|
37
|
+
from pyfect.rpc.group import RpcGroup
|
|
38
|
+
from pyfect.schema import String, Struct
|
|
39
|
+
from pyfect.workflow.workflow import Workflow
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ResumePayload(TypedDict):
|
|
43
|
+
executionId: str
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
"""The payload of a ``<Tag>Resume`` operation."""
|
|
47
|
+
|
|
48
|
+
RESUME_PAYLOAD = Struct(ResumePayload)
|
|
49
|
+
|
|
50
|
+
type AnyWorkflow = Workflow[Any, Any, Any]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class WorkflowRpcs[P, A, E](NamedTuple):
|
|
54
|
+
"""The three RPCs of one workflow (``rpcs``)."""
|
|
55
|
+
|
|
56
|
+
execute: Rpc[P, A, E]
|
|
57
|
+
discard: Rpc[P, str, Never]
|
|
58
|
+
resume: Rpc[ResumePayload, None, Never]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class WorkflowEndpoints[P, A, E](NamedTuple):
|
|
62
|
+
"""The three HTTP endpoints of one workflow (``http_endpoints``)."""
|
|
63
|
+
|
|
64
|
+
execute: HttpApiEndpoint[None, None, P, P, None, A, A, E]
|
|
65
|
+
discard: HttpApiEndpoint[None, None, P, P, None, str, str, Never]
|
|
66
|
+
resume: HttpApiEndpoint[None, None, ResumePayload, ResumePayload, None, None, None, Never]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def rpcs[P, A, E](workflow: Workflow[P, A, E], *, prefix: str = "") -> WorkflowRpcs[P, A, E]:
|
|
70
|
+
"""The execute, discard and resume RPCs of ``workflow`` (tags
|
|
71
|
+
``<prefix><Tag>``, ``...Discard``, ``...Resume``), typed, to declare in
|
|
72
|
+
an ``RpcGroup``; ``workflow_proxy_server.layer_rpc_handlers`` serves
|
|
73
|
+
them."""
|
|
74
|
+
tag = f"{prefix}{workflow.tag}"
|
|
75
|
+
return WorkflowRpcs(
|
|
76
|
+
make_rpc(
|
|
77
|
+
tag,
|
|
78
|
+
payload=workflow.payload_schema,
|
|
79
|
+
success=workflow.success_schema,
|
|
80
|
+
error=workflow.error_schema,
|
|
81
|
+
).annotate_merge(workflow.annotations),
|
|
82
|
+
make_rpc(f"{tag}Discard", payload=workflow.payload_schema, success=String).annotate_merge(
|
|
83
|
+
workflow.annotations
|
|
84
|
+
),
|
|
85
|
+
make_rpc(f"{tag}Resume", payload=RESUME_PAYLOAD).annotate_merge(workflow.annotations),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def http_endpoints[P, A, E](workflow: Workflow[P, A, E]) -> WorkflowEndpoints[P, A, E]:
|
|
90
|
+
"""The execute, discard and resume endpoints of ``workflow`` (``POST
|
|
91
|
+
/<tag>``, ``/<tag>/discard``, ``/<tag>/resume``), typed, to declare in
|
|
92
|
+
an ``HttpApiGroup``; ``workflow_proxy_server.handle_http`` serves
|
|
93
|
+
them."""
|
|
94
|
+
path = f"/{tag_to_path(workflow.tag)}"
|
|
95
|
+
return WorkflowEndpoints(
|
|
96
|
+
http_endpoint.post(
|
|
97
|
+
workflow.tag,
|
|
98
|
+
path,
|
|
99
|
+
payload=workflow.payload_schema,
|
|
100
|
+
success=workflow.success_schema,
|
|
101
|
+
error=workflow.error_schema,
|
|
102
|
+
).annotate_merge(workflow.annotations),
|
|
103
|
+
http_endpoint.post(
|
|
104
|
+
f"{workflow.tag}Discard",
|
|
105
|
+
f"{path}/discard",
|
|
106
|
+
payload=workflow.payload_schema,
|
|
107
|
+
success=String,
|
|
108
|
+
).annotate_merge(workflow.annotations),
|
|
109
|
+
http_endpoint.post(
|
|
110
|
+
f"{workflow.tag}Resume", f"{path}/resume", payload=RESUME_PAYLOAD
|
|
111
|
+
).annotate_merge(workflow.annotations),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def to_rpc_group(workflows: Sequence[AnyWorkflow], *, prefix: str = "") -> type[RpcGroup]:
|
|
116
|
+
"""An ``RpcGroup`` with the execute, discard and resume RPCs of each
|
|
117
|
+
workflow, their tags prefixed by ``prefix``. Subclass it to name it::
|
|
118
|
+
|
|
119
|
+
class WorkflowRpcs(workflow_proxy.to_rpc_group([EmailWorkflow])): ...
|
|
120
|
+
|
|
121
|
+
Its client reads them by tag; declare ``rpcs(workflow)`` in a group
|
|
122
|
+
of your own to type them."""
|
|
123
|
+
if not workflows:
|
|
124
|
+
raise ValueError("to_rpc_group needs at least one workflow")
|
|
125
|
+
found: dict[str, Any] = {}
|
|
126
|
+
for workflow in workflows:
|
|
127
|
+
for rpc in rpcs(workflow, prefix=prefix):
|
|
128
|
+
found[rpc.tag] = rpc
|
|
129
|
+
return types.new_class("WorkflowRpcs", (RpcGroup,), {}, lambda ns: ns.update(found))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def tag_to_path(tag: str) -> str:
|
|
133
|
+
return tag.lower()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def to_http_api_group(name: str, workflows: Sequence[AnyWorkflow]) -> type[HttpApiGroup[Never]]:
|
|
137
|
+
"""An ``HttpApiGroup`` with identifier ``name`` holding the execute,
|
|
138
|
+
discard and resume endpoints of each workflow (``POST /<tag>``,
|
|
139
|
+
``/<tag>/discard``, ``/<tag>/resume``). A client calls them with
|
|
140
|
+
``get_group(client, Group).method("<Tag>")``; declare
|
|
141
|
+
``http_endpoints(workflow)`` in a group of your own to type them."""
|
|
142
|
+
if not workflows:
|
|
143
|
+
raise ValueError("to_http_api_group needs at least one workflow")
|
|
144
|
+
group: type[HttpApiGroup[Never]] = types.new_class(name, (HttpApiGroup,), {"identifier": name})
|
|
145
|
+
for workflow in workflows:
|
|
146
|
+
group = group.add(*http_endpoints(workflow))
|
|
147
|
+
return group
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
__all__ = [
|
|
151
|
+
"RESUME_PAYLOAD",
|
|
152
|
+
"AnyWorkflow",
|
|
153
|
+
"ResumePayload",
|
|
154
|
+
"WorkflowEndpoints",
|
|
155
|
+
"WorkflowRpcs",
|
|
156
|
+
"http_endpoints",
|
|
157
|
+
"rpcs",
|
|
158
|
+
"tag_to_path",
|
|
159
|
+
"to_http_api_group",
|
|
160
|
+
"to_rpc_group",
|
|
161
|
+
]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Serving the operations of ``workflow_proxy`` (Effect's
|
|
2
|
+
``effect/workflow/WorkflowProxyServer``)::
|
|
3
|
+
|
|
4
|
+
ApiLive = builder.layer(api).pipe(
|
|
5
|
+
fx.layer.provide(workflow_proxy_server.layer_http_api(api, "workflows", [EmailWorkflow]))
|
|
6
|
+
)
|
|
7
|
+
RpcLive = rpc_server.layer(WorkflowRpcs).pipe(
|
|
8
|
+
fx.layer.provide(workflow_proxy_server.layer_rpc_handlers([EmailWorkflow]))
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
Execute runs the workflow, discard starts it and answers with its
|
|
12
|
+
execution id, resume resumes it; the engine and the workflows' handlers
|
|
13
|
+
stay on the server. A defect is logged.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections.abc import Sequence
|
|
19
|
+
from typing import Any, Never, overload
|
|
20
|
+
|
|
21
|
+
from pyfect._core import Effect
|
|
22
|
+
from pyfect.context import Context
|
|
23
|
+
from pyfect.effect import context
|
|
24
|
+
from pyfect.http_api._handlers import GroupHandlers, Handlers, HttpApiRequest
|
|
25
|
+
from pyfect.http_api.api import HttpApi
|
|
26
|
+
from pyfect.http_api.builder import group as builder_group
|
|
27
|
+
from pyfect.layer import Layer
|
|
28
|
+
from pyfect.layer import effect_context as layer_effect_context
|
|
29
|
+
from pyfect.logger import log_error
|
|
30
|
+
from pyfect.rpc import make as make_rpc
|
|
31
|
+
from pyfect.rpc._rpc import HandlerEntry, RpcRequest, handler_tag
|
|
32
|
+
from pyfect.rpc.group import HandlersFor
|
|
33
|
+
from pyfect.scope import Scope
|
|
34
|
+
from pyfect.workflow._services import WorkflowEngine
|
|
35
|
+
from pyfect.workflow.workflow import Workflow
|
|
36
|
+
|
|
37
|
+
type AnyWorkflow = Workflow[Any, Any, Any]
|
|
38
|
+
type Request = HttpApiRequest[Any, Any, Any, Any]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _logged[A, E, R](effect: Effect[A, E, R], method: str) -> Effect[A, E, R]:
|
|
42
|
+
return effect.tap_defect(log_error).annotate_logs(
|
|
43
|
+
{"module": "WorkflowProxyServer", "method": method}
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@overload
|
|
48
|
+
def handle_http(
|
|
49
|
+
handlers: GroupHandlers[Any], workflows: Sequence[AnyWorkflow]
|
|
50
|
+
) -> Handlers[Any, Any, WorkflowEngine]: ...
|
|
51
|
+
@overload
|
|
52
|
+
def handle_http[GP, GE, R](
|
|
53
|
+
handlers: Handlers[GP, GE, R], workflows: Sequence[AnyWorkflow]
|
|
54
|
+
) -> Handlers[GP, GE, R | WorkflowEngine]: ...
|
|
55
|
+
def handle_http(
|
|
56
|
+
handlers: Handlers[Any, Any, Any] | GroupHandlers[Any], workflows: Sequence[AnyWorkflow]
|
|
57
|
+
) -> Handlers[Any, Any, Any]:
|
|
58
|
+
"""``handlers`` with the execute, discard and resume endpoints of each
|
|
59
|
+
workflow handled: those ``workflow_proxy.http_endpoints(workflow)``
|
|
60
|
+
declares in the handlers' group, next to its other endpoints::
|
|
61
|
+
|
|
62
|
+
builder.group(api, Expenses, lambda h: workflow_proxy_server.handle_http(
|
|
63
|
+
h.handle(Expenses.health)(health), [SubmitExpense]))
|
|
64
|
+
"""
|
|
65
|
+
group = handlers.group
|
|
66
|
+
tags = [t for w in workflows for t in (w.tag, f"{w.tag}Discard", f"{w.tag}Resume")]
|
|
67
|
+
missing = [t for t in tags if t not in group.endpoints]
|
|
68
|
+
if missing:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f'HttpApiGroup "{group.identifier}" has no endpoint {", ".join(missing)}: declare '
|
|
71
|
+
"workflow_proxy.http_endpoints(workflow) in it"
|
|
72
|
+
)
|
|
73
|
+
found: Handlers[Any, Any, Any] = (
|
|
74
|
+
Handlers(group) if isinstance(handlers, GroupHandlers) else handlers
|
|
75
|
+
)
|
|
76
|
+
for workflow in workflows:
|
|
77
|
+
tag = workflow.tag
|
|
78
|
+
|
|
79
|
+
def execute(request: Request, w: AnyWorkflow = workflow) -> Effect[Any, Any, Any]:
|
|
80
|
+
return _logged(w.execute(request.payload), w.tag)
|
|
81
|
+
|
|
82
|
+
def discard(request: Request, w: AnyWorkflow = workflow) -> Effect[Any, Any, Any]:
|
|
83
|
+
return _logged(w.execute(request.payload, discard=True), f"{w.tag}Discard")
|
|
84
|
+
|
|
85
|
+
def resume(request: Request, w: AnyWorkflow = workflow) -> Effect[Any, Any, Any]:
|
|
86
|
+
return _logged(w.resume(request.payload["executionId"]), f"{w.tag}Resume")
|
|
87
|
+
|
|
88
|
+
found = (
|
|
89
|
+
found.handle(group.endpoints[tag])(execute)
|
|
90
|
+
.handle(group.endpoints[f"{tag}Discard"])(discard)
|
|
91
|
+
.handle(group.endpoints[f"{tag}Resume"])(resume)
|
|
92
|
+
)
|
|
93
|
+
return found
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def layer_http_api(
|
|
97
|
+
api: HttpApi[Any], identifier: str, workflows: Sequence[AnyWorkflow]
|
|
98
|
+
) -> Layer[Any, Never, WorkflowEngine]:
|
|
99
|
+
"""The handlers of the group ``identifier`` of ``api``, made by
|
|
100
|
+
``workflow_proxy.to_http_api_group(identifier, workflows)`` (or one
|
|
101
|
+
declaring only ``workflow_proxy.http_endpoints`` of ``workflows``)."""
|
|
102
|
+
group = api.groups.get(identifier)
|
|
103
|
+
if group is None:
|
|
104
|
+
raise ValueError(f'HttpApiGroup "{identifier}" not found in HttpApi "{api.identifier}"')
|
|
105
|
+
return builder_group(api, group, lambda handlers: handle_http(handlers, workflows))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def layer_rpc_handlers(
|
|
109
|
+
workflows: Sequence[AnyWorkflow], *, prefix: str = ""
|
|
110
|
+
) -> Layer[HandlersFor[Any], Never, WorkflowEngine]:
|
|
111
|
+
"""The handlers of the RPCs ``workflow_proxy.to_rpc_group(workflows,
|
|
112
|
+
prefix=prefix)`` declares."""
|
|
113
|
+
|
|
114
|
+
def entries(services: Context[Any]) -> Context[Any]:
|
|
115
|
+
kept = Context({t: s for t, s in services.map_unsafe.items() if t is not Scope})
|
|
116
|
+
found: dict[Any, HandlerEntry] = {}
|
|
117
|
+
for workflow in workflows:
|
|
118
|
+
tag = f"{prefix}{workflow.tag}"
|
|
119
|
+
|
|
120
|
+
def execute(
|
|
121
|
+
request: RpcRequest[Any], w: AnyWorkflow = workflow
|
|
122
|
+
) -> Effect[Any, Any, Any]:
|
|
123
|
+
return w.execute(request.payload)
|
|
124
|
+
|
|
125
|
+
def discard(
|
|
126
|
+
request: RpcRequest[Any], w: AnyWorkflow = workflow
|
|
127
|
+
) -> Effect[Any, Any, Any]:
|
|
128
|
+
return w.execute(request.payload, discard=True)
|
|
129
|
+
|
|
130
|
+
def resume(
|
|
131
|
+
request: RpcRequest[Any], w: AnyWorkflow = workflow
|
|
132
|
+
) -> Effect[Any, Any, Any]:
|
|
133
|
+
return w.resume(request.payload["executionId"])
|
|
134
|
+
|
|
135
|
+
for name, handler in (
|
|
136
|
+
(tag, execute),
|
|
137
|
+
(f"{tag}Discard", discard),
|
|
138
|
+
(f"{tag}Resume", resume),
|
|
139
|
+
):
|
|
140
|
+
found[handler_tag(make_rpc(name))] = HandlerEntry(name, handler, kept)
|
|
141
|
+
return Context(found)
|
|
142
|
+
|
|
143
|
+
return layer_effect_context(context().map(entries))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
__all__ = ["AnyWorkflow", "Request", "handle_http", "layer_http_api", "layer_rpc_handlers"]
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyfect-dev
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Effect-TS for Python: typed effects, a real fiber runtime, structured concurrency, services and layers.
|
|
5
|
+
Author: Tyler Gibbs
|
|
6
|
+
Author-email: Tyler Gibbs <tylergibbs048@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Classifier: Typing :: Typed
|
|
13
|
+
Requires-Dist: typing-extensions>=4.13
|
|
14
|
+
Requires-Dist: brotli>=1.1 ; extra == 'brotli'
|
|
15
|
+
Requires-Dist: cryptography>=46 ; extra == 'eventlog'
|
|
16
|
+
Requires-Dist: hypercorn>=0.18 ; extra == 'http2'
|
|
17
|
+
Requires-Dist: httpx>=0.28 ; extra == 'httpx'
|
|
18
|
+
Requires-Dist: hypothesis>=6.140 ; extra == 'hypothesis'
|
|
19
|
+
Requires-Dist: babel>=2.18 ; extra == 'intl'
|
|
20
|
+
Requires-Dist: msgspec>=0.19 ; extra == 'msgspec'
|
|
21
|
+
Requires-Dist: python-tds>=1.17 ; extra == 'mssql'
|
|
22
|
+
Requires-Dist: pyopenssl>=25 ; extra == 'mssql'
|
|
23
|
+
Requires-Dist: asyncmy>=0.2.17 ; extra == 'mysql'
|
|
24
|
+
Requires-Dist: opentelemetry-api>=1.45 ; extra == 'otel'
|
|
25
|
+
Requires-Dist: opentelemetry-sdk>=1.45 ; extra == 'otel'
|
|
26
|
+
Requires-Dist: psycopg>=3.3 ; extra == 'pg'
|
|
27
|
+
Requires-Dist: pydantic>=2.11 ; extra == 'pydantic'
|
|
28
|
+
Requires-Dist: redis>=8.1 ; extra == 'redis'
|
|
29
|
+
Requires-Dist: regex>=2024.11.6 ; extra == 'regex'
|
|
30
|
+
Requires-Dist: uvicorn>=0.38 ; extra == 'server'
|
|
31
|
+
Requires-Dist: websockets>=15 ; extra == 'server'
|
|
32
|
+
Requires-Dist: websockets>=15 ; extra == 'websockets'
|
|
33
|
+
Requires-Python: >=3.13
|
|
34
|
+
Project-URL: Homepage, https://pyfect.dev
|
|
35
|
+
Project-URL: Repository, https://github.com/tylergibbs1/pyfect
|
|
36
|
+
Project-URL: Documentation, https://pyfect.dev/docs
|
|
37
|
+
Provides-Extra: brotli
|
|
38
|
+
Provides-Extra: eventlog
|
|
39
|
+
Provides-Extra: http2
|
|
40
|
+
Provides-Extra: httpx
|
|
41
|
+
Provides-Extra: hypothesis
|
|
42
|
+
Provides-Extra: intl
|
|
43
|
+
Provides-Extra: msgspec
|
|
44
|
+
Provides-Extra: mssql
|
|
45
|
+
Provides-Extra: mysql
|
|
46
|
+
Provides-Extra: otel
|
|
47
|
+
Provides-Extra: pg
|
|
48
|
+
Provides-Extra: pydantic
|
|
49
|
+
Provides-Extra: redis
|
|
50
|
+
Provides-Extra: regex
|
|
51
|
+
Provides-Extra: server
|
|
52
|
+
Provides-Extra: websockets
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
<img src="assets/logo.svg" width="72" height="72" alt="pyfect logo">
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
<h1 align="center">pyfect</h1>
|
|
60
|
+
|
|
61
|
+
<p align="center">
|
|
62
|
+
<a href="https://effect.website">Effect</a> for Python. Errors, dependencies and concurrency checked by pyright.
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
<p align="center">
|
|
66
|
+
<a href="https://pyfect.dev/docs"><img alt="Docs" src="https://img.shields.io/badge/docs-pyfect.dev-0a0a0a"></a>
|
|
67
|
+
<img alt="Python 3.13+" src="https://img.shields.io/badge/python-3.13%2B-0a0a0a">
|
|
68
|
+
<img alt="pyright strict" src="https://img.shields.io/badge/pyright-strict-0a0a0a">
|
|
69
|
+
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-0a0a0a"></a>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<p align="center">
|
|
73
|
+
<a href="https://pyfect.dev/docs"><b>Read the docs</b></a> ·
|
|
74
|
+
<a href="https://pyfect.dev/docs/installation">Install</a> ·
|
|
75
|
+
<a href="examples/">Examples</a> ·
|
|
76
|
+
<a href="docs/PARITY.md">Parity with Effect</a> ·
|
|
77
|
+
<a href="CONTRIBUTING.md">Contribute</a>
|
|
78
|
+
</p>
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
pyfect lets you write Python programs whose type says what they return, how they can fail
|
|
83
|
+
and which services they need, so pyright rejects a program that forgets to handle an error
|
|
84
|
+
or provide a dependency. It is for Python developers building services, pipelines and CLIs
|
|
85
|
+
who want Effect's model (typed errors, structured concurrency, layers, streams, schema)
|
|
86
|
+
without leaving Python.
|
|
87
|
+
|
|
88
|
+
## A quick look
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from dataclasses import dataclass
|
|
92
|
+
from typing import Protocol
|
|
93
|
+
|
|
94
|
+
import pyfect as fx
|
|
95
|
+
from pyfect import layer
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class UserStore(Protocol):
|
|
99
|
+
def find(self, user_id: str) -> fx.Effect[str | None]: ...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class Users(fx.Tag[UserStore]): ...
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class InMemoryUsers:
|
|
106
|
+
def find(self, user_id: str) -> fx.Effect[str | None]:
|
|
107
|
+
return fx.succeed({"1": "Ada"}.get(user_id))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class UserNotFound(fx.TaggedError):
|
|
112
|
+
user_id: str
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@fx.fn
|
|
116
|
+
def greet(user_id: str):
|
|
117
|
+
users = yield from Users()
|
|
118
|
+
name = yield from users.find(user_id)
|
|
119
|
+
if name is None:
|
|
120
|
+
return (yield from UserNotFound(user_id))
|
|
121
|
+
return f"Hello, {name}!"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# greet("1") is inferred as Effect[str, UserNotFound, Users]
|
|
125
|
+
|
|
126
|
+
program = (
|
|
127
|
+
greet("1")
|
|
128
|
+
.pipe(fx.catch_tag(UserNotFound, lambda e: fx.succeed("Who?")))
|
|
129
|
+
.pipe(fx.provide(layer.succeed(Users, InMemoryUsers())))
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
fx.run_blocking(program)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Remove the `catch_tag` line and pyright stops you before the program runs:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
error: Argument of type "Effect[str, UserNotFound, Never]" cannot be assigned to
|
|
139
|
+
parameter "eff" of type "Effect[A@run_blocking, Never, Never]"
|
|
140
|
+
Type "UserNotFound" is not assignable to type "Never"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
[`examples/quickstart.py`](examples/quickstart.py) builds on this with a resource-managed
|
|
144
|
+
layer, concurrency, retries and timeouts.
|
|
145
|
+
|
|
146
|
+
## How it works
|
|
147
|
+
|
|
148
|
+
<p align="center">
|
|
149
|
+
<picture>
|
|
150
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/how-it-works-dark.svg">
|
|
151
|
+
<img src="assets/how-it-works-light.svg" alt="An @fx.fn generator builds Effect[A, E, R]; catch_tag removes an error, provide(layer) removes a service, and run_blocking runs the Effect[A, Never, Never] to an Exit." width="225">
|
|
152
|
+
</picture>
|
|
153
|
+
</p>
|
|
154
|
+
|
|
155
|
+
1. **Describe.** A generator decorated with `@fx.fn` builds an `Effect[A, E, R]`: a value
|
|
156
|
+
that describes the work. Nothing runs yet, and pyright infers all three types with no
|
|
157
|
+
annotations.
|
|
158
|
+
2. **Narrow.** Each handler removes exactly the error it handles, and each layer removes
|
|
159
|
+
exactly the services it builds. The type shrinks as you go.
|
|
160
|
+
3. **Run.** Only an effect with no errors and no services left can run. pyfect runs it on
|
|
161
|
+
its own fiber scheduler, synchronously or on asyncio, with structured concurrency and
|
|
162
|
+
interruption that always runs finalizers.
|
|
163
|
+
|
|
164
|
+
## Features
|
|
165
|
+
|
|
166
|
+
- **Typed errors.** Expected failures live in the type; `catch_tag` handles one, and
|
|
167
|
+
defects stay separate. [Errors →](https://pyfect.dev/docs/errors)
|
|
168
|
+
- **Services and layers.** Dependency injection checked by the type checker, with memoized,
|
|
169
|
+
concurrently built layers that release their resources in order.
|
|
170
|
+
[Services →](https://pyfect.dev/docs/services)
|
|
171
|
+
- **A fiber runtime of its own.** The same program runs with `run_blocking` (no event loop) or on
|
|
172
|
+
asyncio. `blocking` moves blocking calls to a thread. [Effects →](https://pyfect.dev/docs/effects)
|
|
173
|
+
- **Structured concurrency.** Children end with their parent; `all` and `for_each` take a
|
|
174
|
+
concurrency limit; races wait for losers to clean up.
|
|
175
|
+
[Concurrency →](https://pyfect.dev/docs/concurrency)
|
|
176
|
+
- **Resources.** Scopes, `acquire_release`, pools, `RcRef` and `RcMap` release exactly once,
|
|
177
|
+
even on interruption. [Resources →](https://pyfect.dev/docs/resources)
|
|
178
|
+
- **Retries and schedules.** Exponential, jittered, cron and composed schedules for `retry`
|
|
179
|
+
and `repeat`. [Schedules →](https://pyfect.dev/docs/schedules)
|
|
180
|
+
- **Streams.** Pull-based streams, sinks and channels with backpressure, plus NDJSON, JSON
|
|
181
|
+
array and Server-Sent Events framers. [Streams →](https://pyfect.dev/docs/streams)
|
|
182
|
+
- **Schema.** Decode and encode data with Effect's error messages, generate JSON Schema and
|
|
183
|
+
Hypothesis strategies, store schemas themselves as JSON documents, and plug into pydantic
|
|
184
|
+
or msgspec.
|
|
185
|
+
[Schema →](https://pyfect.dev/docs/schema)
|
|
186
|
+
- **Transactions.** Software transactional memory: `TxRef`, transactional queues, maps,
|
|
187
|
+
locks and semaphores that commit atomically and retry without blocking a thread.
|
|
188
|
+
[Transactions →](https://pyfect.dev/docs/transactions)
|
|
189
|
+
- **Graphs.** Directed and undirected graphs with stable indexes: walks, shortest paths,
|
|
190
|
+
components, flows and matchings, GraphViz and Mermaid output, and a schema for storing
|
|
191
|
+
them as JSON. [Graphs →](https://pyfect.dev/docs/graph)
|
|
192
|
+
- **Config.** Typed settings from the environment, `.env` files or dicts, with secrets kept
|
|
193
|
+
out of logs. [Config →](https://pyfect.dev/docs/config)
|
|
194
|
+
- **Observability.** Logging, spans, metrics and an OpenTelemetry bridge.
|
|
195
|
+
[Observability →](https://pyfect.dev/docs/observability)
|
|
196
|
+
- **Platform.** Files, child processes, an HTTP client (httpx), an HTTP server (standard library,
|
|
197
|
+
uvicorn, hypercorn for HTTP/2, or any ASGI server), and TCP, TLS, WebSocket
|
|
198
|
+
and UDP sockets, all as services you can swap in tests. [Platform →](https://pyfect.dev/docs/platform)
|
|
199
|
+
- **SQL.** Statements built from literal templates, so values are always bound parameters;
|
|
200
|
+
transactions with savepoints, batching resolvers and migrations, on SQLite (standard
|
|
201
|
+
library), PostgreSQL (psycopg), MySQL (asyncmy), SQL Server (python-tds), libSQL and
|
|
202
|
+
Turso, ClickHouse and Cloudflare D1 (the last three over HTTP). [SQL →](https://pyfect.dev/docs/sql)
|
|
203
|
+
- **CLI.** Typed commands, flags and arguments with subcommands, generated help, shell
|
|
204
|
+
completions and interactive prompts. [CLI →](https://pyfect.dev/docs/cli)
|
|
205
|
+
- **HTTP server.** Routes and middleware added by layers, each handler an effect, served on
|
|
206
|
+
uvicorn or as an ASGI app. [HTTP server →](https://pyfect.dev/docs/http-server)
|
|
207
|
+
- **HttpApi.** Declare an API once with schemas, then serve it, call it with a derived client
|
|
208
|
+
and document it with OpenAPI and Swagger UI. [HttpApi →](https://pyfect.dev/docs/http-api)
|
|
209
|
+
- **RPC.** Typed procedures over HTTP, WebSockets or workers (threads and child processes),
|
|
210
|
+
with streamed results and an in-memory test client. [RPC →](https://pyfect.dev/docs/rpc)
|
|
211
|
+
- **Persistence.** Key-value stores, caches whose results outlive the process and durable
|
|
212
|
+
at-least-once queues, in memory, files, SQL or Redis.
|
|
213
|
+
[Persistence →](https://pyfect.dev/docs/persistence)
|
|
214
|
+
- **Rate limiting and circuit breakers.** Limits and breakers per key, in memory or shared
|
|
215
|
+
by every process through Redis, and HTTP clients that follow a server's rate limit
|
|
216
|
+
headers. [Rate limiting →](https://pyfect.dev/docs/rate-limiting)
|
|
217
|
+
- **Workers.** Run code on threads or in child processes and talk to it by messages or
|
|
218
|
+
typed RPC calls over a pool. [Workers →](https://pyfect.dev/docs/workers)
|
|
219
|
+
- **Workflows.** Durable workflows whose steps run once and are replayed, with durable
|
|
220
|
+
sleeps, waits completed from outside and compensation on failure.
|
|
221
|
+
[Workflows →](https://pyfect.dev/docs/workflows)
|
|
222
|
+
- **AI.** A provider-neutral `LanguageModel` for text, structured output, streaming and
|
|
223
|
+
typed tools, chats that keep their history, typed decisions (classify, rate,
|
|
224
|
+
probability), and Anthropic, OpenAI, OpenAI-compatible, OpenRouter, Cloudflare and
|
|
225
|
+
TypeSafe providers over the HTTP client. [AI →](https://pyfect.dev/docs/ai)
|
|
226
|
+
- **MCP.** Serve a toolkit over the Model Context Protocol on HTTP or stdio, and call any
|
|
227
|
+
MCP server's tools. [MCP →](https://pyfect.dev/docs/mcp)
|
|
228
|
+
- **Models.** One class for a table's row and its insert, update and JSON variants, with
|
|
229
|
+
typed repositories on any SQL client. [Models →](https://pyfect.dev/docs/models)
|
|
230
|
+
- **GraphQL.** Typed queries, mutations and subscriptions over HTTP, Server-Sent Events or
|
|
231
|
+
WebSockets, with middleware, pagination and batched lookups.
|
|
232
|
+
[GraphQL →](https://pyfect.dev/docs/graphql)
|
|
233
|
+
- **Event log.** Events written to a local journal, replayed through handlers and synced
|
|
234
|
+
with a server, encrypted end to end if you like. [Event log →](https://pyfect.dev/docs/eventlog)
|
|
235
|
+
- **Cluster.** Stateful entities sharded over runners that talk over TCP, HTTP or
|
|
236
|
+
WebSockets, with messages and shard locks in SQL, singletons and cron jobs that run once
|
|
237
|
+
across the cluster, and durable workflows that resume on another runner when one stops.
|
|
238
|
+
[Cluster →](https://pyfect.dev/docs/cluster)
|
|
239
|
+
- **Atoms.** Reactive values in a registry: derived atoms that update when what they read
|
|
240
|
+
changes, effect, stream and RPC or HTTP API atoms, and state handed from a server to a
|
|
241
|
+
client by hydration. [Atoms →](https://pyfect.dev/docs/atoms)
|
|
242
|
+
- **Deterministic tests.** A pytest plugin with a virtual clock, so a test of a 30-second
|
|
243
|
+
retry takes no real time. [Testing →](https://pyfect.dev/docs/testing)
|
|
244
|
+
|
|
245
|
+
pyfect follows Effect v4, does what Effect does wherever Python can express it, and runs
|
|
246
|
+
Effect's own test cases as its spec: over 21,000 tests, with a test that fails on any Effect
|
|
247
|
+
test title neither ported nor omitted with a reason, and about 94% of Effect's API so far. Every
|
|
248
|
+
export of the core modules is listed against Effect's in [`docs/coverage/`](docs/coverage), and
|
|
249
|
+
[`docs/PARITY.md`](docs/PARITY.md) tracks what is done, what is open and what still differs. One
|
|
250
|
+
difference to know: a bare number as a `Duration` is seconds here and milliseconds in Effect.
|
|
251
|
+
|
|
252
|
+
## Installation
|
|
253
|
+
|
|
254
|
+
pyfect needs Python 3.13 or later; its one dependency is `typing-extensions`. Install the
|
|
255
|
+
`pyfect-dev` distribution from PyPI and `import pyfect` (PyPI's `pyfect` is an unrelated
|
|
256
|
+
project).
|
|
257
|
+
|
|
258
|
+
```sh
|
|
259
|
+
uv add pyfect-dev
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Optional extras: `otel` (OpenTelemetry), `hypothesis`, `pydantic`, `msgspec`, `httpx` (the
|
|
263
|
+
HTTP client backend), `websockets` (WebSocket clients and servers), `server` (the uvicorn
|
|
264
|
+
HTTP server), `pg` (the PostgreSQL driver, on psycopg), `mysql` (the MySQL driver, on
|
|
265
|
+
asyncmy), `mssql` (the SQL Server driver, on python-tds), `redis` (the Redis client, on
|
|
266
|
+
redis-py), `eventlog` (the event log's encryption, on cryptography), `brotli` (`br` response
|
|
267
|
+
compression on a live server), `http2` (the HTTP/2 server, on hypercorn), `intl` (`Intl`-style
|
|
268
|
+
date formatting, on babel) and `regex` (more ECMAScript patterns in schemas), for example
|
|
269
|
+
`uv add "pyfect-dev[otel]"`.
|
|
270
|
+
|
|
271
|
+
pyright is the type checker pyfect is designed for. In strict mode, add
|
|
272
|
+
`reportUnknownParameterType = false`: pyright reads an unannotated generator's send type as
|
|
273
|
+
`Unknown` ([pyright#10279](https://github.com/microsoft/pyright/issues/10279)), and effect
|
|
274
|
+
generators never use it. See [Installation](https://pyfect.dev/docs/installation).
|
|
275
|
+
|
|
276
|
+
## Learn more
|
|
277
|
+
|
|
278
|
+
- [Documentation](https://pyfect.dev/docs): guides for each part of the library.
|
|
279
|
+
- [API reference](https://pyfect.dev/docs/reference): every public function and class.
|
|
280
|
+
- [Examples](examples/): a quickstart, batching, a FastAPI-style service, a stream pipeline
|
|
281
|
+
and schema decoding, all checked by pyright.
|
|
282
|
+
- [Effect's docs](https://effect.website/docs): the concepts carry over; pyfect uses
|
|
283
|
+
snake_case and `yield from` in place of `yield*`.
|
|
284
|
+
|
|
285
|
+
## Contributing
|
|
286
|
+
|
|
287
|
+
Contributions are welcome. [`CONTRIBUTING.md`](CONTRIBUTING.md) covers setup, the one
|
|
288
|
+
command that checks everything, and how a change is ported from Effect. Issues labelled
|
|
289
|
+
[good first issue](https://github.com/tylergibbs1/pyfect/labels/good%20first%20issue) are a
|
|
290
|
+
good place to start. Everyone taking part agrees to the
|
|
291
|
+
[Code of Conduct](CODE_OF_CONDUCT.md).
|
|
292
|
+
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
[MIT](LICENSE). pyfect is an independent port and is not affiliated with Effectful
|
|
296
|
+
Technologies; Effect is MIT-licensed by its authors.
|