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,84 @@
|
|
|
1
|
+
"""Running one effect as a test: ``run_test`` and ``run_test_async``."""
|
|
2
|
+
# pyright: reportPrivateUsage=false
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Never
|
|
7
|
+
|
|
8
|
+
from pyfect._core import Effect
|
|
9
|
+
from pyfect._effect_args import require_effect
|
|
10
|
+
from pyfect.clock import Clock
|
|
11
|
+
from pyfect.console import CurrentConsole
|
|
12
|
+
from pyfect.context import Context
|
|
13
|
+
from pyfect.duration import Duration, to_seconds
|
|
14
|
+
from pyfect.exit import Exit
|
|
15
|
+
from pyfect.run import unwrap
|
|
16
|
+
from pyfect.scheduler import AsyncioScheduler
|
|
17
|
+
from pyfect.testing._clock import TestClock
|
|
18
|
+
from pyfect.testing._console import TestConsole
|
|
19
|
+
from pyfect.testing._runtime import SyncRuntime, run_on_loop
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def run_test_exit[A, E](
|
|
23
|
+
eff: Effect[A, E, Never],
|
|
24
|
+
*,
|
|
25
|
+
stall_timeout: float = 1.0,
|
|
26
|
+
warning_delay: Duration = 1.0,
|
|
27
|
+
timeout: Duration | None = None,
|
|
28
|
+
) -> Exit[A, E]:
|
|
29
|
+
"""Run ``eff`` on a synchronous scheduler with a ``TestClock`` and a
|
|
30
|
+
``TestConsole``.
|
|
31
|
+
|
|
32
|
+
Raises ``StalledError`` when every fiber waits with nothing to wake it,
|
|
33
|
+
and ``TestTimeoutError`` after ``timeout`` of real time; either way the
|
|
34
|
+
effect is interrupted and its finalizers run first."""
|
|
35
|
+
require_effect(eff, "eff")
|
|
36
|
+
clock = TestClock(warning_delay=warning_delay)
|
|
37
|
+
limit = None if timeout is None else to_seconds(timeout)
|
|
38
|
+
try:
|
|
39
|
+
return SyncRuntime(stall_timeout=stall_timeout).run(
|
|
40
|
+
eff, Context.empty().add(Clock, clock).add(CurrentConsole, TestConsole()), timeout=limit
|
|
41
|
+
)
|
|
42
|
+
finally:
|
|
43
|
+
clock.stop_warning()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def run_test[A](
|
|
47
|
+
eff: Effect[A, Never, Never],
|
|
48
|
+
*,
|
|
49
|
+
stall_timeout: float = 1.0,
|
|
50
|
+
warning_delay: Duration = 1.0,
|
|
51
|
+
timeout: Duration | None = None,
|
|
52
|
+
) -> A:
|
|
53
|
+
return unwrap(
|
|
54
|
+
run_test_exit(
|
|
55
|
+
eff, stall_timeout=stall_timeout, warning_delay=warning_delay, timeout=timeout
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def run_test_async_exit[A, E](
|
|
61
|
+
eff: Effect[A, E, Never], *, warning_delay: Duration = 1.0, timeout: Duration | None = None
|
|
62
|
+
) -> Exit[A, E]:
|
|
63
|
+
"""Run ``eff`` on the running asyncio loop with a ``TestClock`` and a
|
|
64
|
+
``TestConsole``, so
|
|
65
|
+
``from_async`` works on virtual time. Cancelling the awaiting task
|
|
66
|
+
interrupts the effect and waits for its finalizers."""
|
|
67
|
+
require_effect(eff, "eff")
|
|
68
|
+
clock = TestClock(warning_delay=warning_delay)
|
|
69
|
+
limit = None if timeout is None else to_seconds(timeout)
|
|
70
|
+
try:
|
|
71
|
+
return await run_on_loop(
|
|
72
|
+
AsyncioScheduler.current(),
|
|
73
|
+
eff,
|
|
74
|
+
Context.empty().add(Clock, clock).add(CurrentConsole, TestConsole()),
|
|
75
|
+
limit,
|
|
76
|
+
)
|
|
77
|
+
finally:
|
|
78
|
+
clock.stop_warning()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def run_test_async[A](
|
|
82
|
+
eff: Effect[A, Never, Never], *, warning_delay: Duration = 1.0, timeout: Duration | None = None
|
|
83
|
+
) -> A:
|
|
84
|
+
return unwrap(await run_test_async_exit(eff, warning_delay=warning_delay, timeout=timeout))
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Where tests run: a synchronous runtime and an asyncio one.
|
|
2
|
+
|
|
3
|
+
Both run an effect with a given context and an optional real-time
|
|
4
|
+
``timeout``. When the timeout passes, the test's fiber is interrupted and
|
|
5
|
+
its finalizers run to completion before ``TestTimeoutError`` is raised.
|
|
6
|
+
"""
|
|
7
|
+
# pyright: reportPrivateUsage=false
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
from typing import Any, Never, Protocol
|
|
13
|
+
|
|
14
|
+
from pyfect._core import Effect
|
|
15
|
+
from pyfect._fiber import Fiber, root_fiber
|
|
16
|
+
from pyfect.clock import Clock
|
|
17
|
+
from pyfect.context import Context
|
|
18
|
+
from pyfect.duration import Seconds
|
|
19
|
+
from pyfect.exit import Exit, Failure
|
|
20
|
+
from pyfect.run import await_fiber
|
|
21
|
+
from pyfect.scheduler import AsyncioScheduler, Cancel, Scheduler, StalledError, SyncScheduler
|
|
22
|
+
from pyfect.testing._clock import TestClock
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestTimeoutError(TimeoutError):
|
|
26
|
+
"""A test ran past its timeout. It was interrupted and its finalizers
|
|
27
|
+
ran before this was raised."""
|
|
28
|
+
|
|
29
|
+
__test__ = False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Runtime(Protocol):
|
|
33
|
+
def run[A, E](
|
|
34
|
+
self, eff: Effect[A, E, Never], context: Context, *, timeout: Seconds | None
|
|
35
|
+
) -> Exit[A, E]: ...
|
|
36
|
+
|
|
37
|
+
def close(self) -> None: ...
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class _Deadline:
|
|
41
|
+
"""Interrupts a fiber once ``timeout`` of real time has passed."""
|
|
42
|
+
|
|
43
|
+
__slots__ = ("_cancel", "expired", "timeout")
|
|
44
|
+
|
|
45
|
+
def __init__(self, fiber: Fiber[Any, Any], timeout: Seconds | None) -> None:
|
|
46
|
+
self.timeout = timeout
|
|
47
|
+
self.expired = False
|
|
48
|
+
self._cancel: Cancel | None = None
|
|
49
|
+
if timeout is None:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
def expire() -> None:
|
|
53
|
+
self.expired = True
|
|
54
|
+
fiber.interrupt_as_unsafe(None)
|
|
55
|
+
|
|
56
|
+
# A daemon timer: a stalled test still fails fast with StalledError.
|
|
57
|
+
self._cancel = fiber._scheduler.call_later(timeout, expire, daemon=True)
|
|
58
|
+
|
|
59
|
+
def check[A, E](self, ex: Exit[A, E], context: Context) -> Exit[A, E]:
|
|
60
|
+
if self._cancel is not None:
|
|
61
|
+
self._cancel()
|
|
62
|
+
if not self.expired:
|
|
63
|
+
return ex
|
|
64
|
+
error = TestTimeoutError(
|
|
65
|
+
f"test timed out after {self.timeout}s; it was interrupted and its finalizers ran"
|
|
66
|
+
)
|
|
67
|
+
if isinstance(ex, Failure) and not ex.cause.has_interrupts_only:
|
|
68
|
+
error.add_note("While interrupting it:\n" + ex.cause.pretty())
|
|
69
|
+
_note_sleepers(error, context)
|
|
70
|
+
raise error
|
|
71
|
+
|
|
72
|
+
def stalled_after_timeout(self, stall: StalledError, context: Context) -> TestTimeoutError:
|
|
73
|
+
"""The real failure when interrupting at the deadline left a finalizer stuck: the
|
|
74
|
+
timeout, which ``StalledError`` would otherwise hide."""
|
|
75
|
+
error = TestTimeoutError(
|
|
76
|
+
f"test timed out after {self.timeout}s; it was interrupted and a finalizer never "
|
|
77
|
+
f"finished ({stall})"
|
|
78
|
+
)
|
|
79
|
+
_note_sleepers(error, context)
|
|
80
|
+
return error
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _note_sleepers(error: BaseException, context: Context) -> None:
|
|
84
|
+
_, clock = context.lookup(Clock)
|
|
85
|
+
if isinstance(clock, TestClock) and clock.pending_sleeps:
|
|
86
|
+
error.add_note(
|
|
87
|
+
f"{clock.pending_sleeps} fiber(s) are sleeping on the TestClock. "
|
|
88
|
+
"Move time with testing.adjust(...)."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _start[A, E](
|
|
93
|
+
scheduler: Scheduler, eff: Effect[A, E, Never], context: Context, timeout: Seconds | None
|
|
94
|
+
) -> tuple[Fiber[A, E], _Deadline]:
|
|
95
|
+
fiber: Fiber[A, E] = root_fiber(scheduler, context)
|
|
96
|
+
fiber._start(eff)
|
|
97
|
+
return fiber, _Deadline(fiber, timeout)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class SyncRuntime:
|
|
101
|
+
"""Runs on a ``SyncScheduler``. Several runs share it, so fibers a
|
|
102
|
+
shared layer forked keep running during each test."""
|
|
103
|
+
|
|
104
|
+
def __init__(self, *, stall_timeout: float | None = 1.0) -> None:
|
|
105
|
+
self.scheduler = SyncScheduler()
|
|
106
|
+
self._stall_timeout = stall_timeout
|
|
107
|
+
|
|
108
|
+
def run[A, E](
|
|
109
|
+
self, eff: Effect[A, E, Never], context: Context, *, timeout: Seconds | None
|
|
110
|
+
) -> Exit[A, E]:
|
|
111
|
+
fiber, deadline = _start(self.scheduler, eff, context, timeout)
|
|
112
|
+
|
|
113
|
+
def done() -> bool:
|
|
114
|
+
return fiber._exit is not None
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
self.scheduler.run_until(done, stall_timeout=self._stall_timeout)
|
|
118
|
+
except StalledError as err:
|
|
119
|
+
if deadline.expired:
|
|
120
|
+
raise deadline.stalled_after_timeout(err, context) from err
|
|
121
|
+
err.add_note("Every fiber is waiting; the test was interrupted to run its finalizers.")
|
|
122
|
+
_note_sleepers(err, context)
|
|
123
|
+
self._drain(fiber)
|
|
124
|
+
raise
|
|
125
|
+
except KeyboardInterrupt:
|
|
126
|
+
self._drain(fiber)
|
|
127
|
+
raise
|
|
128
|
+
ex = fiber._exit
|
|
129
|
+
assert ex is not None
|
|
130
|
+
return deadline.check(ex, context)
|
|
131
|
+
|
|
132
|
+
def _drain(self, fiber: Fiber[Any, Any]) -> None:
|
|
133
|
+
"""Interrupt ``fiber`` and let its finalizers run, unless they stall
|
|
134
|
+
too."""
|
|
135
|
+
fiber.interrupt_as_unsafe(None)
|
|
136
|
+
try:
|
|
137
|
+
self.scheduler.run_until(
|
|
138
|
+
lambda: fiber._exit is not None, stall_timeout=self._stall_timeout
|
|
139
|
+
)
|
|
140
|
+
except StalledError:
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
def close(self) -> None:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class AsyncioRuntime:
|
|
148
|
+
"""Runs on an event loop of its own, kept across runs."""
|
|
149
|
+
|
|
150
|
+
def __init__(self) -> None:
|
|
151
|
+
self._loop = asyncio.new_event_loop()
|
|
152
|
+
self.scheduler = AsyncioScheduler(self._loop)
|
|
153
|
+
|
|
154
|
+
def run[A, E](
|
|
155
|
+
self, eff: Effect[A, E, Never], context: Context, *, timeout: Seconds | None
|
|
156
|
+
) -> Exit[A, E]:
|
|
157
|
+
return self._loop.run_until_complete(run_on_loop(self.scheduler, eff, context, timeout))
|
|
158
|
+
|
|
159
|
+
def close(self) -> None:
|
|
160
|
+
self._loop.run_until_complete(self._loop.shutdown_asyncgens())
|
|
161
|
+
self._loop.close()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
async def run_on_loop[A, E](
|
|
165
|
+
scheduler: AsyncioScheduler,
|
|
166
|
+
eff: Effect[A, E, Never],
|
|
167
|
+
context: Context,
|
|
168
|
+
timeout: Seconds | None,
|
|
169
|
+
) -> Exit[A, E]:
|
|
170
|
+
"""Run on the running loop. Cancelling the awaiting task interrupts the
|
|
171
|
+
fiber and waits for its finalizers."""
|
|
172
|
+
fiber, deadline = _start(scheduler, eff, context, timeout)
|
|
173
|
+
ex = await await_fiber(fiber)
|
|
174
|
+
return deadline.check(ex, context)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Schema test assertions (Effect's ``TestSchema.Asserts``)::
|
|
2
|
+
|
|
3
|
+
asserts = testing.SchemaAsserts(S.String)
|
|
4
|
+
asserts.decoding().succeed("a")
|
|
5
|
+
asserts.decoding().fail(1, "Expected string")
|
|
6
|
+
asserts.encoding(report_input=True).fail(1, "Expected string, got 1")
|
|
7
|
+
|
|
8
|
+
``succeed(input)`` expects the input back; ``succeed(input, expected)`` a
|
|
9
|
+
given value. ``fail(input, message)`` expects a schema failure whose issue
|
|
10
|
+
prints as ``message``. A defect fails the assertion with the whole cause.
|
|
11
|
+
``succeed_effect`` / ``fail_effect`` are the same assertions as effects
|
|
12
|
+
that parse each time they run, with the running fiber's services: a
|
|
13
|
+
mismatch is an ``AssertionError`` defect. ``verify_generation()`` checks
|
|
14
|
+
the values ``to_strategy`` draws; ``verify_round_trip()`` that each one
|
|
15
|
+
encodes and decodes back to an equivalent value.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from typing import Any, Generic, Never, TypeVar, Unpack
|
|
23
|
+
|
|
24
|
+
from pyfect._core import Effect
|
|
25
|
+
from pyfect._extras import missing_extra
|
|
26
|
+
from pyfect.cause import Cause, Die, Fail, from_reasons
|
|
27
|
+
from pyfect.context import Context, TagLike
|
|
28
|
+
from pyfect.effect import context, fail_cause, provide_service, succeed, suspend, sync, void
|
|
29
|
+
from pyfect.exit import Exit, Failure, Success
|
|
30
|
+
from pyfect.result import Err, Ok, Result
|
|
31
|
+
from pyfect.run import run_blocking, run_blocking_exit
|
|
32
|
+
from pyfect.schema import (
|
|
33
|
+
Codec,
|
|
34
|
+
ParseOptions,
|
|
35
|
+
SchemaError,
|
|
36
|
+
decode_unknown_effect,
|
|
37
|
+
encode_unknown_effect,
|
|
38
|
+
is_,
|
|
39
|
+
to_equivalence,
|
|
40
|
+
to_strategy,
|
|
41
|
+
)
|
|
42
|
+
from pyfect.testing._assertions import describe_exit
|
|
43
|
+
|
|
44
|
+
_SAME = object()
|
|
45
|
+
R_co = TypeVar("R_co", covariant=True)
|
|
46
|
+
RD_co = TypeVar("RD_co", covariant=True)
|
|
47
|
+
RE_co = TypeVar("RE_co", covariant=True)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _schema_result[R](
|
|
51
|
+
eff: Effect[object, SchemaError, R],
|
|
52
|
+
) -> Effect[Result[object, str], Never, R]:
|
|
53
|
+
"""``Ok(value)``, or ``Err(message)`` for a failure made only of schema
|
|
54
|
+
errors; any other cause stays a failure, its typed errors made defects
|
|
55
|
+
(Effect's ``schemaResult``)."""
|
|
56
|
+
|
|
57
|
+
def from_exit(ex: Exit[object, SchemaError]) -> Effect[Result[object, str], Never, R]:
|
|
58
|
+
if isinstance(ex, Success):
|
|
59
|
+
return succeed(Ok(ex.value))
|
|
60
|
+
reasons = ex.cause.reasons
|
|
61
|
+
if reasons and all(isinstance(r, Fail) for r in reasons):
|
|
62
|
+
first = reasons[0]
|
|
63
|
+
assert isinstance(first, Fail)
|
|
64
|
+
return succeed(Err(first.error.message))
|
|
65
|
+
return fail_cause(_as_defects(ex.cause))
|
|
66
|
+
|
|
67
|
+
return eff.exit().flat_map(from_exit)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _as_defects(cause: Cause[SchemaError]) -> Cause[Never]:
|
|
71
|
+
return from_reasons(
|
|
72
|
+
Die(r.error, annotations=r.annotations) if isinstance(r, Fail) else r for r in cause.reasons
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _assert_result(actual: Result[object, str], expected: Result[object, str]) -> None:
|
|
77
|
+
if actual != expected:
|
|
78
|
+
raise AssertionError(f"expected {expected!r}, got {actual!r}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass(frozen=True, slots=True)
|
|
82
|
+
class SchemaCheck(Generic[R_co]):
|
|
83
|
+
"""Assertions on one direction of a schema: make, decode or encode,
|
|
84
|
+
needing the services ``R``. Assertions run once none are left:
|
|
85
|
+
``provide(tag, service)`` gives a direction its one service."""
|
|
86
|
+
|
|
87
|
+
parse: Callable[[object], Effect[object, SchemaError, R_co]]
|
|
88
|
+
|
|
89
|
+
def run(self: SchemaCheck[Never], input: object) -> Exit[object, SchemaError]:
|
|
90
|
+
return run_blocking_exit(self.parse(input))
|
|
91
|
+
|
|
92
|
+
def provide[S, T](self: SchemaCheck[T], tag: TagLike[S, T], service: S) -> SchemaCheck[Never]:
|
|
93
|
+
parse = self.parse
|
|
94
|
+
return SchemaCheck(lambda input: parse(input).pipe(provide_service(tag, service)))
|
|
95
|
+
|
|
96
|
+
def succeed(self: SchemaCheck[Never], input: object, expected: object = _SAME) -> None:
|
|
97
|
+
want = input if expected is _SAME else expected
|
|
98
|
+
ex = self.run(input)
|
|
99
|
+
if not (isinstance(ex, Success) and ex.value == want):
|
|
100
|
+
raise AssertionError(f"expected Success({want!r}), got {describe_exit(ex)}")
|
|
101
|
+
|
|
102
|
+
def succeed_effect(self, input: object, expected: object = _SAME) -> Effect[None, Never, R_co]:
|
|
103
|
+
"""``succeed`` as an effect, parsing each time it runs."""
|
|
104
|
+
want: Result[object, str] = Ok(input if expected is _SAME else expected)
|
|
105
|
+
return self._assert_effect(input, want)
|
|
106
|
+
|
|
107
|
+
def fail_effect(self, input: object, message: str) -> Effect[None, Never, R_co]:
|
|
108
|
+
"""``fail`` as an effect, parsing each time it runs."""
|
|
109
|
+
return self._assert_effect(input, Err(message))
|
|
110
|
+
|
|
111
|
+
def _assert_effect(
|
|
112
|
+
self, input: object, expected: Result[object, str]
|
|
113
|
+
) -> Effect[None, Never, R_co]:
|
|
114
|
+
parse = self.parse
|
|
115
|
+
return suspend(lambda: _schema_result(parse(input))).flat_map(
|
|
116
|
+
lambda actual: sync(lambda: _assert_result(actual, expected))
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def fail(self: SchemaCheck[Never], input: object, message: str) -> None:
|
|
120
|
+
ex = self.run(input)
|
|
121
|
+
if isinstance(ex, Failure) and len(ex.cause.reasons) == 1:
|
|
122
|
+
match ex.cause.find_error():
|
|
123
|
+
case Ok(error) if error.message == message:
|
|
124
|
+
return
|
|
125
|
+
case _:
|
|
126
|
+
pass
|
|
127
|
+
raise AssertionError(f"expected a SchemaError {message!r}, got {describe_exit(ex)}")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class _Abort(BaseException):
|
|
131
|
+
"""Ends a Hypothesis run at once (it shrinks on an ``Exception``) with
|
|
132
|
+
a round trip's cause that is not an assertion failure."""
|
|
133
|
+
|
|
134
|
+
def __init__(self, cause: Cause[Never]) -> None:
|
|
135
|
+
self.cause = cause
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass(frozen=True, slots=True)
|
|
139
|
+
class SchemaAsserts(Generic[RD_co, RE_co]):
|
|
140
|
+
"""Assertions for one schema; a schema needing services to decode
|
|
141
|
+
(``RD``) or encode (``RE``) gets them from ``provide`` on each direction."""
|
|
142
|
+
|
|
143
|
+
schema: Codec[object, object, RD_co, RE_co]
|
|
144
|
+
|
|
145
|
+
def make(self, **options: Unpack[ParseOptions]) -> SchemaCheck[RD_co]:
|
|
146
|
+
"""Construction: the decoded side, with constructor defaults."""
|
|
147
|
+
schema = self.schema.__codec__()
|
|
148
|
+
|
|
149
|
+
def parse(input: object) -> Effect[object, SchemaError, RD_co]:
|
|
150
|
+
return schema.make_effect(input, **options).map_error(SchemaError)
|
|
151
|
+
|
|
152
|
+
return SchemaCheck(parse)
|
|
153
|
+
|
|
154
|
+
def decoding(self, **options: Unpack[ParseOptions]) -> SchemaCheck[RD_co]:
|
|
155
|
+
return SchemaCheck(decode_unknown_effect(self.schema, **options))
|
|
156
|
+
|
|
157
|
+
def encoding(self, **options: Unpack[ParseOptions]) -> SchemaCheck[RE_co]:
|
|
158
|
+
return SchemaCheck(encode_unknown_effect(self.schema, **options))
|
|
159
|
+
|
|
160
|
+
def verify_round_trip(
|
|
161
|
+
self: SchemaAsserts[Never, Never], *, runs: int = 100, seed: int | None = None
|
|
162
|
+
) -> None:
|
|
163
|
+
"""Each value ``to_strategy`` draws encodes, then decodes to a value
|
|
164
|
+
the schema's equivalence calls equal (Effect's ``verifyRoundTrip``;
|
|
165
|
+
needs Hypothesis). A failing value is shrunk and reported."""
|
|
166
|
+
verify: Effect[None] = self.verify_round_trip_effect(runs=runs, seed=seed)
|
|
167
|
+
run_blocking(verify)
|
|
168
|
+
|
|
169
|
+
def verify_round_trip_effect[R](
|
|
170
|
+
self: SchemaAsserts[R, R], *, runs: int = 100, seed: int | None = None
|
|
171
|
+
) -> Effect[None, Never, R]:
|
|
172
|
+
"""``verify_round_trip`` as an effect with the running fiber's
|
|
173
|
+
services (Effect's ``verifyRoundTripEffect``): a mismatch is an
|
|
174
|
+
``AssertionError`` defect; any other failure than a schema error
|
|
175
|
+
ends the check and fails the effect with its cause. Hypothesis
|
|
176
|
+
draws synchronously, so each value's round trip runs to its end in
|
|
177
|
+
a nested synchronous run (no asyncio, no interruption midway)."""
|
|
178
|
+
try:
|
|
179
|
+
from hypothesis import HealthCheck, given, settings
|
|
180
|
+
from hypothesis import seed as hypothesis_seed
|
|
181
|
+
except ImportError as error:
|
|
182
|
+
raise missing_extra("verify_round_trip", "hypothesis") from error
|
|
183
|
+
|
|
184
|
+
schema = self.schema
|
|
185
|
+
|
|
186
|
+
# The services are the running fiber's: whatever ``R`` it was given.
|
|
187
|
+
def verify(ctx: Context[Any]) -> Effect[None]:
|
|
188
|
+
encode = encode_unknown_effect(schema)
|
|
189
|
+
decode = decode_unknown_effect(schema)
|
|
190
|
+
equivalent = to_equivalence(schema)
|
|
191
|
+
|
|
192
|
+
@settings(
|
|
193
|
+
max_examples=runs,
|
|
194
|
+
database=None,
|
|
195
|
+
deadline=None,
|
|
196
|
+
suppress_health_check=[HealthCheck.too_slow],
|
|
197
|
+
)
|
|
198
|
+
@given(to_strategy(schema))
|
|
199
|
+
def check(value: object) -> None:
|
|
200
|
+
round_trip = _schema_result(encode(value).flat_map(decode))
|
|
201
|
+
ex = run_blocking_exit(round_trip, context=ctx)
|
|
202
|
+
if not isinstance(ex, Success):
|
|
203
|
+
raise _Abort(ex.cause)
|
|
204
|
+
match ex.value:
|
|
205
|
+
case Ok(back) if equivalent(back, value):
|
|
206
|
+
return
|
|
207
|
+
case result:
|
|
208
|
+
raise AssertionError(f"{value!r} did not round-trip: {result!r}")
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
(check if seed is None else hypothesis_seed(seed)(check))()
|
|
212
|
+
except _Abort as abort:
|
|
213
|
+
return fail_cause(abort.cause)
|
|
214
|
+
return void
|
|
215
|
+
|
|
216
|
+
return context().flat_map(lambda ctx: suspend(lambda: verify(ctx)))
|
|
217
|
+
|
|
218
|
+
def verify_generation(self, count: int = 30) -> None:
|
|
219
|
+
"""``to_strategy`` draws ``count`` values, each one of the schema's
|
|
220
|
+
(Effect's ``arbitrary().verifyGeneration()``; needs Hypothesis)."""
|
|
221
|
+
try:
|
|
222
|
+
from hypothesis import HealthCheck, given, settings
|
|
223
|
+
except ImportError as error:
|
|
224
|
+
raise missing_extra("verify_generation", "hypothesis") from error
|
|
225
|
+
|
|
226
|
+
is_value = is_(self.schema)
|
|
227
|
+
|
|
228
|
+
@settings(
|
|
229
|
+
max_examples=count,
|
|
230
|
+
database=None,
|
|
231
|
+
deadline=None,
|
|
232
|
+
suppress_health_check=[HealthCheck.too_slow],
|
|
233
|
+
)
|
|
234
|
+
@given(to_strategy(self.schema))
|
|
235
|
+
def check(value: object) -> None:
|
|
236
|
+
assert is_value(value), f"generated a value the schema refuses: {value!r}"
|
|
237
|
+
|
|
238
|
+
check()
|
pyfect/testing/plugin.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""The pytest plugin, registered through the ``pytest11`` entry point.
|
|
2
|
+
|
|
3
|
+
- The ``pyfect_timeout`` ini option sets the default real-time timeout of
|
|
4
|
+
``@effect`` and ``@live`` tests (seconds; ``0`` disables it).
|
|
5
|
+
- The ``pyfect_effect`` and ``pyfect_live`` markers are put on those tests,
|
|
6
|
+
so ``-m "not pyfect_live"`` skips the tests that take real time.
|
|
7
|
+
- A failed ``==`` involving an ``Exit`` failure shows its ``Cause.pretty()``,
|
|
8
|
+
or each side's annotations when only those differ.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
from pyfect.duration import Seconds
|
|
16
|
+
from pyfect.exit import Failure
|
|
17
|
+
from pyfect.testing._assertions import annotation_difference, describe_exit
|
|
18
|
+
from pyfect.testing._decorators import settings
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def pytest_addoption(parser: pytest.Parser) -> None:
|
|
22
|
+
parser.addini(
|
|
23
|
+
"pyfect_timeout",
|
|
24
|
+
"Default real-time timeout, in seconds, of @fx.testing.effect and @fx.testing.live "
|
|
25
|
+
"tests (0 disables it)",
|
|
26
|
+
default="5",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_previous_timeout = pytest.StashKey[Seconds | None]()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def pytest_configure(config: pytest.Config) -> None:
|
|
34
|
+
config.addinivalue_line("markers", "pyfect_effect: a test run with a TestClock")
|
|
35
|
+
config.addinivalue_line("markers", "pyfect_live: a test run with the live clock")
|
|
36
|
+
config.stash[_previous_timeout] = settings.timeout
|
|
37
|
+
timeout = float(config.getini("pyfect_timeout"))
|
|
38
|
+
settings.timeout = Seconds(timeout) if timeout > 0 else None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def pytest_unconfigure(config: pytest.Config) -> None:
|
|
42
|
+
# A nested in-process run (pytester) must not change the outer defaults.
|
|
43
|
+
settings.timeout = config.stash.get(_previous_timeout, settings.timeout)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def pytest_assertrepr_compare(op: str, left: object, right: object) -> list[str] | None:
|
|
47
|
+
if op != "==":
|
|
48
|
+
return None
|
|
49
|
+
failures: list[Failure[object]] = [side for side in (left, right) if isinstance(side, Failure)]
|
|
50
|
+
if not failures:
|
|
51
|
+
return None
|
|
52
|
+
lines = [f"{left!r} == {right!r}"]
|
|
53
|
+
if len(failures) == 2: # both sides failed
|
|
54
|
+
difference = annotation_difference(failures[0].cause, failures[1].cause)
|
|
55
|
+
if difference:
|
|
56
|
+
return [*lines, *difference]
|
|
57
|
+
for failure in failures:
|
|
58
|
+
lines.extend(describe_exit(failure).splitlines())
|
|
59
|
+
return lines
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Tracing (Effect's ``Tracer``): spans, the tracer that makes them, and the
|
|
2
|
+
references that steer them.
|
|
3
|
+
|
|
4
|
+
``eff.with_span("name")`` runs ``eff`` inside a new span, the parent of
|
|
5
|
+
the spans made inside it; ``@fx.fn("name")`` does the same for every call
|
|
6
|
+
of a generator function::
|
|
7
|
+
|
|
8
|
+
@fx.fn("get_user")
|
|
9
|
+
def get_user(user_id: str):
|
|
10
|
+
yield from fx.annotate_current_span("user.id", user_id)
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
program.pipe(fx.provide(layer.span("startup")))
|
|
14
|
+
|
|
15
|
+
The native tracer keeps spans in memory (``NativeSpan``) and exports
|
|
16
|
+
nothing; install another with ``eff.with_tracer(tracer)`` or a layer
|
|
17
|
+
providing ``CurrentTracer``. Logs inside a span are recorded as its events
|
|
18
|
+
(``logger.tracer_logger``).
|
|
19
|
+
|
|
20
|
+
Spans also record where they were written: a failure inside ``with_span``
|
|
21
|
+
or an ``@fx.fn`` call carries the ``StackFrame`` chain as its ``StackTrace``
|
|
22
|
+
annotation, and ``Cause.pretty()`` prints it under the traceback. Turn it
|
|
23
|
+
off per span with ``capture_stack_trace=False``.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from pyfect._stack_frame import (
|
|
27
|
+
CaptureStackTrace,
|
|
28
|
+
CurrentStackFrame,
|
|
29
|
+
InterruptorStackTrace,
|
|
30
|
+
StackFrame,
|
|
31
|
+
StackTrace,
|
|
32
|
+
)
|
|
33
|
+
from pyfect._tracer_ref import CurrentTracer
|
|
34
|
+
from pyfect.tracer._model import (
|
|
35
|
+
AnySpan,
|
|
36
|
+
ContextHook,
|
|
37
|
+
Ended,
|
|
38
|
+
ExternalSpan,
|
|
39
|
+
Span,
|
|
40
|
+
SpanKind,
|
|
41
|
+
SpanLink,
|
|
42
|
+
SpanOptions,
|
|
43
|
+
SpanRequest,
|
|
44
|
+
SpanStatus,
|
|
45
|
+
Started,
|
|
46
|
+
TracedSpanOptions,
|
|
47
|
+
Tracer,
|
|
48
|
+
)
|
|
49
|
+
from pyfect.tracer._native import NativeSpan, NoopSpan, SpanEvent, native_tracer
|
|
50
|
+
from pyfect.tracer._refs import (
|
|
51
|
+
CurrentTraceLevel,
|
|
52
|
+
DisablePropagation,
|
|
53
|
+
MinimumTraceLevel,
|
|
54
|
+
ParentSpan,
|
|
55
|
+
TracerEnabled,
|
|
56
|
+
TracerSpanAnnotations,
|
|
57
|
+
TracerSpanLinks,
|
|
58
|
+
TracerTimingEnabled,
|
|
59
|
+
)
|
|
60
|
+
from pyfect.tracer._spans import (
|
|
61
|
+
annotate_current_span,
|
|
62
|
+
current_parent_span,
|
|
63
|
+
current_span,
|
|
64
|
+
make_span,
|
|
65
|
+
make_span_scoped,
|
|
66
|
+
use_span,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
"AnySpan",
|
|
71
|
+
"CaptureStackTrace",
|
|
72
|
+
"ContextHook",
|
|
73
|
+
"CurrentStackFrame",
|
|
74
|
+
"CurrentTraceLevel",
|
|
75
|
+
"CurrentTracer",
|
|
76
|
+
"DisablePropagation",
|
|
77
|
+
"Ended",
|
|
78
|
+
"ExternalSpan",
|
|
79
|
+
"InterruptorStackTrace",
|
|
80
|
+
"MinimumTraceLevel",
|
|
81
|
+
"NativeSpan",
|
|
82
|
+
"NoopSpan",
|
|
83
|
+
"ParentSpan",
|
|
84
|
+
"Span",
|
|
85
|
+
"SpanEvent",
|
|
86
|
+
"SpanKind",
|
|
87
|
+
"SpanLink",
|
|
88
|
+
"SpanOptions",
|
|
89
|
+
"SpanRequest",
|
|
90
|
+
"SpanStatus",
|
|
91
|
+
"StackFrame",
|
|
92
|
+
"StackTrace",
|
|
93
|
+
"Started",
|
|
94
|
+
"TracedSpanOptions",
|
|
95
|
+
"Tracer",
|
|
96
|
+
"TracerEnabled",
|
|
97
|
+
"TracerSpanAnnotations",
|
|
98
|
+
"TracerSpanLinks",
|
|
99
|
+
"TracerTimingEnabled",
|
|
100
|
+
"annotate_current_span",
|
|
101
|
+
"current_parent_span",
|
|
102
|
+
"current_span",
|
|
103
|
+
"make_span",
|
|
104
|
+
"make_span_scoped",
|
|
105
|
+
"native_tracer",
|
|
106
|
+
"use_span",
|
|
107
|
+
]
|