<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: member_1151f59c</title>
    <description>The latest articles on DEV Community by member_1151f59c (@member_1151f59c).</description>
    <link>https://dev.to/member_1151f59c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3841362%2F7ba4de03-2772-42dd-a151-412ed56c931e.png</url>
      <title>DEV Community: member_1151f59c</title>
      <link>https://dev.to/member_1151f59c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/member_1151f59c"/>
    <language>en</language>
    <item>
      <title>boto3 alternative for Python</title>
      <dc:creator>member_1151f59c</dc:creator>
      <pubDate>Mon, 19 Oct 2026 11:19:14 +0000</pubDate>
      <link>https://dev.to/member_1151f59c/boto3-alternative-for-python-284g</link>
      <guid>https://dev.to/member_1151f59c/boto3-alternative-for-python-284g</guid>
      <description>&lt;p&gt;Every time I write boto3 code I end up with the AWS docs open in another tab. My editor has no idea what &lt;code&gt;put_object&lt;/code&gt; accepts, what comes back is a plain dict, and if I misspell a parameter I find out when the request fails. You can fix part of this with stub packages, but it's still a sync library, and the async options (aioboto3, aiobotocore) get there by patching botocore's internals, which isn't a reliable base to build on.&lt;/p&gt;

&lt;p&gt;So I started generating my own SDK from the same Smithy models AWS uses to describe its APIs. It's called capo, and it's at the point where I think other people can try it.&lt;/p&gt;

&lt;p&gt;Here's what using it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;capo_s3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncS3Client&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;AsyncS3Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;create_bucket_configuration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eu-central-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-bucket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;report.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything you pass in and get back is a TypedDict, so autocomplete works all the way down the nested parameters, and you don't import a separate class for each one. If you don't want async, drop the &lt;code&gt;Async&lt;/code&gt; prefix and the &lt;code&gt;await&lt;/code&gt; and it's the same API.&lt;/p&gt;

&lt;p&gt;Errors are actual exception classes, so you write &lt;code&gt;except NoSuchKey:&lt;/code&gt; instead of digging through &lt;code&gt;ClientError.response["Error"]["Code"]&lt;/code&gt;. Paginated operations get an &lt;code&gt;iter_&lt;/code&gt; method and waiters get a &lt;code&gt;wait_until_&lt;/code&gt; method, so there's no separate paginator object to set up.&lt;/p&gt;

&lt;p&gt;Each service is its own package (&lt;code&gt;capo-s3&lt;/code&gt;, &lt;code&gt;capo-ec2&lt;/code&gt;, &lt;code&gt;capo-lambda&lt;/code&gt;, and 400+ more), so installing S3 doesn't pull in EC2's huge model. Import time also stays flat even for EC2, because the serialization code is generated for each operation instead of looked up at runtime.&lt;/p&gt;

&lt;p&gt;Credentials and config work the way you'd expect: env vars, &lt;code&gt;~/.aws/config&lt;/code&gt; profiles, SSO, AssumeRole, IRSA, and instance metadata. If your boto3 setup works, this should pick it up with no changes.&lt;/p&gt;

&lt;p&gt;It's all pure Python, so it also runs in the browser through Pyodide.&lt;/p&gt;

&lt;p&gt;The API is mostly stable, but the project is still pre-1.0, so pin your versions for now. If you try it and something behaves differently from boto3, please open an issue. That's the kind of feedback I need most right now.&lt;/p&gt;

&lt;p&gt;Docs: &lt;a href="https://capo-sdk.dev" rel="noopener noreferrer"&gt;https://capo-sdk.dev&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/kap-sh/capo" rel="noopener noreferrer"&gt;https://github.com/kap-sh/capo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>aws</category>
      <category>s3</category>
      <category>dynamodb</category>
    </item>
    <item>
      <title>Zapros - Modern HTTP client for Python</title>
      <dc:creator>member_1151f59c</dc:creator>
      <pubDate>Tue, 24 Mar 2026 09:00:13 +0000</pubDate>
      <link>https://dev.to/member_1151f59c/zapros-modern-http-client-for-python-1481</link>
      <guid>https://dev.to/member_1151f59c/zapros-modern-http-client-for-python-1481</guid>
      <description>&lt;p&gt;I built &lt;a href="https://zapros.dev" rel="noopener noreferrer"&gt;Zapros&lt;/a&gt; — a modern, extensible Python HTTP client with batteries included!&lt;/p&gt;

</description>
      <category>python</category>
    </item>
  </channel>
</rss>
