diff --git a/CHANGELOG.md b/CHANGELOG.md index bac0c5156..8c9daf236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## Unreleased +### Breaking Changes + +* Generated HTTP clients no longer install aiohttp and urllib3 by default. + Install the `sync` extra for the synchronous client's default transport, the + `async` extra for the asynchronous client's default transport, or both, such + as `uv add "aws_sdk_s3[sync,async]"` or + `pip install "aws_sdk_s3[sync,async]"`. Clients configured with a + custom transport work without either extra. Imports are unchanged. + ### Features * Generated AWS clients validate the checksums of response payloads for operations with the `aws.protocols#httpChecksum` trait, such as Amazon S3's `GetObject`. These clients depend on the `checksums` extra of `smithy-aws-core`. diff --git a/codegen-output/aws-json-1-0/README.md b/codegen-output/aws-json-1-0/README.md index 19ea5f3c1..33cbc70a4 100644 --- a/codegen-output/aws-json-1-0/README.md +++ b/codegen-output/aws-json-1-0/README.md @@ -3,3 +3,23 @@ ## Sample Json 1.0 Protocol Service Client Python client for Sample Json 1.0 Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "awsjson10[sync]" +uv add "awsjson10[async]" +uv add "awsjson10[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "awsjson10[sync]" +pip install "awsjson10[async]" +pip install "awsjson10[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/aws-json-1-0/pyproject.toml b/codegen-output/aws-json-1-0/pyproject.toml index a9b4f5b94..7d9809e48 100644 --- a/codegen-output/aws-json-1-0/pyproject.toml +++ b/codegen-output/aws-json-1-0/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[json]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/aws-json-1-1/README.md b/codegen-output/aws-json-1-1/README.md index 984d1fbde..6e3791a54 100644 --- a/codegen-output/aws-json-1-1/README.md +++ b/codegen-output/aws-json-1-1/README.md @@ -3,3 +3,23 @@ ## Sample Json 1.1 Protocol Service Client Python client for Sample Json 1.1 Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "awsjson11[sync]" +uv add "awsjson11[async]" +uv add "awsjson11[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "awsjson11[sync]" +pip install "awsjson11[async]" +pip install "awsjson11[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/aws-json-1-1/pyproject.toml b/codegen-output/aws-json-1-1/pyproject.toml index c6f588ad9..6366b311b 100644 --- a/codegen-output/aws-json-1-1/pyproject.toml +++ b/codegen-output/aws-json-1-1/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[json]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/aws-query/README.md b/codegen-output/aws-query/README.md index fdd59c80f..1b35dc7cf 100644 --- a/codegen-output/aws-query/README.md +++ b/codegen-output/aws-query/README.md @@ -4,6 +4,26 @@ Python client for Sample Query Protocol Service +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "awsquery[sync]" +uv add "awsquery[async]" +uv add "awsquery[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "awsquery[sync]" +pip install "awsquery[async]" +pip install "awsquery[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. + ### Documentation A query service that sends query requests and XML responses. diff --git a/codegen-output/aws-query/pyproject.toml b/codegen-output/aws-query/pyproject.toml index eade7c2a8..4c7311f37 100644 --- a/codegen-output/aws-query/pyproject.toml +++ b/codegen-output/aws-query/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[xml]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/ec2-query/README.md b/codegen-output/ec2-query/README.md index c24b4b691..81451933e 100644 --- a/codegen-output/ec2-query/README.md +++ b/codegen-output/ec2-query/README.md @@ -4,6 +4,26 @@ Python client for Sample Ec2 Protocol Service +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "ec2query[sync]" +uv add "ec2query[async]" +uv add "ec2query[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "ec2query[sync]" +pip install "ec2query[async]" +pip install "ec2query[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. + ### Documentation An EC2 query service that sends query requests and XML responses. diff --git a/codegen-output/ec2-query/pyproject.toml b/codegen-output/ec2-query/pyproject.toml index 4b6ce3a64..00b332137 100644 --- a/codegen-output/ec2-query/pyproject.toml +++ b/codegen-output/ec2-query/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[xml]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/my-handwritten-json-model/README.md b/codegen-output/my-handwritten-json-model/README.md index 006564531..c427e2bf4 100644 --- a/codegen-output/my-handwritten-json-model/README.md +++ b/codegen-output/my-handwritten-json-model/README.md @@ -4,6 +4,26 @@ Python client for My Handwritten JSON Model +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "my_handwritten_json_model[sync]" +uv add "my_handwritten_json_model[async]" +uv add "my_handwritten_json_model[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "my_handwritten_json_model[sync]" +pip install "my_handwritten_json_model[async]" +pip install "my_handwritten_json_model[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. + ### Documentation The rpcv2Json counterpart of diff --git a/codegen-output/my-handwritten-json-model/pyproject.toml b/codegen-output/my-handwritten-json-model/pyproject.toml index 6f1e2134b..b7753b36f 100644 --- a/codegen-output/my-handwritten-json-model/pyproject.toml +++ b/codegen-output/my-handwritten-json-model/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[eventstream]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[json, aiohttp, urllib3]~=0.7.0" + "smithy_http[json]~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/my-handwritten-model/README.md b/codegen-output/my-handwritten-model/README.md index a7227557a..921e8c506 100644 --- a/codegen-output/my-handwritten-model/README.md +++ b/codegen-output/my-handwritten-model/README.md @@ -3,3 +3,23 @@ ## My Handwritten Model Client Python client for My Handwritten Model + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "my_handwritten_model[sync]" +uv add "my_handwritten_model[async]" +uv add "my_handwritten_model[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "my_handwritten_model[sync]" +pip install "my_handwritten_model[async]" +pip install "my_handwritten_model[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/my-handwritten-model/pyproject.toml b/codegen-output/my-handwritten-model/pyproject.toml index e58cf27f1..86d58fdc1 100644 --- a/codegen-output/my-handwritten-model/pyproject.toml +++ b/codegen-output/my-handwritten-model/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[eventstream]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[cbor, aiohttp, urllib3]~=0.7.0" + "smithy_http[cbor]~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/non-query-compatible-rpcv2-cbor/README.md b/codegen-output/non-query-compatible-rpcv2-cbor/README.md index 7b955d6a8..d70d02be3 100644 --- a/codegen-output/non-query-compatible-rpcv2-cbor/README.md +++ b/codegen-output/non-query-compatible-rpcv2-cbor/README.md @@ -3,3 +3,23 @@ ## Non Query Compatible RpcV2 Protocol Service Client Python client for Non Query Compatible RpcV2 Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "nonquerycompatiblerpcv2cbor[sync]" +uv add "nonquerycompatiblerpcv2cbor[async]" +uv add "nonquerycompatiblerpcv2cbor[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "nonquerycompatiblerpcv2cbor[sync]" +pip install "nonquerycompatiblerpcv2cbor[async]" +pip install "nonquerycompatiblerpcv2cbor[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/non-query-compatible-rpcv2-cbor/pyproject.toml b/codegen-output/non-query-compatible-rpcv2-cbor/pyproject.toml index 5937a8056..5a594da75 100644 --- a/codegen-output/non-query-compatible-rpcv2-cbor/pyproject.toml +++ b/codegen-output/non-query-compatible-rpcv2-cbor/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[cbor, aiohttp, urllib3]~=0.7.0" + "smithy_http[cbor]~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/query-compatible-aws-json-1-0/README.md b/codegen-output/query-compatible-aws-json-1-0/README.md index 2f8ba8249..322f9b04e 100644 --- a/codegen-output/query-compatible-aws-json-1-0/README.md +++ b/codegen-output/query-compatible-aws-json-1-0/README.md @@ -3,3 +3,23 @@ ## Query Compatible Json 1.0 Protocol Service Client Python client for Query Compatible Json 1.0 Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "querycompatibleawsjson10[sync]" +uv add "querycompatibleawsjson10[async]" +uv add "querycompatibleawsjson10[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "querycompatibleawsjson10[sync]" +pip install "querycompatibleawsjson10[async]" +pip install "querycompatibleawsjson10[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/query-compatible-aws-json-1-0/pyproject.toml b/codegen-output/query-compatible-aws-json-1-0/pyproject.toml index 0fdccd779..3441618ae 100644 --- a/codegen-output/query-compatible-aws-json-1-0/pyproject.toml +++ b/codegen-output/query-compatible-aws-json-1-0/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[json]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/query-compatible-rpcv2-cbor/README.md b/codegen-output/query-compatible-rpcv2-cbor/README.md index 5928f1851..6f5efe55b 100644 --- a/codegen-output/query-compatible-rpcv2-cbor/README.md +++ b/codegen-output/query-compatible-rpcv2-cbor/README.md @@ -3,3 +3,23 @@ ## Query Compatible RpcV2 Protocol Service Client Python client for Query Compatible RpcV2 Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "querycompatiblerpcv2cbor[sync]" +uv add "querycompatiblerpcv2cbor[async]" +uv add "querycompatiblerpcv2cbor[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "querycompatiblerpcv2cbor[sync]" +pip install "querycompatiblerpcv2cbor[async]" +pip install "querycompatiblerpcv2cbor[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/query-compatible-rpcv2-cbor/pyproject.toml b/codegen-output/query-compatible-rpcv2-cbor/pyproject.toml index 8be942985..897ee5c00 100644 --- a/codegen-output/query-compatible-rpcv2-cbor/pyproject.toml +++ b/codegen-output/query-compatible-rpcv2-cbor/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[cbor, aiohttp, urllib3]~=0.7.0" + "smithy_http[cbor]~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/rest-json-1/README.md b/codegen-output/rest-json-1/README.md index 239dd0567..50ca0c053 100644 --- a/codegen-output/rest-json-1/README.md +++ b/codegen-output/rest-json-1/README.md @@ -3,3 +3,23 @@ ## Sample Rest Json Protocol Service Client Python client for Sample Rest Json Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "restjson[sync]" +uv add "restjson[async]" +uv add "restjson[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "restjson[sync]" +pip install "restjson[async]" +pip install "restjson[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/rest-json-1/pyproject.toml b/codegen-output/rest-json-1/pyproject.toml index 28eae9ce9..89638e431 100644 --- a/codegen-output/rest-json-1/pyproject.toml +++ b/codegen-output/rest-json-1/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[eventstream, json]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/rest-xml-with-namespace/README.md b/codegen-output/rest-xml-with-namespace/README.md index 9c005dcfc..1ab00ba42 100644 --- a/codegen-output/rest-xml-with-namespace/README.md +++ b/codegen-output/rest-xml-with-namespace/README.md @@ -4,6 +4,26 @@ Python client for Sample Rest Xml Protocol Service With Namespace +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "restxmlwithnamespace[sync]" +uv add "restxmlwithnamespace[async]" +uv add "restxmlwithnamespace[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "restxmlwithnamespace[sync]" +pip install "restxmlwithnamespace[async]" +pip install "restxmlwithnamespace[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. + ### Documentation A REST XML service that sends XML requests and responses. This service diff --git a/codegen-output/rest-xml-with-namespace/pyproject.toml b/codegen-output/rest-xml-with-namespace/pyproject.toml index 2a0359aa8..cae6d76a2 100644 --- a/codegen-output/rest-xml-with-namespace/pyproject.toml +++ b/codegen-output/rest-xml-with-namespace/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[xml]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/rest-xml/README.md b/codegen-output/rest-xml/README.md index 365c8f067..4e441fc8d 100644 --- a/codegen-output/rest-xml/README.md +++ b/codegen-output/rest-xml/README.md @@ -4,6 +4,26 @@ Python client for Sample Rest Xml Protocol Service +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "restxml[sync]" +uv add "restxml[async]" +uv add "restxml[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "restxml[sync]" +pip install "restxml[async]" +pip install "restxml[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. + ### Documentation A REST XML service that sends XML requests and responses. diff --git a/codegen-output/rest-xml/pyproject.toml b/codegen-output/rest-xml/pyproject.toml index 6d34622cd..28e4e01eb 100644 --- a/codegen-output/rest-xml/pyproject.toml +++ b/codegen-output/rest-xml/pyproject.toml @@ -26,10 +26,18 @@ classifiers = [ dependencies = [ "smithy_aws_core[xml]~=0.13.0", "smithy_core~=0.10.0", - "smithy_http[aiohttp, urllib3]~=0.7.0" + "smithy_http~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/rpcv2-cbor/README.md b/codegen-output/rpcv2-cbor/README.md index 7beeef19f..ad4cc3bfd 100644 --- a/codegen-output/rpcv2-cbor/README.md +++ b/codegen-output/rpcv2-cbor/README.md @@ -3,3 +3,23 @@ ## RpcV2 Protocol Service Client Python client for RpcV2 Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "rpcv2cbor[sync]" +uv add "rpcv2cbor[async]" +uv add "rpcv2cbor[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "rpcv2cbor[sync]" +pip install "rpcv2cbor[async]" +pip install "rpcv2cbor[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/rpcv2-cbor/pyproject.toml b/codegen-output/rpcv2-cbor/pyproject.toml index 1c45fc0bd..81a0752fd 100644 --- a/codegen-output/rpcv2-cbor/pyproject.toml +++ b/codegen-output/rpcv2-cbor/pyproject.toml @@ -25,10 +25,18 @@ classifiers = [ dependencies = [ "smithy_core~=0.10.0", - "smithy_http[cbor, aiohttp, urllib3]~=0.7.0" + "smithy_http[cbor]~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen-output/rpcv2-json/README.md b/codegen-output/rpcv2-json/README.md index 49b85983a..a098e3978 100644 --- a/codegen-output/rpcv2-json/README.md +++ b/codegen-output/rpcv2-json/README.md @@ -3,3 +3,23 @@ ## RpcV2 JSON Protocol Service Client Python client for RpcV2 JSON Protocol Service + +### Installation + +Install the `sync` extra for the synchronous client's default HTTP transport, the `async` extra for the asynchronous client's default HTTP transport, or both. In a project managed by uv, add the client with the extras you need: + +```sh +uv add "rpcv2json[sync]" +uv add "rpcv2json[async]" +uv add "rpcv2json[sync,async]" +``` + +Or install it with pip: + +```sh +pip install "rpcv2json[sync]" +pip install "rpcv2json[async]" +pip install "rpcv2json[sync,async]" +``` + +Without either extra, the models can be used, and either client works with an explicitly configured custom transport. diff --git a/codegen-output/rpcv2-json/pyproject.toml b/codegen-output/rpcv2-json/pyproject.toml index 68835928e..005134974 100644 --- a/codegen-output/rpcv2-json/pyproject.toml +++ b/codegen-output/rpcv2-json/pyproject.toml @@ -25,10 +25,18 @@ classifiers = [ dependencies = [ "smithy_core~=0.10.0", - "smithy_http[json, aiohttp, urllib3]~=0.7.0" + "smithy_http[json]~=0.7.0" ] [project.optional-dependencies] +sync = [ + "smithy_http[urllib3]~=0.7.0" +] + +async = [ + "smithy_http[aiohttp]~=0.7.0" +] + awscrt = [ "smithy_http[awscrt]~=0.7.0" ] diff --git a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java index dda9482e2..c94f6b7d4 100644 --- a/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java +++ b/codegen/core/src/main/java/software/amazon/smithy/python/codegen/generators/SetupGenerator.java @@ -4,6 +4,7 @@ */ package software.amazon.smithy.python.codegen.generators; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; @@ -34,6 +35,13 @@ @SmithyInternalApi public final class SetupGenerator { + // Client mode extras, each mapped to the smithy_http extra that provides that mode's default + // transport. These transports are opt-in rather than base requirements so that customers only + // install what the modes they use need. + private static final List> MODE_TRANSPORT_EXTRAS = List.of( + Map.entry("sync", "urllib3"), + Map.entry("async", "aiohttp")); + private SetupGenerator() {} public static void generateSetup( @@ -42,8 +50,10 @@ public static void generateSetup( ) { var dependencies = gatherDependencies(context.writerDelegator().getDependencies().stream()); var description = resolveDescription(settings, context); + var extras = collectOptionalDependencies( + dependencies.getOrDefault(PythonDependency.Type.DEPENDENCY.getType(), Map.of())); writePyproject(settings, description, context.writerDelegator(), dependencies); - writeReadme(settings, description, context); + writeReadme(settings, description, context, extras); } /** @@ -251,6 +261,23 @@ static Map> collectOptionalDependencies( Map dependencies ) { var extras = new LinkedHashMap>(); + var smithyHttp = dependencies.get(SmithyPythonDependency.SMITHY_HTTP.packageName()); + if (smithyHttp == null) { + return extras; + } + var smithyHttpExtras = getOptionalDependencies(smithyHttp); + + // Expose each requested default transport through the extra for the client mode that + // uses it (e.g. pip install [sync]) instead of requiring it in the base package. + // Like awscrt below, this re-exports smithy_http's extra so the version constraint stays + // sourced from smithy-http. The base requirement drops these, see + // getRequiredOptionalDependencies. + for (var mode : MODE_TRANSPORT_EXTRAS) { + if (smithyHttpExtras.contains(mode.getValue())) { + extras.put(mode.getKey(), + List.of("smithy_http[" + mode.getValue() + "]" + smithyHttp.getVersion())); + } + } // Let users opt into the CRT transport (AWSCRTHTTPClient) without guessing a compatible // awscrt version, by re-exporting smithy_http's awscrt extra. This keeps the version @@ -258,19 +285,33 @@ static Map> collectOptionalDependencies( // already requires awscrt as a hard dependency, where there is nothing to opt into. No // client defaults to CRT today, so the guard is currently always taken; it remains as a // safeguard should a client ever pull awscrt as a hard dependency again. - var smithyHttp = dependencies.get(SmithyPythonDependency.SMITHY_HTTP.packageName()); - if (smithyHttp != null && !getOptionalDependencies(smithyHttp).contains("awscrt")) { + if (!smithyHttpExtras.contains("awscrt")) { extras.put("awscrt", List.of("smithy_http[awscrt]" + smithyHttp.getVersion())); } return extras; } + /** + * Get the optional dependencies to require along with a dependency, leaving out the default + * transports of base dependencies, which are exposed through client mode extras instead. + * Other dependency groups, such as test dependencies, keep their requested extras. + */ + private static List getRequiredOptionalDependencies(SymbolDependency dependency) { + var optional = getOptionalDependencies(dependency); + if (!dependency.getDependencyType().equals(PythonDependency.Type.DEPENDENCY.getType()) + || !dependency.getPackageName().equals(SmithyPythonDependency.SMITHY_HTTP.packageName())) { + return optional; + } + var transports = MODE_TRANSPORT_EXTRAS.stream().map(Map.Entry::getValue).toList(); + return optional.stream().filter(extra -> !transports.contains(extra)).toList(); + } + private static void writeDependencyList(PythonWriter writer, Collection dependencies) { for (var iter = dependencies.iterator(); iter.hasNext();) { writer.pushState(); var dependency = iter.next(); - writer.putContext("deps", getOptionalDependencies(dependency)); + writer.putContext("deps", getRequiredOptionalDependencies(dependency)); writer.putContext("isLink", dependency.getProperty(SymbolProperties.IS_LINK).orElse(false)); writer.putContext("last", !iter.hasNext()); writer.write(""" @@ -301,7 +342,8 @@ private static List getOptionalDependencies(SymbolDependency dependency) private static void writeReadme( PythonSettings settings, String description, - GenerationContext context + GenerationContext context, + Map> extras ) { var service = context.model().expectShape(settings.service()); @@ -310,6 +352,11 @@ private static void writeReadme( .map(StringTrait::getValue) .orElse(StringUtils.capitalize(settings.moduleName())); + var modes = MODE_TRANSPORT_EXTRAS.stream() + .map(Map.Entry::getKey) + .filter(extras::containsKey) + .toList(); + context.writerDelegator().useFileWriter("README.md", writer -> { writer.pushState(new ReadmeSection()); writer.write(""" @@ -318,6 +365,41 @@ private static void writeReadme( $L """, title, StringUtils.wrap(description, 80)); + if (!modes.isEmpty()) { + var installs = new ArrayList<>(modes); + if (modes.size() > 1) { + installs.add(String.join(",", modes)); + } + writer.write(""" + ### Installation + + Install the `sync` extra for the synchronous client's default HTTP \ + transport, the `async` extra for the asynchronous client's default HTTP \ + transport, or both. In a project managed by uv, add the client with \ + the extras you need: + + ```sh + $L + ``` + + Or install it with pip: + + ```sh + $L + ``` + + Without either extra, the models can be used, and either client works \ + with an explicitly configured custom transport. + """, + installs.stream() + .map(extra -> "uv add \"" + settings.moduleName() + "[" + extra + "]\"") + .collect(Collectors.joining("\n")), + installs.stream() + .map(extra -> "pip install \"" + settings.moduleName() + "[" + extra + + "]\"") + .collect(Collectors.joining("\n"))); + } + service.getTrait(DocumentationTrait.class).map(StringTrait::getValue).ifPresent(documentation -> { writer.write(""" ### Documentation diff --git a/codegen/core/src/test/java/software/amazon/smithy/python/codegen/generators/SetupGeneratorTest.java b/codegen/core/src/test/java/software/amazon/smithy/python/codegen/generators/SetupGeneratorTest.java index 097191436..9c4c6800a 100644 --- a/codegen/core/src/test/java/software/amazon/smithy/python/codegen/generators/SetupGeneratorTest.java +++ b/codegen/core/src/test/java/software/amazon/smithy/python/codegen/generators/SetupGeneratorTest.java @@ -48,6 +48,55 @@ public void exposesAwscrtExtraForSmithyHttpDependency() { extras); } + /** + * The sync (urllib3) and async (aiohttp) default transports are opt-in through {@code sync} + * and {@code async} mode extras rather than base requirements, while unrelated smithy_http + * extras (here the cbor protocol extra) stay in the base requirement. The README shows how to + * install them. + */ + @Test + public void movesDefaultTransportsIntoModeExtras() { + var service = service("smithy.example#TestService", null); + var model = Model.builder().addShape(service).build(); + var settings = PythonSettings.builder() + .service(service.getId()) + .moduleName("test_client") + .moduleVersion("0.0.1") + .build(); + var manifest = new MockManifest(); + var delegator = new PythonDelegator(manifest, new PythonSymbolProvider(model, settings), settings); + var context = GenerationContext.builder() + .model(model) + .settings(settings) + .fileManifest(manifest) + .writerDelegator(delegator) + .build(); + var smithyHttp = SmithyPythonDependency.SMITHY_HTTP; + delegator.useFileWriter("test_client/client.py", "test_client.client", writer -> { + writer.addDependency(smithyHttp.withOptionalDependencies("cbor")); + writer.addDependency(smithyHttp.withOptionalDependencies("aiohttp")); + writer.addDependency(smithyHttp.withOptionalDependencies("urllib3")); + writer.addDependency(smithyHttp.withOptionalDependencies("aiohttp", "urllib3").asTestDependency()); + }); + + SetupGenerator.generateSetup(settings, context); + delegator.flushWriters(); + String pyproject = manifest.expectFileString("pyproject.toml"); + String readme = manifest.expectFileString("README.md"); + + var version = smithyHttp.version(); + assertAll( + () -> assertTrue(readme.contains("uv add \"test_client[sync,async]\""), readme), + () -> assertTrue(readme.contains("pip install \"test_client[sync,async]\""), readme), + () -> assertTrue(pyproject.contains("\"smithy_http[cbor]" + version + "\"\n]"), pyproject), + () -> assertTrue(pyproject.contains("sync = [\n \"smithy_http[urllib3]" + version + "\"\n]"), + pyproject), + () -> assertTrue(pyproject.contains("async = [\n \"smithy_http[aiohttp]" + version + "\"\n]"), + pyproject), + () -> assertTrue(pyproject.contains("[dependency-groups]\ntest = [\n \"smithy_http[aiohttp, urllib3]" + + version + "\"\n]"), pyproject)); + } + @Test public void readmeKeepsRawTripleQuotesInCodeSpans() { // The README is Markdown, not a docstring, so the docstring quote escaping diff --git a/designs/async-and-sync-duality.md b/designs/async-and-sync-duality.md index ed825bd93..88bb0a96d 100644 --- a/designs/async-and-sync-duality.md +++ b/designs/async-and-sync-duality.md @@ -2,6 +2,30 @@ Smithy-Python generated clients export two different classes that run in Python's synchronous and asynchronous (aio, async/await) operation modes respectively. For example, the `aws_sdk_s3` package will export both the `S3Client` (synchronous) and `AsyncS3Client` (asynchronous) classes. This allows the customer to operate one or both modes depending on application requirements. +## Client mode extras + +Generated HTTP clients expose a `sync` and an `async` extra that install each mode's default HTTP transport (urllib3 for `sync`, aiohttp for `async`). In a project managed by uv: + +```sh +uv add "aws_sdk_s3[sync]" +uv add "aws_sdk_s3[async]" +uv add "aws_sdk_s3[sync,async]" +``` + +Or with pip: + +```sh +pip install "aws_sdk_s3[sync]" +pip install "aws_sdk_s3[async]" +pip install "aws_sdk_s3[sync,async]" +``` + +- A bare install, without either extra, does not install aiohttp or urllib3. It supports the shared models, and either client with an explicitly configured custom transport. Constructing a missing default transport fails with a `MissingDependencyError`. +- Public import paths are unchanged. +- Other extras keep their meaning: protocol requirements stay in the base package, and the `awscrt` extra still opts into the CRT transport. + +This describes the current single-distribution package. If the modes are ever split into separate distributions, how installs that use a custom transport migrate will be designed as part of that split. + ## `make aio-check` `make aio-check` is a verification step that runs as part of CI's static analysis suite. It detects simple mechanical violations of sync/async duality, such as placing a class in the wrong location or with the wrong name. diff --git a/packages/smithy-http/.changes/next-release/smithy-http-enhancement-3f9c2a7e5b1d4e08a6c4d2b9e7f1a053.json b/packages/smithy-http/.changes/next-release/smithy-http-enhancement-3f9c2a7e5b1d4e08a6c4d2b9e7f1a053.json new file mode 100644 index 000000000..65a2307ac --- /dev/null +++ b/packages/smithy-http/.changes/next-release/smithy-http-enhancement-3f9c2a7e5b1d4e08a6c4d2b9e7f1a053.json @@ -0,0 +1,4 @@ +{ + "type": "enhancement", + "description": "The errors raised when aiohttp or urllib3 is missing now identify the matching smithy-http extra without prescribing a package manager." +} \ No newline at end of file diff --git a/packages/smithy-http/src/smithy_http/aio/aiohttp.py b/packages/smithy-http/src/smithy_http/aio/aiohttp.py index 078bb4d5b..f8b9b527e 100644 --- a/packages/smithy-http/src/smithy_http/aio/aiohttp.py +++ b/packages/smithy-http/src/smithy_http/aio/aiohttp.py @@ -213,7 +213,8 @@ def _upload_data(source: Any, body: AsyncBytesReader | None) -> Any: def _assert_aiohttp() -> None: if not HAS_AIOHTTP: raise MissingDependencyError( - "Attempted to use aiohttp component, but aiohttp is not installed." + "Attempted to use aiohttp component, but aiohttp is not installed. " + 'Install the optional dependency "smithy-http[aiohttp]".' ) diff --git a/packages/smithy-http/src/smithy_http/urllib3.py b/packages/smithy-http/src/smithy_http/urllib3.py index 189d28003..f78d1f0ea 100644 --- a/packages/smithy-http/src/smithy_http/urllib3.py +++ b/packages/smithy-http/src/smithy_http/urllib3.py @@ -262,5 +262,6 @@ def close(self) -> None: def _assert_urllib3() -> None: if not HAS_URLLIB3: raise MissingDependencyError( - "Attempted to use the urllib3 component, but urllib3 is not installed." + "Attempted to use the urllib3 component, but urllib3 is not installed. " + 'Install the optional dependency "smithy-http[urllib3]".' ) diff --git a/packages/smithy-http/tests/unit/aio/test_aiohttp.py b/packages/smithy-http/tests/unit/aio/test_aiohttp.py index 058407c9b..7d78d12a7 100644 --- a/packages/smithy-http/tests/unit/aio/test_aiohttp.py +++ b/packages/smithy-http/tests/unit/aio/test_aiohttp.py @@ -7,9 +7,11 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest +import smithy_http.aio.aiohttp from smithy_core import URI from smithy_core.aio.types import AsyncBytesReader from smithy_core.aio.utils import read_streaming_blob_async +from smithy_core.exceptions import MissingDependencyError from smithy_http import Field, Fields, HTTPRequest from smithy_http.aio.aiohttp import AIOHTTPClient, _AIOHTTPStreamingBody from smithy_http.exceptions import SmithyHTTPError @@ -36,6 +38,18 @@ def _create_request() -> HTTPRequest: ) +def test_missing_aiohttp_recommends_installing_the_extra( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr(smithy_http.aio.aiohttp, "HAS_AIOHTTP", False) + + with pytest.raises(MissingDependencyError) as error: + AIOHTTPClient() + + message = str(error.value) + assert 'Install the optional dependency "smithy-http[aiohttp]".' in message + + async def test_close_closes_session() -> None: client, session = _create_client() diff --git a/packages/smithy-http/tests/unit/test_urllib3.py b/packages/smithy-http/tests/unit/test_urllib3.py index 04fb21d8d..04b61454d 100644 --- a/packages/smithy-http/tests/unit/test_urllib3.py +++ b/packages/smithy-http/tests/unit/test_urllib3.py @@ -9,9 +9,10 @@ from unittest.mock import Mock import pytest +import smithy_http.urllib3 import urllib3 from smithy_core import URI -from smithy_core.exceptions import ClientTimeoutError +from smithy_core.exceptions import ClientTimeoutError, MissingDependencyError from smithy_core.interfaces import is_bytes_reader from smithy_core.utils import read_streaming_blob from smithy_http import Field, Fields, HTTPRequest @@ -75,6 +76,18 @@ def test_pool_keeps_the_configured_connections_per_host( client.close() +def test_missing_urllib3_recommends_installing_the_extra( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setattr(smithy_http.urllib3, "HAS_URLLIB3", False) + + with pytest.raises(MissingDependencyError) as error: + Urllib3HTTPClient() + + message = str(error.value) + assert 'Install the optional dependency "smithy-http[urllib3]".' in message + + def test_repeated_response_headers_are_merged_case_insensitively() -> None: pool = Mock() raw_headers = urllib3.HTTPHeaderDict()