<?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: Christian Ledermann</title>
    <description>The latest articles on DEV Community by Christian Ledermann (@ldrscke).</description>
    <link>https://dev.to/ldrscke</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F104896%2Fbeff0443-a031-4a07-9aac-a1a816434283.png</url>
      <title>DEV Community: Christian Ledermann</title>
      <link>https://dev.to/ldrscke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ldrscke"/>
    <language>en</language>
    <item>
      <title>🎮 *brkrs*: A Brand New Take on Classic Brick-Breaking – Play It, Tweak It, Own It!</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Sun, 07 Dec 2025 20:33:55 +0000</pubDate>
      <link>https://dev.to/ldrscke/brkrs-a-brand-new-take-on-classic-brick-breaking-play-it-tweak-it-own-it-3254</link>
      <guid>https://dev.to/ldrscke/brkrs-a-brand-new-take-on-classic-brick-breaking-play-it-tweak-it-own-it-3254</guid>
      <description>&lt;p&gt;Remember the pure, unadulterated joy (and occasional rage) of games like Breakout and Arkanoid? Dodging, bouncing, and strategically smashing bricks for that satisfying &lt;em&gt;thwack&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Well, get ready for &lt;strong&gt;brkrs&lt;/strong&gt; – a modern, full-featured brick-breaker that brings all that classic arcade action to a new generation, built with cutting-edge &lt;strong&gt;&lt;a href="https://rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;&lt;/strong&gt; 🦀 and the incredibly flexible &lt;strong&gt;&lt;a href="https://bevy.org/" rel="noopener noreferrer"&gt;Bevy&lt;/a&gt;&lt;/strong&gt; game engine!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to jump straight into the action or peek under the hood? Find everything here:&lt;/strong&gt; &lt;a href="https://github.com/cleder/brkrs" rel="noopener noreferrer"&gt;&lt;strong&gt;github.com/cleder/brkrs&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;brkrs&lt;/strong&gt; isn't just another clone; it's a love letter to the genre, packed with modern physics, dynamic levels, and a secret weapon: it's entirely open-source, designed for you to play, tinker, and even contribute!&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 The Story: From Retro Dreams to Modern Reality
&lt;/h2&gt;

&lt;p&gt;Many of us have dreamed of remaking our favorite classics. For me, that dream was to revive an old &lt;a href="https://github.com/cleder/yaac" rel="noopener noreferrer"&gt;Arkanoid-style game, "YaAC 🐧"&lt;/a&gt;, using today's best game development tools. What started as a manual journey quickly evolved into something much more: a real game that's also a living showcase of modern game dev practices.&lt;/p&gt;

&lt;p&gt;It’s built on a philosophy of "Kaizen no michi" (改善の道) – making small, continuous improvements. This means the game is always evolving, and every change is carefully considered.&lt;/p&gt;




&lt;h2&gt;
  
  
  🕹️ Play It Now: Levels That Challenge, Physics That Impress
&lt;/h2&gt;

&lt;p&gt;No downloads needed to get a taste of the action!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cleder.github.io/brkrs/" rel="noopener noreferrer"&gt;&lt;strong&gt;Hit up the web version and start smashing bricks here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sorry at this time its only 2 levels (it is still early in the development process), but 70 more (lifted from YAAC) are coming soon, so stay tuned, or even better, help to make it come true ;-)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;brkrs&lt;/strong&gt; extends the classic formula with some seriously cool features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Classic Gameplay, Modern Feel:&lt;/strong&gt; Paddle, ball, and bricks, but with a polished, satisfying punch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich Physics (Rapier3D):&lt;/strong&gt; Experience accurate and engaging ball physics that make every bounce feel real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Levels:&lt;/strong&gt; Human-readable and easy-to-modify level configurations mean endless possibilities for custom stages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paddle Rotation:&lt;/strong&gt; Add a new layer of skill and strategy to your shots.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform Fun:&lt;/strong&gt; Play it on your desktop or directly in your browser thanks to WebAssembly!&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Go Deeper: A Game for Builders, Too
&lt;/h2&gt;

&lt;p&gt;For those who love to dive into the mechanics of their favourite games, &lt;strong&gt;brkrs&lt;/strong&gt; is a treasure trove. It's not just playable; it's also a fantastic example of a well-structured &lt;strong&gt;&lt;a href="https://rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://bevy.org/" rel="noopener noreferrer"&gt;Bevy&lt;/a&gt;&lt;/strong&gt; project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to try building it yourself?&lt;/strong&gt; You'll need Rust, Cargo, and Git.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/cleder/brkrs.git
&lt;span class="nb"&gt;cd &lt;/span&gt;brkrs
cargo run &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Controls:&lt;/strong&gt; Move the paddle with your mouse, use the scroll wheel to rotate (if enabled), and hit ESC to pause.&lt;/p&gt;

&lt;p&gt;This is your chance to not just play, but to truly &lt;strong&gt;tinker&lt;/strong&gt;. Ever wanted to add a new power-up? Change how a brick explodes? Or even design your own crazy levels? &lt;strong&gt;brkrs&lt;/strong&gt; makes it approachable.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Behind the Scenes: Spec-Driven Awesomeness
&lt;/h2&gt;

&lt;p&gt;The game's development isn't just chaotic coding; it's built on &lt;strong&gt;spec-driven development (SDD)&lt;/strong&gt;. This means every feature starts with a clear, detailed plan, much like a game designer's blueprint. We even use &lt;strong&gt;&lt;a href="https://github.com/github/spec-kit?tab=readme-ov-file#-spec-kit" rel="noopener noreferrer"&gt;GitHub's spec-kit&lt;/a&gt;&lt;/strong&gt; to formalize these plans. It's a structured way to ensure every piece of the game works exactly as intended, minimizing bugs and maximizing fun.&lt;/p&gt;

&lt;p&gt;And here's the kicker: this clear, step-by-step approach makes &lt;strong&gt;brkrs&lt;/strong&gt; a perfect playground for experimenting with &lt;strong&gt;AI-assisted coding&lt;/strong&gt;. Imagine using AI to help design a new brick type or tweak game logic – the structured specs make it surprisingly effective!&lt;/p&gt;




&lt;h2&gt;
  
  
  📣 Help Wanted: Your Skills Can Level Up &lt;strong&gt;brkrs&lt;/strong&gt;!
&lt;/h2&gt;

&lt;p&gt;While the code is solid, a great game needs more than just logic! We are actively looking for creative community members to join the effort and help turn &lt;strong&gt;brkrs&lt;/strong&gt; into a visually and aurally stunning experience.&lt;/p&gt;

&lt;p&gt;This is your chance to get your work into a real, playable, open-source game!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🎧 Sound &amp;amp; Music:&lt;/strong&gt; We need satisfying &lt;strong&gt;sound effects&lt;/strong&gt; (the &lt;em&gt;thwack&lt;/em&gt; of a brick, the &lt;em&gt;clink&lt;/em&gt; of a power-up) and engaging &lt;strong&gt;background music&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🎨 Art &amp;amp; Textures:&lt;/strong&gt; Help us create unique &lt;strong&gt;brick textures&lt;/strong&gt;, stylish &lt;strong&gt;paddle designs&lt;/strong&gt;, backgrounds, and other necessary &lt;strong&gt;artwork&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📐 Level Design:&lt;/strong&gt; Got an evil streak? Use the easy-to-modify level configuration files (RON) to create new, challenging, and fun &lt;strong&gt;level designs&lt;/strong&gt;!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🧪 Testing &amp;amp; Feedback:&lt;/strong&gt; Simply playing the game and reporting bugs or suggesting balance tweaks is incredibly valuable!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're a designer, artist, musician, or just a gamer with a great eye for detail, &lt;strong&gt;reach out&lt;/strong&gt; or &lt;strong&gt;submit a Pull Request&lt;/strong&gt; with your contributions!&lt;/p&gt;




&lt;h2&gt;
  
  
  🤝 Join the Fun: Learn, Contribute, Create!
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;brkrs&lt;/strong&gt; is more than a game; it's a community project following "Seika no Ho" (清華の法), "the way of clear planning."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎮️ &lt;strong&gt;Play the Game:&lt;/strong&gt; Enjoy the current levels and discover new strategies.&lt;/li&gt;
&lt;li&gt;🔭️ &lt;strong&gt;Explore the Code:&lt;/strong&gt; See how modern &lt;strong&gt;Rust&lt;/strong&gt; and &lt;strong&gt;Bevy&lt;/strong&gt; work in a real project.&lt;/li&gt;
&lt;li&gt;💡️ &lt;strong&gt;Suggest Ideas:&lt;/strong&gt; What power-ups or brick types would YOU like to see?&lt;/li&gt;
&lt;li&gt;🤝️ &lt;strong&gt;Contribute:&lt;/strong&gt; Even small tweaks or new level designs are welcome!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full documentation, quickstart 🚀️ guides, and developer resources are all available on &lt;a href="https://brkrs.readthedocs.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;brkrs.readthedocs.io&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Ready to break some bricks and make some waves in game development?&lt;/strong&gt;
&lt;/h3&gt;

</description>
      <category>speckit</category>
      <category>gamedev</category>
      <category>bevy</category>
      <category>arkanoid</category>
    </item>
    <item>
      <title>🚀 Breakout to Breakthrough: brkrs —The Rust Game Where Specs Become Code (and AI is Welcome!)</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Sun, 07 Dec 2025 19:55:11 +0000</pubDate>
      <link>https://dev.to/ldrscke/breakout-to-breakthrough-brkrs-the-rust-game-where-specs-become-code-and-ai-is-welcome-4o9c</link>
      <guid>https://dev.to/ldrscke/breakout-to-breakthrough-brkrs-the-rust-game-where-specs-become-code-and-ai-is-welcome-4o9c</guid>
      <description>&lt;p&gt;Tired of tutorial code that stops working the moment the lesson ends? Meet &lt;strong&gt;brkrs&lt;/strong&gt;—a fully playable, Arkanoid/Breakout-style game written in &lt;strong&gt;&lt;a href="https://rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;&lt;/strong&gt; 🦀 and built with the &lt;strong&gt;&lt;a href="https://bevy.org/" rel="noopener noreferrer"&gt;Bevy&lt;/a&gt;&lt;/strong&gt; engine.&lt;/p&gt;

&lt;p&gt;But this isn't just a game. It's an open-source &lt;strong&gt;learning playground&lt;/strong&gt; dedicated to &lt;strong&gt;spec-first development&lt;/strong&gt; and &lt;strong&gt;AI-assisted coding experiments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out the full repository here:&lt;/strong&gt; &lt;a href="https://github.com/cleder/brkrs?tab=readme-ov-file#readme" rel="noopener noreferrer"&gt;&lt;strong&gt;github.com/cleder/brkrs&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As Linus Torvalds famously said: &lt;strong&gt;“Talk is cheap. Show me the code.”&lt;/strong&gt; We say: &lt;strong&gt;"Show me the game, the spec, and the code all at once!"&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 The Philosophy: Spec-First, Incremental, and AI-Ready
&lt;/h2&gt;

&lt;p&gt;Game development, especially in a framework like &lt;strong&gt;Bevy&lt;/strong&gt;, can be a steep climb. The brkrs project was born from the desire to take an old idea &lt;a href="https://github.com/cleder/yaac" rel="noopener noreferrer"&gt;(an Arkanoid clone)&lt;/a&gt; and build it the modern way—a way that accelerates learning and embraces new tooling.&lt;/p&gt;

&lt;p&gt;We follow a simple, yet powerful, development loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Spec-First:&lt;/strong&gt; Every single feature, no matter how small, begins as a clear specification using GitHub's &lt;strong&gt;speckit&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Incremental PRs:&lt;/strong&gt; The spec flows through a small, focused issue or Pull Request. This embodies the "&lt;strong&gt;Kaizen no michi&lt;/strong&gt;" (改善の道) philosophy of small, positive, daily changes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Code &amp;amp; Play:&lt;/strong&gt; The result is working &lt;strong&gt;Rust&lt;/strong&gt; code you can immediately see in the game.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This structured approach makes &lt;strong&gt;brkrs&lt;/strong&gt; the perfect sandbox for the AI coding community:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agentic Testing:&lt;/strong&gt; Need a small, contained task for your coding agent? Point it at a spec and a pending issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Assisted Feature Dev:&lt;/strong&gt; Want to see how your favorite LLM handles adding a new brick behavior or adjusting physics? The clear specs provide the perfect prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow Learning:&lt;/strong&gt; Every merged PR is a clean, documented example of how a real-world feature is implemented in &lt;strong&gt;Rust&lt;/strong&gt;/Bevy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is Spec-Driven Development?
&lt;/h3&gt;

&lt;p&gt;The core of our workflow is the use of &lt;strong&gt;&lt;a href="https://github.com/github/spec-kit?tab=readme-ov-file#-spec-kit" rel="noopener noreferrer"&gt;GitHub's spec-kit&lt;/a&gt;&lt;/strong&gt;. This is a framework for &lt;strong&gt;spec-driven development (SDD)&lt;/strong&gt;, an approach where detailed, human-readable specifications are written &lt;em&gt;before&lt;/em&gt; any code. SDD serves as the single source of truth for the desired behavior of a feature. By providing clear inputs, outputs, and requirements upfront, it minimizes guesswork, aligns team expectations, and provides a perfect, structured input for any AI coding assistant or agent.&lt;/p&gt;




&lt;h2&gt;
  
  
  🕹️ Try It Now: Playable &amp;amp; Pluggable
&lt;/h2&gt;

&lt;p&gt;You don't need to compile anything to get started!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://cleder.github.io/brkrs/" rel="noopener noreferrer"&gt;Play the live web version right now&lt;/a&gt;&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;The core experience extends the classic Breakout formula with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Richer Physics&lt;/strong&gt; (via Rapier3D) constrained to a flat 2D plane.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paddle Rotation&lt;/strong&gt; and customizable per-level settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-readable Levels&lt;/strong&gt; that are easy to modify and extend using RON files.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Quickstart: Play, Tweak, and Learn
&lt;/h2&gt;

&lt;p&gt;Ready to dive into the code? You'll need &lt;strong&gt;Rust&lt;/strong&gt;, Cargo, and Git.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/cleder/brkrs.git
&lt;span class="nb"&gt;cd &lt;/span&gt;brkrs
cargo run &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Controls:&lt;/strong&gt; Move the paddle with the mouse, use the scroll wheel to rotate, and ESC to pause.&lt;/p&gt;

&lt;p&gt;Now, the fun begins. Want to change the gravity for Level 3? Want to create a new &lt;code&gt;HyperBrick&lt;/code&gt; component? The entire architecture—from the &lt;strong&gt;Level Loader&lt;/strong&gt; to the &lt;strong&gt;Brick System&lt;/strong&gt;—is designed for easy modification.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; Following the Samurai principle of "&lt;strong&gt;Seika no Ho&lt;/strong&gt;" (清華の法), "the way of clear planning," pick a small feature, write a mini-spec, and implement it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🤝 Your Learning Path and Contribution
&lt;/h2&gt;

&lt;p&gt;The goal is to make learning modern &lt;strong&gt;Rust&lt;/strong&gt;/Bevy development as enjoyable as playing the game.&lt;/p&gt;

&lt;p&gt;Here’s how you can engage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Read a Spec:&lt;/strong&gt; Check out the repo or wiki for a feature you'd like to see.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Pick an Issue:&lt;/strong&gt; Find a small, contained task that aligns with a spec.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Experiment with AI:&lt;/strong&gt; Use your favourite AI tool (e.g., GitHub Copilot, a local agent) to help draft the code for the task.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Submit a PR:&lt;/strong&gt; Show the community how you turned a spec into working &lt;strong&gt;Rust&lt;/strong&gt; code!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;brkrs&lt;/strong&gt; is more than just a Breakout clone—it’s a living textbook for best practices in modern, spec-driven, and AI-augmented software development.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Documentation
&lt;/h2&gt;

&lt;p&gt;All the details you need to get started are right here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://brkrs.readthedocs.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;Full Documentation&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://brkrs.readthedocs.io/en/latest/quickstart.html" rel="noopener noreferrer"&gt;Quickstart Guide&lt;/a&gt; — Get up and running in 10 minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Ready to break some bricks and code?&lt;/strong&gt;
&lt;/h3&gt;

</description>
      <category>speckit</category>
      <category>rust</category>
      <category>bevy</category>
    </item>
    <item>
      <title>Game development with SpecKit, Rust and Bevy</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Tue, 02 Dec 2025 22:00:00 +0000</pubDate>
      <link>https://dev.to/ldrscke/game-development-with-speckit-rust-and-bevy-86p</link>
      <guid>https://dev.to/ldrscke/game-development-with-speckit-rust-and-bevy-86p</guid>
      <description>&lt;h1&gt;
  
  
  brkrs — a fun, playable brick-breaker game &amp;amp; learning playground
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;brkrs&lt;/strong&gt; is a real, playable Breakout/Arkanoid-style game written in &lt;a href="https://rust-lang.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Rust&lt;/strong&gt; 🦀&lt;/a&gt; using the &lt;a href="https://bevy.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Bevy&lt;/strong&gt;&lt;/a&gt; engine.&lt;br&gt;
It’s also a &lt;strong&gt;hands-on learning project&lt;/strong&gt;, letting you explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec-first development&lt;/strong&gt; with GitHub &lt;strong&gt;speckit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Incremental feature development through issues &amp;amp; PRs&lt;/li&gt;
&lt;li&gt;AI-assisted and agentic coding experiments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every feature starts as a spec, flows through an issue or PR, and ends as working Rust code. You can &lt;strong&gt;play the game, explore the code, and learn modern Rust/Bevy workflows all at the same time&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Linus Torvalds said: &lt;strong&gt;“Talk is cheap. Show me the code.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;brkrs lets you play, tinker, and see the specs come alive in a real game.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Story Behind brkrs
&lt;/h2&gt;

&lt;p&gt;I always wanted to &lt;strong&gt;rewrite my old &lt;a href="https://github.com/cleder/yaac" rel="noopener noreferrer"&gt;Arkanoid/Breakout-style game, YaAC 🐧&lt;/a&gt;&lt;/strong&gt;, in a modern game framework.&lt;/p&gt;

&lt;p&gt;I began by &lt;strong&gt;manually implementing the core gameplay foundations&lt;/strong&gt;: reading documentation, following examples, and building a basic proof-of-concept with the essential mechanics (ball, paddle, bricks).&lt;/p&gt;

&lt;p&gt;It quickly became clear that doing everything manually would involve &lt;strong&gt;a steep learning curve and a lot of time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;brkrs was born as a solution: a way to &lt;strong&gt;learn modern Rust game development&lt;/strong&gt;, apply &lt;strong&gt;spec-first workflows&lt;/strong&gt;, and experiment with &lt;strong&gt;AI-assisted coding&lt;/strong&gt;, all while still having fun playing a real game.&lt;/p&gt;


&lt;h2&gt;
  
  
  Try it now
&lt;/h2&gt;

&lt;p&gt;You can play a web version on &lt;a href="https://cleder.github.io/brkrs/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;brkrs&lt;/code&gt; is a Breakout/Arkanoid style game implemented in Rust with the Bevy engine. It extends the classic formula with richer physics, paddle rotation, and per-level configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Classic Breakout-style gameplay: paddle, ball, bricks, and levels&lt;/li&gt;
&lt;li&gt;Levels are human-readable and easy to modify&lt;/li&gt;
&lt;li&gt;Spec-first workflow: every feature begins as a spec and ends as working Rust code&lt;/li&gt;
&lt;li&gt;Small, incremental PRs demonstrate the development workflow and learning path&lt;/li&gt;
&lt;li&gt;Crate-ready and cross-platform (desktop + WebAssembly builds)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;fun, approachable way to learn Rust, Bevy, and modern coding practices&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Quickstart (play &amp;amp; learn)
&lt;/h2&gt;

&lt;p&gt;Prerequisites: Rust + Cargo + Git&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/cleder/brkrs.git
&lt;span class="nb"&gt;cd &lt;/span&gt;brkrs
cargo run &lt;span class="nt"&gt;--release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Controls: move paddle with mouse, scroll wheel to rotate (if enabled), ESC to pause.&lt;/p&gt;

&lt;p&gt;Play, tweak, and learn — modify levels, bricks, or mechanics to see specs turn into features.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Systems
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Physics (Rapier3D)&lt;/strong&gt; – 3D physics constrained to a flat play plane.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Game State&lt;/strong&gt; – (planned) menu, playing, paused, game over, transitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Level Loader&lt;/strong&gt; – RON file parsing, entity spawning, per-level gravity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brick System&lt;/strong&gt; – Extensible brick behaviors via components &amp;amp; events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pause System&lt;/strong&gt; – ESC to pause, click to resume, with window mode switching (native).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Learning Path &amp;amp; Contribution
&lt;/h2&gt;

&lt;p&gt;This project is intended to be &lt;strong&gt;fun and educational&lt;/strong&gt;. Suggested learning steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read a spec&lt;/strong&gt; in the repo or wiki&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick a small issue&lt;/strong&gt; to implement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Submit a PR&lt;/strong&gt; that fulfills the spec&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experiment&lt;/strong&gt; with AI-assisted features or gameplay tweaks&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;Full documentation is available at &lt;strong&gt;&lt;a href="https://brkrs.readthedocs.io/" rel="noopener noreferrer"&gt;brkrs.readthedocs.io&lt;/a&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://brkrs.readthedocs.io/en/latest/quickstart.html" rel="noopener noreferrer"&gt;Quickstart Guide&lt;/a&gt; — Get running in 10 minutes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://brkrs.readthedocs.io/en/latest/developer-guide.html" rel="noopener noreferrer"&gt;Developer Guide&lt;/a&gt; — Set up a development environment&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://brkrs.readthedocs.io/en/latest/api-reference.html" rel="noopener noreferrer"&gt;API Reference&lt;/a&gt; — Rust API documentation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why You’ll Enjoy It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Play a real game while learning coding practices&lt;/li&gt;
&lt;li&gt;Watch specs transform into working features&lt;/li&gt;
&lt;li&gt;Experiment safely with Rust, Bevy, and AI-assisted workflows&lt;/li&gt;
&lt;li&gt;Learn by doing in a &lt;strong&gt;hands-on, playful way&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>speckit</category>
      <category>gamedev</category>
      <category>bevy</category>
    </item>
    <item>
      <title>Scratching the Itch, Paying the Debt: How Community Keeps Legacy Open Source Projects Alive</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Tue, 28 Oct 2025 16:52:00 +0000</pubDate>
      <link>https://dev.to/ldrscke/scratching-the-itch-paying-the-debt-how-community-keeps-legacy-open-source-projects-alive-5gb0</link>
      <guid>https://dev.to/ldrscke/scratching-the-itch-paying-the-debt-how-community-keeps-legacy-open-source-projects-alive-5gb0</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Every developer has that one project that started as a personal solution and unexpectedly found a life of its own. For me, that was &lt;a href="https://github.com/cleder/fastkml" rel="noopener noreferrer"&gt;FastKML&lt;/a&gt;, a library I built in 2012 to “scratch my own itch.” I needed to embed maps into a website, and at the time, &lt;a href="https://developers.google.com/kml" rel="noopener noreferrer"&gt;&lt;strong&gt;KML&lt;/strong&gt;&lt;/a&gt; was the de facto standard for visualizing geospatial data on the web. &lt;a href="https://geojson.org/" rel="noopener noreferrer"&gt;GeoJSON&lt;/a&gt; existed but was still in its infancy and unsupported by &lt;a href="https://openlayers.org/" rel="noopener noreferrer"&gt;OpenLayers&lt;/a&gt;, which was then the best tool for embedding maps.&lt;/p&gt;

&lt;p&gt;Other Python libraries for KML existed, but most were either limited in scope, lacked Python 3 support, or didn’t meet my performance needs. Performance was crucial, so I built &lt;strong&gt;FastKML&lt;/strong&gt; using &lt;code&gt;lxml&lt;/code&gt; instead of the slower XML DOM used by many contemporaries.&lt;/p&gt;

&lt;p&gt;As FastKML evolved, it depended on &lt;a href="https://github.com/shapely/shapely" rel="noopener noreferrer"&gt;Shapely&lt;/a&gt; for geometry handling, an excellent library, but one that required C extensions and added installation complexity. That led to the birth of &lt;a href="https://github.com/cleder/pygeoif" rel="noopener noreferrer"&gt;PyGeoIf&lt;/a&gt;, a pure Python implementation of basic geospatial objects. PyGeoIf aimed to serve as a lightweight, dependency-free substitute for Shapely when users didn’t need all of its advanced geometry operations. The API mirrored Shapely’s closely, making migration as simple as replacing&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;pygeoif&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with&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;shapely&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over the years, both projects aged gracefully, but not without technical debt. They bore the marks of an earlier Python era: Python 2/3 compatibility hacks (at the very beginning Python 2.6 was still in use), missing type hints, and occasionally ambiguous function signatures.&lt;/p&gt;

&lt;p&gt;Still, they worked. The test coverage exceeded 95%, bugs were rare, and they continued solving real problems for users long after I had moved on to other roles outside GIS. To my surprise, the packages remained popular; downloads were steady, and employers still asked about them. But I knew the code looked dated, and if I had to review it today, it wouldn’t pass.&lt;/p&gt;

&lt;p&gt;Fast forward to 2020. The geospatial landscape had changed; &lt;strong&gt;GeoJSON&lt;/strong&gt; had overtaken KML, Python’s ecosystem had matured, and I had learned a great deal about clean code and maintainability. It was time to modernize these legacy projects for the new decade.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need for Change
&lt;/h2&gt;

&lt;p&gt;Modernization wasn’t just a matter of adding type hints or updating syntax, it was about bringing two long-lived projects in line with modern development practices. The original codebases had served well for years, but they were increasingly difficult to extend. Function signatures were ambiguous, internal logic was tangled, and adding new features often caused &lt;em&gt;shotgun surgery&lt;/em&gt;, requiring edits across multiple unrelated files.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Shapely&lt;/strong&gt; API had evolved too, fully embracing &lt;strong&gt;PEP 8&lt;/strong&gt; naming conventions and adopting more expressive methods. To remain compatible, PyGeoIf needed to evolve alongside it. Meanwhile, Python itself had transformed: type hints, static analysis, and property-based testing were now standard practice rather than novelty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drivers of Change
&lt;/h3&gt;

&lt;p&gt;The single most important motivator was the &lt;strong&gt;introduction of type hints&lt;/strong&gt; in Python. Type annotations have revolutionized how Python code is written, reviewed, and maintained, enhancing readability and catching subtle bugs early through tools like &lt;strong&gt;mypy&lt;/strong&gt;.&lt;br&gt;
The first step was static analysis with tools like mypy, which immediately flagged legacy Python 2 compatibility hacks, ambiguous function signatures, and missing type hints. Extending the tests in tandem ensured that each refactor preserved correctness.&lt;/p&gt;

&lt;p&gt;Beyond that, the desire for clearer APIs, more maintainable structures, and modern testing techniques pushed the modernization effort forward. I wanted code that not only worked but was &lt;strong&gt;readable, testable, and future-proof&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Tale of Two Refactors
&lt;/h3&gt;

&lt;p&gt;For &lt;strong&gt;PyGeoIf&lt;/strong&gt;, version 0.7 had been released in 2017. Four years later, in September 2021, I published the first beta of the 1.0 series: fully type-annotated, statically checked with &lt;code&gt;mypy&lt;/code&gt;, and tested using &lt;strong&gt;property-based testing&lt;/strong&gt; with &lt;a href="https://hypothesis.works/" rel="noopener noreferrer"&gt;Hypothesis&lt;/a&gt; and improved tests with &lt;strong&gt;mutation testing&lt;/strong&gt; with &lt;a href="https://mutmut.readthedocs.io/" rel="noopener noreferrer"&gt;MutMut&lt;/a&gt;. By September 2022, version 1.0 was stable, and by October 2025, it had matured to version 1.5.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;FastKML&lt;/strong&gt;, after a long silence since version 0.11 in 2015, I released version 0.12 in September 2021, incorporating long-neglected pull requests and minor improvements. A month later came &lt;strong&gt;FastKML 1.0 alpha 1&lt;/strong&gt; on PyPI. What I thought would be a quick release became an 18-iteration journey spanning three years, culminating in &lt;strong&gt;version 1.0&lt;/strong&gt; in November 2024; finally the library I had envisioned years earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflecting on Contributions and Community Support
&lt;/h2&gt;

&lt;p&gt;Over the past few years of developing &lt;strong&gt;PyGeoIf&lt;/strong&gt; and &lt;strong&gt;FastKML&lt;/strong&gt;, the journey has been shaped not only by personal effort but also by the support and engagement of the open-source community. One striking example of this has been &lt;strong&gt;Hacktoberfest contributions&lt;/strong&gt; which consistently provided motivation and tangible progress.&lt;/p&gt;

&lt;p&gt;These contributions may seem small individually, but collectively they have &lt;strong&gt;kept the momentum going&lt;/strong&gt;. Seeing community members engage with the projects during Hacktoberfest has been a &lt;strong&gt;continuing source of encouragement&lt;/strong&gt;, reminding me that every bit of contribution helps make the software more robust, maintainable, and welcoming to others.&lt;/p&gt;

&lt;p&gt;The positive impact goes beyond the specific changes. Hacktoberfest contributions have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encouraged ongoing improvement&lt;/strong&gt; by motivating incremental updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highlighted the value of community participation&lt;/strong&gt; in maintaining and modernizing open-source projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reinforced a sense of shared purpose&lt;/strong&gt;, showing that even small efforts can collectively advance a project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ongoing collaboration has made the development process &lt;strong&gt;more rewarding and sustainable&lt;/strong&gt;, reinforcing a simple but powerful lesson: in open-source, community engagement isn’t just about code, it’s about inspiration and momentum.&lt;/p&gt;

&lt;p&gt;Hacktoberfest contributions aren’t just code, they’re &lt;strong&gt;encouragement&lt;/strong&gt;. They spark incremental improvements, highlight the value of shared effort, and inspire continued development. Seeing others invest their time and ideas in these projects has been a &lt;strong&gt;constant source of motivation&lt;/strong&gt; to keep improving, testing, and refining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hacktoberfest and the Power of Community
&lt;/h2&gt;

&lt;p&gt;Developing &lt;strong&gt;PyGeoIf&lt;/strong&gt; and &lt;strong&gt;FastKML&lt;/strong&gt; has been a journey of learning, coding, and refining, but it’s the &lt;strong&gt;community contributions&lt;/strong&gt;, especially during Hacktoberfest, that have truly kept the momentum alive.&lt;/p&gt;

&lt;p&gt;October has consistently brought a wave of engagement: pre-commit hooks, bug fixes, minor enhancements, and automated improvements. Each contribution, no matter how small, &lt;strong&gt;reinforced the sense of progress&lt;/strong&gt; and reminded me that open-source thrives on collaboration.&lt;/p&gt;

&lt;p&gt;Looking forward, this collaborative energy continues to &lt;strong&gt;fuel future features and refinements&lt;/strong&gt;. Hacktoberfest has proven that even small contributions can make a big difference, both in the code and in the spirit of the community.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>Python Code Quality Tools Beyond Linting</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Sun, 05 Oct 2025 14:07:05 +0000</pubDate>
      <link>https://dev.to/ldrscke/python-code-quality-tools-beyond-linting-42d8</link>
      <guid>https://dev.to/ldrscke/python-code-quality-tools-beyond-linting-42d8</guid>
      <description>&lt;p&gt;The landscape of Python software quality tooling is currently defined by two contrasting forces: high-velocity convergence and deep specialization. The recent, rapid adoption of &lt;strong&gt;Ruff&lt;/strong&gt; has solved the long-standing community problem of coordinating dozens of separate linters and formatters, establishing a unified, high-performance axis for standard code quality.&lt;/p&gt;

&lt;p&gt;A second category of tools continues to operate in necessary, but isolated, silos. Tools dedicated to architectural enforcement and deep structural metrics, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://github.com/seddonym/import-linter/" rel="noopener noreferrer"&gt;import-linter&lt;/a&gt;&lt;/strong&gt; (Layered architecture enforcement) &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://github.com/gauge-sh/tach" rel="noopener noreferrer"&gt;tach&lt;/a&gt;&lt;/strong&gt; (Dependency visualization and enforcement) &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://github.com/rohaquinlop/complexipy" rel="noopener noreferrer"&gt;complexipy&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://github.com/rubik/radon" rel="noopener noreferrer"&gt;radon&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://github.com/terryyin/lizard" rel="noopener noreferrer"&gt;lizard&lt;/a&gt;&lt;/strong&gt; (Metrics for overall and cognitive complexity)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;a href="https://github.com/Oaz/module_coupling_metrics" rel="noopener noreferrer"&gt;module_coupling_metrics&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://github.com/potfur/lcom" rel="noopener noreferrer"&gt;lcom&lt;/a&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;a href="https://github.com/mschwager/cohesion" rel="noopener noreferrer"&gt;cohesion&lt;/a&gt;&lt;/strong&gt; (Metrics for coupling and class cohesion) &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/ludo-technologies/pyscn" rel="noopener noreferrer"&gt;pyscn - Python Code Quality Analyzer&lt;/a&gt;&lt;/strong&gt; (Module dependencies, clone detection, complexity)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These projects address fundamental challenges of code maintainability, evolvability, and architectural debt that extend beyond the scope of fast, stylistic linting. The success of Ruff now presents the opportunity to foster a cross-tool discussion focused not just on syntax, but on &lt;em&gt;structure&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Specialized quality tools are vital for long-term maintainability and risk assessment. Tools like &lt;code&gt;import-linter&lt;/code&gt; and &lt;code&gt;tach&lt;/code&gt; mitigate technical risk by enforcing architectural rules, preventing systemic decay, and reducing change costs. Complexity and cohesion metrics from tools such as &lt;code&gt;complexipy&lt;/code&gt;, &lt;code&gt;lcom&lt;/code&gt;, and &lt;code&gt;cohesion&lt;/code&gt; quantitatively flag overly complex or highly coupled components, acting as early warning systems for technical debt. By analysing the combined outputs, risk assessment shifts to predictive modelling: integrating data from individual tools (e.g., &lt;code&gt;import-linter&lt;/code&gt; violations, &lt;code&gt;complexipy&lt;/code&gt; scores) creates a multi-dimensional risk score. Overlaying these results, such as identifying modules that are both low in cohesion and involved in &lt;code&gt;tach&lt;/code&gt;-flagged dependency cycles, generates a "heat map" of technical debt. This unified approach, empirically validated against historical project data like bug frequency and commit rates can yield a predictive risk assessment. It identifies modules that are not just theoretically complex but empirically confirmed sources of instability, transforming abstract quality metrics into concrete, prioritized refactoring tasks for the riskiest codebase components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasons to Connect
&lt;/h2&gt;

&lt;p&gt;Bring the maintainers and core users of these diverse tools into a shared discussion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Increasing Tool Visibility and Sustainability:&lt;/strong&gt; Specialized tools often rely on small, dedicated contributor pools and suffer from knowledge isolation, confining technical debate to their specific GitHub repository. A broader discussion provides these projects with critical outreach, exposure to a wider user base, and a stronger pipeline of new contributors, ensuring their long-term sustainability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/astral-sh/ruff/discussions/20699" rel="noopener noreferrer"&gt;Let's start the conversation&lt;/a&gt; on how to 'measure' maintainable, and architecturally sound Python code.&lt;br&gt;
And keep Goodhart's law: "When a measure becomes a target, it ceases to be a good measure" in mind ;-) &lt;/p&gt;

</description>
      <category>architecture</category>
      <category>python</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Trusted publishing ‐ It has never been easier to publish your python packages</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Thu, 05 Dec 2024 18:52:07 +0000</pubDate>
      <link>https://dev.to/ldrscke/trusted-publishing-it-has-never-been-easier-to-publish-your-python-packages-3dfn</link>
      <guid>https://dev.to/ldrscke/trusted-publishing-it-has-never-been-easier-to-publish-your-python-packages-3dfn</guid>
      <description>&lt;p&gt;Publishing Python packages used to be a daunting task, but not any more. Even better, it has become significantly more secure. Gone are the days of juggling usernames, passwords, or API tokens while relying on &lt;a href="https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#uploading-your-project-to-pypi" rel="noopener noreferrer"&gt;CLI tools&lt;/a&gt;. With trusted publishing, you simply provide PyPI with the details of your GitHub repository, and GitHub Actions takes care of the heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Publish Your Python Package with Trusted Publishing
&lt;/h2&gt;

&lt;p&gt;I will introduce a workflow that will publish your package to TestPyPi when a tag is created (on the &lt;code&gt;development&lt;/code&gt; branch), or to PyPi when you merge to the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare Your Package for Publishing
&lt;/h3&gt;

&lt;p&gt;Ensure your Python package follows PyPI’s packaging guidelines. At a minimum, you’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://dev.to/ldrscke/hypermodernize-your-python-package-3d9m"&gt;&lt;del&gt;setup.py or&lt;/del&gt; pyproject.toml&lt;/a&gt; file defining your package metadata.&lt;/li&gt;
&lt;li&gt;Properly structured code with a clear directory layout.&lt;/li&gt;
&lt;li&gt;A README file to showcase your project on PyPI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a detailed checklist, refer to the &lt;a href="https://packaging.python.org/" rel="noopener noreferrer"&gt;Python Packaging User Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure GitHub Actions in Your Repository
&lt;/h3&gt;

&lt;p&gt;Let's start by creating a new GitHub action &lt;code&gt;.github/workflows/test-build-publish.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-build-publish&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;build-and-check-package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build &amp;amp; inspect our package.&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hynek/build-and-inspect-python-package@v2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;a href="https://github.com/hynek/build-and-inspect-python-package" rel="noopener noreferrer"&gt;action&lt;/a&gt; will build your package and uploads the built wheel and the source distribution (&lt;code&gt;SDist&lt;/code&gt;) as GitHub Actions artefacts.&lt;/p&gt;

&lt;p&gt;Next, we add a step to publish to &lt;a href="https://test.pypi.org/" rel="noopener noreferrer"&gt;TestPyPI&lt;/a&gt;. This step will run whenever a tag is created, ensuring that the build from the previous step has completed successfully. Replace PROJECT_OWNER and PROJECT_NAME with the appropriate values for your repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;test-publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
        &lt;span class="s"&gt;github.event_name == 'push' &amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="s"&gt;github.repository == 'PROJECT_OWNER/PROJECT_NAME' &amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="s"&gt;startsWith(github.ref, 'refs/tags')&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-and-check-package&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test publish on TestPyPI&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-release&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download packages built by build-and-check-package&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Packages&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dist&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload package to Test PyPI&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pypa/gh-action-pypi-publish@release/v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;repository-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://test.pypi.org/legacy/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step downloads the artefacts created during the build process and uploads them to TestPyPI for testing.&lt;/p&gt;

&lt;p&gt;In the last step, we will upload the package to PyPI when a pull request is merged into the &lt;code&gt;main&lt;/code&gt; branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
      &lt;span class="s"&gt;github.event_name == 'push' &amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="s"&gt;github.repository == 'PROJECT_OWNER/PROJECT_NAME' &amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="s"&gt;github.ref == 'refs/heads/main'&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-and-check-package&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to PyPI&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download packages built by build-and-check-package&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Packages&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dist&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish distribution 📦 to PyPI for push to main&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pypa/gh-action-pypi-publish@release/v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure GitHub Environments
&lt;/h3&gt;

&lt;p&gt;To ensure that only specific tags trigger the publishing workflow and maintain control over your release process.&lt;br&gt;
Create a new environment &lt;code&gt;test-release&lt;/code&gt; by navigating to Settings -&amp;gt; Environments in your GitHub repository.&lt;/p&gt;

&lt;p&gt;Set up the environment and add a deployment tag rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze4cijlsqrnl8vul34b0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze4cijlsqrnl8vul34b0.png" alt="Add a new environment" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4zyewqobkjrjnliqypm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4zyewqobkjrjnliqypm.png" alt="Create test environment" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Limit which branches and tags can deploy to this environment based on rules or naming patterns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1h5kqg4t511hz06wcq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1h5kqg4t511hz06wcq6.png" alt="Add deployment tag rule" width="800" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Limit which branches and tags can deploy to this environment based on naming patterns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46ksq4gickq7ysxy8ml8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46ksq4gickq7ysxy8ml8.png" alt="Add target tags" width="505" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the target tags.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokfurlryb8oy0f6bx2go.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokfurlryb8oy0f6bx2go.png" alt="Configure target tags" width="640" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pattern &lt;code&gt;[0-9]*.[0-9]*.[0-9]*&lt;/code&gt; matches semantic versioning tags such as &lt;code&gt;1.2.3&lt;/code&gt;, &lt;code&gt;0.1.0&lt;/code&gt;, or &lt;code&gt;2.5.1b3&lt;/code&gt;, but it excludes arbitrary tags like &lt;code&gt;bugfix-567&lt;/code&gt; or &lt;code&gt;feature-update&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Repeat this for the &lt;code&gt;release&lt;/code&gt; environment to protect the &lt;code&gt;main&lt;/code&gt; branch in the same way, but this time targeting the &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0795ks0roh3a4l1u351.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0795ks0roh3a4l1u351.png" alt="Configure target branch" width="626" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Set Up a PyPI Project and Link Your GitHub Repository
&lt;/h3&gt;

&lt;p&gt;Create an account on &lt;a href="https://test.pypi.org/" rel="noopener noreferrer"&gt;TestPyPI&lt;/a&gt; if you don’t have one.&lt;br&gt;
Navigate to your account, &lt;a href="https://test.pypi.org/manage/account/publishing/" rel="noopener noreferrer"&gt;Publishing&lt;/a&gt; and add a new pending publisher.&lt;br&gt;
Link your GitHub repository to the PyPI project by providing its name, your GitHub username, the repository name, the workflow name (&lt;code&gt;test-build-publish.yml&lt;/code&gt;) and the environment name (&lt;code&gt;test-release&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckh3r49j6tb2mdieszoo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckh3r49j6tb2mdieszoo.png" alt="Configure PyPI trusted publisher" width="579" height="857"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat the above on &lt;a href="https://pypi.org/manage/account/publishing/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt; with the environment name set to &lt;code&gt;release&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test the Workflow
&lt;/h3&gt;

&lt;p&gt;Now whenever you create a tag on your development branch, it will trigger a release to be uploaded to TestPyPI and merging the development branch into main will upload a release to PyPI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Wasn't Covered
&lt;/h2&gt;

&lt;p&gt;While this guide provides an introduction to trusted publishing workflows, there are additional steps and best practices you might consider implementing. For example, setting up &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule" rel="noopener noreferrer"&gt;branch protection rules&lt;/a&gt; can ensure only authorized collaborators can push tags or merge to protected branches, like &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;develop&lt;/code&gt;. You can also enforce status checks or require pull request reviews before merging, adding another layer of quality assurance. &lt;/p&gt;

&lt;p&gt;Have a look at my &lt;a href="https://github.com/cleder/python-repository-template" rel="noopener noreferrer"&gt;python-repository-template&lt;/a&gt; that covers additional enhancement to this workflow, such as requiring unit and static tests to pass, checking the package with &lt;a href="https://github.com/regebro/pyroma" rel="noopener noreferrer"&gt;pyroma&lt;/a&gt; and ensuring that your tag matches the version of your package with &lt;a href="https://github.com/cleder/vercheck" rel="noopener noreferrer"&gt;vercheck&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you've been holding back on sharing your work, now is the perfect time to try trusted publishing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/" rel="noopener noreferrer"&gt;Introducing 'Trusted Publishers'&lt;/a&gt; The Python Package Index Blog highlights a more secure publishing method that does not require long-lived passwords or API tokens to be shared with external systems&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.pypi.org/trusted-publishers/" rel="noopener noreferrer"&gt;Publishing to PyPI with a Trusted Publisher&lt;/a&gt; The official PyPI documentation to get started with using trusted publishers on PyPI.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries" rel="noopener noreferrer"&gt;Building and testing Python&lt;/a&gt; in the official GitHub docs.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>github</category>
      <category>packaging</category>
      <category>security</category>
    </item>
    <item>
      <title>'Hypermodernize' your Python Package</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Thu, 07 Dec 2023 11:28:00 +0000</pubDate>
      <link>https://dev.to/ldrscke/hypermodernize-your-python-package-3d9m</link>
      <guid>https://dev.to/ldrscke/hypermodernize-your-python-package-3d9m</guid>
      <description>&lt;p&gt;In the original &lt;a href="https://cjolowicz.github.io/posts/hypermodern-python-01-setup/#setting-up-a-python-project-using-poetry" rel="noopener noreferrer"&gt;Hypermodern Python Blogpost&lt;/a&gt;, Poetry was recommended as the preferred tool.&lt;br&gt;
There are quite a lot of packaging tools out there which I do not want to go into in depth, instead I recommend Anna-Lena Popkes &lt;a href="https://alpopkes.com/posts/python/packaging_tools/" rel="noopener noreferrer"&gt;An unbiased evaluation of environment management and packaging tools&lt;/a&gt;.&lt;br&gt;
What &lt;em&gt;has&lt;/em&gt; emerged as the preferred way to store packaging information is the &lt;a href="https://packaging.python.org/en/latest/specifications/pyproject-toml/#pyproject-toml-spec" rel="noopener noreferrer"&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;pyproject.toml&lt;/code&gt; over &lt;code&gt;setup.py&lt;/code&gt; has become a preferred choice for several reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Configuration:&lt;/strong&gt; &lt;code&gt;pyproject.toml&lt;/code&gt; is part of the &lt;a href="https://peps.python.org/pep-0518/" rel="noopener noreferrer"&gt;PEP 518&lt;/a&gt; and &lt;a href="https://peps.python.org/pep-0517/" rel="noopener noreferrer"&gt;PEP 517&lt;/a&gt; specifications, offering a standardized and consistent way to declare build configurations and dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PEP 518 Support:&lt;/strong&gt; &lt;code&gt;pyproject.toml&lt;/code&gt; supports PEP 518, allowing the declaration of build system requirements, enabling better compatibility with modern build tools and providing more flexibility in the build process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Tooling:&lt;/strong&gt; Some modern Python tools rely on &lt;code&gt;pyproject.toml&lt;/code&gt; for project configuration. Adopting &lt;code&gt;pyproject.toml&lt;/code&gt; aligns with these tools and facilitates a smoother integration with them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readability and Maintainability:&lt;/strong&gt; The &lt;code&gt;pyproject.toml&lt;/code&gt; format is often considered more readable and straightforward than the Python code used in &lt;code&gt;setup.py&lt;/code&gt;. This can contribute to better maintainability, especially for more complex projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardization:&lt;/strong&gt; As Python packaging evolves, &lt;code&gt;pyproject.toml&lt;/code&gt; is becoming the de facto standard for configuration, supported by tools like &lt;code&gt;pip&lt;/code&gt; and build systems like &lt;code&gt;flit&lt;/code&gt; and &lt;code&gt;poetry&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The adoption of &lt;code&gt;pyproject.toml&lt;/code&gt; aligns with modern best practices in the Python packaging ecosystem.&lt;/p&gt;

&lt;p&gt;Converting a &lt;code&gt;setup.py&lt;/code&gt; base package to a &lt;code&gt;pyproject.toml&lt;/code&gt; based one turns out to be straightforward for most use cases.&lt;br&gt;
While it is &lt;a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-pyproject-toml" rel="noopener noreferrer"&gt;easy to write&lt;/a&gt; a &lt;code&gt;pyproject.toml&lt;/code&gt; there are a number of tools to convert legacy package information into this format.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/gvalkov/setuptools-py2cfg#setuptools-py2cfg" rel="noopener noreferrer"&gt;setuptools-py2cfg&lt;/a&gt; A script for converting &lt;code&gt;setup.py&lt;/code&gt; to &lt;code&gt;setup.cfg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/abravalheri/ini2toml#ini2toml" rel="noopener noreferrer"&gt;ini2toml&lt;/a&gt; which automatically translates &lt;a href="https://docs.python.org/3/library/configparser.html#supported-ini-file-structure" rel="noopener noreferrer"&gt;.ini/.cfg&lt;/a&gt; files into &lt;a href="https://toml.io/" rel="noopener noreferrer"&gt;TOML&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/abravalheri/validate-pyproject#validate-pyproject" rel="noopener noreferrer"&gt;validate-pyproject&lt;/a&gt; for automated checks on &lt;code&gt;pyproject.toml&lt;/code&gt; powered by JSON Schema definitions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kieran-ryan/pyprojectsort#pyprojectsort" rel="noopener noreferrer"&gt;pyprojectsort&lt;/a&gt; enforces consistent formatting of pyproject.toml files, reducing merge request conflicts and saving time otherwise spent on manual formatting.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/regebro/pyroma" rel="noopener noreferrer"&gt;pyroma&lt;/a&gt; Rate your Python packages package friendliness.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Install the required packages&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;pip install setuptools-py2cfg ini2toml[full]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or if you get an error &lt;code&gt;no matches found: ini2toml[full]&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;pip install setuptools-py2cfg ini2toml configupdater tomlkit
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a temporary &lt;code&gt;cfg&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;setuptools-py2cfg &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tmp.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and convert it into a &lt;code&gt;toml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ini2toml --output-file=tmp.toml --profile=setup.cfg tmp.cfg
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apart from the profile &lt;code&gt;setup.cfg&lt;/code&gt; &lt;code&gt;ini2toml&lt;/code&gt; also has profiles to convert settings to their &lt;code&gt;pyproject.toml&lt;/code&gt; equivalent for&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.coveragerc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.isort.cfg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mypy.ini&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pytest.ini&lt;/code&gt; ('ini_options' table)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After you converted the files, cut and paste them into a new (or existing) &lt;code&gt;pyproject.toml&lt;/code&gt; file and validate and format them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;pip install validate-pyproject pyprojectsort pyroma
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;pyprojectsort
validate-pyproject pyproject.toml
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can delete your old setup.py file and verify that your package still builds as expected with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rm setup.py
python -m build
pyroma .
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;code&gt;dist/[my-package-name].tar.gz&lt;/code&gt; file to ensure it builds like before.&lt;/p&gt;

&lt;p&gt;Adjust and tweak the settings as outlined in the &lt;a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-pyproject-toml" rel="noopener noreferrer"&gt;user guide&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional information
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@noexec/creating-and-publishing-a-modern-python-package-e0b5bce9461d" rel="noopener noreferrer"&gt;Authoring a Modern Python Package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/UOdMnlQRRCk" rel="noopener noreferrer"&gt;Moshe Zadka: pyproject.toml, packaging, and you&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>packaging</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>A Tale of Two Kitchens - Hypermodernizing Your Python Code Base</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Sun, 12 Nov 2023 18:58:14 +0000</pubDate>
      <link>https://dev.to/ldrscke/a-tale-of-two-kitchens-hypermodernizing-your-python-code-base-3jnh</link>
      <guid>https://dev.to/ldrscke/a-tale-of-two-kitchens-hypermodernizing-your-python-code-base-3jnh</guid>
      <description>&lt;h2&gt;
  
  
  What is hyper python modern python?
&lt;/h2&gt;

&lt;p&gt;The idea stems from an &lt;a href="https://cjolowicz.github.io/posts/hypermodern-python-01-setup/" rel="noopener noreferrer"&gt;article series&lt;/a&gt; with the same title by Claudio Jolowicz and is an opinionated guideline about best practices and clean code in python in the 21st century. It is a guide to modern Python tooling with a focus on simplicity and minimalism. It walks you through the creation of a complete and up-to-date Python project structure, with unit tests, static analysis, type-checking, documentation, and continuous integration and delivery.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv3sc6x4gc4bpe6lqen6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdv3sc6x4gc4bpe6lqen6.jpg" alt="Hypermodern" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  'A tale of two kitchens'
&lt;/h2&gt;

&lt;p&gt;Imagine two Kitchens Kitchen 1 is messy:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq2ln4y2p6ipkx3ssbwc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq2ln4y2p6ipkx3ssbwc.jpg" alt="A messy Kitchen, sink overflowing with unwashed dishes, dirty dishes with rests of food on the working surface next to the sink, dirty pots and pans partially blackened with dirt on the working surfaces and stove, scraps of food lying around." width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kitchen 2 is clean and tidy:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcleder%2Ftalks%2Frefs%2Fheads%2Fmain%2Fimages%2Fclean-kitchen.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcleder%2Ftalks%2Frefs%2Fheads%2Fmain%2Fimages%2Fclean-kitchen.jpg" alt="A tidy and clean kitchen, sparkling pots, utensils carefully stored away on hooks on the wall, cupboards with well organized utensils and spices, a pot boiling on the brushed stainless steel stove.&amp;lt;br&amp;gt;
" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which kitchen do you think is better in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;security,&lt;/li&gt;
&lt;li&gt;health (including mental health) and safety,&lt;/li&gt;
&lt;li&gt;fast delivery,&lt;/li&gt;
&lt;li&gt;high quality of outcomes,&lt;/li&gt;
&lt;li&gt;job satisfaction,&lt;/li&gt;
&lt;li&gt;personal growth?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a game of would you rather, kitchen one is the unanimous winner.&lt;/p&gt;

&lt;p&gt;But in reality some of us work in a kitchen that looks more like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw6uty71e43ydcrjj43h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw6uty71e43ydcrjj43h.jpg" alt="A  blazing grease fire in a messy Kitchen with fire-damage, sink overflowing with unwashed dishes, dirty dishes with rests of food, mould growing on them, dirty, sooty, mouldy pots and pans on the working surfaces." width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why do we want to clean up our code base and make it hyper modern?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The most important thing I have done as a programmer in recent years is to aggressively pursue static code analysis. Even more valuable than the hundreds of serious bugs I have prevented with it, is the change in mindset about the way I view software reliability and code quality.&lt;br&gt;
-- &lt;cite&gt;&lt;a href="https://en.wikipedia.org/wiki/John_Carmack" rel="noopener noreferrer"&gt;John Carmack&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://xkcd.com/1695/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3thcd704f6ob59yovmjk.png" alt="xkcd Code Quality 2" width="531" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clean consistent code minimizes context switches.&lt;br&gt;
If our code looks the same everywhere we need  less mental overhead to switch in between different code styles.&lt;br&gt;
A '&lt;a href="https://en.wikipedia.org/wiki/Lean_software_development" rel="noopener noreferrer"&gt;LEAN&lt;/a&gt;' principle is to eliminate waste to reduce friction, this helps us with it.&lt;br&gt;
The &lt;a href="https://en.wikipedia.org/wiki/Broken_windows_theory" rel="noopener noreferrer"&gt;Broken Window Theory&lt;/a&gt; suggests that when bad behavior is not corrected immediately, it shows people that there is no downside to breaking the rules, practices or standards. If there is no negative outcome, cutting corners becomes acceptable and in time quality always decreases.&lt;br&gt;
Most engineers have heard of the &lt;a href="https://www.oreilly.com/library/view/97-things-every/9780596809515/ch08.html" rel="noopener noreferrer"&gt;Boy Scout Rule&lt;/a&gt;: 'Always leave the code better than you found it'. It's much easier to leave the place in a better state than you found it, if you found it in good condition in the first place.&lt;/p&gt;
&lt;h2&gt;
  
  
  How do we start to improve the quality of our code?
&lt;/h2&gt;

&lt;p&gt;Improvements over time are the result of incremental progress rather than a huge leaps forward.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The first step toward the management of disease was replacement of demon theories and humours theories by the germ theory. That very step, the beginning of hope, in itself dashed all hopes of magical solutions. It told workers that progress would be made stepwise, at great effort, and that a persistent, unremitting care would have to be paid to a discipline of cleanliness. So it is with software engineering today.&lt;br&gt;
-- &lt;cite&gt;Frederick P. Brooks Jr. &lt;a href="https://www.cs.unc.edu/techreports/86-020.pdf" rel="noopener noreferrer"&gt;No Silver Bullet&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While you could enforce cleaner code manually with with documents like styleguides, it is much easier to outsource these tasks to automated tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc37kjtkn760b303wt2ta.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc37kjtkn760b303wt2ta.jpg" alt="Never send a human to do a machines job" width="752" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To hypermodernize your code, it's essential to maintain high coding standards and ensure that your codebase adheres to those standards consistently. There are several strategies for achieving this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CI system that automatically checks your code whenever you push changes to a code repository (like GitHub). By running tools that check your code against coding standards during every push, you get immediate feedback. If your code doesn't meet the standards, the CI will alert you.&lt;/li&gt;
&lt;li&gt;Linters are tools that scan your code for style and quality issues. Running them in "daemon mode" with a file watcher means that they constantly keep an eye on your code. As you write or modify code in your development environment, the linters provide real-time feedback about any deviations from your coding standards.&lt;/li&gt;
&lt;li&gt;Many Integrated Development Environments (IDEs) come with built-in support for various coding standards and linters. This means that, as you write code, the IDE can highlight issues and suggest improvements in real-time.&lt;/li&gt;
&lt;li&gt;Pre-commit Hooks: &lt;a href="https://pre-commit.com/" rel="noopener noreferrer"&gt;Pre-commit&lt;/a&gt; is a tool that can be set up to enforce coding rules and standards before you commit your changes to your code repository. This ensures that you can't even check in (commit) code that doesn't meet your standards. This allows a code reviewer to focus on the architecture of a change while not wasting time with trivial style nitpicks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These strategies help you keep your code in good shape. They ensure that your code is always checked for quality and style, whether you're writing it, pushing it to the code repository, or committing changes. This way, you catch and fix issues early, making your codebase more modern and maintainable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://xkcd.com/1513/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femynmaze9kjhns6fmzrl.png" alt="Style matters" width="740" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You won't have much discussion about imports, this is a good point to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pycqa.github.io/isort/" rel="noopener noreferrer"&gt;isort&lt;/a&gt; will sort the imports for you&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/MarcoGorelli/absolufy-imports" rel="noopener noreferrer"&gt;absolufy-imports&lt;/a&gt; converts relative imports to absolute ones.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/asmeurer/removestar" rel="noopener noreferrer"&gt;removestar&lt;/a&gt; replaces &lt;code&gt;import *&lt;/code&gt; in Python files with explicit imports.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hakancelik96/unimport" rel="noopener noreferrer"&gt;unimport&lt;/a&gt;: removes unused imports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get all your code into a consistent format the next step is to run a formatter.&lt;br&gt;
I recommend &lt;a href="https://github.com/python/black" rel="noopener noreferrer"&gt;black&lt;/a&gt;, the well-known uncompromising code formatter, which is the most popular choice.&lt;br&gt;
Alternatives to &lt;code&gt;black&lt;/code&gt; are &lt;a href="https://github.com/myint/autoflake" rel="noopener noreferrer"&gt;autoflake&lt;/a&gt;, &lt;a href="https://github.com/prettier/prettier" rel="noopener noreferrer"&gt;prettier&lt;/a&gt; and &lt;a href="https://github.com/google/yapf" rel="noopener noreferrer"&gt;yapf&lt;/a&gt;, if you do not agree with &lt;code&gt;blacks&lt;/code&gt; constraints.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/asottile/pyupgrade" rel="noopener noreferrer"&gt;pyupgrade&lt;/a&gt; and &lt;a href="https://github.com/ikamensh/flynt" rel="noopener noreferrer"&gt;flynt&lt;/a&gt; are examples of tools that modify your code base from earlier python versions into the newest python syntax, rewriting all string formats into f-strings and similar things.&lt;/p&gt;

&lt;p&gt;Ultimately we want to test our code with &lt;a href="https://flake8.pycqa.org/en/latest/" rel="noopener noreferrer"&gt;Flake8&lt;/a&gt; and &lt;a href="https://github.com/DmytroLitvinov/awesome-flake8-extensions" rel="noopener noreferrer"&gt;plugins&lt;/a&gt; to enforce a more consistent code style and to encourage best practices.&lt;br&gt;
When you first introduce &lt;code&gt;flake8&lt;/code&gt; or a new plug-in commonly you have a lot of violations that you can silence with a &lt;code&gt;#noqa&lt;/code&gt; comment.&lt;br&gt;
When you first introduce a new &lt;code&gt;flake8&lt;/code&gt; plugin, you will likely have a lot of violations, which you silence with &lt;code&gt;#noqa&lt;/code&gt; comments. Over time these comments will become obsolete because you fixed the. &lt;a href="https://github.com/asottile/yesqa#yesqa" rel="noopener noreferrer"&gt;yesqa&lt;/a&gt; will automatically remove these unnecessary &lt;code&gt;#noqa&lt;/code&gt; comments.&lt;/p&gt;

&lt;p&gt;A more modern alternative for &lt;code&gt;flake8&lt;/code&gt; is &lt;a href="https://docs.astral.sh/ruff/" rel="noopener noreferrer"&gt;Ruff&lt;/a&gt;: &lt;code&gt;Ruff&lt;/code&gt; can be used to replace &lt;code&gt;Flake8&lt;/code&gt; (plus a variety of plugins), &lt;code&gt;isort&lt;/code&gt;, &lt;code&gt;pydocstyle&lt;/code&gt;, &lt;code&gt;yesqa&lt;/code&gt;, &lt;code&gt;eradicate&lt;/code&gt;, &lt;code&gt;pyupgrade&lt;/code&gt;, and &lt;code&gt;autoflake&lt;/code&gt;, all while executing tens or hundreds of times faster than any individual tool. Ruff supports over 700 lint rules and goes beyond the responsibilities of a traditional linter, instead functioning as an advanced code transformation tool capable of upgrading type annotations, rewriting class definitions, sorting imports, and more.&lt;/p&gt;
&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;In the landscape of hypermodern Python, security is paramount. An array of automated security scanning tools exists to fortify code against vulnerabilities. While some tools primarily focus on securing the code, others offer insights into common errors and potential risks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/PyCQA/flake8-bugbear" rel="noopener noreferrer"&gt;Bugbear&lt;/a&gt; is not specifically a security tool but serves as an effective guard against common coding errors and pitfalls. It pinpoints and rectifies frequent mistakes like setting a list as a default value for a parameter and cautions against such practices, enhancing code robustness.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/PyCQA/bandit" rel="noopener noreferrer"&gt;Bandit&lt;/a&gt; is a dedicated security scanner designed to target critical security concerns such as SQL injection and cross-site scripting exploits. It meticulously scrutinizes the codebase to identify and alert developers about possible security breaches or vulnerabilities, thus fortifying the code against potential exploitation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pyupio/safety" rel="noopener noreferrer"&gt;Safety&lt;/a&gt; and &lt;a href="https://github.com/dependabot" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt; complement these security tools by focusing on external dependencies. Safety takes charge of examining your dependencies, ensuring they are up-to-date and free from any known vulnerabilities. Dependabot works similarly, scanning dependencies, verifying if they're current and assessing them for potential security flaws. This function is crucial as weaknesses in external dependencies can compromise the security of the entire codebase.&lt;/p&gt;

&lt;p&gt;Together, these tools form a comprehensive security net that not only secures the code directly but also safeguards against potential risks from external dependencies, ensuring the development of secure, reliable, and robust Python code within the hypermodern framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  You improve what you measure
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“When a measure becomes a target, it ceases to be a good measure.”&lt;br&gt;
-- Goodhart's Law&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The adage "You improve what you measure" underscores the significance of tracking metrics for improvement. This principle is intertwined with Goodhart's Law, stating that when a measure becomes the sole focus or goal, it loses its value as an effective metric.&lt;/p&gt;

&lt;p&gt;In the realm of codebase modernization, certain metrics can guide the process:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Coverage&lt;/strong&gt;: Tools such as &lt;a href="https://github.com/nedbat/coveragepy" rel="noopener noreferrer"&gt;Coverage&lt;/a&gt; and &lt;a href="https://github.com/Bachmann1234/diff_cover" rel="noopener noreferrer"&gt;Diff Cover&lt;/a&gt; assess how much of your code is under test coverage. While high coverage is valuable, the focus isn't just on achieving a specific percentage. It's crucial to ensure the tests are meaningful and effectively cover the essential aspects of the codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Complexity&lt;/strong&gt;: Metrics like &lt;a href="https://pypi.org/project/mccabe/" rel="noopener noreferrer"&gt;McCabe&lt;/a&gt;, &lt;a href="https://pypi.org/project/radon/" rel="noopener noreferrer"&gt;Radon&lt;/a&gt;, &lt;a href="https://pypi.org/project/xenon/" rel="noopener noreferrer"&gt;Xenon&lt;/a&gt;, and &lt;a href="https://pypi.org/project/lizard/" rel="noopener noreferrer"&gt;Lizard&lt;/a&gt; help evaluate the complexity of code. Lizard, in particular, is an efficient tool, capable of identifying highly complex code sections. It's considered helpful because if Lizard deems code complex, it likely needs simplification or better structuring. &lt;a href="https://pypi.org/project/cognitive-complexity/" rel="noopener noreferrer"&gt;Cognitive Complexity&lt;/a&gt;, also available as a &lt;a href="https://pypi.org/project/flake8-cognitive-complexity/" rel="noopener noreferrer"&gt;Flake8 plugin&lt;/a&gt;, further aids in assessing how humans perceive and interpret code, encompassing factors like decision points and recursive patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lack of Cohhesion in Methods, &lt;a href="https://pypi.org/project/lcom/" rel="noopener noreferrer"&gt;LCOM4&lt;/a&gt;&lt;/strong&gt; measures the relationship between methods within a class. It quantifies how much these methods are interdependent or independent from each other. This helps assess the class's cohesion, determining if methods are tightly or loosely coupled within a class, influencing code maintainability and aiding in targeted refactoring efforts for improved code quality.&lt;/p&gt;

&lt;p&gt;These measures are essential for monitoring and understanding aspects of the codebase that might need improvement. However, it's important not to merely aim for high numbers or low complexity scores. Rather, they should act as guiding posts in the pursuit of maintainable, readable, and efficient code. These metrics help identify areas that might benefit from refactoring, thereby contributing to a more organized, maintainable, and scalable codebase. The focus remains on understanding these metrics to make informed decisions for better code quality rather than solely targeting certain percentages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://xkcd.com/2060/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3q5wyeto4v9s2zr353i.png" alt="hygrometer" width="232" height="456"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Typing
&lt;/h2&gt;

&lt;p&gt;The pursuit of a hyper-modern codebase involves ensuring type correctness, an area where Mypy serves as an invaluable tool. Yet, implementing proper type annotations, especially in legacy code, can pose a significant challenge. Here's a more detailed expansion of the tools and methods to address this challenge:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mypy and Manual Annotation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mypy-lang.org/" rel="noopener noreferrer"&gt;Mypy&lt;/a&gt; stands as an essential static type-checking tool. Its primary function is to verify the correctness of types in your codebase. However, manually annotating types in legacy code can be laborious and time-consuming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MonkeyType:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To alleviate the burden of manual annotation, &lt;a href="https://github.com/Instagram/MonkeyType" rel="noopener noreferrer"&gt;MonkeyType&lt;/a&gt; offers a clever solution. It dynamically observes the types entering and leaving functions during code execution. Based on this observation, it generates a preliminary draft of type annotations. This significantly reduces the effort needed to add type hints to legacy code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pyre, PyRight and PyType:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pyre-check.org/" rel="noopener noreferrer"&gt;Pyre&lt;/a&gt; from Meta, &lt;a href="https://microsoft.github.io/pyright/" rel="noopener noreferrer"&gt;pyright&lt;/a&gt; from Microsoft and &lt;a href="https://google.github.io/pytype/" rel="noopener noreferrer"&gt;PyType&lt;/a&gt; from Google provide additional assistance. They can 'infer' types based on code flow and existing types within the code. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;infer-types:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/orsinium-labs/infer-types#infer-types" rel="noopener noreferrer"&gt;infer-types&lt;/a&gt; CLI tool is another beneficial asset. This tool automatically inserts initial annotations, acting as a useful starting point for adding type hints to the codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type-Checking at Runtime with Typeguard:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://typeguard.readthedocs.io/" rel="noopener noreferrer"&gt;Typeguard&lt;/a&gt; enables runtime type checking in a development environment. It is extremely helpful in ensuring that correct types are being passed around during testing, even if you do not want to activate strict runtime typechecking in your production environment.&lt;/p&gt;

&lt;p&gt;These tools and methods collectively aid in the process of introducing type annotations, especially in the context of legacy code. They offer a spectrum of options for reducing the manual overhead and ensuring type correctness, enabling developers to gradually upgrade and modernize the codebase with better type safety without significantly disrupting existing operations.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuynteik446i7jrivbgko.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuynteik446i7jrivbgko.jpg" alt="I have a very particular set of skills" width="698" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When adopting a hyper-modern approach, it's crucial to revamp and improve the testing ecosystem. Consider the following key aspects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unit Tests Readability and Transition:&lt;/strong&gt;&lt;br&gt;
Enhancing unit tests contributes significantly to code readability and precision. Tools like &lt;code&gt;Pytestify&lt;/code&gt; and &lt;code&gt;Unittest2Pytest&lt;/code&gt; serve as effective means to convert old-style unit tests into Pytest format, aligning them with modern testing standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing Beyond Pytest:&lt;/strong&gt;&lt;br&gt;
While the Hyper Modern Python series doesn’t take a stringent stance on testing beyond Pytest, it’s vital to explore advanced testing methodologies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hypothesis for Property-Based Testing:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://hypothesis.works/" rel="noopener noreferrer"&gt;Hypothesis&lt;/a&gt; is a Python library facilitating property-based testing. It offers a distinct advantage by generating a wide array of input data based on specified properties or invariants within the code. The perks of Hypothesis include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Testing:&lt;/strong&gt; Hypothesis uncovers edge cases and unexpected behaviors that conventional tests might miss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Test Maintenance:&lt;/strong&gt; Tests based on properties are less prone to breaking when the code undergoes refactoring or alterations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Confidence:&lt;/strong&gt; By testing diverse inputs and edge cases, Hypothesis enhances confidence in code correctness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Integration:&lt;/strong&gt; The library seamlessly integrates into existing test suites with a straightforward API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Debugging:&lt;/strong&gt; In case of test failures, Hypothesis simplifies the reproduction of the failed test case, aiding in debugging.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Advantages of Hypothesis:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Diverse Input Types Support:&lt;/strong&gt; Hypothesis accommodates various input types, including integers, strings, lists, and dictionaries, making testing more thorough and reliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Writing and Debugging:&lt;/strong&gt; Writing tests with Hypothesis reduces the burden of generating specific inputs for tests. The &lt;code&gt;hypothesis.extra.ghostwriter&lt;/code&gt; module automatically generates test functions, providing a smooth entry into property-based testing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The ultimate goal is to bolster testing by moving beyond traditional practices and incorporating property-based testing methods. This not only enriches the testing suite with a broader scope but also reduces maintenance efforts, fortifying code against unexpected flaws and changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/schemathesis/schemathesis" rel="noopener noreferrer"&gt;SchemaThesis&lt;/a&gt; is a powerful tool, especially when working with web APIs, and here's how it can enhance your testing capabilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Testing and Schema Verification:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;SchemaThesis&lt;/code&gt; operates in close conjunction with &lt;code&gt;Hypothesis&lt;/code&gt; to provide a comprehensive framework for web API testing. It streamlines the generation of tests and data by aligning them with OpenAPI or GraphQL specifications. This approach ensures the thorough validation of APIs against predetermined schemas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service Accessibility:&lt;/strong&gt;&lt;br&gt;
One of the standout features of &lt;code&gt;SchemaThesis&lt;/code&gt; is its flexibility as a service. It can be effortlessly utilized without necessitating deep coding or technical knowledge. This accessibility enables users without extensive programming backgrounds to take full advantage of its testing capabilities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automated Test Data Generation:&lt;/strong&gt;&lt;br&gt;
By interfacing with the OpenAPI or GraphQL specifications, &lt;code&gt;SchemaThesis&lt;/code&gt; automates the creation of test scenarios and data points that comply with the defined API schema. This function significantly enhances testing robustness and ensures that the API remains compliant with its expected behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficient Schema-Driven Testing:&lt;/strong&gt;&lt;br&gt;
Leveraging a schema-driven approach to API testing ensures that the generated tests are aligned with the expected structure, input, and output of the API. This methodology boosts efficiency and coverage in the testing phase, providing greater confidence in the API's behavior under various conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User-Friendly Testing Solution:&lt;/strong&gt;&lt;br&gt;
Its simplified approach allows users to easily point the tool at an OpenAPI or GraphQL specification, enabling the generation of comprehensive test data without delving into intricate coding or complex testing procedures.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;SchemaThesis&lt;/code&gt; offers a user-friendly and efficient way to test web APIs by utilizing predefined specifications and automatically generating test scenarios and data. This approach ensures adherence to the specified schema, allowing for robust and comprehensive testing without requiring an extensive coding background. It is also available as a &lt;a href="https://schemathesis.io/" rel="noopener noreferrer"&gt;service&lt;/a&gt; designed to handle the heavy lifting of API debugging so you can concentrate on delivering value with your API.&lt;/p&gt;
&lt;h2&gt;
  
  
  Quis custodiet ipsos custodes?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8dukfjvhxzix94s2x35.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8dukfjvhxzix94s2x35.gif" alt="Lack of Faith" width="498" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Who watches the watchmen?" is a question that resonates in many contexts, and in the realm of testing code, the reliability of your tests is a critical concern. Test coverage, often seen as the gold standard, isn't a &lt;a href="https://dev.to/antoinecoulon/dont-target-100-coverage-387o"&gt;guarantee of thoroughness&lt;/a&gt;. That's where mutation testing tools like Mutmut come into play.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mutmut.readthedocs.io/" rel="noopener noreferrer"&gt;Mutmut&lt;/a&gt; introduces a clever approach to scrutinizing your tests. It evaluates the effectiveness of your test suite by slightly altering the code after the tests have been written. If a test fails after a minor change, that's a good sign; it means the test is robust enough to catch those changes. But if the test passes even after the code change, it indicates that the test isn't effectively detecting that alteration – this is what Mutmut terms a "surviving mutant."&lt;/p&gt;

&lt;p&gt;While it's a powerful tool for enhancing test quality, mutation testing like Mutmut comes with a caveat: it can significantly extend the duration of your testing process. The exhaustive nature of this tool means that comprehensive testing might take a long time. Consequently, it's crucial to be selective about what you test with Mutmut to keep the testing duration manageable. Focusing on the core business logic or key functionalities is an effective strategy to use Mutmut without significantly extending the test execution time.&lt;/p&gt;

&lt;p&gt;By selectively targeting specific areas of code or the most crucial functions, you can effectively leverage Mutmut to ensure the strength and accuracy of your tests, thus enhancing their reliability and impact without unduly extending your testing time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Upgrades
&lt;/h2&gt;

&lt;p&gt;Staying current with the latest Python versions and framework updates is crucial for maintaining code health, security and functionality. To streamline these updates, tools like &lt;a href="https://github.com/asottile/pyupgrade" rel="noopener noreferrer"&gt;PyUpgrade&lt;/a&gt; and Ruff are invaluable. PyUpgrade is designed to effortlessly manage Python syntax updates, ensuring that the code remains aligned with the latest Python standards.&lt;/p&gt;

&lt;p&gt;For those working with Django, specific tools like &lt;a href="https://github.com/adamchainz/django-upgrade" rel="noopener noreferrer"&gt;Django-Upgrade&lt;/a&gt; and &lt;a href="https://github.com/browniebroke/django-codemod" rel="noopener noreferrer"&gt;Django-Codemod&lt;/a&gt; offer essential support. These dedicated tools aid in the seamless transition of Django code from earlier versions to the most recent one. They automate the process of converting legacy Django code to adapt to the latest version's syntax and conventions.&lt;/p&gt;

&lt;p&gt;Should a more tailored modification tool be necessary, developers can utilize &lt;a href="https://github.com/Instagram/LibCST" rel="noopener noreferrer"&gt;LibCST&lt;/a&gt; (Concrete Syntax Tree) to craft their own code transformation tool. LibCST offers a flexible approach, enabling users to build custom tools aligned with their unique requirements, allowing for modifications in code structure or style.&lt;/p&gt;

&lt;p&gt;These tools collectively facilitate the efficient and timely upgrading of Python codebases, allowing for smoother transitions to new language features, the latest syntax standards, and ensuring compatibility with the most recent frameworks and libraries. This keeps the codebase relevant and optimally aligned with the current Python ecosystem.&lt;/p&gt;
&lt;h3&gt;
  
  
  Refactoring as a Service.
&lt;/h3&gt;

&lt;p&gt;Refactoring is a vital aspect of maintaining code health and quality. &lt;a href="https://sourcery.ai/" rel="noopener noreferrer"&gt;Sourcery&lt;/a&gt; is a fantastic tool that helps with small-scale refactorings by automatically suggesting and implementing code improvements. It offers an automated approach to identify and carry out minor code alterations that can enhance readability, reduce duplication, or improve overall code structure.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;a href="https://www.sonarsource.com/" rel="noopener noreferrer"&gt;SonarCloud&lt;/a&gt; is a comprehensive code analysis service designed to identify and rectify issues related to code quality, security, and maintainability. It continuously scans and analyzes code repositories, ensuring adherence to coding standards, finding potential bugs, and offering comprehensive insights into code health. This platform flags problematic areas in the code, allowing developers to refactor and improve code quality effectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codescene.com/" rel="noopener noreferrer"&gt;CodeScene&lt;/a&gt; is another tool to manage technical debt. It helps you to identify the most critical areas and plan goals to reduce technical debt in each hotspot.&lt;/p&gt;

&lt;p&gt;Sourcery SonarCloud and CodeScene serve as powerful assistants in enhancing code quality and readability. Sourcery focuses on specific, smaller-scale refactoring tasks, while SonarCloud provides a broader perspective by analyzing codebases for overall health, security, and maintainability, guiding developers in making comprehensive improvements across their projects.&lt;/p&gt;
&lt;h2&gt;
  
  
  Refactoring
&lt;/h2&gt;

&lt;p&gt;When refactoring code, it is important to remember that "perfect is the opposite of done". Refactoring is an iterative process, and there may be times where code is not perfect, but is still useful and can be improved upon over time.&lt;/p&gt;

&lt;p&gt;Refactoring is about refining code to be maintainable, extensible, and modular by recognizing patterns and reducing redundancy. It involves adhering to principles like the &lt;a href="https://en.wikipedia.org/wiki/SOLID" rel="noopener noreferrer"&gt;SOLID&lt;/a&gt; principles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt;ingle Responsibility Principle: Each module/class should have a single responsibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O&lt;/strong&gt;pen-Closed Principle: Classes/modules should be open for extension but closed for modification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt;iskov Substitution Principle: Subtypes should be replaceable with their base types without altering the program's correctness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I&lt;/strong&gt;nterface Segregation Principle: Many client-specific interfaces are better than a single general-purpose interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;ependency Inversion Principle: Depend on abstractions, not on concrete implementations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The CUPID principles (from the lightning talk &lt;a href="https://dannorth.net/cupid-the-back-story/" rel="noopener noreferrer"&gt;"Why Every Single SOLID Principle is Wrong"&lt;/a&gt; take a descriptive rather than a prescriptive approach.&lt;/p&gt;

&lt;p&gt;The five CUPID properties are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dannorth.net/cupid-for-joyful-coding/#composable" rel="noopener noreferrer"&gt;&lt;strong&gt;C&lt;/strong&gt;omposable&lt;/a&gt;: plays well with others&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dannorth.net/cupid-for-joyful-coding/#unix-philosophy" rel="noopener noreferrer"&gt;&lt;strong&gt;U&lt;/strong&gt;nix philosophy&lt;/a&gt;: does one thing well&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dannorth.net/cupid-for-joyful-coding/#predictable" rel="noopener noreferrer"&gt;&lt;strong&gt;P&lt;/strong&gt;redictable&lt;/a&gt;: does what you expect&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dannorth.net/cupid-for-joyful-coding/#idiomatic" rel="noopener noreferrer"&gt;&lt;strong&gt;I&lt;/strong&gt;diomatic&lt;/a&gt;: feels natural&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dannorth.net/cupid-for-joyful-coding/#domain-based" rel="noopener noreferrer"&gt;&lt;strong&gt;D&lt;/strong&gt;omain-based&lt;/a&gt;: the solution domain models the problem domain in language and structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These principles guide better code creation, emphasizing maintainability and extensibility over perfection. They are guidelines, good advice, rather than hard rules, not natural laws like &lt;em&gt;Isaac Newtons&lt;/em&gt; &lt;strong&gt;Philosophiæ Naturalis Principia Mathematica&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe68hrj6siuoqvaoks2yp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe68hrj6siuoqvaoks2yp.jpg" alt="Isaac Newton" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In refactoring, remember that perfection can impede progress and  "perfect is the opposite of done". Refactoring is an iterative process that leads to better code incrementally. Code doesn’t need to be perfect but useful. The goal is maintainability and improvement over time rather than perfection at once. &lt;br&gt;
It is important to focus on creating code that is maintainable and extensible, rather than striving for perfection.&lt;/p&gt;
&lt;h2&gt;
  
  
  Examples of "Hypermodernization"
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Talk is cheap. Show me the code.”&lt;br&gt;
― &lt;cite&gt;Linus Torvalds&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While most of my 'hypermodernizing' was done on proprietary code, there is a good example in &lt;a href="https://github.com/cleder/pygeoif" rel="noopener noreferrer"&gt;pygeoif&lt;/a&gt;, which was brought up to the standard 10 years after the first version was released. The &lt;a href="https://github.com/cleder/pygeoif/compare/0.6...main" rel="noopener noreferrer"&gt;diff&lt;/a&gt; is not very helpful, almost every line was touched in the end, but you can compare the &lt;a href="https://github.com/cleder/pygeoif/tree/0.6" rel="noopener noreferrer"&gt;version 0.6&lt;/a&gt; to the current implementation. &lt;a href="https://github.com/cleder/fastkml" rel="noopener noreferrer"&gt;FastKML&lt;/a&gt; is still actively in the process of modernizing and refactoring.&lt;/p&gt;
&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cjolowicz.github.io/posts/hypermodern-python-01-setup/" rel="noopener noreferrer"&gt;Hypermodern Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cjolowicz/cookiecutter-hypermodern-python" rel="noopener noreferrer"&gt;Hypermodern Python Cookiecutter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oreilly.com/library/view/hypermodern-python-tooling/9781098139575/" rel="noopener noreferrer"&gt;Hypermodern Python Tooling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/antoinecoulon/dont-target-100-coverage-387o"&gt;Don't target 100% coverage... but achieve it anyway!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cleder/talks#tools" rel="noopener noreferrer"&gt;Tools&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The list of tools mentioned in this article is far from exhaustive, you will find more on the awsome 🕶️ lists&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/life4/awesome-python-code-formatters" rel="noopener noreferrer"&gt;Awesome Python Code Formatters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DmytroLitvinov/awesome-flake8-extensions" rel="noopener noreferrer"&gt;Awesome Flake8 Extensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/typeddjango/awesome-python-typing" rel="noopener noreferrer"&gt;Awesome Python Typing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cleder/awesome-python-testing" rel="noopener noreferrer"&gt;Awesome Python Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/augustogoulart/awesome-pytest" rel="noopener noreferrer"&gt;Awesome PyTest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/cleder/talks#security" rel="noopener noreferrer"&gt;Security&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/PyCQA/bandit" rel="noopener noreferrer"&gt;Bandit&lt;/a&gt; is a tool designed to find common security issues in Python code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/datadog/guarddog" rel="noopener noreferrer"&gt;GuardDog&lt;/a&gt; is a CLI tool that allows to identify malicious PyPI packages.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pyupio/safety" rel="noopener noreferrer"&gt;Safety&lt;/a&gt; checks Python dependencies for known security vulnerabilities and suggests the proper remediation for vulnerabilities detected.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  EuroPython 2022, PyCon Ireland Dublin 2022, Limerick 2023 Presentations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/cleder/talks#slides" rel="noopener noreferrer"&gt;Slides&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/cleder/talks/blob/main/pycon-limerick-2023.pdf" rel="noopener noreferrer"&gt;A Tale of Two Kitchens&lt;/a&gt; Hypermodernizing Your Codebase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/uwmQgCrCh2s" rel="noopener noreferrer"&gt;EuroPython July 2022 Recording&lt;/a&gt;&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/uwmQgCrCh2s"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/ZBTRWYK_4t4" rel="noopener noreferrer"&gt;PyCon Ireland November 2022 Recording&lt;/a&gt;&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZBTRWYK_4t4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>python</category>
      <category>refactoring</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Love Your Representation</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Sat, 04 Nov 2023 21:39:15 +0000</pubDate>
      <link>https://dev.to/ldrscke/love-your-representation-27mm</link>
      <guid>https://dev.to/ldrscke/love-your-representation-27mm</guid>
      <description>&lt;p&gt;Embrace the power of &lt;code&gt;__repr__&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;There are two ways out that Python provides us with, out of the box, to convert an object into a string: &lt;code&gt;__str__&lt;/code&gt; and &lt;code&gt;__repr__&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;__str__&lt;/code&gt; gets all the love, &lt;code&gt;__repr__&lt;/code&gt; is a bit more obscure.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Simple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;My name is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Simple(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;simple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Simple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Christian&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;__str__&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;__repr__&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;print(simple)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;repr(simple)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'%s' % simple&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'%r' % simple&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'{}'.format(simple)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'{!r}'.format(simple)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;f'{simple}'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;f'{simple!r}'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;'My name is Christian'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;'Simple(Christian)'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;F-Srings in newer Python versions also provide a nice shortcut: &lt;code&gt;f'{simple=}'&lt;/code&gt; will produce &lt;code&gt;'simple=Simple(Christian)'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnr3v20io10xc9rgicp2l.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnr3v20io10xc9rgicp2l.jpg" alt="Python loves string" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is the difference anyway?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;__str__&lt;/code&gt; is meant to define the "informal" or user-friendly string representation of an object. It's used when you want to provide a human-readable description of the object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;__repr__&lt;/code&gt; is meant for the "formal" or unambiguous string representation of an object. It's used for debugging, development, and to represent the object in a way that could be used to recreate the same object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of a Python object as a toy. When we want to describe this toy to someone, we can use two ways:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;__str__&lt;/code&gt; is like giving a friendly and simple description of the toy. It's what we tell our friends when we want them to know what the toy looks like or what it does. It's like saying, "This toy is colourful and fun to play with!"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxf8crrjlsgvj9m80u7d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxf8crrjlsgvj9m80u7d.jpg" alt="Python toy" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;__repr__&lt;/code&gt; is like giving a very detailed and technical description of the toy. It's what we tell someone who needs to know all the nitty-gritty details about the toy. A look under the hood.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q64ujv9a3pnrw5mzkpa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3q64ujv9a3pnrw5mzkpa.jpg" alt="Tech Python" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should I care about implementing a &lt;code&gt;__repr__&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Implementing a &lt;code&gt;__repr__&lt;/code&gt; method for your Python classes can be valuable for several reasons, particularly in debugging, development, and logging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debugging&lt;/strong&gt;: When you're debugging your code, having a well-defined &lt;code&gt;__repr__&lt;/code&gt; method can provide you with a clear and informative representation of an object's state. This can help you quickly identify issues and understand the data stored in an object, making debugging more efficient.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development&lt;/strong&gt;: During development, you may frequently need to inspect the contents of objects for testing and problem-solving. A useful &lt;code&gt;__repr__&lt;/code&gt; method can simplify this process by displaying relevant object details, helping you develop and fine-tune your code more effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging&lt;/strong&gt;: When you log information about your program's execution, having a good &lt;code&gt;__repr__&lt;/code&gt; method can be invaluable. Logging the string representation of objects can help you track the program's state, record important data, and diagnose issues when they occur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readable Output&lt;/strong&gt;: A well-implemented &lt;code&gt;__repr__&lt;/code&gt; method can provide human-readable output, making it easier to understand the state of objects. This is especially useful when you need to inspect the program's behavior during development or debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Environments&lt;/strong&gt;: In interactive Python environments like Jupyter notebooks, IPython, and REPL, the &lt;code&gt;__repr__&lt;/code&gt; method is used automatically when you evaluate an object. This makes it convenient to understand and explore the object's content without explicitly calling the &lt;code&gt;__repr__&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation and Exploration&lt;/strong&gt;: When working with libraries and frameworks, having a descriptive &lt;code&gt;__repr__&lt;/code&gt; can enhance the documentation and help others understand how to use your classes effectively. It also makes it easier for colleagues or users to explore and experiment with your code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementing a meaningful &lt;code&gt;__repr__&lt;/code&gt; method is a good practice because it provides a clear and informative way to represent your objects, making your code more maintainable, debuggable, and user-friendly. It's especially useful when you need to interact with and understand objects during the development and debugging process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoseq38dwj09ux6scxxk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoseq38dwj09ux6scxxk.jpg" alt="Inspection of a python" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples From The Python Standard Library
&lt;/h2&gt;

&lt;p&gt;An example is the &lt;code&gt;datetime&lt;/code&gt; module.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;862092&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Data classes provide a nice implementation.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InventoryItem&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;Class for keeping track of an item in inventory.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;unit_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;quantity_on_hand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ii&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InventoryItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;python&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;897&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ii&lt;/span&gt;
&lt;span class="nc"&gt;InventoryItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;python&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;897&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity_on_hand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg40slq5ttb1owwb64g59.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg40slq5ttb1owwb64g59.jpg" alt="Weird python" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even in the Python Standard Library the implementation of &lt;code&gt;__repr__&lt;/code&gt; may vary.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;int_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;uint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;short&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;short&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not undisputed and can be overridden (this is copied from: &lt;a href="https://docs.python.org/3/library/enum.html#enum.Enum.__repr__" rel="noopener noreferrer"&gt;3.12.0 Documentation » The Python Standard Library » Data Types » enum — Support for enumerations&lt;/a&gt;)&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;auto&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OtherStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;ALTERNATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;OTHER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;SOMETHING_ELSE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;cls_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cls_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OtherStyle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALTERNATE&lt;/span&gt;
&lt;span class="n"&gt;OtherStyle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ALTERNATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71u8341z7wkbsmebq6gc.jpg" alt="Polished python" width="800" height="457"&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/cleder/fastkml" rel="noopener noreferrer"&gt;fastkml&lt;/a&gt; is a library to work with &lt;code&gt;&amp;lt;KML /&amp;gt;&lt;/code&gt; a geospatial &lt;code&gt;&amp;lt;XML /&amp;gt;&lt;/code&gt; format.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastkml.gx&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Track&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;...&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Track&lt;/span&gt; &lt;span class="n"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://www.google.com/kml/ext/2.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;kml&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://www.opengis.net/kml/2.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;kml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="n"&gt;T02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;kml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;kml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="n"&gt;T02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;kml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;kml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;angles&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;45.54&lt;/span&gt; &lt;span class="mf"&gt;66.23&lt;/span&gt; &lt;span class="mf"&gt;77.0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;angles&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;angles&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;angles&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;75.54&lt;/span&gt; &lt;span class="mf"&gt;86.23&lt;/span&gt; &lt;span class="mf"&gt;17.0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;angles&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;-&lt;/span&gt;&lt;span class="mf"&gt;122.20&lt;/span&gt; &lt;span class="mf"&gt;37.37&lt;/span&gt; &lt;span class="mf"&gt;156.00&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;-&lt;/span&gt;&lt;span class="mf"&gt;122.20&lt;/span&gt; &lt;span class="mf"&gt;37.37&lt;/span&gt; &lt;span class="mf"&gt;152.00&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;-&lt;/span&gt;&lt;span class="mf"&gt;122.20&lt;/span&gt; &lt;span class="mf"&gt;37.37&lt;/span&gt; &lt;span class="mf"&gt;147.00&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Track&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Out of the box, python provides an unambiguous representation.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;fastkml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Track&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f3d3b5fa230&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is not much help.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;__init__&lt;/code&gt; method of this class is defined as:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_Geometry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A track describes how an object moves through the world over a given time period.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;target_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;extrude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tessellate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;altitude_mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AltitudeMode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;track_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TrackItem&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we define the &lt;code&gt;__repr__&lt;/code&gt; 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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ns=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target_id=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_id&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extrude=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extrude&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tessellate=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tessellate&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;altitude_mode=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;altitude_mode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;track_items=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;track_items&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The representation looks much better&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;
&lt;span class="nc"&gt;Track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://www.google.com/kml/ext/2.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;extrude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tessellate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;altitude_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;track_items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;TrackItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;tzutc&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;122.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;37.37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;156.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Angle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;45.54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tilt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;66.23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;77.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;TrackItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2010&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;tzutc&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;122.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;37.37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;152.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;TrackItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;122.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;37.37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;147.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Angle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;75.54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tilt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;86.23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;17.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern is simple, construct a string out of the objects attributes for each parameter in the &lt;code&gt;__init__&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;This could be the end. Just add a &lt;code&gt;__repr__&lt;/code&gt; that mirrors the &lt;code&gt;__init_&lt;/code&gt; signature of the class. There is nothing much to it, easily done and it will save you time and effort in the future when you have to debug your code or figure out what caused a bug from the log files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7zt2131earcc8z2yo9q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq7zt2131earcc8z2yo9q.jpg" alt="The End?" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Implementing this pattern to your existing code looks like a lot of tedious, error-prone, ungrateful, repetitive, manual labour. It is a major chore when your project has dozens of classes, let alone hundreds or thousands. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funnw7m8rxkg1tgh3b51w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funnw7m8rxkg1tgh3b51w.jpg" alt="Manual labour" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Being a developer, I’m too lazy to spend 8 hours mindlessly performing a function, but not too lazy to spend 16 hours automating it.&lt;br&gt;
&lt;cite&gt;&lt;a href="https://github.com/timothycrosley" rel="noopener noreferrer"&gt;Timothy Crosley&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky7pxjt3mw7ytbp2tlyh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky7pxjt3mw7ytbp2tlyh.jpg" alt="A Python coding" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/cleder/crepr" rel="noopener noreferrer"&gt;crepr&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It is pronounced "kraypr" (/kɹeɪpr/) like Americans and Australians pronounce crêpe, the French pancake. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://xkcd.com/2619/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5dlxgzzm98xk4fjudjw.png" alt="XKCD crepe " width="183" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u4wza3qbn9fcvdzwm16.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u4wza3qbn9fcvdzwm16.jpg" alt="crepe" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Python script that takes a file name as a command-line argument, imports the specified module, and then prints a &lt;code&gt;__repr__&lt;/code&gt; method for each class defined in the module. It creates the &lt;code&gt;__repr__&lt;/code&gt; by inspecting the keyword arguments of the &lt;code&gt;__init__&lt;/code&gt; method of the class.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Given the file &lt;code&gt;tests/classes/kw_only_test.py&lt;/code&gt; with the contents:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KwOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ crepr tests/kw_only_test.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;produces&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KwOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a string (c)representation for KwOnly.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__module__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;age=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;repr()&lt;/code&gt; of an instance of this class will be:&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tests.classes.kw_only_test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;KwOnly&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;kwo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KwOnly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Christian&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;kwo&lt;/span&gt;
&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kw_only_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;KwOnly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Christian&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is Unix-ish, does just one thing: it prints the modified file to &lt;code&gt;StdOut&lt;/code&gt; so you can redirect or pipe the output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixvwdk1s0wzr7smkaf98.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixvwdk1s0wzr7smkaf98.jpg" alt="Unix python" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code is tidy and clean, follows best practises, is fully tested and type checked as a &lt;a href="https://youtu.be/uwmQgCrCh2s?si=usohCOVmJK0Z4jPx" rel="noopener noreferrer"&gt;hypermodern&lt;/a&gt; package should be.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84z7lft9i5o81gh638ab.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84z7lft9i5o81gh638ab.jpg" alt="hyper modern kitchen" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is still in its infancy, but you can install it from &lt;a href="https://pypi.org/project/crepr/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgiksa4vepou89p12btfr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgiksa4vepou89p12btfr.jpg" alt="Baby python" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Implementing a &lt;code&gt;__repr__&lt;/code&gt; method is a best practice as it improves the debugging and development process, enhances code readability, and aids in effective communication of your object's structure and state. Do not forget to include the &lt;code&gt;!r&lt;/code&gt; qualifier when logging your objects.&lt;/p&gt;

&lt;p&gt;Give your representations some love.&lt;/p&gt;

&lt;p&gt;❤️&lt;code&gt;.__repr__(self) -&amp;gt; str:&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjoqvdbur1tdg4r2ccym0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjoqvdbur1tdg4r2ccym0.jpg" alt="Love your repr" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>cleancode</category>
      <category>observability</category>
    </item>
    <item>
      <title>Type Annotate an existing Python Django Codebase with MonkeyType</title>
      <dc:creator>Christian Ledermann</dc:creator>
      <pubDate>Tue, 21 Jul 2020 17:50:10 +0000</pubDate>
      <link>https://dev.to/ldrscke/type-annotate-an-existing-python-django-codebase-with-monkeytype-254i</link>
      <guid>https://dev.to/ldrscke/type-annotate-an-existing-python-django-codebase-with-monkeytype-254i</guid>
      <description>&lt;h1&gt;
  
  
  Why Add Types?
&lt;/h1&gt;

&lt;p&gt;There has been quite a buzz around type annotations in the recent past, so you may ask yourself if type checking is something you should do. This article is not a general introduction to type annotations and type checks, I recommend &lt;a href="https://cjolowicz.github.io/posts/hypermodern-python-04-typing/" rel="noopener noreferrer"&gt;Hypermodern Python: Typing&lt;/a&gt; to get an overview of the background. &lt;/p&gt;

&lt;p&gt;In a nutshell citing the above article: &lt;br&gt;
&lt;em&gt;"Type annotations are a way to annotate functions and variables with types. Combined with tooling that understands them, they can make your programs easier to understand, debug, and maintain. A static type checker can use type annotations and type inference to verify the type correctness of your program without executing it, helping you discover many bugs that would otherwise go unnoticed."&lt;/em&gt; or Dropbox puts it like this: &lt;em&gt;"If you aren’t using type checking in your large-scale Python project, now is a good time to get started — nobody who has made the jump I’ve talked to has regretted it. It really makes Python a much better language for large projects."&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;In addition to the above the tooling around type annotations is growing continuously and helps to keep your code DRY.&lt;br&gt;
There are libraries like &lt;a href="https://desert.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Desert&lt;/a&gt; or &lt;a href="https://python-typical.org/" rel="noopener noreferrer"&gt;Typical&lt;/a&gt; to help with data validation, &lt;a href="https://github.com/agronholm/typeguard" rel="noopener noreferrer"&gt;Typeguard&lt;/a&gt; or &lt;a href="https://github.com/FelixTheC/strongtyping" rel="noopener noreferrer"&gt;strongtyping&lt;/a&gt; to check types at runtime, &lt;a href="https://github.com/agronholm/sphinx-autodoc-typehints" rel="noopener noreferrer"&gt;sphinx-autodoc-typehints&lt;/a&gt; to help build documentation, &lt;a href="https://typer.tiangolo.com/" rel="noopener noreferrer"&gt;Typer&lt;/a&gt; to build CLIs, &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; a web framework for building APIs, and even transpilers to compile your python code with &lt;a href="https://github.com/python/mypy/tree/master/mypyc" rel="noopener noreferrer"&gt;Mypy to Python C&lt;/a&gt; or &lt;a href="https://medium.com/@konchunas/monkeytype-type-inference-for-transpiling-python-to-rust-64fa5a9eb966" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a more complete list please refer to &lt;a href="https://github.com/typeddjango/awesome-python-typing" rel="noopener noreferrer"&gt;Awesome Python Typing&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Do Not Let Legacy Code Become Technical Debt
&lt;/h1&gt;

&lt;p&gt;As Dropbox describes in &lt;a href="https://dropbox.tech/application/our-journey-to-type-checking-4-million-lines-of-python" rel="noopener noreferrer"&gt;Our journey to type checking 4 million lines of Python&lt;/a&gt; it is quite daunting to type annotate an existing codebase. The article describes in depth how you can approach gradual improvements with type annotations to your codebase.&lt;/p&gt;
&lt;h1&gt;
  
  
  Enter MonkeyType
&lt;/h1&gt;

&lt;p&gt;But why do I have to write my annotations manually? After all Python knows what type a variable is at runtime. The folks at Instagram thought the same and created MonkeyType. A similar projects is &lt;a href="https://github.com/dropbox/pyannotate" rel="noopener noreferrer"&gt;PyAnnotate&lt;/a&gt; created by Dropbox. &lt;a href="https://google.github.io/pytype/" rel="noopener noreferrer"&gt;Pytype&lt;/a&gt; generates files of inferred type information, located by default in &lt;code&gt;.pytype/pyi&lt;/code&gt;. &lt;a href="https://pyre-check.org/" rel="noopener noreferrer"&gt;Pyre&lt;/a&gt; has a powerful feature for migrating codebases to a typed format. The &lt;a href="https://pyre-check.org/docs/pysa-coverage/" rel="noopener noreferrer"&gt;infer&lt;/a&gt; command-line option ingests a file or directory, makes educated guesses about the types used, and applies the annotations to the files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://monkeytype.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;MonkeyType&lt;/a&gt; collects runtime types of function arguments and return values, and can automatically generate stub files or even add draft type annotations directly to your Python code based on the types collected at runtime.&lt;/p&gt;

&lt;p&gt;Have a look at the  &lt;a href="https://instagram-engineering.com/let-your-code-type-hint-itself-introducing-open-source-monkeytype-a855c7284881" rel="noopener noreferrer"&gt;Introduction to MonkeyType&lt;/a&gt; for an overview of its functionality or listen to the &lt;a href="https://www.pythonpodcast.com/monkeytype-with-carl-meyer-and-matt-page-episode-146/" rel="noopener noreferrer"&gt;MonkeyType podcast&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Setup MonkeyType For a Django Project With PyTest
&lt;/h1&gt;

&lt;p&gt;While you can run MonkeyType in a production like environment and get real world data on how you code is called, the most common use case is probably to get this data out of test runs. If you do not use pytest with django &lt;a href="https://djangostars.com/blog/django-pytest-testing/" rel="noopener noreferrer"&gt;Testing Your Django App With Pytest&lt;/a&gt; should get you started.&lt;/p&gt;

&lt;p&gt;Install the required dependencies by creating a file &lt;code&gt;monkeytype.txt&lt;/code&gt; with the contents&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mypy
mypy-extensions
MonkeyType
pytest-monkeytype
django-stubs
djangorestframework-stubs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and install the dependencies with &lt;code&gt;pip install -r monkeytype.txt&lt;/code&gt; As of writing this &lt;code&gt;pytest-monkeytype&lt;/code&gt; needs &lt;code&gt;MonkeyType==19.11.2&lt;/code&gt;, this is fixed in this &lt;a href="https://github.com/mariusvniekerk/pytest-monkeytype/pull/3" rel="noopener noreferrer"&gt;Pull Request&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure mypy with &lt;code&gt;mypy.ini&lt;/code&gt; e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[mypy]
disallow_any_generics = True
disallow_untyped_calls = True
disallow_untyped_defs = True
disallow_untyped_decorators = True
ignore_errors = False
ignore_missing_imports = True
implicit_reexport = False
strict_optional = True
strict_equality = True
no_implicit_optional = True
warn_unused_ignores = True
warn_redundant_casts = True
warn_unused_configs = True
warn_unreachable = True
warn_no_return = True
warn_return_any = True

plugins =
    mypy_django_plugin.main,
    mypy_drf_plugin.main

[mypy.plugins.django-stubs]
django_settings_module = "test_settings"

[mypy-drfdapc.test_permissions]
ignore_errors = True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;monkeytype_config.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Standard Library
import os
from contextlib import contextmanager
from typing import Iterator

# 3rd-party
from monkeytype.config import DefaultConfig


class MonkeyConfig(DefaultConfig):
    @contextmanager
    def cli_context(self, command: str) -&amp;gt; Iterator[None]:
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_settings")
        import django

        django.setup()
        yield


CONFIG = MonkeyConfig()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the &lt;code&gt;DJANGO_SETTINGS_MODULE&lt;/code&gt; to the one that is used in tests.&lt;br&gt;
Now you can run your tests with &lt;code&gt;pytest --monkeytype-output=./monkeytype.sqlite3&lt;/code&gt; and &lt;code&gt;export MT_DB_PATH=./monkeytype.sqlite3&lt;/code&gt; on Bash, or &lt;code&gt;setenv MT_DB_PATH ./monkeytype.sqlite3&lt;/code&gt; on C Shell. &lt;/p&gt;

&lt;p&gt;Check which modules MonkeyType has collected infomation for with &lt;code&gt;monkeytype list-modules&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drfdapc$ monkeytype list-modules
drfdapc.permissions
drfdapc.test_permissions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then use the &lt;code&gt;monkeytype stub some.module&lt;/code&gt; command to generate a stub file for a module, or apply the type annotations directly to your code with &lt;code&gt;monkeytype apply some.module&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drfdapc$ monkeytype stub drfdapc.permissions
from django.core.handlers.wsgi import WSGIRequest
from unittest.mock import Mock


def allow_all(*args, **kwargs) -&amp;gt; bool: ...


def allow_authenticated(request: WSGIRequest, *args, **kwargs) -&amp;gt; bool: ...


def allow_authorized_key(
    request: WSGIRequest,
    view: Mock,
    *args,
    **kwargs
) -&amp;gt; bool: ...


def allow_staff(request: WSGIRequest, *args, **kwargs) -&amp;gt; bool: ...


def allow_superuser(request: WSGIRequest, *args, **kwargs) -&amp;gt; bool: ...


def deny_all(*args, **kwargs) -&amp;gt; bool: ...


class DABasePermission:
    def has_object_permission(
        self,
        request: WSGIRequest,
        view: None,
        obj: Mock
    ) -&amp;gt; bool: ...
    def has_permission(self, request: WSGIRequest, view: None) -&amp;gt; bool: ...


class DACrudBasePermission:
    def has_object_permission(
        self,
        request: WSGIRequest,
        view: None,
        obj: Mock
    ) -&amp;gt; bool: ...
    def has_permission(self, request: WSGIRequest, view: None) -&amp;gt; bool: ...


class DARWBasePermission:
    def has_object_permission(
        self,
        request: WSGIRequest,
        view: None,
        obj: Mock
    ) -&amp;gt; bool: ...
    def has_permission(self, request: WSGIRequest, view: None) -&amp;gt; bool: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking the annotations with mypy shows that there is still some work to do and it demonstrates nicely which errors mypy catches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drfdapc$ mypy drfdapc/permissions.py
...
drfdapc/permissions.py:71: error: Function is missing a type annotation for one or more arguments
drfdapc/permissions.py:86: error: Untyped decorator makes function "allow_superuser" untyped
...
drfdapc/permissions.py:162: error: Argument 2 of "has_permission" is incompatible with supertype "BasePermission"; supertype defines the argument type as "View"
drfdapc/permissions.py:162: note: This violates the Liskov substitution principle
drfdapc/permissions.py:162: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
...
Found 17 errors in 1 file (checked 1 source file)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that MonkeyType’s annotations are an informative first draft, to be checked and corrected by a developer.&lt;br&gt;
In the above example we never pass a view, all objects are mocked and &lt;code&gt;WSGIRequest&lt;/code&gt; is too specific, so we need to adjust this. Also run &lt;code&gt;black&lt;/code&gt; and &lt;code&gt;isort&lt;/code&gt; on this file to have a nicely formatted code. You can see the workflow in &lt;a href="https://github.com/PrimarySite/drf-deny-allow-pc/pull/11" rel="noopener noreferrer"&gt;this pull request&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start with small modules that have few (or even better no) dependencies on other parts of your code, apply the types, check the correctness of the annotations with &lt;code&gt;mypy some/module.py&lt;/code&gt; fix the typing information, and move on to the next module.&lt;/p&gt;

&lt;h1&gt;
  
  
  Little Helpers
&lt;/h1&gt;

&lt;p&gt;You will want to keep your type information clean, readable and consistent. To achieve this there are some plugins for flake8.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/flake8-annotations-complexity/" rel="noopener noreferrer"&gt;flake8-annotations-complexity&lt;/a&gt; reports on too complex, hard to read type annotations. Complex type annotations often means bad annotation usage, wrong code decomposition or improper data structure choice. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/flake8-annotations-coverage/" rel="noopener noreferrer"&gt;flake8-annotations-coverage&lt;/a&gt; reports on files with a lot of code without type annotations. This is mostly useful when you add type annotations to existing large codebase and want to know if new code in annotated modules is annotated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/flake8-type-annotations/" rel="noopener noreferrer"&gt;flake8-type-annotations&lt;/a&gt; is used to validate type annotations syntax as it was originally proposed &lt;/p&gt;

&lt;p&gt;&lt;a href="https://pypi.org/project/flake8-annotations/" rel="noopener noreferrer"&gt;flake8-annotations&lt;/a&gt; detects the absence of function annotations.&lt;br&gt;
What this won't do: Check variable annotations respect stub files, or replace mypy.&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>typehints</category>
      <category>refactoring</category>
    </item>
  </channel>
</rss>
