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/tracer.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""pyfect tracing into OpenTelemetry (Effect's ``OtelTracer``).
|
|
2
|
+
|
|
3
|
+
The tracer this module installs makes each pyfect span an OpenTelemetry
|
|
4
|
+
span (``OtelSpan``) and keeps the current span active in OpenTelemetry's
|
|
5
|
+
context while a fiber runs, so libraries instrumented with OpenTelemetry
|
|
6
|
+
see it as their parent::
|
|
7
|
+
|
|
8
|
+
tracing = otel.tracer.layer.pipe(layer.provide(provider_layer))
|
|
9
|
+
program.with_span("request").pipe(fx.provide(tracing))
|
|
10
|
+
|
|
11
|
+
A pyfect parent wins over the active OpenTelemetry span; with no pyfect
|
|
12
|
+
parent, a span started by other OpenTelemetry code is the parent.
|
|
13
|
+
``make_external_span`` and ``with_span_context`` continue a remote trace.
|
|
14
|
+
Nothing is exported unless the tracer provider has span processors (see
|
|
15
|
+
``otel.sdk``).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from typing import Any, Final, Never
|
|
22
|
+
|
|
23
|
+
from opentelemetry import context as otel_context
|
|
24
|
+
from opentelemetry import trace
|
|
25
|
+
from opentelemetry.trace import SpanContext
|
|
26
|
+
|
|
27
|
+
from pyfect import layer as _layer
|
|
28
|
+
from pyfect._core import Effect
|
|
29
|
+
from pyfect._fiber import Fiber
|
|
30
|
+
from pyfect._tracer_ref import CurrentTracer
|
|
31
|
+
from pyfect.clock import Clock, ClockService
|
|
32
|
+
from pyfect.context import Tag
|
|
33
|
+
from pyfect.errors import NoSuchElementException
|
|
34
|
+
from pyfect.layer import Layer
|
|
35
|
+
from pyfect.otel._attributes import PYFECT_VERSION
|
|
36
|
+
from pyfect.otel._span import (
|
|
37
|
+
EFFECT_SPAN_KEY,
|
|
38
|
+
OtelSpan,
|
|
39
|
+
OtelTraceFlags,
|
|
40
|
+
OtelTraceState,
|
|
41
|
+
external_span_of,
|
|
42
|
+
make_external_span,
|
|
43
|
+
populate_context,
|
|
44
|
+
propagated_span,
|
|
45
|
+
)
|
|
46
|
+
from pyfect.otel._wrapper import EffectSpanView
|
|
47
|
+
from pyfect.tracer import ParentSpan, Span, Tracer, current_span
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"EffectSpanView",
|
|
51
|
+
"NoSuchElementException",
|
|
52
|
+
"OtelSpan",
|
|
53
|
+
"OtelTraceFlags",
|
|
54
|
+
"OtelTraceState",
|
|
55
|
+
"OtelTracer",
|
|
56
|
+
"OtelTracerProvider",
|
|
57
|
+
"current_otel_span",
|
|
58
|
+
"layer",
|
|
59
|
+
"layer_global",
|
|
60
|
+
"layer_global_provider",
|
|
61
|
+
"layer_global_tracer",
|
|
62
|
+
"layer_tracer",
|
|
63
|
+
"layer_without_otel_tracer",
|
|
64
|
+
"make",
|
|
65
|
+
"make_external_span",
|
|
66
|
+
"with_span_context",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class OtelTracer(Tag[trace.Tracer]):
|
|
71
|
+
"""The OpenTelemetry tracer pyfect spans are started with."""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class OtelTracerProvider(Tag[trace.TracerProvider]):
|
|
75
|
+
"""The OpenTelemetry tracer provider ``layer_tracer`` gets a tracer
|
|
76
|
+
from."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _context_hook[T](run: Callable[[], T], fiber: Fiber[Any, Any], /) -> T:
|
|
80
|
+
"""Runs a fiber step with the fiber's current span active in
|
|
81
|
+
OpenTelemetry's context."""
|
|
82
|
+
span = propagated_span(fiber.get_ref(ParentSpan))
|
|
83
|
+
if span is None:
|
|
84
|
+
return run()
|
|
85
|
+
context = populate_context(otel_context.get_current(), span)
|
|
86
|
+
context = otel_context.set_value(EFFECT_SPAN_KEY, trace.get_current_span(context), context)
|
|
87
|
+
token = otel_context.attach(context)
|
|
88
|
+
try:
|
|
89
|
+
return run()
|
|
90
|
+
finally:
|
|
91
|
+
otel_context.detach(token)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _tracer(otel_tracer: trace.Tracer) -> Tracer:
|
|
95
|
+
return Tracer(lambda request: OtelSpan(otel_tracer, request), _context_hook)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
make: Final[Effect[Tracer, Never, OtelTracer]] = OtelTracer().map(_tracer)
|
|
99
|
+
"""A pyfect tracer starting OpenTelemetry spans with the ``OtelTracer``."""
|
|
100
|
+
|
|
101
|
+
layer_global_provider: Final[Layer[OtelTracerProvider]] = _layer.sync(
|
|
102
|
+
OtelTracerProvider, trace.get_tracer_provider
|
|
103
|
+
)
|
|
104
|
+
"""The global OpenTelemetry tracer provider."""
|
|
105
|
+
|
|
106
|
+
layer_tracer: Final[Layer[OtelTracer, Never, OtelTracerProvider]] = _layer.effect(
|
|
107
|
+
OtelTracer, OtelTracerProvider().map(lambda p: p.get_tracer("pyfect", PYFECT_VERSION))
|
|
108
|
+
)
|
|
109
|
+
"""A tracer from the provider, with ``pyfect`` and its version as the
|
|
110
|
+
instrumentation scope."""
|
|
111
|
+
|
|
112
|
+
layer_global_tracer: Final[Layer[OtelTracer]] = layer_tracer.pipe(
|
|
113
|
+
_layer.provide(layer_global_provider)
|
|
114
|
+
)
|
|
115
|
+
"""A tracer from the global tracer provider."""
|
|
116
|
+
|
|
117
|
+
layer_without_otel_tracer: Final[Layer[Never, Never, OtelTracer]] = _layer.effect(
|
|
118
|
+
CurrentTracer, make
|
|
119
|
+
)
|
|
120
|
+
"""Installs the bridge's tracer, using the ``OtelTracer`` provided."""
|
|
121
|
+
|
|
122
|
+
layer: Final[Layer[OtelTracer, Never, OtelTracerProvider]] = layer_without_otel_tracer.pipe(
|
|
123
|
+
_layer.provide_merge(layer_tracer)
|
|
124
|
+
)
|
|
125
|
+
"""Installs the bridge's tracer, with a tracer from the provider."""
|
|
126
|
+
|
|
127
|
+
layer_global: Final[Layer[OtelTracer]] = layer_without_otel_tracer.pipe(
|
|
128
|
+
_layer.provide_merge(layer_global_tracer)
|
|
129
|
+
)
|
|
130
|
+
"""Installs the bridge's tracer, with a tracer from the global provider."""
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _otel_span(span: Span, clock: ClockService) -> trace.Span:
|
|
134
|
+
return span.span if isinstance(span, OtelSpan) else EffectSpanView(span, clock)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def current_otel_span() -> Effect[trace.Span, NoSuchElementException]:
|
|
138
|
+
"""The current span as an OpenTelemetry span: the bridge's own, or a
|
|
139
|
+
view writing through to a span another tracer made. Fails outside every
|
|
140
|
+
span, or in an external one."""
|
|
141
|
+
return Clock().flat_map(lambda clock: current_span().map(lambda s: _otel_span(s, clock)))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class _WithSpanContext:
|
|
145
|
+
__slots__ = ("_span_context",)
|
|
146
|
+
|
|
147
|
+
def __init__(self, span_context: SpanContext) -> None:
|
|
148
|
+
self._span_context = span_context
|
|
149
|
+
|
|
150
|
+
def __call__[A, E, R](self, eff: Effect[A, E, R], /) -> Effect[A, E, R]:
|
|
151
|
+
return eff.with_parent_span(external_span_of(self._span_context))
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def with_span_context(span_context: SpanContext) -> _WithSpanContext:
|
|
155
|
+
"""Run an effect with the OpenTelemetry span context as its parent span
|
|
156
|
+
(use with ``.pipe``), continuing a trace begun outside pyfect."""
|
|
157
|
+
return _WithSpanContext(span_context)
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""``PartitionedSemaphore[K]``: one pool of permits shared fairly by keys.
|
|
2
|
+
|
|
3
|
+
Waiters are grouped by key, and freed permits go round-robin across keys,
|
|
4
|
+
one permit per turn, so a key with many waiters cannot starve the others. A
|
|
5
|
+
waiter that needs several permits collects them as they come and is woken
|
|
6
|
+
once it has them all. Only the wait is interruptible: an interrupt at any
|
|
7
|
+
point before the permits are guarded gives back what the waiter collected.
|
|
8
|
+
|
|
9
|
+
A request for more permits than the capacity never completes, as in Effect.
|
|
10
|
+
"""
|
|
11
|
+
# pyright: reportPrivateUsage=false
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from collections.abc import Callable, Hashable
|
|
16
|
+
from typing import TYPE_CHECKING, Any
|
|
17
|
+
|
|
18
|
+
from pyfect._core import (
|
|
19
|
+
Effect,
|
|
20
|
+
Frame,
|
|
21
|
+
Restore,
|
|
22
|
+
Step,
|
|
23
|
+
_Callback,
|
|
24
|
+
_Succeed,
|
|
25
|
+
_Suspend,
|
|
26
|
+
_Sync,
|
|
27
|
+
_WithFiber,
|
|
28
|
+
uninterruptible_mask,
|
|
29
|
+
)
|
|
30
|
+
from pyfect._effect_args import require_effect
|
|
31
|
+
from pyfect._round_robin import RoundRobin
|
|
32
|
+
from pyfect.cause import Cause
|
|
33
|
+
from pyfect.effect import never
|
|
34
|
+
from pyfect.exit import Failure
|
|
35
|
+
from pyfect.option import Option, Some
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from pyfect._fiber import Fiber
|
|
39
|
+
|
|
40
|
+
_VOID: Effect[None] = _Succeed(None)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class _Waiter:
|
|
44
|
+
__slots__ = ("missing", "resume")
|
|
45
|
+
|
|
46
|
+
def __init__(self, missing: int) -> None:
|
|
47
|
+
self.missing = missing
|
|
48
|
+
self.resume: Callable[[Effect[None]], None] | None = None
|
|
49
|
+
|
|
50
|
+
def register(self, resume: Callable[[Effect[None]], None]) -> None:
|
|
51
|
+
if self.missing == 0: # handed its permits before it parked
|
|
52
|
+
resume(_VOID)
|
|
53
|
+
else:
|
|
54
|
+
self.resume = resume
|
|
55
|
+
|
|
56
|
+
def wake(self) -> None:
|
|
57
|
+
if self.resume is not None:
|
|
58
|
+
self.resume(_VOID)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class _GiveBackOnFailure(Frame):
|
|
62
|
+
"""Below a standalone ``take``: once it has taken its permits, give
|
|
63
|
+
them back if the take still fails (an interrupt landing as it returns),
|
|
64
|
+
as Effect's ``take`` does."""
|
|
65
|
+
|
|
66
|
+
__slots__ = ("armed", "release")
|
|
67
|
+
# Right below the claim: the permits reach the caller with no pause.
|
|
68
|
+
claims = True
|
|
69
|
+
|
|
70
|
+
def __init__(self, release: Callable[[], object]) -> None:
|
|
71
|
+
self.release = release
|
|
72
|
+
self.armed = False
|
|
73
|
+
|
|
74
|
+
def _on_failure(self, cause: Cause[Any], fiber: Fiber[Any, Any]) -> Step:
|
|
75
|
+
if self.armed:
|
|
76
|
+
self.release()
|
|
77
|
+
return Failure(cause)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class PartitionedSemaphore[K: Hashable = Hashable]:
|
|
81
|
+
__slots__ = ("_pending", "_releasing", "_total", "_waiters", "_waiting", "capacity")
|
|
82
|
+
|
|
83
|
+
def __init__(self, permits: int) -> None:
|
|
84
|
+
self.capacity = max(0, permits)
|
|
85
|
+
self._total = self.capacity # free permits nobody is waiting for
|
|
86
|
+
self._waiting = 0 # permits waiters still miss
|
|
87
|
+
self._pending = 0 # released permits not yet handed out
|
|
88
|
+
self._releasing = False
|
|
89
|
+
# Typed by Hashable, not K, so K only goes in (contravariant, as in Effect).
|
|
90
|
+
self._waiters: RoundRobin[Hashable, _Waiter] = RoundRobin()
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def make(cls, permits: int) -> Effect[PartitionedSemaphore[K]]:
|
|
94
|
+
"""``PartitionedSemaphore[str].make(4)``"""
|
|
95
|
+
return _Sync(lambda: cls(permits))
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def available(self) -> Effect[int]:
|
|
99
|
+
return _Sync(lambda: self._total)
|
|
100
|
+
|
|
101
|
+
def take(self, key: K, n: int = 1) -> Effect[None]:
|
|
102
|
+
"""Wait in ``key``'s queue for ``n`` permits and take them. Prefer
|
|
103
|
+
``with_permits``, which also gives them back."""
|
|
104
|
+
if n <= 0:
|
|
105
|
+
return _VOID
|
|
106
|
+
if self.capacity < n:
|
|
107
|
+
return never
|
|
108
|
+
|
|
109
|
+
def run(fiber: Fiber[Any, Any]) -> Effect[None]:
|
|
110
|
+
guard = _GiveBackOnFailure(lambda: self._release(n))
|
|
111
|
+
fiber._push(guard)
|
|
112
|
+
|
|
113
|
+
def arm(_: None) -> None:
|
|
114
|
+
guard.armed = True
|
|
115
|
+
|
|
116
|
+
return uninterruptible_mask(lambda restore: self._acquire(key, n, restore).map(arm))
|
|
117
|
+
|
|
118
|
+
return _WithFiber(run)
|
|
119
|
+
|
|
120
|
+
def release(self, n: int = 1) -> Effect[int]:
|
|
121
|
+
"""Give back ``n`` permits; succeeds with the number now free."""
|
|
122
|
+
return _Sync(lambda: self._release(n))
|
|
123
|
+
|
|
124
|
+
def with_permits[A, E, R](self, key: K, eff: Effect[A, E, R], n: int = 1) -> Effect[A, E, R]:
|
|
125
|
+
"""Run ``eff`` holding ``n`` permits taken in ``key``'s queue,
|
|
126
|
+
releasing them however it ends."""
|
|
127
|
+
require_effect(eff, "eff")
|
|
128
|
+
if n <= 0:
|
|
129
|
+
return eff
|
|
130
|
+
if self.capacity < n:
|
|
131
|
+
return never
|
|
132
|
+
release = self.release(n)
|
|
133
|
+
return uninterruptible_mask(
|
|
134
|
+
lambda restore: self._acquire(key, n, restore).and_then(restore(eff).ensuring(release))
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def with_permits_if_available[A, E, R](
|
|
138
|
+
self, eff: Effect[A, E, R], n: int = 1
|
|
139
|
+
) -> Effect[Option[A], E, R]:
|
|
140
|
+
"""Run ``eff`` holding ``n`` permits if they are free now (``Some``
|
|
141
|
+
of its result); otherwise ``None`` without running it."""
|
|
142
|
+
require_effect(eff, "eff")
|
|
143
|
+
if n <= 0:
|
|
144
|
+
return eff.map(Some)
|
|
145
|
+
|
|
146
|
+
def body(restore: Restore) -> Effect[Option[A], E, R]:
|
|
147
|
+
def run() -> Effect[Option[A], E, R]:
|
|
148
|
+
if self._total < n:
|
|
149
|
+
return _Succeed(None)
|
|
150
|
+
self._total -= n
|
|
151
|
+
return restore(eff.map(Some)).ensuring(self.release(n))
|
|
152
|
+
|
|
153
|
+
return _Suspend(run)
|
|
154
|
+
|
|
155
|
+
return uninterruptible_mask(body)
|
|
156
|
+
|
|
157
|
+
def _acquire(self, key: K, n: int, restore: Restore) -> Effect[None]:
|
|
158
|
+
"""Take ``n`` permits; runs uninterruptibly, with only the wait
|
|
159
|
+
restored. Once it succeeds the caller holds them."""
|
|
160
|
+
|
|
161
|
+
def run() -> Effect[None]:
|
|
162
|
+
if self._total >= n:
|
|
163
|
+
self._total -= n
|
|
164
|
+
return _VOID
|
|
165
|
+
waiter = _Waiter(n - self._total)
|
|
166
|
+
self._total = 0
|
|
167
|
+
self._waiting += waiter.missing
|
|
168
|
+
self._waiters.add(key, waiter)
|
|
169
|
+
return restore(_Callback(waiter.register)).on_error(
|
|
170
|
+
lambda _: _Sync(lambda: self._give_up(key, n, waiter))
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return _Suspend(run)
|
|
174
|
+
|
|
175
|
+
def _give_up(self, key: K, n: int, waiter: _Waiter) -> None:
|
|
176
|
+
self._waiters.remove(key, waiter)
|
|
177
|
+
self._waiting -= waiter.missing
|
|
178
|
+
self._release(n - waiter.missing)
|
|
179
|
+
|
|
180
|
+
def _release(self, n: int) -> int:
|
|
181
|
+
if n > 0:
|
|
182
|
+
self._pending += n
|
|
183
|
+
# A woken waiter can release again from inside this loop; the
|
|
184
|
+
# running loop hands those permits out instead of recursing.
|
|
185
|
+
if not self._releasing:
|
|
186
|
+
self._releasing = True
|
|
187
|
+
try:
|
|
188
|
+
self._hand_out()
|
|
189
|
+
finally:
|
|
190
|
+
self._releasing = False
|
|
191
|
+
# Free permits must be visible before a reentrant release returns.
|
|
192
|
+
if self._waiting == 0:
|
|
193
|
+
self._total = min(self.capacity, self._total + self._pending)
|
|
194
|
+
self._pending = 0
|
|
195
|
+
return self._total
|
|
196
|
+
|
|
197
|
+
def _hand_out(self) -> None:
|
|
198
|
+
while self._pending > 0 and self._waiting > 0:
|
|
199
|
+
found = self._waiters.next()
|
|
200
|
+
if found is None:
|
|
201
|
+
return
|
|
202
|
+
key, waiter = found
|
|
203
|
+
waiter.missing -= 1
|
|
204
|
+
self._waiting -= 1
|
|
205
|
+
self._pending -= 1
|
|
206
|
+
if waiter.missing == 0:
|
|
207
|
+
self._waiters.remove(key, waiter)
|
|
208
|
+
waiter.wake()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
__all__ = ["PartitionedSemaphore"]
|
pyfect/path.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""Path operations as a service (Effect's ``Path``).
|
|
2
|
+
|
|
3
|
+
Programs that take the ``Path`` service instead of importing ``os.path``
|
|
4
|
+
can be run against another platform's rules (``layer_posix``,
|
|
5
|
+
``layer_win32``) or a test double::
|
|
6
|
+
|
|
7
|
+
p = yield from Path()
|
|
8
|
+
config = p.join(home, ".config", "app.toml")
|
|
9
|
+
|
|
10
|
+
``posix`` follows Node's ``path.posix``, as Effect does (``join("/a", "/b")``
|
|
11
|
+
is ``"/a/b"``, ``dirname("/a/b/")`` is ``"/a"``, ``relative`` of a path to
|
|
12
|
+
itself is ``""``). ``win32`` is ``ntpath``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import ntpath
|
|
18
|
+
import os
|
|
19
|
+
import posixpath
|
|
20
|
+
import re
|
|
21
|
+
import urllib.parse
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from typing import Never, Protocol
|
|
24
|
+
|
|
25
|
+
from pyfect import _path_posix
|
|
26
|
+
from pyfect._core import Effect
|
|
27
|
+
from pyfect.context import Tag
|
|
28
|
+
from pyfect.effect import fail, succeed
|
|
29
|
+
from pyfect.layer import Layer
|
|
30
|
+
from pyfect.layer import succeed as layer_succeed
|
|
31
|
+
from pyfect.platform_error import BadArgument
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _PathModule(Protocol):
|
|
35
|
+
"""The part of ``posixpath`` / ``ntpath`` the service uses."""
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def sep(self) -> str: ...
|
|
39
|
+
def basename(self, p: str, /) -> str: ...
|
|
40
|
+
def dirname(self, p: str, /) -> str: ...
|
|
41
|
+
def splitext(self, p: str, /) -> tuple[str, str]: ...
|
|
42
|
+
def splitroot(self, p: str, /) -> tuple[str, str, str]: ...
|
|
43
|
+
def isabs(self, s: str, /) -> bool: ...
|
|
44
|
+
def join(self, a: str, /, *paths: str) -> str: ...
|
|
45
|
+
def normpath(self, path: str, /) -> str: ...
|
|
46
|
+
def relpath(self, path: str, start: str | None = None) -> str: ...
|
|
47
|
+
def abspath(self, path: str, /) -> str: ...
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True)
|
|
51
|
+
class Parsed:
|
|
52
|
+
"""The parts of a path (Node's ``path.parse``): ``/home/u/f.txt`` has
|
|
53
|
+
root ``"/"``, dir ``"/home/u"``, base ``"f.txt"``, name ``"f"`` and ext
|
|
54
|
+
``".txt"``."""
|
|
55
|
+
|
|
56
|
+
root: str = ""
|
|
57
|
+
dir: str = ""
|
|
58
|
+
base: str = ""
|
|
59
|
+
ext: str = ""
|
|
60
|
+
name: str = ""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
_ENCODED_SEPARATOR = re.compile(r"%2f", re.IGNORECASE)
|
|
64
|
+
_ENCODED_BACKSLASH = re.compile(r"%5c", re.IGNORECASE)
|
|
65
|
+
_BAD_ESCAPE = re.compile(r"%(?![0-9A-Fa-f]{2})")
|
|
66
|
+
# WHATWG's path percent-encode set leaves these as they are.
|
|
67
|
+
_URL_SAFE = "/!$&'()*+,;=:@-._~"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class PathService:
|
|
71
|
+
"""``Path`` over one ``os.path`` module (``posixpath`` or ``ntpath``)."""
|
|
72
|
+
|
|
73
|
+
__slots__ = ("_m", "_windows")
|
|
74
|
+
|
|
75
|
+
def __init__(self, module: _PathModule, *, windows: bool) -> None:
|
|
76
|
+
self._m = module
|
|
77
|
+
self._windows = windows
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def sep(self) -> str:
|
|
81
|
+
return self._m.sep
|
|
82
|
+
|
|
83
|
+
def basename(self, path: str, suffix: str = "") -> str:
|
|
84
|
+
"""The last part of ``path``, without ``suffix`` when it ends with
|
|
85
|
+
it (and is more than it)."""
|
|
86
|
+
base = self._m.basename(path)
|
|
87
|
+
if suffix and base.endswith(suffix) and base != suffix:
|
|
88
|
+
return base[: -len(suffix)]
|
|
89
|
+
return base
|
|
90
|
+
|
|
91
|
+
def dirname(self, path: str) -> str:
|
|
92
|
+
return self._m.dirname(path)
|
|
93
|
+
|
|
94
|
+
def extname(self, path: str) -> str:
|
|
95
|
+
"""The extension of the last part, dot included (``""`` for
|
|
96
|
+
``.bashrc``)."""
|
|
97
|
+
return self._m.splitext(path)[1]
|
|
98
|
+
|
|
99
|
+
def format(
|
|
100
|
+
self, *, root: str = "", dir: str = "", base: str = "", name: str = "", ext: str = ""
|
|
101
|
+
) -> str:
|
|
102
|
+
"""Node's ``path.format``: ``dir`` (else ``root``), then ``base``
|
|
103
|
+
(else ``name + ext``)."""
|
|
104
|
+
directory = dir or root
|
|
105
|
+
last = base or name + ext
|
|
106
|
+
if not directory:
|
|
107
|
+
return last
|
|
108
|
+
if directory == root:
|
|
109
|
+
return directory + last
|
|
110
|
+
return directory + self.sep + last
|
|
111
|
+
|
|
112
|
+
def from_file_url(self, url: str) -> Effect[str, BadArgument]:
|
|
113
|
+
"""The path a ``file:`` URL names. Fails for another scheme, a host
|
|
114
|
+
(but a Windows share), an encoded separator or a broken escape."""
|
|
115
|
+
parts = urllib.parse.urlsplit(url)
|
|
116
|
+
if parts.scheme != "file":
|
|
117
|
+
return _bad_url("URL must be of scheme file")
|
|
118
|
+
host = parts.hostname or ""
|
|
119
|
+
if host == "localhost":
|
|
120
|
+
host = ""
|
|
121
|
+
if host and not self._windows:
|
|
122
|
+
return _bad_url("Invalid file URL host")
|
|
123
|
+
path = _remove_dot_segments(parts.path)
|
|
124
|
+
encoded = _ENCODED_SEPARATOR.search(path) or (
|
|
125
|
+
self._windows and _ENCODED_BACKSLASH.search(path)
|
|
126
|
+
)
|
|
127
|
+
if encoded:
|
|
128
|
+
return _bad_url("must not include encoded / characters")
|
|
129
|
+
try:
|
|
130
|
+
decoded = _decode_uri_component(path)
|
|
131
|
+
except ValueError as error:
|
|
132
|
+
return fail(BadArgument(module="Path", method="from_file_url", cause=error))
|
|
133
|
+
return succeed(_windows_path(host, decoded) if self._windows else decoded)
|
|
134
|
+
|
|
135
|
+
def is_absolute(self, path: str) -> bool:
|
|
136
|
+
return self._m.isabs(path)
|
|
137
|
+
|
|
138
|
+
def join(self, *paths: str) -> str:
|
|
139
|
+
return self._m.join(*paths) if paths else "."
|
|
140
|
+
|
|
141
|
+
def normalize(self, path: str) -> str:
|
|
142
|
+
return self._m.normpath(path)
|
|
143
|
+
|
|
144
|
+
def parse(self, path: str) -> Parsed:
|
|
145
|
+
drive, root, _tail = self._m.splitroot(path)
|
|
146
|
+
base = self._m.basename(path)
|
|
147
|
+
name, ext = self._m.splitext(base)
|
|
148
|
+
return Parsed(root=drive + root, dir=self._m.dirname(path), base=base, ext=ext, name=name)
|
|
149
|
+
|
|
150
|
+
def relative(self, from_: str, to: str) -> str:
|
|
151
|
+
"""The path from ``from_`` to ``to`` (``os.path.relpath``); ``to``
|
|
152
|
+
made absolute when no relative path leads there (another drive)."""
|
|
153
|
+
try:
|
|
154
|
+
return self._m.relpath(to, from_)
|
|
155
|
+
except ValueError:
|
|
156
|
+
return self._m.abspath(to)
|
|
157
|
+
|
|
158
|
+
def resolve(self, *segments: str) -> str:
|
|
159
|
+
"""The absolute path ``segments`` lead to from the working
|
|
160
|
+
directory."""
|
|
161
|
+
return self._m.abspath(self._m.join("", *segments))
|
|
162
|
+
|
|
163
|
+
def resolve_within(self, root: str, input: str) -> str | None:
|
|
164
|
+
"""``input`` resolved below ``root``, or ``None`` when it is rooted
|
|
165
|
+
or leads outside ``root`` (a ``..`` past it)."""
|
|
166
|
+
if self.parse(input).root != "":
|
|
167
|
+
return None
|
|
168
|
+
base = self.resolve(root)
|
|
169
|
+
candidate = self.resolve(base, input)
|
|
170
|
+
relative = self.relative(base, candidate)
|
|
171
|
+
if relative == ".." or relative.startswith(".." + self.sep) or self.is_absolute(relative):
|
|
172
|
+
return None
|
|
173
|
+
return candidate
|
|
174
|
+
|
|
175
|
+
def to_file_url(self, path: str) -> Effect[str, BadArgument]:
|
|
176
|
+
"""The ``file:`` URL of ``path``, made absolute first."""
|
|
177
|
+
resolved = self.resolve(path)
|
|
178
|
+
if self._windows:
|
|
179
|
+
resolved = resolved.replace("\\", "/")
|
|
180
|
+
if resolved.startswith("//"):
|
|
181
|
+
host, _, rest = resolved[2:].partition("/")
|
|
182
|
+
return succeed(f"file://{host}/{urllib.parse.quote(rest, safe=_URL_SAFE)}")
|
|
183
|
+
resolved = "/" + resolved
|
|
184
|
+
elif path.endswith("/") and not resolved.endswith("/"):
|
|
185
|
+
resolved += "/"
|
|
186
|
+
return succeed("file://" + urllib.parse.quote(resolved, safe=_URL_SAFE))
|
|
187
|
+
|
|
188
|
+
def to_namespaced_path(self, path: str) -> str:
|
|
189
|
+
"""Windows' ``\\\\?\\`` form of an absolute path; on POSIX, ``path``."""
|
|
190
|
+
if not self._windows or not path:
|
|
191
|
+
return path
|
|
192
|
+
resolved = self.resolve(path)
|
|
193
|
+
if resolved.startswith("\\\\?\\"):
|
|
194
|
+
return resolved
|
|
195
|
+
if resolved.startswith("\\\\"):
|
|
196
|
+
return "\\\\?\\UNC\\" + resolved[2:]
|
|
197
|
+
if re.match(r"[A-Za-z]:\\", resolved):
|
|
198
|
+
return "\\\\?\\" + resolved
|
|
199
|
+
return path
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
_SINGLE_DOT = frozenset((".", "%2e"))
|
|
203
|
+
_DOUBLE_DOT = frozenset(("..", ".%2e", "%2e.", "%2e%2e"))
|
|
204
|
+
_DRIVE = re.compile(r"[A-Za-z][:|]")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _remove_dot_segments(path: str) -> str:
|
|
208
|
+
"""The WHATWG URL parser's handling of ``.`` and ``..`` segments (encoded
|
|
209
|
+
dots included) in a ``file:`` URL's path; ``..`` never leaves a drive
|
|
210
|
+
letter."""
|
|
211
|
+
segments = path.split("/")[1:]
|
|
212
|
+
kept: list[str] = []
|
|
213
|
+
for index, segment in enumerate(segments):
|
|
214
|
+
last = index == len(segments) - 1
|
|
215
|
+
dots = segment.lower()
|
|
216
|
+
if dots in _DOUBLE_DOT:
|
|
217
|
+
if kept and not (len(kept) == 1 and _DRIVE.fullmatch(kept[0])):
|
|
218
|
+
kept.pop()
|
|
219
|
+
if last:
|
|
220
|
+
kept.append("")
|
|
221
|
+
elif dots in _SINGLE_DOT:
|
|
222
|
+
if last:
|
|
223
|
+
kept.append("")
|
|
224
|
+
else:
|
|
225
|
+
kept.append(segment)
|
|
226
|
+
return "/" + "/".join(kept) if segments else path
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _windows_path(host: str, decoded: str) -> str:
|
|
230
|
+
path = decoded.replace("/", "\\")
|
|
231
|
+
if host:
|
|
232
|
+
return f"\\\\{host}{path}"
|
|
233
|
+
return path[1:] if re.match(r"\\[A-Za-z]:", path) else path
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _bad_url(description: str) -> Effect[Never, BadArgument]:
|
|
237
|
+
return fail(BadArgument(module="Path", method="from_file_url", description=description))
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _decode_uri_component(text: str) -> str:
|
|
241
|
+
"""JavaScript's ``decodeURIComponent``: a ``%`` without two hex digits,
|
|
242
|
+
or escapes that are not UTF-8, raise ``ValueError``."""
|
|
243
|
+
if _BAD_ESCAPE.search(text):
|
|
244
|
+
raise ValueError(f"URI malformed: {text}")
|
|
245
|
+
return urllib.parse.unquote_to_bytes(text).decode("utf-8", errors="strict")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class Path(Tag[PathService]):
|
|
249
|
+
"""The path rules a program uses."""
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class _NodePosix(PathService):
|
|
253
|
+
"""The operations whose results differ between Node and ``posixpath``."""
|
|
254
|
+
|
|
255
|
+
__slots__ = ()
|
|
256
|
+
|
|
257
|
+
def basename(self, path: str, suffix: str = "") -> str:
|
|
258
|
+
return _path_posix.basename(path, suffix)
|
|
259
|
+
|
|
260
|
+
def dirname(self, path: str) -> str:
|
|
261
|
+
return _path_posix.dirname(path)
|
|
262
|
+
|
|
263
|
+
def extname(self, path: str) -> str:
|
|
264
|
+
return _path_posix.extname(path)
|
|
265
|
+
|
|
266
|
+
def join(self, *paths: str) -> str:
|
|
267
|
+
return _path_posix.join(*paths)
|
|
268
|
+
|
|
269
|
+
def normalize(self, path: str) -> str:
|
|
270
|
+
return _path_posix.normalize(path)
|
|
271
|
+
|
|
272
|
+
def parse(self, path: str) -> Parsed:
|
|
273
|
+
root, directory, base, ext, name = _path_posix.parse(path)
|
|
274
|
+
return Parsed(root=root, dir=directory, base=base, ext=ext, name=name)
|
|
275
|
+
|
|
276
|
+
def relative(self, from_: str, to: str) -> str:
|
|
277
|
+
return _path_posix.relative(from_, to)
|
|
278
|
+
|
|
279
|
+
def resolve(self, *segments: str) -> str:
|
|
280
|
+
return _path_posix.resolve(*segments)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
posix: PathService = _NodePosix(posixpath, windows=False)
|
|
284
|
+
"""POSIX rules (Node's ``path.posix``)."""
|
|
285
|
+
|
|
286
|
+
win32 = PathService(ntpath, windows=True)
|
|
287
|
+
"""Windows rules (``ntpath``)."""
|
|
288
|
+
|
|
289
|
+
layer_posix: Layer[Path] = layer_succeed(Path, posix)
|
|
290
|
+
layer_win32: Layer[Path] = layer_succeed(Path, win32)
|
|
291
|
+
layer: Layer[Path] = layer_win32 if os.name == "nt" else layer_posix
|
|
292
|
+
"""The host's rules."""
|
|
293
|
+
|
|
294
|
+
__all__ = [
|
|
295
|
+
"BadArgument",
|
|
296
|
+
"Parsed",
|
|
297
|
+
"Path",
|
|
298
|
+
"PathService",
|
|
299
|
+
"layer",
|
|
300
|
+
"layer_posix",
|
|
301
|
+
"layer_win32",
|
|
302
|
+
"posix",
|
|
303
|
+
"win32",
|
|
304
|
+
]
|