pyfect-dev 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyfect/__init__.py +729 -0
- pyfect/_all.py +242 -0
- pyfect/_background.py +28 -0
- pyfect/_binary_codecs.py +124 -0
- pyfect/_cached.py +165 -0
- pyfect/_catch.py +128 -0
- pyfect/_catch_reason.py +574 -0
- pyfect/_concurrency.py +291 -0
- pyfect/_context_effects.py +163 -0
- pyfect/_context_managers.py +76 -0
- pyfect/_core.py +368 -0
- pyfect/_digits.py +71 -0
- pyfect/_effect_args.py +46 -0
- pyfect/_effect_base.py +71 -0
- pyfect/_effect_concurrency.py +133 -0
- pyfect/_effect_eager.py +103 -0
- pyfect/_effect_errors.py +344 -0
- pyfect/_effect_folding.py +110 -0
- pyfect/_effect_hash.py +140 -0
- pyfect/_effect_logging.py +57 -0
- pyfect/_effect_metrics.py +102 -0
- pyfect/_effect_resources.py +179 -0
- pyfect/_effect_services.py +25 -0
- pyfect/_effect_timing.py +199 -0
- pyfect/_effect_tracing.py +99 -0
- pyfect/_equal.py +195 -0
- pyfect/_extras.py +43 -0
- pyfect/_fiber.py +969 -0
- pyfect/_fiber_all.py +134 -0
- pyfect/_fiber_collections.py +106 -0
- pyfect/_fn.py +706 -0
- pyfect/_hamt.py +174 -0
- pyfect/_interrupt_reason.py +33 -0
- pyfect/_isaac.py +149 -0
- pyfect/_iterables.py +231 -0
- pyfect/_js.py +263 -0
- pyfect/_js_date.py +87 -0
- pyfect/_json_text.py +286 -0
- pyfect/_json_value.py +9 -0
- pyfect/_layer_core.py +403 -0
- pyfect/_layer_for_life.py +33 -0
- pyfect/_layer_tracing.py +70 -0
- pyfect/_locked_ref.py +147 -0
- pyfect/_loop_thread.py +152 -0
- pyfect/_metric_ref.py +29 -0
- pyfect/_one_or_many.py +14 -0
- pyfect/_os_wait.py +118 -0
- pyfect/_path_posix.py +129 -0
- pyfect/_primitives.py +434 -0
- pyfect/_public.py +36 -0
- pyfect/_reasons.py +64 -0
- pyfect/_released.py +67 -0
- pyfect/_root.py +87 -0
- pyfect/_round_robin.py +73 -0
- pyfect/_some.py +17 -0
- pyfect/_stack.py +52 -0
- pyfect/_stack_frame.py +114 -0
- pyfect/_string_hash.py +35 -0
- pyfect/_subscriber_queue.py +47 -0
- pyfect/_tags.py +14 -0
- pyfect/_tracer_ref.py +21 -0
- pyfect/_tx_ops.py +77 -0
- pyfect/_unsubscripted.py +23 -0
- pyfect/_url.py +54 -0
- pyfect/_waiters.py +127 -0
- pyfect/_wake_tracking.py +98 -0
- pyfect/_wire_codec.py +123 -0
- pyfect/ai/__init__.py +73 -0
- pyfect/ai/_approvals.py +249 -0
- pyfect/ai/_decision_answers.py +185 -0
- pyfect/ai/_generated_client.py +298 -0
- pyfect/ai/_generated_support.py +84 -0
- pyfect/ai/_mcp/__init__.py +1 -0
- pyfect/ai/_mcp/batch.py +68 -0
- pyfect/ai/_mcp/client_transport.py +412 -0
- pyfect/ai/_mcp/client_wire.py +284 -0
- pyfect/ai/_mcp/core.py +490 -0
- pyfect/ai/_mcp/dispatch.py +1048 -0
- pyfect/ai/_mcp/http.py +462 -0
- pyfect/ai/_mcp/http_admission.py +269 -0
- pyfect/ai/_mcp/http_status.py +27 -0
- pyfect/ai/_mcp/input_wire.py +226 -0
- pyfect/ai/_mcp/log_levels.py +55 -0
- pyfect/ai/_mcp/param_headers.py +63 -0
- pyfect/ai/_mcp/projection.py +248 -0
- pyfect/ai/_mcp/register.py +683 -0
- pyfect/ai/_mcp/reverse_policy.py +155 -0
- pyfect/ai/_mcp/routing_header.py +24 -0
- pyfect/ai/_mcp/sessions.py +114 -0
- pyfect/ai/_mcp/stdio.py +110 -0
- pyfect/ai/_mcp/subscriptions.py +134 -0
- pyfect/ai/_mcp/tool_schema.py +35 -0
- pyfect/ai/_noul_answers.py +64 -0
- pyfect/ai/_openai_all_of.py +137 -0
- pyfect/ai/_openai_errors.py +233 -0
- pyfect/ai/_provider.py +439 -0
- pyfect/ai/_provider_tools.py +129 -0
- pyfect/ai/_stream_resolution.py +231 -0
- pyfect/ai/_structured_output_checks.py +138 -0
- pyfect/ai/_structured_output_codec.py +229 -0
- pyfect/ai/_structured_output_json.py +109 -0
- pyfect/ai/_tool_call_ids.py +80 -0
- pyfect/ai/ai_error.py +785 -0
- pyfect/ai/anthropic/__init__.py +28 -0
- pyfect/ai/anthropic/_errors.py +164 -0
- pyfect/ai/anthropic/_prompt.py +375 -0
- pyfect/ai/anthropic/_response.py +443 -0
- pyfect/ai/anthropic/_schema.py +206 -0
- pyfect/ai/anthropic/_stream.py +377 -0
- pyfect/ai/anthropic/_tools.py +248 -0
- pyfect/ai/anthropic/client.py +289 -0
- pyfect/ai/anthropic/config.py +32 -0
- pyfect/ai/anthropic/error.py +27 -0
- pyfect/ai/anthropic/language_model.py +366 -0
- pyfect/ai/anthropic/telemetry.py +51 -0
- pyfect/ai/anthropic/tool.py +570 -0
- pyfect/ai/anthropic_structured_output.py +138 -0
- pyfect/ai/chat.py +548 -0
- pyfect/ai/cloudflare/__init__.py +15 -0
- pyfect/ai/cloudflare/_errors.py +108 -0
- pyfect/ai/cloudflare/client.py +174 -0
- pyfect/ai/cloudflare/decision_model.py +117 -0
- pyfect/ai/cloudflare/schema.py +144 -0
- pyfect/ai/decision.py +290 -0
- pyfect/ai/decision_model.py +283 -0
- pyfect/ai/embedding_model.py +177 -0
- pyfect/ai/id_generator.py +79 -0
- pyfect/ai/language_model.py +1077 -0
- pyfect/ai/mcp_client.py +682 -0
- pyfect/ai/mcp_client_error.py +223 -0
- pyfect/ai/mcp_protocol.py +205 -0
- pyfect/ai/mcp_schema.py +1300 -0
- pyfect/ai/mcp_server.py +404 -0
- pyfect/ai/model.py +56 -0
- pyfect/ai/openai/__init__.py +49 -0
- pyfect/ai/openai/_errors.py +89 -0
- pyfect/ai/openai/_prompt.py +527 -0
- pyfect/ai/openai/_requests.py +34 -0
- pyfect/ai/openai/_response.py +569 -0
- pyfect/ai/openai/_schema.py +769 -0
- pyfect/ai/openai/_stream.py +771 -0
- pyfect/ai/openai/_tools.py +128 -0
- pyfect/ai/openai/_web_socket.py +295 -0
- pyfect/ai/openai/client.py +296 -0
- pyfect/ai/openai/client_generated.py +123 -0
- pyfect/ai/openai/config.py +34 -0
- pyfect/ai/openai/decision_model.py +177 -0
- pyfect/ai/openai/embedding_model.py +116 -0
- pyfect/ai/openai/error.py +8 -0
- pyfect/ai/openai/generated/__init__.py +7213 -0
- pyfect/ai/openai/generated/_recursive.py +18 -0
- pyfect/ai/openai/generated/_schemas_0.py +1015 -0
- pyfect/ai/openai/generated/_schemas_1.py +1016 -0
- pyfect/ai/openai/generated/_schemas_2.py +1017 -0
- pyfect/ai/openai/generated/_schemas_3.py +615 -0
- pyfect/ai/openai/generated/client.py +5298 -0
- pyfect/ai/openai/generated/types.py +8123 -0
- pyfect/ai/openai/language_model.py +304 -0
- pyfect/ai/openai/telemetry.py +19 -0
- pyfect/ai/openai/tool.py +391 -0
- pyfect/ai/openai_compat/__init__.py +25 -0
- pyfect/ai/openai_compat/_prompt.py +191 -0
- pyfect/ai/openai_compat/_request.py +153 -0
- pyfect/ai/openai_compat/_response.py +407 -0
- pyfect/ai/openai_compat/_schema.py +97 -0
- pyfect/ai/openai_compat/client.py +234 -0
- pyfect/ai/openai_compat/config.py +32 -0
- pyfect/ai/openai_compat/embedding_model.py +139 -0
- pyfect/ai/openai_compat/error.py +7 -0
- pyfect/ai/openai_compat/language_model.py +245 -0
- pyfect/ai/openai_compat/telemetry.py +51 -0
- pyfect/ai/openai_structured_output.py +118 -0
- pyfect/ai/openrouter/__init__.py +36 -0
- pyfect/ai/openrouter/_errors.py +186 -0
- pyfect/ai/openrouter/_prompt.py +272 -0
- pyfect/ai/openrouter/_response.py +226 -0
- pyfect/ai/openrouter/_stream.py +320 -0
- pyfect/ai/openrouter/client.py +283 -0
- pyfect/ai/openrouter/config.py +36 -0
- pyfect/ai/openrouter/decision_model.py +161 -0
- pyfect/ai/openrouter/error.py +23 -0
- pyfect/ai/openrouter/generated/__init__.py +3029 -0
- pyfect/ai/openrouter/generated/_recursive.py +15 -0
- pyfect/ai/openrouter/generated/_schemas_0.py +1015 -0
- pyfect/ai/openrouter/generated/_schemas_1.py +522 -0
- pyfect/ai/openrouter/generated/client.py +1445 -0
- pyfect/ai/openrouter/generated/types.py +4769 -0
- pyfect/ai/openrouter/language_model.py +345 -0
- pyfect/ai/openrouter/schema.py +165 -0
- pyfect/ai/prompt.py +664 -0
- pyfect/ai/response.py +768 -0
- pyfect/ai/response_id_tracker.py +82 -0
- pyfect/ai/telemetry.py +126 -0
- pyfect/ai/tokenizer.py +61 -0
- pyfect/ai/tool.py +536 -0
- pyfect/ai/toolkit.py +490 -0
- pyfect/ai/typesafe/__init__.py +17 -0
- pyfect/ai/typesafe/_errors.py +120 -0
- pyfect/ai/typesafe/client.py +146 -0
- pyfect/ai/typesafe/config.py +34 -0
- pyfect/ai/typesafe/decision_model.py +90 -0
- pyfect/ai/typesafe/schema.py +140 -0
- pyfect/batching/__init__.py +58 -0
- pyfect/batching/_api.py +71 -0
- pyfect/batching/_engine.py +229 -0
- pyfect/batching/_from_effect.py +76 -0
- pyfect/batching/_persisted.py +98 -0
- pyfect/batching/_request.py +133 -0
- pyfect/batching/_resolver.py +477 -0
- pyfect/batching/_with_cache.py +90 -0
- pyfect/big_decimal.py +500 -0
- pyfect/brand.py +114 -0
- pyfect/byte_size.py +354 -0
- pyfect/cache/__init__.py +29 -0
- pyfect/cache/_cache.py +426 -0
- pyfect/cache/_expiry.py +69 -0
- pyfect/cache/_scoped_cache.py +546 -0
- pyfect/case.py +174 -0
- pyfect/cause.py +462 -0
- pyfect/channel/__init__.py +155 -0
- pyfect/channel/_base.py +92 -0
- pyfect/channel/_catch.py +158 -0
- pyfect/channel/_catch_reason.py +572 -0
- pyfect/channel/_channel.py +158 -0
- pyfect/channel/_combinators.py +220 -0
- pyfect/channel/_concurrency_methods.py +217 -0
- pyfect/channel/_concurrent_map.py +118 -0
- pyfect/channel/_constructors.py +349 -0
- pyfect/channel/_context.py +232 -0
- pyfect/channel/_error_methods.py +336 -0
- pyfect/channel/_errors.py +180 -0
- pyfect/channel/_filters.py +225 -0
- pyfect/channel/_lifecycle.py +71 -0
- pyfect/channel/_lifecycle_methods.py +206 -0
- pyfect/channel/_loop.py +100 -0
- pyfect/channel/_merge.py +246 -0
- pyfect/channel/_repeat.py +154 -0
- pyfect/channel/_run.py +149 -0
- pyfect/channel/_run_methods.py +341 -0
- pyfect/channel/_sinks.py +88 -0
- pyfect/channel/_sources.py +238 -0
- pyfect/channel/_text.py +176 -0
- pyfect/channel/_transform_methods.py +415 -0
- pyfect/channel/_upstream.py +67 -0
- pyfect/channel_schema.py +131 -0
- pyfect/cli/__init__.py +79 -0
- pyfect/cli/_ansi.py +82 -0
- pyfect/cli/_command.py +469 -0
- pyfect/cli/_config.py +164 -0
- pyfect/cli/_environment.py +10 -0
- pyfect/cli/_help.py +195 -0
- pyfect/cli/_lexer.py +81 -0
- pyfect/cli/_parser.py +465 -0
- pyfect/cli/_settings.py +85 -0
- pyfect/cli/_suggest.py +33 -0
- pyfect/cli/_width.py +134 -0
- pyfect/cli/_wizard.py +317 -0
- pyfect/cli/argument.py +212 -0
- pyfect/cli/cli_config.py +40 -0
- pyfect/cli/cli_error.py +242 -0
- pyfect/cli/cli_output.py +305 -0
- pyfect/cli/command.py +440 -0
- pyfect/cli/completions/__init__.py +50 -0
- pyfect/cli/completions/_completer.ps1 +190 -0
- pyfect/cli/completions/_descriptor.py +182 -0
- pyfect/cli/completions/bash.py +300 -0
- pyfect/cli/completions/fish.py +164 -0
- pyfect/cli/completions/powershell.py +156 -0
- pyfect/cli/completions/zsh.py +189 -0
- pyfect/cli/flag.py +246 -0
- pyfect/cli/global_flag.py +99 -0
- pyfect/cli/help_doc.py +63 -0
- pyfect/cli/param.py +789 -0
- pyfect/cli/primitive.py +308 -0
- pyfect/cli/prompt/__init__.py +75 -0
- pyfect/cli/prompt/_auto_complete.py +205 -0
- pyfect/cli/prompt/_choices.py +104 -0
- pyfect/cli/prompt/_confirm.py +195 -0
- pyfect/cli/prompt/_core.py +512 -0
- pyfect/cli/prompt/_date.py +173 -0
- pyfect/cli/prompt/_date_parts.py +387 -0
- pyfect/cli/prompt/_file.py +372 -0
- pyfect/cli/prompt/_multi_select.py +204 -0
- pyfect/cli/prompt/_number.py +257 -0
- pyfect/cli/prompt/_select.py +117 -0
- pyfect/cli/prompt/_text.py +239 -0
- pyfect/cli/terminal_capabilities.py +118 -0
- pyfect/clock.py +150 -0
- pyfect/cluster/__init__.py +155 -0
- pyfect/cluster/_cluster_abandon.py +36 -0
- pyfect/cluster/_cluster_layer.py +122 -0
- pyfect/cluster/_entity_activation.py +16 -0
- pyfect/cluster/_entity_manager/__init__.py +22 -0
- pyfect/cluster/_entity_manager/_activation.py +540 -0
- pyfect/cluster/_entity_manager/_core.py +186 -0
- pyfect/cluster/_entity_manager/_decode.py +60 -0
- pyfect/cluster/_entity_manager/_manager.py +423 -0
- pyfect/cluster/_entity_manager/_state.py +113 -0
- pyfect/cluster/_entity_reaper.py +94 -0
- pyfect/cluster/_hash.py +13 -0
- pyfect/cluster/_interruptors.py +88 -0
- pyfect/cluster/_resource_map.py +179 -0
- pyfect/cluster/_resource_ref.py +266 -0
- pyfect/cluster/_shard_lock.py +14 -0
- pyfect/cluster/cluster_cron.py +230 -0
- pyfect/cluster/cluster_error.py +81 -0
- pyfect/cluster/cluster_http.py +210 -0
- pyfect/cluster/cluster_metrics.py +54 -0
- pyfect/cluster/cluster_schema.py +137 -0
- pyfect/cluster/cluster_socket.py +197 -0
- pyfect/cluster/cluster_workflow_engine/__init__.py +24 -0
- pyfect/cluster/cluster_workflow_engine/_engine.py +1099 -0
- pyfect/cluster/cluster_workflow_engine/_entities.py +298 -0
- pyfect/cluster/deliver_at.py +31 -0
- pyfect/cluster/entity/__init__.py +39 -0
- pyfect/cluster/entity/_entity.py +332 -0
- pyfect/cluster/entity/_handlers.py +168 -0
- pyfect/cluster/entity/_keep_alive.py +74 -0
- pyfect/cluster/entity/_queue.py +103 -0
- pyfect/cluster/entity/_request.py +34 -0
- pyfect/cluster/entity/_services.py +23 -0
- pyfect/cluster/entity/_test_client.py +237 -0
- pyfect/cluster/entity_address.py +25 -0
- pyfect/cluster/entity_id.py +20 -0
- pyfect/cluster/entity_proxy.py +161 -0
- pyfect/cluster/entity_proxy_server.py +103 -0
- pyfect/cluster/entity_resource.py +122 -0
- pyfect/cluster/entity_type.py +20 -0
- pyfect/cluster/envelope.py +307 -0
- pyfect/cluster/http_runner.py +254 -0
- pyfect/cluster/k8s_http_client.py +281 -0
- pyfect/cluster/k8s_types.py +1394 -0
- pyfect/cluster/machine_id.py +21 -0
- pyfect/cluster/message.py +219 -0
- pyfect/cluster/message_storage/__init__.py +67 -0
- pyfect/cluster/message_storage/_encoded.py +393 -0
- pyfect/cluster/message_storage/_memory.py +386 -0
- pyfect/cluster/message_storage/_noop.py +109 -0
- pyfect/cluster/message_storage/_service.py +388 -0
- pyfect/cluster/reply.py +236 -0
- pyfect/cluster/runner.py +32 -0
- pyfect/cluster/runner_address.py +23 -0
- pyfect/cluster/runner_health.py +93 -0
- pyfect/cluster/runner_server.py +286 -0
- pyfect/cluster/runner_storage.py +212 -0
- pyfect/cluster/runners/__init__.py +55 -0
- pyfect/cluster/runners/_local.py +460 -0
- pyfect/cluster/runners/_noop.py +57 -0
- pyfect/cluster/runners/_rpc.py +290 -0
- pyfect/cluster/runners/_service.py +113 -0
- pyfect/cluster/shard_id.py +39 -0
- pyfect/cluster/sharding/__init__.py +12 -0
- pyfect/cluster/sharding/_acquisition.py +490 -0
- pyfect/cluster/sharding/_clients.py +271 -0
- pyfect/cluster/sharding/_make.py +388 -0
- pyfect/cluster/sharding/_routing.py +296 -0
- pyfect/cluster/sharding/_runners_sync.py +198 -0
- pyfect/cluster/sharding/_service.py +91 -0
- pyfect/cluster/sharding/_singletons.py +183 -0
- pyfect/cluster/sharding/_state.py +296 -0
- pyfect/cluster/sharding/_storage_loop.py +421 -0
- pyfect/cluster/sharding_config.py +230 -0
- pyfect/cluster/sharding_registration_event.py +28 -0
- pyfect/cluster/single_runner.py +49 -0
- pyfect/cluster/singleton.py +36 -0
- pyfect/cluster/singleton_address.py +20 -0
- pyfect/cluster/snowflake.py +133 -0
- pyfect/cluster/socket_runner.py +67 -0
- pyfect/cluster/sql_message_storage.py +1001 -0
- pyfect/cluster/sql_runner_storage.py +841 -0
- pyfect/cluster/test_runner.py +23 -0
- pyfect/config/__init__.py +93 -0
- pyfect/config/_all.py +96 -0
- pyfect/config/_collections.py +291 -0
- pyfect/config/_config.py +261 -0
- pyfect/config/_constructors.py +106 -0
- pyfect/config/_error.py +27 -0
- pyfect/config/_leaf.py +92 -0
- pyfect/config/_scalars.py +193 -0
- pyfect/config/_schema.py +340 -0
- pyfect/config_provider/__init__.py +60 -0
- pyfect/config_provider/_dir.py +84 -0
- pyfect/config_provider/_dotenv.py +127 -0
- pyfect/config_provider/_env.py +79 -0
- pyfect/config_provider/_layers.py +80 -0
- pyfect/config_provider/_node.py +90 -0
- pyfect/config_provider/_provider.py +118 -0
- pyfect/config_provider/_unknown.py +62 -0
- pyfect/console/__init__.py +63 -0
- pyfect/console/_effects.py +134 -0
- pyfect/console/_live.py +156 -0
- pyfect/console/_service.py +49 -0
- pyfect/console/_table.py +90 -0
- pyfect/context.py +414 -0
- pyfect/cron/__init__.py +30 -0
- pyfect/cron/_model.py +183 -0
- pyfect/cron/_search.py +268 -0
- pyfect/cron/_text.py +202 -0
- pyfect/cron/_wall.py +42 -0
- pyfect/crypto.py +257 -0
- pyfect/date_time/__init__.py +77 -0
- pyfect/date_time/_calendar.py +209 -0
- pyfect/date_time/_current.py +118 -0
- pyfect/date_time/_epoch.py +22 -0
- pyfect/date_time/_iso.py +80 -0
- pyfect/date_time/_zones.py +135 -0
- pyfect/date_time/intl.py +144 -0
- pyfect/deferred.py +145 -0
- pyfect/devtools/__init__.py +24 -0
- pyfect/devtools/client.py +323 -0
- pyfect/devtools/layers.py +32 -0
- pyfect/devtools/schema.py +301 -0
- pyfect/devtools/server.py +72 -0
- pyfect/duration.py +295 -0
- pyfect/effect.py +565 -0
- pyfect/effectable.py +37 -0
- pyfect/encoding/__init__.py +62 -0
- pyfect/encoding/_archive.py +197 -0
- pyfect/encoding/_json_array_scanner.py +477 -0
- pyfect/encoding/base64.py +23 -0
- pyfect/encoding/base64_url.py +19 -0
- pyfect/encoding/encoding_error.py +17 -0
- pyfect/encoding/hex.py +28 -0
- pyfect/encoding/ini.py +188 -0
- pyfect/encoding/json_array.py +246 -0
- pyfect/encoding/ndjson.py +461 -0
- pyfect/encoding/schema_binary/__init__.py +257 -0
- pyfect/encoding/schema_binary/_channels.py +173 -0
- pyfect/encoding/schema_binary/_compile.py +584 -0
- pyfect/encoding/schema_binary/_decode.py +848 -0
- pyfect/encoding/schema_binary/_dictionary.py +90 -0
- pyfect/encoding/schema_binary/_encode.py +838 -0
- pyfect/encoding/schema_binary/_fingerprint.py +168 -0
- pyfect/encoding/schema_binary/_layout.py +350 -0
- pyfect/encoding/schema_binary/_literals.py +47 -0
- pyfect/encoding/schema_binary/_parser.py +163 -0
- pyfect/encoding/schema_binary/_reader.py +148 -0
- pyfect/encoding/schema_binary/_target.py +310 -0
- pyfect/encoding/schema_binary/_values.py +133 -0
- pyfect/encoding/schema_binary/_wire.py +253 -0
- pyfect/encoding/sse.py +476 -0
- pyfect/encoding/tar/__init__.py +396 -0
- pyfect/encoding/tar/_decoder.py +220 -0
- pyfect/encoding/tar/_encoder.py +83 -0
- pyfect/encoding/tar/_header.py +494 -0
- pyfect/encoding/tar/_model.py +82 -0
- pyfect/encoding/toml.py +375 -0
- pyfect/encoding/xml/__init__.py +222 -0
- pyfect/encoding/xml/_chars.py +119 -0
- pyfect/encoding/xml/_collector.py +106 -0
- pyfect/encoding/xml/_inputs.py +126 -0
- pyfect/encoding/xml/_model.py +114 -0
- pyfect/encoding/xml/_parser.py +694 -0
- pyfect/encoding/xml/_scanner.py +180 -0
- pyfect/encoding/xml/_writer.py +238 -0
- pyfect/encoding/yaml.py +739 -0
- pyfect/encoding/zip/__init__.py +392 -0
- pyfect/encoding/zip/_decoder.py +378 -0
- pyfect/encoding/zip/_format.py +122 -0
- pyfect/encoding/zip/_inflate.py +65 -0
- pyfect/encoding/zip/_model.py +86 -0
- pyfect/encoding/zip/_writer.py +311 -0
- pyfect/error_reporter.py +240 -0
- pyfect/errors.py +321 -0
- pyfect/eventlog/__init__.py +77 -0
- pyfect/eventlog/_conflicts.py +94 -0
- pyfect/eventlog/_crypto.py +64 -0
- pyfect/eventlog/_identity.py +26 -0
- pyfect/eventlog/_identity_derivation.py +79 -0
- pyfect/eventlog/_transferable_bytes.py +55 -0
- pyfect/eventlog/_uuid.py +33 -0
- pyfect/eventlog/event.py +115 -0
- pyfect/eventlog/event_group.py +55 -0
- pyfect/eventlog/event_journal.py +630 -0
- pyfect/eventlog/event_log.py +1039 -0
- pyfect/eventlog/event_log_encryption.py +219 -0
- pyfect/eventlog/event_log_message.py +357 -0
- pyfect/eventlog/event_log_remote.py +465 -0
- pyfect/eventlog/event_log_server.py +442 -0
- pyfect/eventlog/event_log_server_encrypted.py +322 -0
- pyfect/eventlog/event_log_server_unencrypted.py +814 -0
- pyfect/eventlog/event_log_session_auth.py +250 -0
- pyfect/eventlog/event_row.py +423 -0
- pyfect/eventlog/sql_event_journal.py +692 -0
- pyfect/eventlog/sql_event_log_server_encrypted.py +440 -0
- pyfect/eventlog/sql_event_log_server_unencrypted.py +568 -0
- pyfect/execution_plan/__init__.py +42 -0
- pyfect/execution_plan/_events.py +128 -0
- pyfect/execution_plan/_model.py +108 -0
- pyfect/execution_plan/_run.py +176 -0
- pyfect/exit.py +205 -0
- pyfect/fiber_handle.py +305 -0
- pyfect/fiber_map.py +350 -0
- pyfect/fiber_set.py +232 -0
- pyfect/file_system/__init__.py +69 -0
- pyfect/file_system/_copy.py +116 -0
- pyfect/file_system/_errors.py +32 -0
- pyfect/file_system/_file.py +177 -0
- pyfect/file_system/_live.py +351 -0
- pyfect/file_system/_model.py +72 -0
- pyfect/file_system/_noop.py +189 -0
- pyfect/file_system/_service.py +336 -0
- pyfect/file_system/_stat.py +69 -0
- pyfect/file_system/_watch.py +107 -0
- pyfect/filters.py +245 -0
- pyfect/graph/__init__.py +101 -0
- pyfect/graph/_all_pairs.py +150 -0
- pyfect/graph/_combine.py +259 -0
- pyfect/graph/_compact.py +143 -0
- pyfect/graph/_components.py +200 -0
- pyfect/graph/_cycles.py +144 -0
- pyfect/graph/_enumerate.py +168 -0
- pyfect/graph/_errors.py +51 -0
- pyfect/graph/_flow.py +164 -0
- pyfect/graph/_forest.py +109 -0
- pyfect/graph/_graph.py +269 -0
- pyfect/graph/_lowlink.py +98 -0
- pyfect/graph/_matching.py +101 -0
- pyfect/graph/_mutate.py +211 -0
- pyfect/graph/_query.py +138 -0
- pyfect/graph/_records.py +119 -0
- pyfect/graph/_render.py +109 -0
- pyfect/graph/_shortest.py +274 -0
- pyfect/graph/_snapshot.py +50 -0
- pyfect/graph/_store.py +136 -0
- pyfect/graph/_traverse.py +289 -0
- pyfect/graph/_view.py +384 -0
- pyfect/graph/_walker.py +70 -0
- pyfect/graphql/__init__.py +87 -0
- pyfect/graphql/_codec.py +117 -0
- pyfect/graphql/_http.py +309 -0
- pyfect/graphql/_schemas.py +39 -0
- pyfect/graphql/_slices.py +110 -0
- pyfect/graphql/_template.py +111 -0
- pyfect/graphql/_types.py +115 -0
- pyfect/graphql/_ws.py +395 -0
- pyfect/graphql/_ws_messages.py +120 -0
- pyfect/graphql/client.py +428 -0
- pyfect/graphql/client_error.py +285 -0
- pyfect/graphql/group.py +142 -0
- pyfect/graphql/middleware.py +98 -0
- pyfect/graphql/operation.py +409 -0
- pyfect/graphql/protocol.py +119 -0
- pyfect/graphql/resolver.py +574 -0
- pyfect/graphql/template.py +61 -0
- pyfect/hash_map.py +317 -0
- pyfect/hash_ring.py +206 -0
- pyfect/hash_set.py +163 -0
- pyfect/http/__init__.py +125 -0
- pyfect/http/_asgi_listener.py +272 -0
- pyfect/http/_authority.py +63 -0
- pyfect/http/_brotli.py +27 -0
- pyfect/http/_compression.py +52 -0
- pyfect/http/_field.py +18 -0
- pyfect/http/_foreign_path.py +27 -0
- pyfect/http/_form_data.py +34 -0
- pyfect/http/_httpx_options.py +120 -0
- pyfect/http/_in_memory.py +76 -0
- pyfect/http/_request_state.py +59 -0
- pyfect/http/_vary.py +14 -0
- pyfect/http/abort.py +65 -0
- pyfect/http/asgi/__init__.py +59 -0
- pyfect/http/asgi/_app.py +155 -0
- pyfect/http/asgi/_http.py +201 -0
- pyfect/http/asgi/_mount.py +263 -0
- pyfect/http/asgi/_scope.py +86 -0
- pyfect/http/asgi/_serve.py +72 -0
- pyfect/http/asgi/_types.py +13 -0
- pyfect/http/asgi/_web_socket.py +325 -0
- pyfect/http/body.py +437 -0
- pyfect/http/client/__init__.py +111 -0
- pyfect/http/client/_circuit_breaker.py +113 -0
- pyfect/http/client/_cookie_jar.py +120 -0
- pyfect/http/client/_make.py +252 -0
- pyfect/http/client/_rate_limit.py +423 -0
- pyfect/http/client/_redirects.py +93 -0
- pyfect/http/client/_retry.py +115 -0
- pyfect/http/client/_service.py +497 -0
- pyfect/http/cookies.py +444 -0
- pyfect/http/error.py +188 -0
- pyfect/http/etag.py +96 -0
- pyfect/http/file_like.py +49 -0
- pyfect/http/find_my_way/__init__.py +10 -0
- pyfect/http/find_my_way/_nodes.py +186 -0
- pyfect/http/find_my_way/_paths.py +43 -0
- pyfect/http/find_my_way/_query_string.py +67 -0
- pyfect/http/find_my_way/_router.py +401 -0
- pyfect/http/headers.py +343 -0
- pyfect/http/http_effect.py +198 -0
- pyfect/http/httpx_client.py +291 -0
- pyfect/http/hypercorn_server.py +122 -0
- pyfect/http/incoming_message.py +140 -0
- pyfect/http/live_server/__init__.py +191 -0
- pyfect/http/live_server/_body.py +164 -0
- pyfect/http/live_server/_exchange.py +321 -0
- pyfect/http/live_server/_head.py +194 -0
- pyfect/http/live_server/_inbound.py +156 -0
- pyfect/http/live_server/_listener.py +211 -0
- pyfect/http/live_server/_serve.py +225 -0
- pyfect/http/live_server/_timers.py +20 -0
- pyfect/http/live_server/_upgrade.py +108 -0
- pyfect/http/live_server/_websocket.py +286 -0
- pyfect/http/live_server/_ws_frames.py +242 -0
- pyfect/http/method.py +30 -0
- pyfect/http/middleware/__init__.py +61 -0
- pyfect/http/middleware/_compression.py +156 -0
- pyfect/http/middleware/_cors.py +129 -0
- pyfect/http/middleware/_decompression.py +274 -0
- pyfect/http/middleware/_forwarded.py +113 -0
- pyfect/http/middleware/_logger.py +73 -0
- pyfect/http/middleware/_search_params.py +34 -0
- pyfect/http/middleware/_tracer.py +153 -0
- pyfect/http/middleware/_types.py +16 -0
- pyfect/http/mime/__init__.py +69 -0
- pyfect/http/mime/_types.py +361 -0
- pyfect/http/multipart.py +613 -0
- pyfect/http/multipart_parser/__init__.py +348 -0
- pyfect/http/multipart_parser/_content_type.py +109 -0
- pyfect/http/multipart_parser/_headers.py +206 -0
- pyfect/http/multipart_parser/_search.py +85 -0
- pyfect/http/origin.py +146 -0
- pyfect/http/platform.py +593 -0
- pyfect/http/prometheus.py +34 -0
- pyfect/http/request.py +384 -0
- pyfect/http/response.py +474 -0
- pyfect/http/router/__init__.py +82 -0
- pyfect/http/router/_api.py +462 -0
- pyfect/http/router/_in_memory.py +76 -0
- pyfect/http/router/_markers.py +47 -0
- pyfect/http/router/_middleware.py +181 -0
- pyfect/http/router/_middleware_stack.py +51 -0
- pyfect/http/router/_provided.py +31 -0
- pyfect/http/router/_route.py +114 -0
- pyfect/http/router/_router.py +308 -0
- pyfect/http/schema.py +276 -0
- pyfect/http/server.py +199 -0
- pyfect/http/server_error.py +261 -0
- pyfect/http/server_request.py +729 -0
- pyfect/http/server_respondable.py +77 -0
- pyfect/http/server_response.py +569 -0
- pyfect/http/static_server/__init__.py +257 -0
- pyfect/http/static_server/_conditional.py +72 -0
- pyfect/http/static_server/_mime.py +55 -0
- pyfect/http/static_server/_range.py +53 -0
- pyfect/http/status.py +51 -0
- pyfect/http/template.py +161 -0
- pyfect/http/trace_context.py +91 -0
- pyfect/http/url.py +442 -0
- pyfect/http/url_params.py +304 -0
- pyfect/http/uvicorn_server.py +119 -0
- pyfect/http/web.py +171 -0
- pyfect/http/web_handler.py +463 -0
- pyfect/http/wire.py +209 -0
- pyfect/http_api/__init__.py +83 -0
- pyfect/http_api/_assets/scalar.js +8 -0
- pyfect/http_api/_assets/swagger-ui.css +3 -0
- pyfect/http_api/_assets/swagger-ui.js +4 -0
- pyfect/http_api/_client_method.py +220 -0
- pyfect/http_api/_client_payload.py +144 -0
- pyfect/http_api/_client_response.py +377 -0
- pyfect/http_api/_handlers.py +282 -0
- pyfect/http_api/_html.py +38 -0
- pyfect/http_api/_media_type.py +6 -0
- pyfect/http_api/_openapi_responses.py +164 -0
- pyfect/http_api/_openapi_schemas.py +163 -0
- pyfect/http_api/_path.py +102 -0
- pyfect/http_api/_payload.py +260 -0
- pyfect/http_api/_response.py +363 -0
- pyfect/http_api/_security.py +174 -0
- pyfect/http_api/api.py +265 -0
- pyfect/http_api/builder.py +447 -0
- pyfect/http_api/client.py +474 -0
- pyfect/http_api/endpoint.py +969 -0
- pyfect/http_api/error.py +280 -0
- pyfect/http_api/group.py +215 -0
- pyfect/http_api/middleware.py +232 -0
- pyfect/http_api/openapi.py +396 -0
- pyfect/http_api/scalar.py +143 -0
- pyfect/http_api/schema.py +713 -0
- pyfect/http_api/security.py +91 -0
- pyfect/http_api/swagger.py +69 -0
- pyfect/http_api/test.py +105 -0
- pyfect/json_patch.py +255 -0
- pyfect/json_pointer.py +100 -0
- pyfect/json_schema/__init__.py +66 -0
- pyfect/json_schema/_api.py +96 -0
- pyfect/json_schema/_converter.py +258 -0
- pyfect/json_schema/_draft04.py +198 -0
- pyfect/json_schema/_draft07.py +203 -0
- pyfect/json_schema/_keywords.py +202 -0
- pyfect/json_schema/_model.py +201 -0
- pyfect/json_schema/_openapi.py +208 -0
- pyfect/latch.py +127 -0
- pyfect/layer.py +479 -0
- pyfect/layer_map.py +279 -0
- pyfect/layer_ref.py +208 -0
- pyfect/logger/__init__.py +91 -0
- pyfect/logger/_batched.py +49 -0
- pyfect/logger/_console.py +116 -0
- pyfect/logger/_file.py +31 -0
- pyfect/logger/_formats.py +104 -0
- pyfect/logger/_layer.py +50 -0
- pyfect/logger/_level.py +53 -0
- pyfect/logger/_log.py +142 -0
- pyfect/logger/_model.py +163 -0
- pyfect/logger/_refs.py +78 -0
- pyfect/logger/_stdlib.py +54 -0
- pyfect/logger/_text.py +263 -0
- pyfect/logger/_tracer.py +35 -0
- pyfect/managed_runtime.py +393 -0
- pyfect/metric/__init__.py +119 -0
- pyfect/metric/_boundaries.py +27 -0
- pyfect/metric/_kinds.py +417 -0
- pyfect/metric/_metric.py +302 -0
- pyfect/metric/_prometheus.py +150 -0
- pyfect/metric/_refs.py +78 -0
- pyfect/metric/_runtime.py +75 -0
- pyfect/metric/_snapshot.py +97 -0
- pyfect/metric/_state.py +74 -0
- pyfect/msgspec.py +98 -0
- pyfect/mutable_hash_map.py +140 -0
- pyfect/mutable_hash_set.py +95 -0
- pyfect/net/__init__.py +18 -0
- pyfect/net/_errors.py +29 -0
- pyfect/net/address_resolver.py +179 -0
- pyfect/net/dns/__init__.py +92 -0
- pyfect/net/dns/_error.py +43 -0
- pyfect/net/dns/_records.py +335 -0
- pyfect/net/dns/_service.py +287 -0
- pyfect/net/dns/_system.py +89 -0
- pyfect/net/host.py +311 -0
- pyfect/net/ip_interface.py +202 -0
- pyfect/net/ip_network.py +250 -0
- pyfect/net/net_address/__init__.py +245 -0
- pyfect/net/net_address/_inet.py +412 -0
- pyfect/net/net_address/_ip.py +348 -0
- pyfect/net/net_address/_mac.py +119 -0
- pyfect/optic/__init__.py +50 -0
- pyfect/optic/_builtins.py +90 -0
- pyfect/optic/_data.py +97 -0
- pyfect/optic/_optic.py +422 -0
- pyfect/optic/_steps.py +151 -0
- pyfect/option.py +362 -0
- pyfect/otel/__init__.py +22 -0
- pyfect/otel/_attributes.py +67 -0
- pyfect/otel/_producer.py +322 -0
- pyfect/otel/_shutdown.py +26 -0
- pyfect/otel/_span.py +292 -0
- pyfect/otel/_wrapper.py +92 -0
- pyfect/otel/logger.py +154 -0
- pyfect/otel/metrics.py +126 -0
- pyfect/otel/resource.py +111 -0
- pyfect/otel/sdk.py +195 -0
- pyfect/otel/tracer.py +157 -0
- pyfect/partitioned_semaphore.py +211 -0
- pyfect/path.py +304 -0
- pyfect/persistence/__init__.py +77 -0
- pyfect/persistence/_columns.py +32 -0
- pyfect/persistence/_error.py +14 -0
- pyfect/persistence/_seconds.py +68 -0
- pyfect/persistence/_sql_keys.py +47 -0
- pyfect/persistence/circuit_breaker/__init__.py +120 -0
- pyfect/persistence/circuit_breaker/_breaker.py +317 -0
- pyfect/persistence/circuit_breaker/_errors.py +67 -0
- pyfect/persistence/circuit_breaker/_memory.py +243 -0
- pyfect/persistence/circuit_breaker/_redis.py +509 -0
- pyfect/persistence/circuit_breaker/_store.py +60 -0
- pyfect/persistence/circuit_breaker/_types.py +191 -0
- pyfect/persistence/key_value_store/__init__.py +51 -0
- pyfect/persistence/key_value_store/_layers.py +216 -0
- pyfect/persistence/key_value_store/_schema_store.py +91 -0
- pyfect/persistence/key_value_store/_sql.py +197 -0
- pyfect/persistence/key_value_store/_store.py +212 -0
- pyfect/persistence/persistable.py +164 -0
- pyfect/persistence/persisted_cache.py +159 -0
- pyfect/persistence/persisted_queue/__init__.py +75 -0
- pyfect/persistence/persisted_queue/_error.py +42 -0
- pyfect/persistence/persisted_queue/_mailbox.py +101 -0
- pyfect/persistence/persisted_queue/_memory.py +182 -0
- pyfect/persistence/persisted_queue/_queue.py +272 -0
- pyfect/persistence/persisted_queue/_redis.py +455 -0
- pyfect/persistence/persisted_queue/_redis_scripts.py +395 -0
- pyfect/persistence/persisted_queue/_sql.py +334 -0
- pyfect/persistence/persisted_queue/_sql_migrations.py +319 -0
- pyfect/persistence/persisted_queue/_sql_statements.py +441 -0
- pyfect/persistence/persisted_queue/_store.py +147 -0
- pyfect/persistence/persistence/__init__.py +84 -0
- pyfect/persistence/persistence/_kvs.py +135 -0
- pyfect/persistence/persistence/_memory.py +62 -0
- pyfect/persistence/persistence/_redis.py +162 -0
- pyfect/persistence/persistence/_service.py +276 -0
- pyfect/persistence/persistence/_sql.py +499 -0
- pyfect/persistence/rate_limiter/__init__.py +82 -0
- pyfect/persistence/rate_limiter/_errors.py +59 -0
- pyfect/persistence/rate_limiter/_limiter.py +325 -0
- pyfect/persistence/rate_limiter/_memory.py +217 -0
- pyfect/persistence/rate_limiter/_redis.py +485 -0
- pyfect/persistence/rate_limiter/_store.py +89 -0
- pyfect/persistence/redis/__init__.py +52 -0
- pyfect/persistence/redis/_error.py +12 -0
- pyfect/persistence/redis/_make.py +234 -0
- pyfect/persistence/redis/_script.py +78 -0
- pyfect/persistence/redis/_service.py +104 -0
- pyfect/persistence/redis_py.py +303 -0
- pyfect/persistence/user_directories.py +195 -0
- pyfect/platform.py +26 -0
- pyfect/platform_error.py +256 -0
- pyfect/pool/__init__.py +36 -0
- pyfect/pool/_pool.py +707 -0
- pyfect/pool/_state.py +161 -0
- pyfect/pool/_ttl.py +105 -0
- pyfect/pool/_waiters.py +116 -0
- pyfect/primary_key.py +35 -0
- pyfect/process/__init__.py +82 -0
- pyfect/process/_child.py +213 -0
- pyfect/process/_command.py +358 -0
- pyfect/process/_handle.py +77 -0
- pyfect/process/_live.py +455 -0
- pyfect/process/_os.py +78 -0
- pyfect/process/_pipes.py +291 -0
- pyfect/process/_spawner.py +75 -0
- pyfect/pubsub/__init__.py +33 -0
- pyfect/pubsub/_atomic.py +181 -0
- pyfect/pubsub/_hub.py +214 -0
- pyfect/pubsub/_pubsub.py +214 -0
- pyfect/pubsub/_replay.py +107 -0
- pyfect/pubsub/_subscription.py +226 -0
- pyfect/pull.py +132 -0
- pyfect/py.typed +0 -0
- pyfect/pydantic.py +188 -0
- pyfect/queue/__init__.py +33 -0
- pyfect/queue/_queue.py +525 -0
- pyfect/queue/_state.py +111 -0
- pyfect/queue/_views.py +55 -0
- pyfect/random.py +146 -0
- pyfect/rc_map.py +363 -0
- pyfect/rc_ref.py +271 -0
- pyfect/reactivity/__init__.py +53 -0
- pyfect/reactivity/_async_result_schema.py +227 -0
- pyfect/reactivity/_hydration.py +18 -0
- pyfect/reactivity/_live.py +21 -0
- pyfect/reactivity/_reactivity.py +256 -0
- pyfect/reactivity/_result_effect.py +17 -0
- pyfect/reactivity/_time.py +11 -0
- pyfect/reactivity/async_result.py +679 -0
- pyfect/reactivity/atom/__init__.py +143 -0
- pyfect/reactivity/atom/_combinators.py +223 -0
- pyfect/reactivity/atom/_constructors.py +80 -0
- pyfect/reactivity/atom/_conversions.py +79 -0
- pyfect/reactivity/atom/_effect.py +252 -0
- pyfect/reactivity/atom/_fn.py +341 -0
- pyfect/reactivity/atom/_kvs.py +101 -0
- pyfect/reactivity/atom/_optimistic.py +162 -0
- pyfect/reactivity/atom/_runtime.py +323 -0
- pyfect/reactivity/atom/_serializable.py +65 -0
- pyfect/reactivity/atom/_subscription_ref.py +102 -0
- pyfect/reactivity/atom/_swr.py +69 -0
- pyfect/reactivity/atom/_types.py +435 -0
- pyfect/reactivity/atom_http_api.py +667 -0
- pyfect/reactivity/atom_ref.py +260 -0
- pyfect/reactivity/atom_registry/__init__.py +219 -0
- pyfect/reactivity/atom_registry/_batch.py +96 -0
- pyfect/reactivity/atom_registry/_dispatcher.py +60 -0
- pyfect/reactivity/atom_registry/_lifetime.py +202 -0
- pyfect/reactivity/atom_registry/_node.py +423 -0
- pyfect/reactivity/atom_registry/_registry.py +380 -0
- pyfect/reactivity/atom_rpc.py +283 -0
- pyfect/reactivity/hydration.py +200 -0
- pyfect/redacted.py +83 -0
- pyfect/ref.py +129 -0
- pyfect/resource.py +98 -0
- pyfect/result.py +306 -0
- pyfect/rpc/__init__.py +64 -0
- pyfect/rpc/_encoded_schema.py +86 -0
- pyfect/rpc/_json_rpc.py +246 -0
- pyfect/rpc/_methods.py +177 -0
- pyfect/rpc/_rpc.py +498 -0
- pyfect/rpc/client/__init__.py +100 -0
- pyfect/rpc/client/_core.py +449 -0
- pyfect/rpc/client/_http.py +257 -0
- pyfect/rpc/client/_protocol.py +171 -0
- pyfect/rpc/client/_serialized.py +321 -0
- pyfect/rpc/client/_transports.py +328 -0
- pyfect/rpc/client/_worker.py +283 -0
- pyfect/rpc/client_error.py +35 -0
- pyfect/rpc/group.py +296 -0
- pyfect/rpc/message.py +299 -0
- pyfect/rpc/middleware.py +182 -0
- pyfect/rpc/schema.py +89 -0
- pyfect/rpc/serialization.py +366 -0
- pyfect/rpc/server/__init__.py +181 -0
- pyfect/rpc/server/_core.py +549 -0
- pyfect/rpc/server/_protocol.py +123 -0
- pyfect/rpc/server/_serialized.py +363 -0
- pyfect/rpc/server/_stdio.py +101 -0
- pyfect/rpc/server/_transports.py +427 -0
- pyfect/rpc/server/_worker.py +108 -0
- pyfect/rpc/test.py +74 -0
- pyfect/rpc/worker.py +92 -0
- pyfect/run.py +393 -0
- pyfect/schedule/__init__.py +91 -0
- pyfect/schedule/_combinators.py +168 -0
- pyfect/schedule/_constructors.py +223 -0
- pyfect/schedule/_cron.py +49 -0
- pyfect/schedule/_drive.py +191 -0
- pyfect/schedule/_model.py +405 -0
- pyfect/scheduler.py +575 -0
- pyfect/schema/__init__.py +866 -0
- pyfect/schema/_adapters.py +39 -0
- pyfect/schema/_arbitrary_constraint.py +242 -0
- pyfect/schema/_arbitrary_graph.py +325 -0
- pyfect/schema/_arbitrary_links.py +179 -0
- pyfect/schema/_arbitrary_number.py +112 -0
- pyfect/schema/_arrays.py +287 -0
- pyfect/schema/_assignable.py +118 -0
- pyfect/schema/_boolean_literals.py +31 -0
- pyfect/schema/_checks_number.py +216 -0
- pyfect/schema/_checks_order.py +140 -0
- pyfect/schema/_checks_size.py +277 -0
- pyfect/schema/_checks_string.py +241 -0
- pyfect/schema/_class.py +569 -0
- pyfect/schema/_codec_derivation.py +227 -0
- pyfect/schema/_codec_links.py +221 -0
- pyfect/schema/_codecs.py +316 -0
- pyfect/schema/_collections.py +172 -0
- pyfect/schema/_constructor_default.py +89 -0
- pyfect/schema/_data.py +440 -0
- pyfect/schema/_data_codecs.py +145 -0
- pyfect/schema/_decimal_regexp.py +243 -0
- pyfect/schema/_declare.py +68 -0
- pyfect/schema/_derive.py +292 -0
- pyfect/schema/_duration_codecs.py +27 -0
- pyfect/schema/_ecma_pattern.py +416 -0
- pyfect/schema/_error.py +26 -0
- pyfect/schema/_error_instance.py +222 -0
- pyfect/schema/_graph.py +271 -0
- pyfect/schema/_instances.py +240 -0
- pyfect/schema/_jsonschema/__init__.py +90 -0
- pyfect/schema/_jsonschema/_annotations.py +114 -0
- pyfect/schema/_jsonschema/_checks.py +253 -0
- pyfect/schema/_jsonschema/_compile.py +346 -0
- pyfect/schema/_jsonschema/_exact.py +19 -0
- pyfect/schema/_jsonschema/_merge.py +211 -0
- pyfect/schema/_jsonschema/_objects.py +231 -0
- pyfect/schema/_jsonschema/_references.py +221 -0
- pyfect/schema/_jsonschema/_template.py +69 -0
- pyfect/schema/_literals.py +147 -0
- pyfect/schema/_middleware.py +209 -0
- pyfect/schema/_net.py +522 -0
- pyfect/schema/_options.py +49 -0
- pyfect/schema/_parse.py +351 -0
- pyfect/schema/_parsed_text.py +37 -0
- pyfect/schema/_primitives.py +48 -0
- pyfect/schema/_record.py +41 -0
- pyfect/schema/_refine.py +81 -0
- pyfect/schema/_schema.py +158 -0
- pyfect/schema/_semver.py +144 -0
- pyfect/schema/_strategy/__init__.py +258 -0
- pyfect/schema/_strategy/_big_decimal.py +203 -0
- pyfect/schema/_strategy/_composites.py +119 -0
- pyfect/schema/_strategy/_leaves.py +94 -0
- pyfect/schema/_strategy/_net.py +133 -0
- pyfect/schema/_struct.py +87 -0
- pyfect/schema/_struct_keys.py +128 -0
- pyfect/schema/_tagged.py +254 -0
- pyfect/schema/_template_literal.py +172 -0
- pyfect/schema/_to_codec_array_from_single.py +115 -0
- pyfect/schema/_to_codec_go_json.py +174 -0
- pyfect/schema/_to_codec_json.py +73 -0
- pyfect/schema/_to_codec_string_tree.py +115 -0
- pyfect/schema/_to_differ_json_patch.py +44 -0
- pyfect/schema/_to_encoder_xml.py +119 -0
- pyfect/schema/_to_equivalence.py +186 -0
- pyfect/schema/_to_formatter.py +180 -0
- pyfect/schema/_to_iso.py +73 -0
- pyfect/schema/_to_strategy.py +31 -0
- pyfect/schema/_transform.py +210 -0
- pyfect/schema/_tree.py +27 -0
- pyfect/schema/_type_walk.py +53 -0
- pyfect/schema/_undefined.py +58 -0
- pyfect/schema/_value_codecs.py +84 -0
- pyfect/schema/_values.py +123 -0
- pyfect/schema/_void.py +86 -0
- pyfect/schema/ast/__init__.py +227 -0
- pyfect/schema/ast/_annotations.py +43 -0
- pyfect/schema/ast/_arrays.py +210 -0
- pyfect/schema/ast/_base.py +295 -0
- pyfect/schema/ast/_candidates.py +309 -0
- pyfect/schema/ast/_check.py +181 -0
- pyfect/schema/ast/_class.py +159 -0
- pyfect/schema/ast/_constructors.py +65 -0
- pyfect/schema/ast/_declaration.py +97 -0
- pyfect/schema/ast/_encoding.py +111 -0
- pyfect/schema/ast/_json.py +129 -0
- pyfect/schema/ast/_keywords.py +125 -0
- pyfect/schema/ast/_literal.py +157 -0
- pyfect/schema/ast/_objects.py +356 -0
- pyfect/schema/ast/_record.py +36 -0
- pyfect/schema/ast/_result.py +116 -0
- pyfect/schema/ast/_steps.py +209 -0
- pyfect/schema/ast/_suspend.py +65 -0
- pyfect/schema/ast/_template_literal.py +256 -0
- pyfect/schema/ast/_union.py +172 -0
- pyfect/schema/getter/__init__.py +136 -0
- pyfect/schema/getter/_binary.py +104 -0
- pyfect/schema/getter/_coerce.py +196 -0
- pyfect/schema/getter/_getter.py +243 -0
- pyfect/schema/getter/_make.py +151 -0
- pyfect/schema/getter/_text.py +141 -0
- pyfect/schema/getter/_tree_record.py +170 -0
- pyfect/schema/issue.py +420 -0
- pyfect/schema/model/__init__.py +120 -0
- pyfect/schema/model/_class.py +245 -0
- pyfect/schema/model/_dates.py +103 -0
- pyfect/schema/model/_fields.py +77 -0
- pyfect/schema/model/_toolkit.py +114 -0
- pyfect/schema/model/_uuid.py +69 -0
- pyfect/schema/representation/__init__.py +424 -0
- pyfect/schema/representation/_builtin_revivers.py +331 -0
- pyfect/schema/representation/_code.py +106 -0
- pyfect/schema/representation/_code_builtins.py +281 -0
- pyfect/schema/representation/_code_document.py +563 -0
- pyfect/schema/representation/_code_sort.py +111 -0
- pyfect/schema/representation/_from.py +278 -0
- pyfect/schema/representation/_import/__init__.py +78 -0
- pyfect/schema/representation/_import/_finalize.py +121 -0
- pyfect/schema/representation/_import/_intersect.py +467 -0
- pyfect/schema/representation/_import/_js_order.py +66 -0
- pyfect/schema/representation/_import/_keywords.py +224 -0
- pyfect/schema/representation/_import/_nodes.py +199 -0
- pyfect/schema/representation/_import/_translate.py +455 -0
- pyfect/schema/representation/_json.py +520 -0
- pyfect/schema/representation/_json_schema.py +178 -0
- pyfect/schema/representation/_model.py +284 -0
- pyfect/schema/representation/_revivers.py +89 -0
- pyfect/schema/representation/_to.py +249 -0
- pyfect/schema/transformation/__init__.py +99 -0
- pyfect/schema/transformation/_builtins.py +314 -0
- pyfect/schema/transformation/_durations.py +150 -0
- pyfect/schema/transformation/_transformation.py +177 -0
- pyfect/schema/variant_schema/__init__.py +69 -0
- pyfect/schema/variant_schema/_class.py +188 -0
- pyfect/schema/variant_schema/_defaults.py +30 -0
- pyfect/schema/variant_schema/_field.py +32 -0
- pyfect/schema/variant_schema/_struct.py +130 -0
- pyfect/schema/variant_schema/_toolkit.py +160 -0
- pyfect/scope.py +374 -0
- pyfect/scoped_ref.py +124 -0
- pyfect/semaphore.py +276 -0
- pyfect/semver.py +403 -0
- pyfect/semver_range.py +420 -0
- pyfect/sink/__init__.py +131 -0
- pyfect/sink/_combinators.py +151 -0
- pyfect/sink/_constructors.py +187 -0
- pyfect/sink/_context.py +87 -0
- pyfect/sink/_folds.py +336 -0
- pyfect/sink/_for_each.py +87 -0
- pyfect/sink/_sink.py +265 -0
- pyfect/sink/_steps.py +34 -0
- pyfect/sink/_taking.py +183 -0
- pyfect/socket/__init__.py +146 -0
- pyfect/socket/_accepted.py +65 -0
- pyfect/socket/_connection.py +384 -0
- pyfect/socket/_errors.py +135 -0
- pyfect/socket/_loop.py +66 -0
- pyfect/socket/_net.py +281 -0
- pyfect/socket/_socket.py +245 -0
- pyfect/socket/_tls.py +88 -0
- pyfect/socket/_tls_session.py +117 -0
- pyfect/socket/_web_socket.py +555 -0
- pyfect/socket/datagram/__init__.py +110 -0
- pyfect/socket/datagram/_core.py +460 -0
- pyfect/socket/datagram/_errors.py +102 -0
- pyfect/socket/datagram/_live.py +675 -0
- pyfect/socket/datagram/_model.py +217 -0
- pyfect/socket/server.py +258 -0
- pyfect/socket/ws.py +484 -0
- pyfect/sql/__init__.py +60 -0
- pyfect/sql/_loop.py +134 -0
- pyfect/sql/clickhouse/__init__.py +74 -0
- pyfect/sql/clickhouse/_connection.py +237 -0
- pyfect/sql/clickhouse/_rows.py +87 -0
- pyfect/sql/clickhouse/client.py +322 -0
- pyfect/sql/clickhouse/config.py +241 -0
- pyfect/sql/clickhouse/driver.py +434 -0
- pyfect/sql/clickhouse/error.py +106 -0
- pyfect/sql/clickhouse/migrator.py +47 -0
- pyfect/sql/clickhouse/params.py +168 -0
- pyfect/sql/client/__init__.py +35 -0
- pyfect/sql/client/_client.py +309 -0
- pyfect/sql/client/_sqlite_acquirers.py +130 -0
- pyfect/sql/client/_transaction.py +410 -0
- pyfect/sql/connection.py +87 -0
- pyfect/sql/d1/__init__.py +48 -0
- pyfect/sql/d1/api.py +270 -0
- pyfect/sql/d1/client.py +344 -0
- pyfect/sql/d1/config.py +55 -0
- pyfect/sql/error.py +311 -0
- pyfect/sql/libsql/__init__.py +58 -0
- pyfect/sql/libsql/_connection.py +110 -0
- pyfect/sql/libsql/_hrana.py +258 -0
- pyfect/sql/libsql/_local.py +251 -0
- pyfect/sql/libsql/_targets.py +28 -0
- pyfect/sql/libsql/_values.py +46 -0
- pyfect/sql/libsql/client.py +225 -0
- pyfect/sql/libsql/config.py +170 -0
- pyfect/sql/libsql/migrator.py +43 -0
- pyfect/sql/libsql/sdk.py +123 -0
- pyfect/sql/migrator.py +434 -0
- pyfect/sql/model.py +324 -0
- pyfect/sql/mssql/__init__.py +79 -0
- pyfect/sql/mssql/_connection.py +169 -0
- pyfect/sql/mssql/_driver.py +103 -0
- pyfect/sql/mssql/_names.py +9 -0
- pyfect/sql/mssql/_pool.py +125 -0
- pyfect/sql/mssql/_tds.py +447 -0
- pyfect/sql/mssql/_tds_params.py +155 -0
- pyfect/sql/mssql/_worker.py +155 -0
- pyfect/sql/mssql/client.py +344 -0
- pyfect/sql/mssql/config.py +118 -0
- pyfect/sql/mssql/error.py +111 -0
- pyfect/sql/mssql/migrator.py +43 -0
- pyfect/sql/mssql/parameter.py +44 -0
- pyfect/sql/mssql/procedure.py +119 -0
- pyfect/sql/mssql/types.py +137 -0
- pyfect/sql/mysql/__init__.py +60 -0
- pyfect/sql/mysql/_asyncmy.py +168 -0
- pyfect/sql/mysql/_bind.py +76 -0
- pyfect/sql/mysql/_connection.py +156 -0
- pyfect/sql/mysql/_pool.py +63 -0
- pyfect/sql/mysql/_session.py +262 -0
- pyfect/sql/mysql/client.py +244 -0
- pyfect/sql/mysql/config.py +108 -0
- pyfect/sql/mysql/error.py +104 -0
- pyfect/sql/mysql/migrator.py +44 -0
- pyfect/sql/pg/__init__.py +82 -0
- pyfect/sql/pg/_resolve.py +284 -0
- pyfect/sql/pg/_session.py +380 -0
- pyfect/sql/pg/client.py +398 -0
- pyfect/sql/pg/config.py +142 -0
- pyfect/sql/pg/connection.py +585 -0
- pyfect/sql/pg/error.py +131 -0
- pyfect/sql/pg/migrator.py +139 -0
- pyfect/sql/pg/pool.py +265 -0
- pyfect/sql/resolver.py +294 -0
- pyfect/sql/result.py +24 -0
- pyfect/sql/schema.py +116 -0
- pyfect/sql/sqlite/__init__.py +26 -0
- pyfect/sql/sqlite/_connection.py +230 -0
- pyfect/sql/sqlite/_worker.py +71 -0
- pyfect/sql/sqlite/client.py +248 -0
- pyfect/sql/sqlite/migrator.py +43 -0
- pyfect/sql/statement/__init__.py +144 -0
- pyfect/sql/statement/_compiler.py +384 -0
- pyfect/sql/statement/_segments.py +487 -0
- pyfect/sql/statement/_statement.py +526 -0
- pyfect/sql/statement/_transforms.py +93 -0
- pyfect/sql/stream.py +88 -0
- pyfect/stdio.py +206 -0
- pyfect/stream/__init__.py +151 -0
- pyfect/stream/_async_iterable.py +129 -0
- pyfect/stream/_base.py +69 -0
- pyfect/stream/_bridge_methods.py +139 -0
- pyfect/stream/_catch.py +135 -0
- pyfect/stream/_catch_reason.py +558 -0
- pyfect/stream/_channel_methods.py +61 -0
- pyfect/stream/_chunk_methods.py +169 -0
- pyfect/stream/_chunking.py +310 -0
- pyfect/stream/_concat.py +65 -0
- pyfect/stream/_constructors.py +341 -0
- pyfect/stream/_context.py +147 -0
- pyfect/stream/_error_methods.py +254 -0
- pyfect/stream/_execution_plan.py +199 -0
- pyfect/stream/_fanout.py +154 -0
- pyfect/stream/_fanout_methods.py +183 -0
- pyfect/stream/_filter_methods.py +203 -0
- pyfect/stream/_grouping.py +168 -0
- pyfect/stream/_many.py +65 -0
- pyfect/stream/_map_methods.py +233 -0
- pyfect/stream/_mapping.py +126 -0
- pyfect/stream/_run.py +114 -0
- pyfect/stream/_run_methods.py +114 -0
- pyfect/stream/_service_methods.py +36 -0
- pyfect/stream/_sink_methods.py +151 -0
- pyfect/stream/_sinking.py +277 -0
- pyfect/stream/_sources.py +124 -0
- pyfect/stream/_stream.py +162 -0
- pyfect/stream/_taking.py +330 -0
- pyfect/stream/_text.py +43 -0
- pyfect/stream/_timing.py +294 -0
- pyfect/stream/_timing_methods.py +252 -0
- pyfect/stream/_zip_methods.py +279 -0
- pyfect/stream/_zipping.py +213 -0
- pyfect/subscription_ref.py +66 -0
- pyfect/synchronized_ref.py +38 -0
- pyfect/take.py +36 -0
- pyfect/terminal/__init__.py +41 -0
- pyfect/terminal/_keys.py +113 -0
- pyfect/terminal/_live.py +223 -0
- pyfect/terminal/_service.py +73 -0
- pyfect/testing/__init__.py +122 -0
- pyfect/testing/_assertions.py +92 -0
- pyfect/testing/_clock.py +309 -0
- pyfect/testing/_console.py +164 -0
- pyfect/testing/_crypto.py +44 -0
- pyfect/testing/_decorators.py +211 -0
- pyfect/testing/_flaky.py +34 -0
- pyfect/testing/_layers.py +153 -0
- pyfect/testing/_prop.py +255 -0
- pyfect/testing/_runners.py +84 -0
- pyfect/testing/_runtime.py +174 -0
- pyfect/testing/_schema.py +238 -0
- pyfect/testing/plugin.py +59 -0
- pyfect/tracer/__init__.py +107 -0
- pyfect/tracer/_model.py +176 -0
- pyfect/tracer/_native.py +172 -0
- pyfect/tracer/_refs.py +87 -0
- pyfect/tracer/_spans.py +269 -0
- pyfect/transaction.py +340 -0
- pyfect/trie.py +317 -0
- pyfect/tx_chunk.py +127 -0
- pyfect/tx_deferred.py +75 -0
- pyfect/tx_hash_map.py +265 -0
- pyfect/tx_hash_set.py +151 -0
- pyfect/tx_priority_queue.py +224 -0
- pyfect/tx_pubsub.py +194 -0
- pyfect/tx_queue/__init__.py +27 -0
- pyfect/tx_queue/_queue.py +359 -0
- pyfect/tx_queue/_views.py +60 -0
- pyfect/tx_reentrant_lock.py +201 -0
- pyfect/tx_ref.py +79 -0
- pyfect/tx_semaphore.py +136 -0
- pyfect/tx_subscription_ref.py +118 -0
- pyfect/workers/__init__.py +62 -0
- pyfect/workers/_frames.py +82 -0
- pyfect/workers/_mailbox.py +51 -0
- pyfect/workers/_port.py +106 -0
- pyfect/workers/_process.py +155 -0
- pyfect/workers/_thread.py +100 -0
- pyfect/workers/python_worker.py +133 -0
- pyfect/workers/python_worker_runner.py +112 -0
- pyfect/workers/transferable.py +103 -0
- pyfect/workers/worker.py +273 -0
- pyfect/workers/worker_error.py +86 -0
- pyfect/workers/worker_runner.py +85 -0
- pyfect/workflow/__init__.py +78 -0
- pyfect/workflow/_crypto.py +23 -0
- pyfect/workflow/_encoded.py +381 -0
- pyfect/workflow/_memory.py +424 -0
- pyfect/workflow/_result.py +43 -0
- pyfect/workflow/_services.py +241 -0
- pyfect/workflow/_suspension.py +153 -0
- pyfect/workflow/activity.py +427 -0
- pyfect/workflow/durable_clock.py +72 -0
- pyfect/workflow/durable_deferred.py +403 -0
- pyfect/workflow/durable_queue.py +259 -0
- pyfect/workflow/workflow.py +509 -0
- pyfect/workflow/workflow_engine.py +42 -0
- pyfect/workflow/workflow_proxy.py +161 -0
- pyfect/workflow/workflow_proxy_server.py +146 -0
- pyfect_dev-0.1.0.dist-info/METADATA +296 -0
- pyfect_dev-0.1.0.dist-info/RECORD +1265 -0
- pyfect_dev-0.1.0.dist-info/WHEEL +4 -0
- pyfect_dev-0.1.0.dist-info/entry_points.txt +3 -0
- pyfect_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"""Zip archive bytes to entries, pulled, from the local headers alone."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import struct
|
|
6
|
+
import zlib
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from datetime import timedelta
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from pyfect._js import js_number
|
|
12
|
+
from pyfect._json_text import quote as quoted
|
|
13
|
+
from pyfect.encoding._archive import NEED_INPUT, ByteQueue, NeedInput
|
|
14
|
+
from pyfect.encoding.zip._format import (
|
|
15
|
+
CENTRAL,
|
|
16
|
+
DESCRIPTOR,
|
|
17
|
+
END,
|
|
18
|
+
END64,
|
|
19
|
+
EPOCH,
|
|
20
|
+
EXTRA_TIMESTAMP,
|
|
21
|
+
EXTRA_UNICODE_PATH,
|
|
22
|
+
EXTRA_ZIP64,
|
|
23
|
+
FLAG_DESCRIPTOR,
|
|
24
|
+
FLAG_ENCRYPTED,
|
|
25
|
+
FLAG_STRONG_ENCRYPTION,
|
|
26
|
+
FLAG_UTF8,
|
|
27
|
+
LOCAL,
|
|
28
|
+
MAX32,
|
|
29
|
+
MAX_SAFE_INTEGER,
|
|
30
|
+
decode_name,
|
|
31
|
+
from_dos,
|
|
32
|
+
invalid_header,
|
|
33
|
+
size_mismatch,
|
|
34
|
+
)
|
|
35
|
+
from pyfect.encoding.zip._inflate import InflateError, Inflater
|
|
36
|
+
from pyfect.encoding.zip._model import Header, ZipError
|
|
37
|
+
|
|
38
|
+
_DESCRIPTOR_PEEK = 28
|
|
39
|
+
"""A ZIP64 data descriptor with its signature, and the signature after it."""
|
|
40
|
+
|
|
41
|
+
_LOCAL_SIZE = 30
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _hex(n: int) -> str:
|
|
45
|
+
return f"0x{n:08x}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _u16(data: bytes, at: int) -> int:
|
|
49
|
+
return int.from_bytes(data[at : at + 2], "little")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _u32(data: bytes, at: int) -> int:
|
|
53
|
+
return int.from_bytes(data[at : at + 4], "little")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _u64(data: bytes, at: int) -> int:
|
|
57
|
+
value = struct.unpack_from("<Q", data, at)[0]
|
|
58
|
+
if value > MAX_SAFE_INTEGER:
|
|
59
|
+
raise invalid_header("a 64-bit field exceeds 2^53 - 1")
|
|
60
|
+
return value
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _is_header_signature(signature: int) -> bool:
|
|
64
|
+
return signature in (LOCAL, CENTRAL, END, END64)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _truncated(at: int) -> ZipError:
|
|
68
|
+
return ZipError("Truncated", f"unexpected end of zip archive at byte {at}")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(slots=True)
|
|
72
|
+
class _Current:
|
|
73
|
+
header: Header
|
|
74
|
+
inflater: Inflater | None
|
|
75
|
+
descriptor: bool
|
|
76
|
+
zip64: bool
|
|
77
|
+
# The values of the local header; sizes are unknown when they follow the data
|
|
78
|
+
crc: int
|
|
79
|
+
compressed_size: int | None
|
|
80
|
+
size: int | None
|
|
81
|
+
# Queue position where the data starts
|
|
82
|
+
start: int
|
|
83
|
+
remaining: int
|
|
84
|
+
data_done: bool = False
|
|
85
|
+
actual_crc: int = 0
|
|
86
|
+
actual_size: int = 0
|
|
87
|
+
actual_compressed_size: int = 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Decoder:
|
|
91
|
+
"""``next`` skips the rest of the current body; ``read`` consumes it. Both keep
|
|
92
|
+
their progress across ``NEED_INPUT``."""
|
|
93
|
+
|
|
94
|
+
def __init__(self) -> None:
|
|
95
|
+
self._queue = ByteQueue()
|
|
96
|
+
self._ended = False
|
|
97
|
+
self._finished = False
|
|
98
|
+
self._closed = False
|
|
99
|
+
# Incremented whenever the current body is invalidated
|
|
100
|
+
self.generation = 0
|
|
101
|
+
self._current: _Current | None = None
|
|
102
|
+
# The error that ended the archive, repeated by every later call
|
|
103
|
+
self._failure: ZipError | None = None
|
|
104
|
+
|
|
105
|
+
def push(self, chunk: bytes) -> None:
|
|
106
|
+
if not self._finished:
|
|
107
|
+
self._queue.push(chunk)
|
|
108
|
+
|
|
109
|
+
def end(self) -> None:
|
|
110
|
+
self._ended = True
|
|
111
|
+
|
|
112
|
+
def close(self) -> None:
|
|
113
|
+
self._closed = True
|
|
114
|
+
self.generation += 1
|
|
115
|
+
self._queue.clear()
|
|
116
|
+
|
|
117
|
+
def next(self) -> Header | NeedInput | None:
|
|
118
|
+
"""The next entry header, or ``None`` once the entries and the input have
|
|
119
|
+
ended. Input after the entries is discarded."""
|
|
120
|
+
self.generation += 1
|
|
121
|
+
return self._guard(self._advance)
|
|
122
|
+
|
|
123
|
+
def _guard(self, run: Any) -> Any:
|
|
124
|
+
if self._failure is not None:
|
|
125
|
+
raise self._failure
|
|
126
|
+
try:
|
|
127
|
+
return run()
|
|
128
|
+
except ZipError as error:
|
|
129
|
+
self._failure = error
|
|
130
|
+
raise
|
|
131
|
+
|
|
132
|
+
def _advance(self) -> Header | NeedInput | None: # noqa: PLR0911
|
|
133
|
+
while self._current is not None:
|
|
134
|
+
if self._read_current() is NEED_INPUT:
|
|
135
|
+
return NEED_INPUT
|
|
136
|
+
if self._finished:
|
|
137
|
+
return self._finish()
|
|
138
|
+
queue = self._queue
|
|
139
|
+
if queue.length < 4:
|
|
140
|
+
return self._need_input()
|
|
141
|
+
at = queue.consumed
|
|
142
|
+
head = queue.peek(min(queue.length, _LOCAL_SIZE))
|
|
143
|
+
signature = _u32(head, 0)
|
|
144
|
+
if signature in (CENTRAL, END, END64):
|
|
145
|
+
return self._finish()
|
|
146
|
+
if signature != LOCAL:
|
|
147
|
+
raise invalid_header(f"unexpected signature {_hex(signature)} at byte {at}")
|
|
148
|
+
if len(head) < _LOCAL_SIZE:
|
|
149
|
+
return self._need_input()
|
|
150
|
+
length = _LOCAL_SIZE + _u16(head, 26) + _u16(head, 28)
|
|
151
|
+
if queue.length < length:
|
|
152
|
+
return self._need_input()
|
|
153
|
+
try:
|
|
154
|
+
current = self._parse_local(queue.take(length))
|
|
155
|
+
except ZipError as error:
|
|
156
|
+
raise ZipError(error.kind, f"{error.message} (header at byte {at})") from None
|
|
157
|
+
self._current = current
|
|
158
|
+
return current.header
|
|
159
|
+
|
|
160
|
+
def read(self, generation: int) -> tuple[bytes, ...] | NeedInput | None:
|
|
161
|
+
"""The buffered body bytes of the entry from ``generation``, or ``None``
|
|
162
|
+
once the whole body has been read and checked."""
|
|
163
|
+
if generation != self.generation:
|
|
164
|
+
raise ZipError(
|
|
165
|
+
"BodyConsumed",
|
|
166
|
+
"zip entry body read after the archive stream ended"
|
|
167
|
+
if self._closed
|
|
168
|
+
else "zip entry body read after moving to the next entry",
|
|
169
|
+
)
|
|
170
|
+
return self._guard(self._read_current)
|
|
171
|
+
|
|
172
|
+
def _read_current(self) -> tuple[bytes, ...] | NeedInput | None: # noqa: PLR0911, PLR0912
|
|
173
|
+
current = self._current
|
|
174
|
+
if current is None:
|
|
175
|
+
return None
|
|
176
|
+
queue = self._queue
|
|
177
|
+
if not current.data_done:
|
|
178
|
+
if current.inflater is None:
|
|
179
|
+
if current.remaining > 0:
|
|
180
|
+
if queue.length == 0:
|
|
181
|
+
return self._need_input()
|
|
182
|
+
chunks: list[bytes] = []
|
|
183
|
+
while current.remaining > 0 and queue.length > 0:
|
|
184
|
+
chunk = queue.take_some(current.remaining)
|
|
185
|
+
current.remaining -= len(chunk)
|
|
186
|
+
current.actual_crc = zlib.crc32(chunk, current.actual_crc)
|
|
187
|
+
current.actual_size += len(chunk)
|
|
188
|
+
chunks.append(chunk)
|
|
189
|
+
current.actual_compressed_size = current.actual_size
|
|
190
|
+
return tuple(chunks)
|
|
191
|
+
else:
|
|
192
|
+
try:
|
|
193
|
+
inflated = current.inflater.pull()
|
|
194
|
+
except InflateError as error:
|
|
195
|
+
raise ZipError("InvalidData", str(error)) from None
|
|
196
|
+
if inflated is NEED_INPUT:
|
|
197
|
+
return self._need_input()
|
|
198
|
+
if inflated is not None:
|
|
199
|
+
current.actual_size += len(inflated)
|
|
200
|
+
# Fail before handing out bytes past the reported size, so that
|
|
201
|
+
# consumers can limit what a small compressed entry inflates to
|
|
202
|
+
size = current.header.size
|
|
203
|
+
if size is not None and current.actual_size > size:
|
|
204
|
+
raise size_mismatch(current.header.path, current.actual_size, size)
|
|
205
|
+
current.actual_crc = zlib.crc32(inflated, current.actual_crc)
|
|
206
|
+
return (inflated,)
|
|
207
|
+
current.actual_compressed_size = queue.consumed - current.start
|
|
208
|
+
current.data_done = True
|
|
209
|
+
if current.descriptor:
|
|
210
|
+
descriptor = self._descriptor(current)
|
|
211
|
+
if descriptor is NEED_INPUT:
|
|
212
|
+
return NEED_INPUT
|
|
213
|
+
self._check(current, *descriptor)
|
|
214
|
+
else:
|
|
215
|
+
assert current.compressed_size is not None
|
|
216
|
+
assert current.size is not None
|
|
217
|
+
self._check(current, current.crc, current.compressed_size, current.size)
|
|
218
|
+
self._current = None
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
def _check(self, current: _Current, crc: int, compressed_size: int, size: int) -> None:
|
|
222
|
+
if (
|
|
223
|
+
current.actual_crc == crc
|
|
224
|
+
and current.actual_compressed_size == compressed_size
|
|
225
|
+
and current.actual_size == size
|
|
226
|
+
):
|
|
227
|
+
return
|
|
228
|
+
path = quoted(current.header.path)
|
|
229
|
+
if current.actual_crc != crc:
|
|
230
|
+
raise ZipError(
|
|
231
|
+
"Checksum",
|
|
232
|
+
f"CRC-32 of {path} is {_hex(current.actual_crc)}, "
|
|
233
|
+
f"but the archive records {_hex(crc)}",
|
|
234
|
+
)
|
|
235
|
+
raise ZipError(
|
|
236
|
+
"SizeMismatch",
|
|
237
|
+
f"{path} has {current.actual_size} bytes "
|
|
238
|
+
f"({current.actual_compressed_size} compressed), "
|
|
239
|
+
f"but the archive records {js_number(float(size))} "
|
|
240
|
+
f"({js_number(float(compressed_size))} compressed)",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def _descriptor(self, current: _Current) -> tuple[int, int, int] | NeedInput:
|
|
244
|
+
"""Reads a data descriptor, whose signature is optional and whose sizes are
|
|
245
|
+
4 bytes unless the local header has a ZIP64 field. Writers that widen the
|
|
246
|
+
sizes only on overflow are handled by matching each layout against the data
|
|
247
|
+
read; among several matches, one followed by a header signature wins."""
|
|
248
|
+
queue = self._queue
|
|
249
|
+
if queue.length < _DESCRIPTOR_PEEK and not self._ended:
|
|
250
|
+
return NEED_INPUT
|
|
251
|
+
data = queue.peek(min(queue.length, _DESCRIPTOR_PEEK))
|
|
252
|
+
candidates: list[tuple[int, int, int, int]] = []
|
|
253
|
+
offsets = (4, 0) if len(data) >= 4 and _u32(data, 0) == DESCRIPTOR else (0,)
|
|
254
|
+
for offset in offsets:
|
|
255
|
+
for wide in (True, False) if current.zip64 else (False, True):
|
|
256
|
+
length = offset + (20 if wide else 12)
|
|
257
|
+
if length > len(data):
|
|
258
|
+
continue
|
|
259
|
+
crc = _u32(data, offset)
|
|
260
|
+
if wide:
|
|
261
|
+
compressed, size = struct.unpack_from("<QQ", data, offset + 4)
|
|
262
|
+
else:
|
|
263
|
+
compressed, size = struct.unpack_from("<II", data, offset + 4)
|
|
264
|
+
candidates.append((length, crc, compressed, size))
|
|
265
|
+
actual = (current.actual_crc, current.actual_compressed_size, current.actual_size)
|
|
266
|
+
matches = [candidate for candidate in candidates if candidate[1:] == actual]
|
|
267
|
+
match = next(
|
|
268
|
+
(
|
|
269
|
+
candidate
|
|
270
|
+
for candidate in matches
|
|
271
|
+
if candidate[0] + 4 <= len(data) and _is_header_signature(_u32(data, candidate[0]))
|
|
272
|
+
),
|
|
273
|
+
matches[0] if matches else None,
|
|
274
|
+
)
|
|
275
|
+
if match is not None:
|
|
276
|
+
queue.skip(match[0])
|
|
277
|
+
return match[1:]
|
|
278
|
+
# A complete archive has at least an end record after the descriptor
|
|
279
|
+
if not candidates or (self._ended and len(data) < _DESCRIPTOR_PEEK):
|
|
280
|
+
raise _truncated(queue.consumed + queue.length)
|
|
281
|
+
queue.skip(candidates[0][0])
|
|
282
|
+
return candidates[0][1:]
|
|
283
|
+
|
|
284
|
+
def _parse_local(self, data: bytes) -> _Current: # noqa: PLR0912, PLR0915
|
|
285
|
+
flags = _u16(data, 6)
|
|
286
|
+
method = _u16(data, 8)
|
|
287
|
+
time = _u16(data, 10)
|
|
288
|
+
date = _u16(data, 12)
|
|
289
|
+
crc = _u32(data, 14)
|
|
290
|
+
compressed_size = _u32(data, 18)
|
|
291
|
+
size = _u32(data, 22)
|
|
292
|
+
name_length = _u16(data, 26)
|
|
293
|
+
raw_name = data[_LOCAL_SIZE : _LOCAL_SIZE + name_length]
|
|
294
|
+
utf8 = flags & FLAG_UTF8 != 0
|
|
295
|
+
path = decode_name(raw_name, utf8)
|
|
296
|
+
# As in Info-ZIP and 7-Zip, only the first Unicode path field is read, and
|
|
297
|
+
# only without the UTF-8 flag
|
|
298
|
+
read_unicode_path = not utf8
|
|
299
|
+
seconds: int | None = None
|
|
300
|
+
zip64 = False
|
|
301
|
+
at = _LOCAL_SIZE + name_length
|
|
302
|
+
while at + 4 <= len(data):
|
|
303
|
+
field_id = _u16(data, at)
|
|
304
|
+
start = at + 4
|
|
305
|
+
end = start + _u16(data, at + 2)
|
|
306
|
+
if end > len(data):
|
|
307
|
+
raise invalid_header("extra field overflows the header")
|
|
308
|
+
if field_id == EXTRA_ZIP64:
|
|
309
|
+
zip64 = True
|
|
310
|
+
field = start
|
|
311
|
+
if size == MAX32 and field + 8 <= end:
|
|
312
|
+
size = _u64(data, field)
|
|
313
|
+
field += 8
|
|
314
|
+
if compressed_size == MAX32 and field + 8 <= end:
|
|
315
|
+
compressed_size = _u64(data, field)
|
|
316
|
+
elif field_id == EXTRA_TIMESTAMP and end - start >= 5 and data[start] & 1 != 0:
|
|
317
|
+
# Info-ZIP writes times before 1970 as negative numbers, and others
|
|
318
|
+
# write times after 2038 as unsigned ones; the DOS date tells them apart
|
|
319
|
+
value = _u32(data, start + 1)
|
|
320
|
+
seconds = (
|
|
321
|
+
value - 0x100000000
|
|
322
|
+
if value >= 0x80000000 and (date >> 9) + 1980 < 2038
|
|
323
|
+
else value
|
|
324
|
+
)
|
|
325
|
+
elif field_id == EXTRA_UNICODE_PATH and read_unicode_path:
|
|
326
|
+
read_unicode_path = False
|
|
327
|
+
if (
|
|
328
|
+
end - start >= 5
|
|
329
|
+
and data[start] == 1
|
|
330
|
+
and _u32(data, start + 1) == zlib.crc32(raw_name)
|
|
331
|
+
):
|
|
332
|
+
path = data[start + 5 : end].decode("utf-8", "replace")
|
|
333
|
+
at = end
|
|
334
|
+
name = quoted(path)
|
|
335
|
+
# Other tools end the name at a NUL character, or reject it
|
|
336
|
+
if "\0" in path:
|
|
337
|
+
raise invalid_header(f"path {name} contains a NUL character")
|
|
338
|
+
if flags & (FLAG_ENCRYPTED | FLAG_STRONG_ENCRYPTION) != 0:
|
|
339
|
+
raise ZipError("Unsupported", f"entry {name} is encrypted")
|
|
340
|
+
if method not in (0, 8):
|
|
341
|
+
raise ZipError("Unsupported", f"entry {name} uses compression method {method}")
|
|
342
|
+
descriptor = flags & FLAG_DESCRIPTOR != 0
|
|
343
|
+
# Writers that put sizes after the data usually leave zeros here
|
|
344
|
+
known = not descriptor or compressed_size != 0 or size != 0
|
|
345
|
+
# Searching for the descriptor of stored data would let its content forge
|
|
346
|
+
# the entries after it
|
|
347
|
+
if method == 0 and not known:
|
|
348
|
+
raise ZipError("Unsupported", f"stored entry {name} has no size before its data")
|
|
349
|
+
if method == 0 and compressed_size != size:
|
|
350
|
+
raise invalid_header(f"stored entry {name} has different sizes")
|
|
351
|
+
header = Header(
|
|
352
|
+
path=path,
|
|
353
|
+
type="Directory" if path.endswith("/") else "File",
|
|
354
|
+
method="Deflate" if method == 8 else "Store",
|
|
355
|
+
mtime=from_dos(time, date) if seconds is None else EPOCH + timedelta(seconds=seconds),
|
|
356
|
+
size=size if known and (method == 0 or not descriptor) else None,
|
|
357
|
+
)
|
|
358
|
+
return _Current(
|
|
359
|
+
header=header,
|
|
360
|
+
inflater=Inflater(self._queue) if method == 8 else None,
|
|
361
|
+
descriptor=descriptor,
|
|
362
|
+
zip64=zip64,
|
|
363
|
+
crc=crc,
|
|
364
|
+
compressed_size=compressed_size if known else None,
|
|
365
|
+
size=size if known else None,
|
|
366
|
+
start=self._queue.consumed,
|
|
367
|
+
remaining=compressed_size if method == 0 else 0,
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
def _need_input(self) -> NeedInput:
|
|
371
|
+
if self._ended:
|
|
372
|
+
raise _truncated(self._queue.consumed + self._queue.length)
|
|
373
|
+
return NEED_INPUT
|
|
374
|
+
|
|
375
|
+
def _finish(self) -> NeedInput | None:
|
|
376
|
+
self._finished = True
|
|
377
|
+
self._queue.clear()
|
|
378
|
+
return None if self._ended else NEED_INPUT
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Zip's records, times, names and validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import UTC, datetime, timedelta
|
|
6
|
+
from typing import Final
|
|
7
|
+
|
|
8
|
+
from pyfect._json_text import quote as quoted
|
|
9
|
+
from pyfect.encoding.zip._model import Header, ZipError
|
|
10
|
+
|
|
11
|
+
LOCAL: Final = 0x04034B50
|
|
12
|
+
CENTRAL: Final = 0x02014B50
|
|
13
|
+
DESCRIPTOR: Final = 0x08074B50
|
|
14
|
+
END: Final = 0x06054B50
|
|
15
|
+
END64: Final = 0x06064B50
|
|
16
|
+
LOCATOR64: Final = 0x07064B50
|
|
17
|
+
|
|
18
|
+
FLAG_ENCRYPTED: Final = 0x0001
|
|
19
|
+
FLAG_DESCRIPTOR: Final = 0x0008
|
|
20
|
+
FLAG_STRONG_ENCRYPTION: Final = 0x0040
|
|
21
|
+
FLAG_UTF8: Final = 0x0800
|
|
22
|
+
|
|
23
|
+
EXTRA_ZIP64: Final = 0x0001
|
|
24
|
+
EXTRA_TIMESTAMP: Final = 0x5455
|
|
25
|
+
EXTRA_UNICODE_PATH: Final = 0x7075
|
|
26
|
+
|
|
27
|
+
MAX16: Final = 0xFFFF
|
|
28
|
+
MAX32: Final = 0xFFFFFFFF
|
|
29
|
+
MAX_SAFE_INTEGER: Final = 2**53 - 1
|
|
30
|
+
|
|
31
|
+
EPOCH: Final = datetime(1970, 1, 1, tzinfo=UTC)
|
|
32
|
+
MIN_MTIME: Final = datetime(1980, 1, 1, tzinfo=UTC)
|
|
33
|
+
|
|
34
|
+
_PAST_DOS_TIMES = datetime(2108, 1, 1, tzinfo=UTC)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def invalid_header(message: str) -> ZipError:
|
|
38
|
+
return ZipError("InvalidHeader", message)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def size_mismatch(path: str, actual: int, expected: int) -> ZipError:
|
|
42
|
+
return ZipError(
|
|
43
|
+
"SizeMismatch",
|
|
44
|
+
f"body of {quoted(path)} has {'at least' if actual > expected else 'only'} {actual} "
|
|
45
|
+
f"bytes, but its size is {expected}",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def to_dos(mtime: datetime) -> tuple[int, int]:
|
|
50
|
+
"""``(time, date)``: DOS times have two-second resolution and cover 1980 to 2107."""
|
|
51
|
+
if mtime < MIN_MTIME:
|
|
52
|
+
return 0, (1 << 5) | 1
|
|
53
|
+
if mtime >= _PAST_DOS_TIMES:
|
|
54
|
+
return (23 << 11) | (59 << 5) | 29, (127 << 9) | (12 << 5) | 31
|
|
55
|
+
utc = mtime.astimezone(UTC)
|
|
56
|
+
return (
|
|
57
|
+
(utc.hour << 11) | (utc.minute << 5) | (utc.second >> 1),
|
|
58
|
+
((utc.year - 1980) << 9) | (utc.month << 5) | utc.day,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def from_dos(time: int, date: int) -> datetime:
|
|
63
|
+
"""``Date.UTC`` of DOS fields, whose out-of-range values carry over: month 0 is
|
|
64
|
+
the previous December, and day 0 the last day of the previous month."""
|
|
65
|
+
month = ((date >> 5) & 0xF) - 1
|
|
66
|
+
first = datetime((date >> 9) + 1980 + month // 12, month % 12 + 1, 1, tzinfo=UTC)
|
|
67
|
+
return first + timedelta(
|
|
68
|
+
days=(date & 0x1F) - 1,
|
|
69
|
+
hours=time >> 11,
|
|
70
|
+
minutes=(time >> 5) & 0x3F,
|
|
71
|
+
seconds=(time & 0x1F) * 2,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def decode_name(data: bytes, utf8: bool) -> str:
|
|
76
|
+
"""Paths keep a leading byte order mark, as other zip tools do."""
|
|
77
|
+
if utf8:
|
|
78
|
+
return data.decode("utf-8", "replace")
|
|
79
|
+
try:
|
|
80
|
+
return data.decode()
|
|
81
|
+
except UnicodeDecodeError:
|
|
82
|
+
return data.decode("cp437")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
_DRIVE_LETTERS = frozenset("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def validate(header: Header) -> None: # noqa: PLR0912
|
|
89
|
+
path = header.path
|
|
90
|
+
if path == "":
|
|
91
|
+
raise invalid_header("path is empty")
|
|
92
|
+
name = quoted(path)
|
|
93
|
+
if path.startswith("/"):
|
|
94
|
+
raise invalid_header(f"path {name} is absolute")
|
|
95
|
+
# The zip format forbids these, and libarchive reads them as separators and roots
|
|
96
|
+
if "\\" in path:
|
|
97
|
+
raise invalid_header(f"path {name} contains a backslash")
|
|
98
|
+
if path[:1] in _DRIVE_LETTERS and path[1:2] == ":":
|
|
99
|
+
raise invalid_header(f"path {name} starts with a drive letter")
|
|
100
|
+
if "\0" in path:
|
|
101
|
+
raise invalid_header(f"path {name} contains a NUL character")
|
|
102
|
+
if header.type == "File":
|
|
103
|
+
if path.endswith("/"):
|
|
104
|
+
raise invalid_header(f'file path {name} ends with "/"')
|
|
105
|
+
if header.method not in ("Store", "Deflate"):
|
|
106
|
+
raise invalid_header(f"unknown method for {name}")
|
|
107
|
+
elif header.type == "Directory":
|
|
108
|
+
if not path.endswith("/"):
|
|
109
|
+
raise invalid_header(f'directory path {name} does not end with "/"')
|
|
110
|
+
if header.size not in (None, 0):
|
|
111
|
+
raise invalid_header(f"directory {name} has a size")
|
|
112
|
+
else:
|
|
113
|
+
raise invalid_header(f"unknown entry type for {name}")
|
|
114
|
+
size = header.size
|
|
115
|
+
if size is not None and not (isinstance(size, int) and 0 <= size <= MAX_SAFE_INTEGER): # pyright: ignore[reportUnnecessaryIsInstance]
|
|
116
|
+
raise invalid_header(f"size of {name} is not a non-negative safe integer")
|
|
117
|
+
mode = header.mode
|
|
118
|
+
if mode is not None and not (isinstance(mode, int) and 0 <= mode <= 0o7777): # pyright: ignore[reportUnnecessaryIsInstance]
|
|
119
|
+
raise invalid_header(f"mode of {name} is not a permission mode")
|
|
120
|
+
# A naive ``datetime`` names no instant: JavaScript's invalid date
|
|
121
|
+
if header.mtime.utcoffset() is None:
|
|
122
|
+
raise invalid_header(f"mtime of {name} is not a valid date")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""One raw deflate stream read from a ``ByteQueue`` on ``zlib``, which hands back
|
|
2
|
+
the input past the stream's end (Effect writes its own inflater for this; the
|
|
3
|
+
standard library's is incremental, bounded per call and much faster than one in
|
|
4
|
+
Python). Its corrupt-data checks are the ones Effect's inflater copies."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import zlib
|
|
9
|
+
|
|
10
|
+
from pyfect.encoding._archive import NEED_INPUT, ByteQueue, NeedInput
|
|
11
|
+
|
|
12
|
+
_OUTPUT_SIZE = 64 * 1024
|
|
13
|
+
_INPUT_SIZE = 64 * 1024
|
|
14
|
+
"""``zlib`` copies what it leaves unused on each call, so a whole archive held in one
|
|
15
|
+
chunk would cost a copy of what is left of it per 64 KiB of output and per entry."""
|
|
16
|
+
|
|
17
|
+
_MESSAGES = {
|
|
18
|
+
"invalid stored block lengths": "stored block length is corrupt",
|
|
19
|
+
"too many length or distance symbols": "too many length or distance codes",
|
|
20
|
+
"invalid code -- missing end-of-block": "missing end-of-block code",
|
|
21
|
+
"invalid literal/length code": "invalid literal or length code",
|
|
22
|
+
"invalid distance too far back": "distance is beyond the start of the data",
|
|
23
|
+
}
|
|
24
|
+
"""zlib's messages where Effect's say the same in other words. Effect tells apart
|
|
25
|
+
what zlib reports as one ("invalid code lengths set", "invalid bit length repeat"),
|
|
26
|
+
and those keep zlib's text."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class InflateError(Exception):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Inflater:
|
|
34
|
+
__slots__ = ("_decompressor", "_more", "_queue", "_tail")
|
|
35
|
+
|
|
36
|
+
def __init__(self, queue: ByteQueue) -> None:
|
|
37
|
+
self._queue = queue
|
|
38
|
+
self._decompressor = zlib.decompressobj(-15)
|
|
39
|
+
self._tail = b""
|
|
40
|
+
# Whether the last call stopped at the output limit, so zlib may hold more
|
|
41
|
+
self._more = False
|
|
42
|
+
|
|
43
|
+
def pull(self) -> bytes | NeedInput | None:
|
|
44
|
+
"""Up to 64 KiB of output, ``NEED_INPUT``, or ``None`` once the stream has
|
|
45
|
+
ended, when the input past it is back on the queue."""
|
|
46
|
+
decompressor = self._decompressor
|
|
47
|
+
while True:
|
|
48
|
+
if decompressor.eof:
|
|
49
|
+
return None
|
|
50
|
+
data = self._tail
|
|
51
|
+
if not data and not self._more:
|
|
52
|
+
if self._queue.length == 0:
|
|
53
|
+
return NEED_INPUT
|
|
54
|
+
data = self._queue.take_some(_INPUT_SIZE)
|
|
55
|
+
try:
|
|
56
|
+
out = decompressor.decompress(data, _OUTPUT_SIZE)
|
|
57
|
+
except zlib.error as error:
|
|
58
|
+
message = str(error).partition(": ")[2]
|
|
59
|
+
raise InflateError(_MESSAGES.get(message, message)) from None
|
|
60
|
+
self._tail = decompressor.unconsumed_tail
|
|
61
|
+
self._more = len(out) == _OUTPUT_SIZE
|
|
62
|
+
if decompressor.eof:
|
|
63
|
+
self._queue.unread(decompressor.unused_data)
|
|
64
|
+
if out:
|
|
65
|
+
return out
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""The entries and error of ``pyfect.encoding.zip``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Generic, Literal, Never, TypeVar
|
|
8
|
+
|
|
9
|
+
from pyfect.errors import TaggedError
|
|
10
|
+
from pyfect.stream import Stream
|
|
11
|
+
|
|
12
|
+
type ZipErrorKind = Literal[
|
|
13
|
+
"InvalidHeader",
|
|
14
|
+
"InvalidData",
|
|
15
|
+
"Truncated",
|
|
16
|
+
"Checksum",
|
|
17
|
+
"SizeMismatch",
|
|
18
|
+
"Unsupported",
|
|
19
|
+
"BodyConsumed",
|
|
20
|
+
]
|
|
21
|
+
type EntryType = Literal["File", "Directory"]
|
|
22
|
+
type Method = Literal["Store", "Deflate"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
E_co = TypeVar("E_co", covariant=True, default=Never)
|
|
26
|
+
R_co = TypeVar("R_co", covariant=True, default=Never)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(eq=False)
|
|
30
|
+
class ZipError(TaggedError):
|
|
31
|
+
"""A zip archive could not be encoded or decoded.
|
|
32
|
+
|
|
33
|
+
- ``"InvalidHeader"``: a header has an unknown signature or a malformed field,
|
|
34
|
+
or an entry has a value that cannot be written.
|
|
35
|
+
- ``"InvalidData"``: the compressed data of an entry is corrupt.
|
|
36
|
+
- ``"Truncated"``: the input ended inside an entry or before the central
|
|
37
|
+
directory.
|
|
38
|
+
- ``"Checksum"``: the data of an entry does not match its CRC-32.
|
|
39
|
+
- ``"SizeMismatch"``: a body produced a byte count other than its ``size``, or
|
|
40
|
+
the data of an entry does not match the sizes in the archive.
|
|
41
|
+
- ``"Unsupported"``: the archive uses a feature this module does not read, such
|
|
42
|
+
as encryption or a compression method other than store and deflate.
|
|
43
|
+
- ``"BodyConsumed"``: a body was read after the decoder had moved past it."""
|
|
44
|
+
|
|
45
|
+
kind: ZipErrorKind
|
|
46
|
+
message: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, slots=True)
|
|
50
|
+
class Header:
|
|
51
|
+
"""The metadata of an entry.
|
|
52
|
+
|
|
53
|
+
``path`` is the archive path, as written, with ``/`` separators; directory paths
|
|
54
|
+
end with ``/``. ``size`` is the uncompressed byte length when it is known before
|
|
55
|
+
the body: decoded entries whose sizes follow their data have none. ``mode`` holds
|
|
56
|
+
Unix permission bits, written to the central directory, which ``decode`` does not
|
|
57
|
+
read. ``mtime`` has a resolution of one second with an extended timestamp and two
|
|
58
|
+
seconds otherwise."""
|
|
59
|
+
|
|
60
|
+
path: str
|
|
61
|
+
type: EntryType
|
|
62
|
+
method: Method
|
|
63
|
+
mtime: datetime
|
|
64
|
+
size: int | None = None
|
|
65
|
+
mode: int | None = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True, slots=True)
|
|
69
|
+
class Entry(Generic[E_co, R_co]):
|
|
70
|
+
"""A header and a stream of the uncompressed body bytes. ``decode`` writes these
|
|
71
|
+
and ``file_stream`` builds them; ``encode`` writes the sizes and CRC-32 of a
|
|
72
|
+
streamed body in a data descriptor after the data."""
|
|
73
|
+
|
|
74
|
+
header: Header
|
|
75
|
+
body: Stream[bytes, E_co, R_co]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True, slots=True)
|
|
79
|
+
class ContentEntry:
|
|
80
|
+
"""An entry whose uncompressed content is in memory. ``file`` and ``directory``
|
|
81
|
+
build these; since the content is known first, ``encode`` puts its sizes and
|
|
82
|
+
CRC-32 in the local header and writes no data descriptor, which every zip reader
|
|
83
|
+
accepts."""
|
|
84
|
+
|
|
85
|
+
header: Header
|
|
86
|
+
content: bytes
|