pyfect-dev 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
pyfect/otel/_wrapper.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""An OpenTelemetry ``Span`` over a pyfect span the bridge did not make
|
|
2
|
+
(Effect's ``makeOtelSpan``), for ``current_otel_span``."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import traceback
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
|
|
9
|
+
from opentelemetry import trace
|
|
10
|
+
from opentelemetry.trace import SpanContext, Status, StatusCode, TraceFlags
|
|
11
|
+
from opentelemetry.util import types
|
|
12
|
+
|
|
13
|
+
from pyfect.clock import ClockService
|
|
14
|
+
from pyfect.exit import Exit, Success, die
|
|
15
|
+
from pyfect.otel._span import external_span_of, hex_id
|
|
16
|
+
from pyfect.tracer import Span, SpanLink
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EffectSpanView(trace.Span):
|
|
20
|
+
"""Writes through to ``span``: attributes, events, links and the end,
|
|
21
|
+
with the exit the last ``set_status`` chose (an error status ends it
|
|
22
|
+
with a defect). Times default to ``clock``'s now."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, span: Span, clock: ClockService) -> None:
|
|
25
|
+
self._span = span
|
|
26
|
+
self._clock = clock
|
|
27
|
+
self._exit: Exit[object, object] = Success(None)
|
|
28
|
+
self._context = SpanContext(
|
|
29
|
+
hex_id(span.trace_id),
|
|
30
|
+
hex_id(span.span_id),
|
|
31
|
+
False,
|
|
32
|
+
TraceFlags(TraceFlags.SAMPLED if span.sampled else TraceFlags.DEFAULT),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def _time(self, nanos: int | None) -> int:
|
|
36
|
+
return self._clock.now_nanos_unsafe() if nanos is None else nanos
|
|
37
|
+
|
|
38
|
+
def get_span_context(self) -> SpanContext:
|
|
39
|
+
return self._context
|
|
40
|
+
|
|
41
|
+
def set_attribute(self, key: str, value: types.AttributeValue) -> None:
|
|
42
|
+
self._span.attribute(key, value)
|
|
43
|
+
|
|
44
|
+
def set_attributes(self, attributes: Mapping[str, types.AttributeValue]) -> None:
|
|
45
|
+
for key, value in attributes.items():
|
|
46
|
+
self._span.attribute(key, value)
|
|
47
|
+
|
|
48
|
+
def add_event(
|
|
49
|
+
self, name: str, attributes: types.Attributes = None, timestamp: int | None = None
|
|
50
|
+
) -> None:
|
|
51
|
+
self._span.event(name, self._time(timestamp), attributes)
|
|
52
|
+
|
|
53
|
+
def add_link(self, context: SpanContext, attributes: types.Attributes = None) -> None:
|
|
54
|
+
self._span.add_links([_link(context, attributes)])
|
|
55
|
+
|
|
56
|
+
def update_name(self, name: str) -> None:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
def is_recording(self) -> bool:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
def set_status(self, status: Status | StatusCode, description: str | None = None) -> None:
|
|
63
|
+
code = status.status_code if isinstance(status, Status) else status
|
|
64
|
+
if code is StatusCode.ERROR:
|
|
65
|
+
message = status.description if isinstance(status, Status) else description
|
|
66
|
+
self._exit = die(Exception(message or "Unknown error"))
|
|
67
|
+
else:
|
|
68
|
+
self._exit = Success(None)
|
|
69
|
+
|
|
70
|
+
def end(self, end_time: int | None = None) -> None:
|
|
71
|
+
self._span.end(self._time(end_time), self._exit)
|
|
72
|
+
|
|
73
|
+
def record_exception(
|
|
74
|
+
self,
|
|
75
|
+
exception: BaseException,
|
|
76
|
+
attributes: types.Attributes = None,
|
|
77
|
+
timestamp: int | None = None,
|
|
78
|
+
escaped: bool = False,
|
|
79
|
+
) -> None:
|
|
80
|
+
self._span.event(
|
|
81
|
+
str(exception),
|
|
82
|
+
self._time(timestamp),
|
|
83
|
+
{
|
|
84
|
+
"exception.type": type(exception).__name__,
|
|
85
|
+
"exception.message": str(exception),
|
|
86
|
+
"exception.stacktrace": "".join(traceback.format_exception(exception)),
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _link(context: SpanContext, attributes: types.Attributes) -> SpanLink:
|
|
92
|
+
return SpanLink(external_span_of(context), dict(attributes or {}))
|
pyfect/otel/logger.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""pyfect logs as OpenTelemetry log records (Effect's ``OtelLogger``).
|
|
2
|
+
|
|
3
|
+
``layer()`` installs a logger emitting every log through the
|
|
4
|
+
``OtelLoggerProvider``; ``layer_logger_provider(processors)`` makes an SDK
|
|
5
|
+
logger provider, flushed and shut down when the layer is released. A log
|
|
6
|
+
inside a span carries the span's context, so backends correlate the two.
|
|
7
|
+
"""
|
|
8
|
+
# pyright: reportPrivateUsage=false
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import traceback
|
|
13
|
+
from collections.abc import Sequence
|
|
14
|
+
from datetime import UTC, datetime, timedelta
|
|
15
|
+
from typing import Final, Never
|
|
16
|
+
|
|
17
|
+
from opentelemetry import context as otel_context
|
|
18
|
+
from opentelemetry._logs import LogRecord, SeverityNumber
|
|
19
|
+
from opentelemetry.sdk import _logs as sdk_logs
|
|
20
|
+
from opentelemetry.sdk.resources import Resource
|
|
21
|
+
from opentelemetry.util.types import AnyValue
|
|
22
|
+
|
|
23
|
+
from pyfect import layer as _layer
|
|
24
|
+
from pyfect.clock import Clock, ClockService
|
|
25
|
+
from pyfect.context import Tag
|
|
26
|
+
from pyfect.duration import Duration
|
|
27
|
+
from pyfect.effect import gen, sync
|
|
28
|
+
from pyfect.layer import Layer
|
|
29
|
+
from pyfect.logger import CurrentLogAnnotations, CurrentLogSpans, Logger, LogLevel, Options
|
|
30
|
+
from pyfect.logger import layer as logger_layer
|
|
31
|
+
from pyfect.otel._attributes import PYFECT_VERSION, to_attribute_value
|
|
32
|
+
from pyfect.otel._shutdown import DEFAULT_SHUTDOWN_TIMEOUT, flush_and_shut_down
|
|
33
|
+
from pyfect.otel._span import populate_context
|
|
34
|
+
from pyfect.otel.resource import OtelResource
|
|
35
|
+
from pyfect.scope import acquire_release
|
|
36
|
+
from pyfect.tracer import ParentSpan
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"OtelLoggerProvider",
|
|
40
|
+
"layer",
|
|
41
|
+
"layer_logger_provider",
|
|
42
|
+
"log_level_to_severity_number",
|
|
43
|
+
"make",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class OtelLoggerProvider(Tag[sdk_logs.LoggerProvider]):
|
|
48
|
+
"""The OpenTelemetry logger provider pyfect logs are emitted through."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
_SEVERITIES: Final = {
|
|
52
|
+
LogLevel.TRACE: SeverityNumber.TRACE,
|
|
53
|
+
LogLevel.DEBUG: SeverityNumber.DEBUG,
|
|
54
|
+
LogLevel.INFO: SeverityNumber.INFO,
|
|
55
|
+
LogLevel.WARN: SeverityNumber.WARN,
|
|
56
|
+
LogLevel.ERROR: SeverityNumber.ERROR,
|
|
57
|
+
LogLevel.FATAL: SeverityNumber.FATAL,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def log_level_to_severity_number(level: LogLevel) -> SeverityNumber:
|
|
62
|
+
"""The OpenTelemetry severity of a level (1 to 24); ``ALL`` and ``NONE``
|
|
63
|
+
are ``UNSPECIFIED``."""
|
|
64
|
+
return _SEVERITIES.get(level, SeverityNumber.UNSPECIFIED)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
_EPOCH: Final = datetime(1970, 1, 1, tzinfo=UTC)
|
|
68
|
+
_MICROSECOND: Final = timedelta(microseconds=1)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _record(options: Options, observed: int) -> LogRecord:
|
|
72
|
+
fiber = options.fiber
|
|
73
|
+
attributes: dict[str, AnyValue] = {
|
|
74
|
+
key: to_attribute_value(value)
|
|
75
|
+
for key, value in fiber.get_ref(CurrentLogAnnotations).items()
|
|
76
|
+
}
|
|
77
|
+
attributes["effect.fiberId"] = fiber.id
|
|
78
|
+
span = fiber.get_ref(ParentSpan)
|
|
79
|
+
context = otel_context.get_current()
|
|
80
|
+
if span is not None:
|
|
81
|
+
context = populate_context(context, span, span.annotations)
|
|
82
|
+
attributes["spanId"] = span.span_id
|
|
83
|
+
attributes["traceId"] = span.trace_id
|
|
84
|
+
now = options.millis
|
|
85
|
+
# Spans are innermost first, so the outermost wins a repeated label.
|
|
86
|
+
for label, start in fiber.get_ref(CurrentLogSpans):
|
|
87
|
+
attributes[f"effect.log_span.{label}"] = now - start
|
|
88
|
+
errors = options.cause.pretty_errors()
|
|
89
|
+
if errors:
|
|
90
|
+
attributes["exception.type"] = type(errors[0]).__name__
|
|
91
|
+
attributes["exception.message"] = str(errors[0])
|
|
92
|
+
attributes["exception.stacktrace"] = "\n".join(
|
|
93
|
+
"".join(traceback.format_exception(error)) for error in errors
|
|
94
|
+
)
|
|
95
|
+
message = [to_attribute_value(item) for item in options.message]
|
|
96
|
+
return LogRecord(
|
|
97
|
+
timestamp=(options.date - _EPOCH) // _MICROSECOND * 1000,
|
|
98
|
+
observed_timestamp=observed,
|
|
99
|
+
context=context,
|
|
100
|
+
severity_text=str(options.log_level),
|
|
101
|
+
severity_number=log_level_to_severity_number(options.log_level),
|
|
102
|
+
body=message[0] if len(message) == 1 else message,
|
|
103
|
+
attributes=attributes,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _logger(provider: sdk_logs.LoggerProvider, clock: ClockService) -> Logger[None]:
|
|
108
|
+
otel_logger = provider.get_logger("pyfect", PYFECT_VERSION)
|
|
109
|
+
return Logger(lambda options: otel_logger.emit(_record(options, clock.now_nanos_unsafe())))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@gen
|
|
113
|
+
def make():
|
|
114
|
+
"""A logger emitting each log as a record through the
|
|
115
|
+
``OtelLoggerProvider``: the log annotations, ``effect.fiberId``, the
|
|
116
|
+
span ids, each log span's elapsed milliseconds
|
|
117
|
+
(``effect.log_span.<label>``) and the cause's first error
|
|
118
|
+
(``exception.*``) as attributes."""
|
|
119
|
+
provider = yield from OtelLoggerProvider()
|
|
120
|
+
clock = yield from Clock()
|
|
121
|
+
return _logger(provider, clock)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def layer(*, merge_with_existing: bool = True) -> Layer[Never, Never, OtelLoggerProvider]:
|
|
125
|
+
"""Installs ``make``'s logger, next to the current loggers (or instead
|
|
126
|
+
of them, with ``merge_with_existing=False``)."""
|
|
127
|
+
return logger_layer([make], merge_with_existing=merge_with_existing)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def layer_logger_provider(
|
|
131
|
+
processors: sdk_logs.LogRecordProcessor | Sequence[sdk_logs.LogRecordProcessor],
|
|
132
|
+
*,
|
|
133
|
+
shutdown_timeout: Duration = DEFAULT_SHUTDOWN_TIMEOUT,
|
|
134
|
+
) -> Layer[OtelLoggerProvider, Never, OtelResource]:
|
|
135
|
+
"""An SDK logger provider with the ``OtelResource`` and ``processors``,
|
|
136
|
+
flushed (for at most ``shutdown_timeout``) and shut down when the layer
|
|
137
|
+
is released."""
|
|
138
|
+
given = [processors] if isinstance(processors, sdk_logs.LogRecordProcessor) else processors
|
|
139
|
+
|
|
140
|
+
def acquire(resource: Resource) -> sdk_logs.LoggerProvider:
|
|
141
|
+
provider = sdk_logs.LoggerProvider(resource=resource, shutdown_on_exit=False)
|
|
142
|
+
for processor in given:
|
|
143
|
+
provider.add_log_record_processor(processor)
|
|
144
|
+
return provider
|
|
145
|
+
|
|
146
|
+
return _layer.effect(
|
|
147
|
+
OtelLoggerProvider,
|
|
148
|
+
OtelResource().flat_map(
|
|
149
|
+
lambda resource: acquire_release(
|
|
150
|
+
sync(lambda: acquire(resource)),
|
|
151
|
+
lambda provider, _exit: flush_and_shut_down(provider, shutdown_timeout),
|
|
152
|
+
)
|
|
153
|
+
),
|
|
154
|
+
)
|
pyfect/otel/metrics.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""pyfect metrics as OpenTelemetry metrics (Effect's ``OtelMetrics``).
|
|
2
|
+
|
|
3
|
+
``layer(lambda: reader)`` hands the current metric registry to an SDK
|
|
4
|
+
``MetricReader`` (a ``PeriodicExportingMetricReader`` exporting over OTLP,
|
|
5
|
+
say), which collects it whenever it reads::
|
|
6
|
+
|
|
7
|
+
metrics = otel.metrics.layer(lambda: PeriodicExportingMetricReader(exporter))
|
|
8
|
+
program.pipe(fx.provide(metrics.pipe(layer.provide(otel.resource.layer_empty))))
|
|
9
|
+
|
|
10
|
+
Readers are bound to pyfect's producer instead of a ``MeterProvider``; a
|
|
11
|
+
reader can be bound once. Counters, frequencies and summaries become sums,
|
|
12
|
+
gauges gauges, histograms explicit-bucket histograms.
|
|
13
|
+
"""
|
|
14
|
+
# pyright: reportPrivateUsage=false
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import contextlib
|
|
19
|
+
from collections.abc import Callable, Sequence
|
|
20
|
+
from typing import Any, Never, Protocol
|
|
21
|
+
|
|
22
|
+
from opentelemetry.sdk.metrics.export import MetricReader, MetricsData
|
|
23
|
+
|
|
24
|
+
from pyfect import layer as _layer
|
|
25
|
+
from pyfect._context_effects import context
|
|
26
|
+
from pyfect._core import Effect
|
|
27
|
+
from pyfect.duration import Duration, to_millis
|
|
28
|
+
from pyfect.effect import die, gen, suspend, sync
|
|
29
|
+
from pyfect.layer import Layer
|
|
30
|
+
from pyfect.otel._producer import MetricProducer, TemporalityPreference
|
|
31
|
+
from pyfect.otel._shutdown import DEFAULT_SHUTDOWN_TIMEOUT
|
|
32
|
+
from pyfect.otel.resource import OtelResource
|
|
33
|
+
from pyfect.scope import Scope, acquire_release
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"MetricProducer",
|
|
37
|
+
"Readers",
|
|
38
|
+
"TemporalityPreference",
|
|
39
|
+
"layer",
|
|
40
|
+
"make_producer",
|
|
41
|
+
"register_producer",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
type Readers = Callable[[], MetricReader | Sequence[MetricReader]]
|
|
45
|
+
"""Makes the readers to bind, when the layer builds."""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def make_producer(
|
|
49
|
+
temporality: TemporalityPreference = "cumulative",
|
|
50
|
+
) -> Effect[MetricProducer, Never, OtelResource]:
|
|
51
|
+
"""A producer of the current metric registry, with the ``OtelResource``."""
|
|
52
|
+
|
|
53
|
+
@gen
|
|
54
|
+
def make():
|
|
55
|
+
resource = yield from OtelResource()
|
|
56
|
+
services = yield from context()
|
|
57
|
+
return MetricProducer(resource, services, temporality)
|
|
58
|
+
|
|
59
|
+
return make
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _bind(reader: MetricReader, producer: MetricProducer) -> None:
|
|
63
|
+
if reader._collect is not None:
|
|
64
|
+
raise RuntimeError(f"MetricReader {reader} has been registered already")
|
|
65
|
+
|
|
66
|
+
def collect(_reader: MetricReader, timeout_millis: float = 10_000) -> MetricsData:
|
|
67
|
+
return producer.collect()
|
|
68
|
+
|
|
69
|
+
reader._set_collect_callback(collect)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class _ShutsDown(Protocol):
|
|
73
|
+
def shutdown(self, timeout_millis: float = ...) -> None: ...
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _shut_down(readers: Sequence[_ShutsDown], timeout: Duration) -> Effect[None]:
|
|
77
|
+
"""Shut every reader down, ignoring errors, as Effect does."""
|
|
78
|
+
|
|
79
|
+
def run() -> None:
|
|
80
|
+
for reader in readers:
|
|
81
|
+
with contextlib.suppress(Exception):
|
|
82
|
+
reader.shutdown(timeout_millis=to_millis(timeout))
|
|
83
|
+
|
|
84
|
+
return sync(run)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def register_producer(
|
|
88
|
+
producer: MetricProducer,
|
|
89
|
+
readers: Readers,
|
|
90
|
+
*,
|
|
91
|
+
shutdown_timeout: Duration = DEFAULT_SHUTDOWN_TIMEOUT,
|
|
92
|
+
) -> Effect[list[MetricReader], Never, Scope]:
|
|
93
|
+
"""Bind each reader to its own fork of ``producer``, and shut them down
|
|
94
|
+
when the scope closes. If a reader cannot be bound (it is bound
|
|
95
|
+
already), the readers bound so far are shut down and the effect dies."""
|
|
96
|
+
|
|
97
|
+
def acquire() -> Effect[list[MetricReader]]:
|
|
98
|
+
made = readers()
|
|
99
|
+
given = [made] if isinstance(made, MetricReader) else list(made)
|
|
100
|
+
bound: list[MetricReader] = []
|
|
101
|
+
try:
|
|
102
|
+
for reader in given:
|
|
103
|
+
_bind(reader, producer.fork())
|
|
104
|
+
bound.append(reader)
|
|
105
|
+
except Exception as error:
|
|
106
|
+
failure = error
|
|
107
|
+
return _shut_down(bound, shutdown_timeout).flat_map(lambda _: die(failure))
|
|
108
|
+
return sync(lambda: bound)
|
|
109
|
+
|
|
110
|
+
return acquire_release(
|
|
111
|
+
suspend(acquire), lambda bound, _exit: _shut_down(bound, shutdown_timeout)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def layer(
|
|
116
|
+
readers: Readers,
|
|
117
|
+
*,
|
|
118
|
+
temporality: TemporalityPreference = "cumulative",
|
|
119
|
+
shutdown_timeout: Duration = DEFAULT_SHUTDOWN_TIMEOUT,
|
|
120
|
+
) -> Layer[Never, Never, OtelResource]:
|
|
121
|
+
"""Bind the readers ``readers()`` makes to a producer of the current
|
|
122
|
+
registry while the layer is in use."""
|
|
123
|
+
registered: Effect[Any, Never, OtelResource | Scope] = make_producer(temporality).flat_map(
|
|
124
|
+
lambda producer: register_producer(producer, readers, shutdown_timeout=shutdown_timeout)
|
|
125
|
+
)
|
|
126
|
+
return _layer.effect_discard(registered)
|
pyfect/otel/resource.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""The OpenTelemetry resource: what process or service emits the spans,
|
|
2
|
+
logs and metrics (Effect's ``Resource``).
|
|
3
|
+
|
|
4
|
+
``layer(service_name=...)`` builds one from service metadata;
|
|
5
|
+
``layer_from_env()`` reads ``OTEL_SERVICE_NAME`` and
|
|
6
|
+
``OTEL_RESOURCE_ATTRIBUTES`` through ``Config``; ``layer_empty`` has no
|
|
7
|
+
attributes.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from collections.abc import Mapping
|
|
13
|
+
from typing import Final
|
|
14
|
+
|
|
15
|
+
from opentelemetry.sdk.resources import Resource
|
|
16
|
+
from opentelemetry.util.types import AttributeValue
|
|
17
|
+
|
|
18
|
+
from pyfect import config
|
|
19
|
+
from pyfect import layer as _layer
|
|
20
|
+
from pyfect.context import Tag
|
|
21
|
+
from pyfect.layer import Layer
|
|
22
|
+
from pyfect.option import Option, Some
|
|
23
|
+
from pyfect.otel._attributes import PYFECT_VERSION
|
|
24
|
+
|
|
25
|
+
__all__ = ["OtelResource", "config_to_attributes", "layer", "layer_empty", "layer_from_env"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class OtelResource(Tag[Resource]):
|
|
29
|
+
"""The resource attached to everything the bridge exports."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _sdk_attributes() -> dict[str, AttributeValue]:
|
|
33
|
+
return {
|
|
34
|
+
"telemetry.sdk.name": "pyfect",
|
|
35
|
+
"telemetry.sdk.language": "python",
|
|
36
|
+
"telemetry.sdk.version": PYFECT_VERSION,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def config_to_attributes(
|
|
41
|
+
*,
|
|
42
|
+
service_name: str,
|
|
43
|
+
service_version: str | None = None,
|
|
44
|
+
attributes: Mapping[str, AttributeValue] | None = None,
|
|
45
|
+
) -> dict[str, AttributeValue]:
|
|
46
|
+
"""The SDK defaults, then ``attributes``, then the service name and
|
|
47
|
+
version (an empty version counts as none), later ones winning."""
|
|
48
|
+
merged: dict[str, AttributeValue] = {
|
|
49
|
+
**_sdk_attributes(),
|
|
50
|
+
**(attributes or {}),
|
|
51
|
+
"service.name": service_name,
|
|
52
|
+
}
|
|
53
|
+
if service_version:
|
|
54
|
+
merged["service.version"] = service_version
|
|
55
|
+
return merged
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def layer(
|
|
59
|
+
*,
|
|
60
|
+
service_name: str,
|
|
61
|
+
service_version: str | None = None,
|
|
62
|
+
attributes: Mapping[str, AttributeValue] | None = None,
|
|
63
|
+
) -> Layer[OtelResource]:
|
|
64
|
+
"""A resource made from ``config_to_attributes``."""
|
|
65
|
+
return _layer.succeed(
|
|
66
|
+
OtelResource,
|
|
67
|
+
Resource(
|
|
68
|
+
config_to_attributes(
|
|
69
|
+
service_name=service_name,
|
|
70
|
+
service_version=service_version,
|
|
71
|
+
attributes=attributes,
|
|
72
|
+
)
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _env_attributes(text: str) -> dict[str, AttributeValue]:
|
|
78
|
+
"""``OTEL_RESOURCE_ATTRIBUTES``: comma-separated ``key=value`` pairs,
|
|
79
|
+
split at the first ``=``; pairs without one or with an empty key are
|
|
80
|
+
skipped."""
|
|
81
|
+
attributes: dict[str, AttributeValue] = {}
|
|
82
|
+
for pair in text.split(","):
|
|
83
|
+
key, eq, value = pair.partition("=")
|
|
84
|
+
if eq and key.strip():
|
|
85
|
+
attributes[key.strip()] = value.strip()
|
|
86
|
+
return attributes
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def layer_from_env(
|
|
90
|
+
additional_attributes: Mapping[str, AttributeValue] | None = None,
|
|
91
|
+
) -> Layer[OtelResource]:
|
|
92
|
+
"""A resource from the SDK defaults, then ``OTEL_RESOURCE_ATTRIBUTES``
|
|
93
|
+
and ``OTEL_SERVICE_NAME`` (read through the ``CurrentConfigProvider``
|
|
94
|
+
when the layer builds; a config error is a defect), then
|
|
95
|
+
``additional_attributes``, later ones winning."""
|
|
96
|
+
|
|
97
|
+
def make(service_name: Option[str], text: str) -> Resource:
|
|
98
|
+
attributes = _env_attributes(text)
|
|
99
|
+
if isinstance(service_name, Some):
|
|
100
|
+
attributes["service.name"] = service_name.value
|
|
101
|
+
return Resource({**_sdk_attributes(), **attributes, **(additional_attributes or {})})
|
|
102
|
+
|
|
103
|
+
read = config.all(
|
|
104
|
+
config.option(config.string("OTEL_SERVICE_NAME")),
|
|
105
|
+
config.string("OTEL_RESOURCE_ATTRIBUTES").with_default(""),
|
|
106
|
+
)
|
|
107
|
+
return _layer.effect(OtelResource, read.map(lambda pair: make(*pair)).or_die())
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
layer_empty: Final[Layer[OtelResource]] = _layer.succeed(OtelResource, Resource.get_empty())
|
|
111
|
+
"""A resource with no attributes."""
|
pyfect/otel/sdk.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""One layer setting up OpenTelemetry for a pyfect program (Effect's
|
|
2
|
+
``NodeSdk``).
|
|
3
|
+
|
|
4
|
+
``layer(Configuration(...))`` provides the ``OtelResource`` (from the
|
|
5
|
+
environment and the ``resource`` given) and turns on each signal given
|
|
6
|
+
something to send it to: tracing with span processors, metrics with metric
|
|
7
|
+
readers, logging with log record processors::
|
|
8
|
+
|
|
9
|
+
otel_layer = otel.sdk.layer(
|
|
10
|
+
otel.sdk.Configuration(
|
|
11
|
+
resource=otel.sdk.ResourceConfig(service_name="api"),
|
|
12
|
+
span_processor=BatchSpanProcessor(OTLPSpanExporter()),
|
|
13
|
+
)
|
|
14
|
+
)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from typing import Any, Never, cast, overload
|
|
22
|
+
|
|
23
|
+
from opentelemetry.sdk import _logs as sdk_logs
|
|
24
|
+
from opentelemetry.sdk.metrics.export import MetricReader
|
|
25
|
+
from opentelemetry.sdk.resources import Resource
|
|
26
|
+
from opentelemetry.sdk.trace import SpanLimits, SpanProcessor, TracerProvider
|
|
27
|
+
from opentelemetry.sdk.trace.id_generator import IdGenerator
|
|
28
|
+
from opentelemetry.sdk.trace.sampling import Sampler
|
|
29
|
+
from opentelemetry.util.types import AttributeValue
|
|
30
|
+
|
|
31
|
+
from pyfect import layer as _layer
|
|
32
|
+
from pyfect._core import Effect
|
|
33
|
+
from pyfect.duration import Duration
|
|
34
|
+
from pyfect.effect import sync
|
|
35
|
+
from pyfect.layer import Layer
|
|
36
|
+
from pyfect.otel import logger as otel_logger
|
|
37
|
+
from pyfect.otel import metrics as otel_metrics
|
|
38
|
+
from pyfect.otel import resource as otel_resource
|
|
39
|
+
from pyfect.otel import tracer as otel_tracer
|
|
40
|
+
from pyfect.otel._shutdown import DEFAULT_SHUTDOWN_TIMEOUT, flush_and_shut_down
|
|
41
|
+
from pyfect.otel.resource import OtelResource
|
|
42
|
+
from pyfect.scope import acquire_release
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"Configuration",
|
|
46
|
+
"ResourceConfig",
|
|
47
|
+
"layer",
|
|
48
|
+
"layer_empty",
|
|
49
|
+
"layer_tracer_provider",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True, slots=True)
|
|
54
|
+
class ResourceConfig:
|
|
55
|
+
"""The service the telemetry is about (``resource.config_to_attributes``)."""
|
|
56
|
+
|
|
57
|
+
service_name: str
|
|
58
|
+
service_version: str | None = None
|
|
59
|
+
attributes: Mapping[str, AttributeValue] | None = None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True, slots=True)
|
|
63
|
+
class Configuration:
|
|
64
|
+
"""What ``layer`` sets up. A signal with nothing to send it to (no span
|
|
65
|
+
processors, metric readers or log record processors) stays off.
|
|
66
|
+
|
|
67
|
+
- ``sampler``, ``id_generator``, ``span_limits``: for the tracer provider
|
|
68
|
+
- ``metric_temporality``: ``"cumulative"`` (default) or ``"delta"``
|
|
69
|
+
- ``logger_merge_with_existing``: keep the current loggers (default)
|
|
70
|
+
- ``shutdown_timeout``: how long each provider may flush when released
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
span_processor: SpanProcessor | Sequence[SpanProcessor] = ()
|
|
74
|
+
sampler: Sampler | None = None
|
|
75
|
+
id_generator: IdGenerator | None = None
|
|
76
|
+
span_limits: SpanLimits | None = None
|
|
77
|
+
metric_reader: MetricReader | Sequence[MetricReader] = ()
|
|
78
|
+
metric_temporality: otel_metrics.TemporalityPreference = "cumulative"
|
|
79
|
+
log_record_processor: sdk_logs.LogRecordProcessor | Sequence[sdk_logs.LogRecordProcessor] = ()
|
|
80
|
+
logger_merge_with_existing: bool = True
|
|
81
|
+
resource: ResourceConfig | None = None
|
|
82
|
+
shutdown_timeout: Duration = DEFAULT_SHUTDOWN_TIMEOUT
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _all[T](given: T | Sequence[T], kind: type[T]) -> list[T]:
|
|
86
|
+
"""One item or several, as a list."""
|
|
87
|
+
if isinstance(given, kind):
|
|
88
|
+
return [given]
|
|
89
|
+
return list(cast("Sequence[T]", given))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def layer_tracer_provider(
|
|
93
|
+
processors: SpanProcessor | Sequence[SpanProcessor],
|
|
94
|
+
*,
|
|
95
|
+
sampler: Sampler | None = None,
|
|
96
|
+
id_generator: IdGenerator | None = None,
|
|
97
|
+
span_limits: SpanLimits | None = None,
|
|
98
|
+
shutdown_timeout: Duration = DEFAULT_SHUTDOWN_TIMEOUT,
|
|
99
|
+
) -> Layer[otel_tracer.OtelTracerProvider, Never, OtelResource]:
|
|
100
|
+
"""An SDK tracer provider with the ``OtelResource`` and ``processors``,
|
|
101
|
+
flushed (for at most ``shutdown_timeout``) and shut down when the layer
|
|
102
|
+
is released."""
|
|
103
|
+
given = _all(processors, SpanProcessor)
|
|
104
|
+
|
|
105
|
+
def make(resource: Resource) -> TracerProvider:
|
|
106
|
+
provider = TracerProvider(
|
|
107
|
+
sampler=sampler,
|
|
108
|
+
resource=resource,
|
|
109
|
+
shutdown_on_exit=False,
|
|
110
|
+
id_generator=id_generator,
|
|
111
|
+
span_limits=span_limits,
|
|
112
|
+
)
|
|
113
|
+
for processor in given:
|
|
114
|
+
provider.add_span_processor(processor)
|
|
115
|
+
return provider
|
|
116
|
+
|
|
117
|
+
return _layer.effect(
|
|
118
|
+
otel_tracer.OtelTracerProvider,
|
|
119
|
+
OtelResource().flat_map(
|
|
120
|
+
lambda resource: acquire_release(
|
|
121
|
+
sync(lambda: make(resource)),
|
|
122
|
+
lambda provider, _exit: flush_and_shut_down(provider, shutdown_timeout),
|
|
123
|
+
)
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _signals(config: Configuration) -> Layer[OtelResource]:
|
|
129
|
+
signals: list[Layer[Never, Never, OtelResource]] = []
|
|
130
|
+
span_processors = _all(config.span_processor, SpanProcessor)
|
|
131
|
+
if span_processors:
|
|
132
|
+
provider = layer_tracer_provider(
|
|
133
|
+
span_processors,
|
|
134
|
+
sampler=config.sampler,
|
|
135
|
+
id_generator=config.id_generator,
|
|
136
|
+
span_limits=config.span_limits,
|
|
137
|
+
shutdown_timeout=config.shutdown_timeout,
|
|
138
|
+
)
|
|
139
|
+
signals.append(otel_tracer.layer.pipe(_layer.provide(provider)))
|
|
140
|
+
readers = _all(config.metric_reader, MetricReader)
|
|
141
|
+
if readers:
|
|
142
|
+
signals.append(
|
|
143
|
+
otel_metrics.layer(
|
|
144
|
+
lambda: readers,
|
|
145
|
+
temporality=config.metric_temporality,
|
|
146
|
+
shutdown_timeout=config.shutdown_timeout,
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
log_processors = _all(config.log_record_processor, sdk_logs.LogRecordProcessor)
|
|
150
|
+
if log_processors:
|
|
151
|
+
logger_provider = otel_logger.layer_logger_provider(
|
|
152
|
+
log_processors, shutdown_timeout=config.shutdown_timeout
|
|
153
|
+
)
|
|
154
|
+
signals.append(
|
|
155
|
+
otel_logger.layer(merge_with_existing=config.logger_merge_with_existing).pipe(
|
|
156
|
+
_layer.provide(logger_provider)
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
resource = config.resource
|
|
160
|
+
resource_layer = otel_resource.layer_from_env(
|
|
161
|
+
None
|
|
162
|
+
if resource is None
|
|
163
|
+
else otel_resource.config_to_attributes(
|
|
164
|
+
service_name=resource.service_name,
|
|
165
|
+
service_version=resource.service_version,
|
|
166
|
+
attributes=resource.attributes,
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
merged: Layer[Never, Never, OtelResource] = _layer.empty
|
|
170
|
+
for signal in signals:
|
|
171
|
+
merged = _layer.merge(merged, signal)
|
|
172
|
+
return merged.pipe(_layer.provide_merge(resource_layer))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@overload
|
|
176
|
+
def layer(config: Configuration | Callable[[], Configuration], /) -> Layer[OtelResource]: ...
|
|
177
|
+
@overload
|
|
178
|
+
def layer[E, R](config: Effect[Configuration, E, R], /) -> Layer[OtelResource, E, R]: ...
|
|
179
|
+
def layer(
|
|
180
|
+
config: Configuration | Callable[[], Configuration] | Effect[Configuration, Any, Any], /
|
|
181
|
+
) -> Layer[Any, Any, Any]:
|
|
182
|
+
"""Set up OpenTelemetry from ``config``: a configuration, a function
|
|
183
|
+
making one, or an effect making one when the layer builds."""
|
|
184
|
+
match config:
|
|
185
|
+
case Configuration():
|
|
186
|
+
made: Effect[Configuration, Any, Any] = sync(lambda: config)
|
|
187
|
+
case Effect():
|
|
188
|
+
made = config
|
|
189
|
+
case _:
|
|
190
|
+
made = sync(config)
|
|
191
|
+
return _layer.unwrap(made.map(_signals))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
layer_empty: Layer[OtelResource] = otel_resource.layer_empty
|
|
195
|
+
"""Only an empty ``OtelResource``."""
|