<?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: Carles Julià</title>
    <description>The latest articles on DEV Community by Carles Julià (@chaosct).</description>
    <link>https://dev.to/chaosct</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1070753%2Fa4f8265f-72fb-43dc-81c9-a2ff4da0eb93.jpeg</url>
      <title>DEV Community: Carles Julià</title>
      <link>https://dev.to/chaosct</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chaosct"/>
    <language>en</language>
    <item>
      <title>Improving the Python code quality of the team with pylint (and ChatGPT)</title>
      <dc:creator>Carles Julià</dc:creator>
      <pubDate>Tue, 20 Jun 2023 13:16:13 +0000</pubDate>
      <link>https://dev.to/chaosct/improving-the-python-code-quality-of-the-team-with-pylint-and-chatgpt-18af</link>
      <guid>https://dev.to/chaosct/improving-the-python-code-quality-of-the-team-with-pylint-and-chatgpt-18af</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;ℹ️ Note&lt;/p&gt;

&lt;p&gt;This is the english version of &lt;a href="https://dev.to/chaosct/augmentant-la-qualitat-de-codi-python-de-lequip-amb-pylint-i-chatgpt-26d8"&gt;the original post, in Catalan&lt;/a&gt;. &lt;br&gt;
It was translated using ChatGPT (GPT-4).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Modern Development in Python
&lt;/h2&gt;

&lt;p&gt;Python is not what it used to be. For a while now, modern development with Python involves a whole set of tools that help us improve code quality.&lt;/p&gt;

&lt;p&gt;Linting projects such as &lt;code&gt;pylint&lt;/code&gt;, and more recently &lt;code&gt;ruff&lt;/code&gt;, have been analyzing code statically to find issues for some time.&lt;/p&gt;

&lt;p&gt;More recently, &lt;code&gt;mypy&lt;/code&gt; helps us verify that the code is correct through an explicit declaration of types.&lt;/p&gt;

&lt;p&gt;And to top it off, &lt;code&gt;pre-commit&lt;/code&gt; provides a way to automate these tools to run regularly.&lt;/p&gt;

&lt;p&gt;All of these tools turn out to be essential in the day-to-day life of the development team.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If you have experience with &lt;code&gt;mypy&lt;/code&gt;, you have probably encountered situations where it does not behave as you expected. The temptation to add a &lt;code&gt;# ignore: typing&lt;/code&gt; is great, or to use &lt;code&gt;typing.cast&lt;/code&gt; to remove the error. After all, if we don't satisfy &lt;code&gt;mypy&lt;/code&gt;, we won't pass the &lt;code&gt;pre-commit&lt;/code&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="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'a'&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;B&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;b&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;A&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="s"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;

&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# We know that b is an instance of B, but mypy doesn't
&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;# mypy doesn't like this
&lt;/span&gt;
&lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;# mypy accepts the change
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this satisfies mypy, it is very dangerous. What if the part of the code changes and &lt;code&gt;b&lt;/code&gt; ends up being from class &lt;code&gt;A&lt;/code&gt;? It could possibly lead to runtime errors, but in less evident places. If instead of directly calling &lt;code&gt;b.b()&lt;/code&gt; we passed it to another function, it could go unnoticed for a long time:&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="nn"&gt;queue&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;

&lt;span class="n"&gt;cua&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Queue&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;do_something_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;B&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="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;cua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;b_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;# mypy accepts the change
&lt;/span&gt;&lt;span class="n"&gt;do_something_later&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will not see the error until the element is consumed from the queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;The simplest alternative to &lt;code&gt;typing.cast&lt;/code&gt; is &lt;code&gt;assert isinstance&lt;/code&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="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# We know that b is an instance of B, but mypy doesn't
&lt;/span&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;# now mypy does
&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;# No problem
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you notice, the code reads: "I, the programmer, assure you that I know &lt;code&gt;b&lt;/code&gt; is an instance of &lt;code&gt;B&lt;/code&gt;, and if I'm wrong, let an exception be raised!". This prevents us from experiencing what happened in the previous example where the error went undetected for a long time.&lt;/p&gt;

&lt;p&gt;Also, in a production environment, &lt;code&gt;assert&lt;/code&gt;s can be disabled, eliminating any cost overhead.&lt;/p&gt;

&lt;p&gt;However, this does not work for types like &lt;code&gt;dict[str,B]&lt;/code&gt;. To solve this, we can use &lt;code&gt;pydantic&lt;/code&gt; and create a replacement command:&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="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;parse_obj_as&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assert_cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="s"&gt;"""Drop-in replacement for typing.cast, but with runtime checks.
&amp;nbsp; &amp;nbsp; If assert is disabled, this function does nothing.
&amp;nbsp; &amp;nbsp; Checks that value is of type expected_type and returns it as such.
&amp;nbsp; &amp;nbsp; this also works with arbitrary types such as dict[...], Pydantic models, etc.
&amp;nbsp; &amp;nbsp; Because of how Pydantic works, we can't prevent the check to use type coercion.
    This should be solved in Pydantic V2, in the future"""&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;# If asserts are disabled, we don't want to do anything
&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;__debug__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;parse_obj_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Expected value of type &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; but got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; instead"&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now replace &lt;code&gt;cast&lt;/code&gt; with &lt;code&gt;assert_cast&lt;/code&gt;, and you will have all the benefits of cast and assert at the same time.&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="n"&gt;assert_cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&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;B&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;b_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Remember that you can also ask ChatGPT to generate unit tests for the function. And remember to review them!&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Once we have identified the problem in our codebase, we need to find a way to prevent it from happening again. Obviously, we can explain it to the team members, but we cannot rely solely on memory; it will happen again.&lt;/p&gt;

&lt;p&gt;We would need to find a way to detect &lt;code&gt;typing.cast&lt;/code&gt; in the pre-commit to prevent it from slipping through, but how? Well, let's make a &lt;code&gt;pylint&lt;/code&gt; plugin.&lt;/p&gt;

&lt;p&gt;The problem is that writing a &lt;code&gt;pylint&lt;/code&gt; plugin seems to be a long and boring task. And, worst of all, we would become experts in &lt;code&gt;pylint&lt;/code&gt; plugins and would be constantly asked to write them. Nobody wants that.&lt;/p&gt;

&lt;p&gt;Therefore, the best alternative is to entrust it to ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT
&lt;/h2&gt;

&lt;p&gt;For ChatGPT to make us a &lt;code&gt;pylint&lt;/code&gt; plugin we have to ask for it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please write a pylint plugin that warns about using &lt;code&gt;typing.cast&lt;/code&gt; function anywhere in the code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For best results, it's better to use &lt;code&gt;GPT-4&lt;/code&gt;, &lt;code&gt;GPT-3.5&lt;/code&gt; hallucinates too much.&lt;/p&gt;

&lt;p&gt;On the first attempt, it will generate correct code, but it will miss some cases. For example, initially it only detected when &lt;code&gt;typing.cast&lt;/code&gt; was called but not when &lt;code&gt;cast&lt;/code&gt; was called after doing &lt;code&gt;from typing import cast&lt;/code&gt;. The best approach top solve that is to keep asking ChatGPT to add these cases.&lt;/p&gt;

&lt;p&gt;It helps a lot to have various &lt;em&gt;canary&lt;/em&gt; files where we deliberately add &lt;code&gt;typing.cast&lt;/code&gt; in different ways, to verify that the plugin indeed recognizes them.&lt;/p&gt;

&lt;p&gt;Once it works, it's very helpful to make a document in the same repository explaining the whole rationale for banning the use of &lt;code&gt;typing.cast&lt;/code&gt; and referring to it in the warning message. That way, everyone will be able to understand what's going on when the error pops up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovering the &lt;code&gt;pre-commit&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Once the plugin is added, the &lt;code&gt;pre-commit&lt;/code&gt; will fail, finding all the times &lt;code&gt;typing.cast&lt;/code&gt; appears in the code.&lt;/p&gt;

&lt;p&gt;We find ourselves in the situation where we would have to solve all pylint errors before being able to push the rule into th repository, and this can be a too big of a task.&lt;/p&gt;

&lt;p&gt;In this case, the strategy we chose was to add the pylint rule as a warning and not an error, so that over time, we can make the change and progressively replace the cases of &lt;code&gt;typing.cast&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pylint&lt;/code&gt; and &lt;code&gt;pre-commit&lt;/code&gt; are fantastic for this use case: enforcing a rule that bans the use of a function. This way we don't have to manually monitor that people don't skip it.&lt;/p&gt;

&lt;p&gt;We discovered that ChatGPT is very effective for this task.&lt;/p&gt;

&lt;p&gt;If you can't get the pre-commit green in one go, you will need to organize sessions to finish eliminating the banned function.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Augmentant la qualitat de codi Python de l'equip amb pylint (i ChatGPT)</title>
      <dc:creator>Carles Julià</dc:creator>
      <pubDate>Mon, 12 Jun 2023 08:09:35 +0000</pubDate>
      <link>https://dev.to/chaosct/augmentant-la-qualitat-de-codi-python-de-lequip-amb-pylint-i-chatgpt-26d8</link>
      <guid>https://dev.to/chaosct/augmentant-la-qualitat-de-codi-python-de-lequip-amb-pylint-i-chatgpt-26d8</guid>
      <description>&lt;h2&gt;
  
  
  Desenvolupament modern en Python
&lt;/h2&gt;

&lt;p&gt;Python ja no és el que era. Ja fa un temps que el desenvolupament modern amb Python involucra tot d'eines que ens ajuden a augmentar la qualitat del codi.&lt;/p&gt;

&lt;p&gt;Ja de temps enrere, projectes de linting com &lt;code&gt;pylint&lt;/code&gt;, i més recentment &lt;code&gt;ruff&lt;/code&gt;, analitzen el codi de forma estàtica per a trobar-ne problemes.&lt;/p&gt;

&lt;p&gt;Més recentment &lt;code&gt;mypy&lt;/code&gt; ens ajuda a comprovar que el codi sigui correcte a traves de una declaració explícita dels tipus.&lt;/p&gt;

&lt;p&gt;I per a reblar el clau, &lt;code&gt;pre-commit&lt;/code&gt; ens dóna la forma d'automatitzar aquestes eines per a que s'executin de forma regular.&lt;/p&gt;

&lt;p&gt;Totes aquestes eines resulten ser essencials en el dia a dia de l'equip de desenvolupament.&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Si teniu experiencia amb &lt;code&gt;mypy&lt;/code&gt;, segur que us haureu trobat amb situacions on no es comporta com esperàveu. La temptació de afegi un &lt;code&gt;# ignore: typing&lt;/code&gt; és gran, o també de fer servir &lt;code&gt;typing.cast&lt;/code&gt; per a treure l'error. Al cap i a la fi, si no aconseguim acontentar a &lt;code&gt;mypy&lt;/code&gt; no ens passarà el &lt;code&gt;pre-commit&lt;/code&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="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&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;a&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'a'&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;B&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&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;b&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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;A&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="s"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;

&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Sabem que b és instancia de B, però mypy no
&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Això no li agrada a mypy
&lt;/span&gt;
&lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# mypy accepta el canvi
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tot i que això acontenta a mypy, és molt perillós. I si la part del codi canvia i &lt;code&gt;b&lt;/code&gt; acaba sent de la classe &lt;code&gt;A&lt;/code&gt;? És possible que ens provoqui errors en temps d'execució, però en llocs poc evidents. Si en lloc de cridar directament &lt;code&gt;b.b()&lt;/code&gt; ho passéssim a una altra funció, podria passar desapercebut durant molt de temps:&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="nn"&gt;queue&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;

&lt;span class="n"&gt;cua&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Queue&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;fes_algo_mes_tard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;B&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;cua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;b_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# mypy accepta el canvi
&lt;/span&gt;&lt;span class="n"&gt;fes_algo_mes_tard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Llavors no veurem l'error fins que l'element sigui consumit de la cua.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;L'alternativa més senzilla a &lt;code&gt;typing.cast&lt;/code&gt; és &lt;code&gt;assert isinstance&lt;/code&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="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Sabem que b és instancia de B, però mypy no
&lt;/span&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ara mypy sí que ho sap
&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Cap problema
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si us hi fixeu, el codi llegeix: "Jo, programador, us asseguro que sé que &lt;code&gt;b&lt;/code&gt; és instància de &lt;code&gt;B&lt;/code&gt;, i si no tinc raó, que salti una excepció!". Això ens impedeix que ens passi com en l'exemple anterior on l'error quedava sense detectar durant temps.&lt;/p&gt;

&lt;p&gt;A més, en entorn de producció es poden desactivar els &lt;code&gt;assert&lt;/code&gt;s, eliminant qualsevol penalització en l'eficiència.&lt;/p&gt;

&lt;p&gt;Així i tot, això no funciona per tipus com &lt;code&gt;dict[str,B]&lt;/code&gt;. Per a solucionar-ho podem usar &lt;code&gt;pydantic&lt;/code&gt; i crear una comanda de substitució:&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="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;parse_obj_as&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assert_cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;"""Drop-in replacement for typing.cast, but with runtime checks.
    If assert is disabled, this function does nothing.
    Checks that value is of type expected_type and returns it as such.
    this also works with arbitrary types such as Union, Pydanitc models, etc.
    Because of how Pydantic works, we can't prevent the check to use type coercion.
    This should be solved in Pydantic V2, in the future"""&lt;/span&gt;

    &lt;span class="c1"&gt;# If asserts are disabled, we don't want to do anything
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;__debug__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;parse_obj_as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Expected value of type &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected_type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; but got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; instead"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ara substituïu &lt;code&gt;cast&lt;/code&gt; per &lt;code&gt;assert_cast&lt;/code&gt;, i així tindreu tots els beneficis de cast i assert alhora.&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="n"&gt;assert_cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&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;B&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;b_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nota: Recordeu que també podeu demanar a ChatGPT que generi tests unitaris per la funció. I recordeu-vos de revisar-los!&lt;/p&gt;

&lt;h2&gt;
  
  
  Implantació
&lt;/h2&gt;

&lt;p&gt;Un cop tenim identificat el problema en la nostra base de codi, cal trobar una forma perquè no torni a passar. Evidentment, se'ls pot explicar als membres de l'equip, però no podem confiar només en la memòria; tornarà a passar.&lt;/p&gt;

&lt;p&gt;Caldria trobar la forma de detectar &lt;code&gt;typing.cast&lt;/code&gt; en el pre-commit per a evitar que se'ns passi, però com? Doncs fem un plugin de &lt;code&gt;pylint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;El problema és que fer un plugin de &lt;code&gt;pylint&lt;/code&gt; té pinta de ser una tasca llarga i avorrida. I, pitjor de tot, ens convertiríem en experts en plugins de &lt;code&gt;pylint&lt;/code&gt; i ens n'encarregarien constantment. Ningú ho vol, això.&lt;/p&gt;

&lt;p&gt;Per tant, la millor alternativa és encarregar-ho a ChatGPT.&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT
&lt;/h2&gt;

&lt;p&gt;Perquè ChatGPT ens faci un plugin de &lt;code&gt;pylint&lt;/code&gt; li ho hem d'encarregar:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please write a pylint plugin that warns about using &lt;code&gt;typing.cast&lt;/code&gt; function anywhere in the code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Per a tenir uns bons resultats, millor usem &lt;code&gt;GPT-4&lt;/code&gt;, &lt;code&gt;GPT-3.5&lt;/code&gt; al·lucina massa.&lt;/p&gt;

&lt;p&gt;Al primer intent, ens generarà codi correcte, però que es deixa alguns casos. Per exemple, al principi només ens detectava quan es cridava &lt;code&gt;typing.cast&lt;/code&gt; però no quan es cridava &lt;code&gt;cast&lt;/code&gt;després de fer &lt;code&gt;from typing import cast&lt;/code&gt;. La tècnica és anar encarregant-li al ChatGPT que afegeixi aquests casos.&lt;/p&gt;

&lt;p&gt;Ajuda molt tenir diversos fitxers &lt;em&gt;canaris&lt;/em&gt; on afegim deliberadament &lt;code&gt;typing.cast&lt;/code&gt; de diferents formes, per a comprovar que efectivament, el plugin els reconeix.&lt;/p&gt;

&lt;p&gt;Un cop funcionant, ajuda molt fer un document en el mateix repositori on explica tot el raonament de per què es prohibeix l'ús de &lt;code&gt;typing.cast&lt;/code&gt; i referir-s'hi en el missatge del warning. D'aquesta forma tothom podrà entendre de què va el tema quan li salti l'error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recuperant el &lt;code&gt;pre-commit&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Un cop afegit el plugin, el &lt;code&gt;pre-commit&lt;/code&gt; ens fallarà, trobant totes les vegades que hi ha &lt;code&gt;typing.cast&lt;/code&gt; en el codi.&lt;/p&gt;

&lt;p&gt;Ens trobem en la situació que hauríem de solucionar tots els errors de pylint abans de poder fer push de la regla, i això pot ser una tasca massa gran, si n'hi ha masses.&lt;/p&gt;

&lt;p&gt;En aquest cas, l'estratègia que vam triar va ser la de afegir la regla de pylint com a warning i no com a error, de forma que, al llarg del temps, podem fer el canvi i anar substituint els casos de &lt;code&gt;typing.cast&lt;/code&gt;de forma progressiva. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pylint&lt;/code&gt; i &lt;code&gt;pre-commit&lt;/code&gt; són fantàstics per aquest cas d'ús: reforçar una norma que prohibeix l'ús d'una funció. D'aquesta manera no cal vigilar manualment que la gent no se la salti.&lt;/p&gt;

&lt;p&gt;Un descobriment és com d'efectiu és ChatGPT per a aquesta tasca.&lt;/p&gt;

&lt;p&gt;Si no es pot deixar el pre-commit en verd d'una sola passada, caldrà organitzar sessions per acabar d'eliminar la funció prohibida.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
