tag:blogger.com,1999:blog-92667172024-03-13T01:40:07.641-03:00Only PythonThis blog deals almost exclusively with my Python coding activities.André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.comBlogger275125tag:blogger.com,1999:blog-9266717.post-39331716659405087202022-10-27T16:46:00.000-03:002022-10-27T16:46:36.049-03:00Better NameError messages for Python<p>Python 3.11 is barely out and already the 3.12 alpha has some improvements for NameError messages. I suspect that these will be backported to 3.11 in time for the next release. </p><p>On <a href="https://discuss.python.org/t/suggestions-could-consider-standard-library-module-names-as-well/19348" target="_blank">Ideas Python Discussion</a>, <a href="https://twitter.com/pamelafox" target="_blank">Pamela Fox</a> suggested that it might be useful to consider potential missing import when a NameError was raised. Thus, instead of having</p>
<pre>>>> stream = io.StringIO()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'io' is not defined. Did you mean 'id'?
</pre>
<p>one would see</p>
<pre>>>> stream = io.StringIO()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'io' is not defined. Did you mean 'id'? Or did you forget to import 'io'?
</pre>
<p>Of course, something like this was already done by friendly/friendly-traceback (aka <b>Friendly</b>). However, in this particular case, the information provided by Friendly contained too much information; this has been since fixed.</p><p>To no one's surprise, <a href="https://twitter.com/pyblogsal" target="_blank">Pablo Galindo Salgado</a> came up with a version of this for Python, where names found in sys.stdlib_module_names were considered and potentially added, with the result as initially suggested by Pamela Fox. Pamela then made a second suggestion to see if names of popular third-party libraries could also be considered. This, for now, appears to be out of scope for Python.</p><p>This set the stage for a friendly (pun intended) competition...</p><p>I decided to revise what I had done for Friendly in such cases and found some room for improvements. First, let's look at a couple of examples (with screenshots) of the new behaviour for Python.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgSMSTQ_UK9fnq87019ZflN0bRkbA_DnQFevBKCA5m9KU94Xmd-nUebGOULWv8RFtE9TD9IThi996_75DBD97xv0vlbkk1Mq2d1p-SjzsLe1ahimstm6mGr9CaHy6VvSwuTYCchyh0tbi97JoTUr3_Mu3sgmInBfe_SHutMOa_fpTcnRl0njU4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="503" data-original-width="1320" height="245" src="https://blogger.googleusercontent.com/img/a/AVvXsEgSMSTQ_UK9fnq87019ZflN0bRkbA_DnQFevBKCA5m9KU94Xmd-nUebGOULWv8RFtE9TD9IThi996_75DBD97xv0vlbkk1Mq2d1p-SjzsLe1ahimstm6mGr9CaHy6VvSwuTYCchyh0tbi97JoTUr3_Mu3sgmInBfe_SHutMOa_fpTcnRl0njU4=w640-h245" width="640" /></a></div><br />As we can see with the first example, Python first makes suggestions about potential typos ('io' instead of 'id') followed by the suggestion about a missing import. Note that 'id' is a builtin who is never used with a dotted attribute.<p></p><p>The second example suggest a missing import only. However, as I am using Windows, this module does not exist.</p><p>Can Friendly do better? Note that Friendly can be used with Python 3.6+ (including Python 3.12), all of which would show the same output. I've chosen to use Python 3.10 for this example, as I will explain near the end of this post.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg1XA9eSaRzkUH0g-9LJFTqNAv9qbUbsDYtARiavlgmEEGZyAz17Trlg1yZapxlm7Z7EV5EaevKKoPkKeB6NijaASeFQQHuXC5OSH3LwnrKTHpX-biU-32cl2N80j0oOl2UyP3aykq09d_b1O1fSv_KDREnZbpksHlSNedyaZ6BEM18TTsA9so" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="655" data-original-width="1320" height="318" src="https://blogger.googleusercontent.com/img/a/AVvXsEg1XA9eSaRzkUH0g-9LJFTqNAv9qbUbsDYtARiavlgmEEGZyAz17Trlg1yZapxlm7Z7EV5EaevKKoPkKeB6NijaASeFQQHuXC5OSH3LwnrKTHpX-biU-32cl2N80j0oOl2UyP3aykq09d_b1O1fSv_KDREnZbpksHlSNedyaZ6BEM18TTsA9so=w640-h318" width="640" /></a></div><br />The message included in the Python traceback does not include the additional hint about a missing import in this case. However, Friendly adds it on its own. Note that it does not suggest 'id' as a potential typo. But what if we had made such a typo?<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjyfzTbiBcWa1L8SDXdOQBFXQCco0Mty3mFzYHcI3LttmXwwrJYGFGmFxY__GKqbLHiDuMJ2ii_N7ZRPBfq58N_z0KjdOuqjnRpEoJhH4j_RDBEKY_xEfTTLw9vBury77wpupGZR1gP2KwOXT2xLNvOxgm2iY-OzPagdMdqD4ozKUOLP23RlAE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="482" data-original-width="1320" height="234" src="https://blogger.googleusercontent.com/img/a/AVvXsEjyfzTbiBcWa1L8SDXdOQBFXQCco0Mty3mFzYHcI3LttmXwwrJYGFGmFxY__GKqbLHiDuMJ2ii_N7ZRPBfq58N_z0KjdOuqjnRpEoJhH4j_RDBEKY_xEfTTLw9vBury77wpupGZR1gP2KwOXT2xLNvOxgm2iY-OzPagdMdqD4ozKUOLP23RlAE=w640-h234" width="640" /></a></div><br />Here, Friendly does make the suggestion about a potential typo. What about the second example given above?<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhJXwgTuYPwB-vnjA3502enZwLLpLHzXj4W0sXm4QWF-zsYfFxoLYEFT9D7mOaJkpUccNRJjUtbFfu1wWdxhFMFD56ODI0pVr-9FCZPl5nfOFbQKnsFoPibni5zIb9JHDl8qF-sWZX4j8-Ce4reUIXsWLtlu6_nfQV2KbQwpr3CI4dFG6YdZiM" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="371" data-original-width="1320" height="180" src="https://blogger.googleusercontent.com/img/a/AVvXsEhJXwgTuYPwB-vnjA3502enZwLLpLHzXj4W0sXm4QWF-zsYfFxoLYEFT9D7mOaJkpUccNRJjUtbFfu1wWdxhFMFD56ODI0pVr-9FCZPl5nfOFbQKnsFoPibni5zIb9JHDl8qF-sWZX4j8-Ce4reUIXsWLtlu6_nfQV2KbQwpr3CI4dFG6YdZiM=w640-h180" width="640" /></a></div><br /><br /><p></p><p>Friendly also uses sys.stdlib_module_names initially, but also check with importlib.util.find_spec() to see if the module can be located.</p><p>It can also find potentially relevant third-party modules that are installed, but not yet imported.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh2lnBU4jh29l4c9olMAm-T0Nfk6C6UMbLqnaNYXmkUtcOtfYC8rfSNjLqCFc6sIBlGAf0uc59lxChyCwIPrDVQXu8PHcYQt2pG3-xtS9bd0ueqDbmIh7bMiYiKZmasPzPFMaE99R28k6D-2eZTBB9gD6jZ36dxFNsy7ROsCHoG86iUWmbtSzg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="493" data-original-width="1024" height="308" src="https://blogger.googleusercontent.com/img/a/AVvXsEh2lnBU4jh29l4c9olMAm-T0Nfk6C6UMbLqnaNYXmkUtcOtfYC8rfSNjLqCFc6sIBlGAf0uc59lxChyCwIPrDVQXu8PHcYQt2pG3-xtS9bd0ueqDbmIh7bMiYiKZmasPzPFMaE99R28k6D-2eZTBB9gD6jZ36dxFNsy7ROsCHoG86iUWmbtSzg=w640-h308" width="640" /></a></div><br />Using importlib.util.find_spec() allows us to implement Pamela Fox's suggestion about suggesting third-party modules that are installed.<p></p><p>However, we can do even better with some dedicated code. To demonstrate this, I need to use the latest addition to the "friendly-traceback family" - which I have only tested with Python 3.10 so far.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgJuRdmOTtFeRNyzGOy_zAwKF0cSjPRMnvNifjD45O78XZ98zw9GVgP3MBpJbwwvPxRwZFFi9Z1Xz_-USUkFYqsiMaIcQbTRX8WvKA-mFyLNP1ZTVEjHNMnkQxNH2nm5T0_zJt3YOudpAgoNiGGwVKagUE9SyQY-qNP7yhrmAbtyMF08e7xXvU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="556" data-original-width="1074" height="332" src="https://blogger.googleusercontent.com/img/a/AVvXsEgJuRdmOTtFeRNyzGOy_zAwKF0cSjPRMnvNifjD45O78XZ98zw9GVgP3MBpJbwwvPxRwZFFi9Z1Xz_-USUkFYqsiMaIcQbTRX8WvKA-mFyLNP1ZTVEjHNMnkQxNH2nm5T0_zJt3YOudpAgoNiGGwVKagUE9SyQY-qNP7yhrmAbtyMF08e7xXvU=w640-h332" width="640" /></a></div><br />I'll likely have more to say about friendly_pandas in the near future.<p></p><h2 style="text-align: left;">Final thoughts</h2><p>For those excited about the improved traceback with Python 3.11 and PEP 657: Fine-grained error locations in tracebacks, but cannot yet install Python 3.11, please note that Friendly can already something similar, if not better with any Python version 3.6.1+</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiCGydig3B6wLKjwEK0qL6ghFyBI3nJhOdBoanuKc7gdqByzGAcZm0_I8Ayd6GSy-UgJXIJlFXM4eED8SUkEgI-KU2oJsa3oun0RkwcyZ4gVeaol2UMN2SNzrwGN7CFvtm1fFbki9Zd38WtjsA7546uRwgtW-8owLUYLraSW8T3NLuU4K2AYZg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="611" data-original-width="686" height="571" src="https://blogger.googleusercontent.com/img/a/AVvXsEiCGydig3B6wLKjwEK0qL6ghFyBI3nJhOdBoanuKc7gdqByzGAcZm0_I8Ayd6GSy-UgJXIJlFXM4eED8SUkEgI-KU2oJsa3oun0RkwcyZ4gVeaol2UMN2SNzrwGN7CFvtm1fFbki9Zd38WtjsA7546uRwgtW-8owLUYLraSW8T3NLuU4K2AYZg=w640-h571" width="640" /></a></div><br /><br /><p></p><p>I say "better" because, unlike Python's traceback, the information is not limited to a single line of code:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi-7GhShmpx4XFopCkVRWQO3fpwZfebaIXtjjGW2BWFEs4XaNuaG--q5qlL0kdeVpDLKLzCnLHu_bpyMsDvmHvi6obCeaWr58DfRWS0LG5ZkjDbB3azYv0GII3iVd5dEZrf2WKSKGAtvm4EeUffo02DWzj83i6NQSQz241JLsdY3KpXmsY21BQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="648" data-original-width="747" height="555" src="https://blogger.googleusercontent.com/img/a/AVvXsEi-7GhShmpx4XFopCkVRWQO3fpwZfebaIXtjjGW2BWFEs4XaNuaG--q5qlL0kdeVpDLKLzCnLHu_bpyMsDvmHvi6obCeaWr58DfRWS0LG5ZkjDbB3azYv0GII3iVd5dEZrf2WKSKGAtvm4EeUffo02DWzj83i6NQSQz241JLsdY3KpXmsY21BQ=w640-h555" width="640" /></a></div><br /><br /><p></p><p><br /></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-39895785191991243252022-10-18T16:07:00.000-03:002022-10-18T16:07:15.869-03:00pandas' SettingWithCopyWarning: did I get it right?<p> I am just beginning to learn pandas and am looking to provide some automated help. From what I read, it appears that SettingWithCopyWarning is something that confuse many people. Is the following correct?</p>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [2]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">([[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">],</span> <span class="p">[</span><span class="mi">40</span><span class="p">,</span> <span class="mf">50.</span><span class="p">,</span> <span class="mi">60</span><span class="p">]],</span>
<span class="n">index</span><span class="o">=</span><span class="nb">list</span><span class="p">(</span><span class="s2">"ab"</span><span class="p">),</span>
<span class="n">columns</span><span class="o">=</span><span class="nb">list</span><span class="p">(</span><span class="s2">"xyz"</span><span class="p">))</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [3]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">df</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="s2">"b"</span><span class="p">][</span><span class="s2">"x"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">99</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_subarea output_stream output_stderr output_text">
<pre>`SettingWithCopyWarning`:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [4]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="c1"># What is SettingWithCopyWarning ?</span>
<span class="n">what</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;">Pandas occasionally emits a <span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">SettingWithCopyWarning</span> when you use
'chained indexing', either directly or indirectly,and you then attempt
to assign a value to the result. By 'direct chained indexing', we mean
that your code contains something like:
<span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">...[</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">index_1</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">][</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">index_2</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">=</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">...</span><span style="background-color: white;"> </span>
During the first extraction using <span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">index_1</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span>, pandas found that the
series to be created contained values of different types. It
automatically created a new series converting all values to a common
type. The second indexing, <span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">index_2</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span> was then done a this copy instead
of the original dataframe. Thus, the assigment was not done on the
original dataframe, which caused Pandas to emit this warning.
An 'indirect chained indexing' essentially amount to the same problem
except that the second indexing is not done on the same line as that
which was done to extract the first series.
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [5]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="c1"># Can I get more specific information for what I just did?</span>
<span class="n">why</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;">You used direct chained indexing of a dataframe which made a copy of
the original content of the dataframe. If you try to assign a value to
that copy, the original dataframe will not be modified. Instead of
doing a direct chained indexing
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"b"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">][</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">...</span><span style="background-color: white;"> </span>
try:
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"b"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">,</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">...</span><span style="background-color: white;"> </span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [6]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="c1"># What about if I tried to use indirect chaining. </span>
<span class="c1"># There are two possibilities</span>
<span class="n">series</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="s2">"b"</span><span class="p">]</span>
<span class="n">series</span><span class="p">[</span><span class="s2">"x"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">99</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_subarea output_stream output_stderr output_text">
<pre>`SettingWithCopyWarning`:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [7]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">where</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);">Warning issued on line </span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">4</span><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);"> of code block </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">6</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);">.</span>
<span style="background-color: white; color: grey; text-decoration-color: rgb(128, 128, 128);"> 1| # What about if I tried to use indirect chaining. </span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> </span>
<span style="background-color: white; color: grey; text-decoration-color: rgb(128, 128, 128);"> 2| # There are two possibilities</span>
<span style="background-color: white; color: grey; text-decoration-color: rgb(128, 128, 128);"> 3|</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> series</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);"> =</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"b"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span>
<span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);"> > </span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">4|</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> series</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">] =</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> 99</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [8]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">why</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;">I suspect that you used indirect chained indexing of a dataframe.
First, you likely created a series using something like:
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">series</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">=</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[...]</span><span style="background-color: white;"> </span>
This made a copy of the data contained in the dataframe. Next, you
indexed that copy
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">series</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white;"> </span>
This had no effect on the original dataframe. If your goal is to
modify the value of the original dataframe, try something like the
following instead:
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[...,</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white;"> </span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [9]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="c1"># What if I do things in a different order</span>
<span class="n">series_1</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s2">"x"</span><span class="p">]</span>
<span class="n">series_1</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="s2">"b"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">99</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_subarea output_stream output_stderr output_text">
<pre>`SettingWithCopyWarning`:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [10]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">where</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);">Warning issued on line </span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">3</span><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);"> of code block </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">9</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);">.</span>
<span style="background-color: white; color: grey; text-decoration-color: rgb(128, 128, 128);"> 1| # What if I do things in a different order</span>
<span style="background-color: white; color: grey; text-decoration-color: rgb(128, 128, 128);"> 2|</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> series_1</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);"> =</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span>
<span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);"> > </span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">3|</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> series_1</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"b"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">] =</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> 99</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [11]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">why</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;">I suspect that you used indirect chained indexing of a dataframe.
First, you likely created a series using something like:
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">series_1</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">=</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[...]</span><span style="background-color: white;"> </span>
This made a copy of the data contained in the dataframe. Next, you
indexed that copy
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">series_1</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"b"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white;"> </span>
This had no effect on the original dataframe. If your goal is to
modify the value of the original dataframe, try something like the
following instead:
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[...,</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"b"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white;"> </span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [12]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="c1"># What if I had multiples data frames?</span>
<span class="n">df2</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
<span class="n">series</span> <span class="o">=</span> <span class="n">df</span><span class="o">.</span><span class="n">loc</span><span class="p">[</span><span class="s2">"b"</span><span class="p">]</span>
<span class="n">series</span><span class="p">[</span><span class="s2">"x"</span><span class="p">]</span> <span class="o">=</span> <span class="mi">99</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_subarea output_stream output_stderr output_text">
<pre>`SettingWithCopyWarning`:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [13]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">where</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);">Warning issued on line </span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">4</span><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);"> of code block </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">12</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="color: #cc4400; text-decoration-color: rgb(204, 68, 0);">.</span>
<span style="background-color: white; color: grey; text-decoration-color: rgb(128, 128, 128);"> 2|</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> df2</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);"> =</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">copy</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">()</span>
<span style="background-color: white; color: grey; text-decoration-color: rgb(128, 128, 128);"> 3|</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> series</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);"> =</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> df</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"b"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span>
<span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);"> > </span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);">4|</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);"> series</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">] =</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> 99</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [14]:</div>
<div class="inner_cell">
<div class="input_area">
<div class="highlight hl-ipython3"><pre><span></span><span class="n">why</span><span class="p">()</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;"></pre></div>
</div>
<div class="output_area">
<div class="prompt"></div>
<div class="output_html rendered_html output_subarea"><pre style="line-height: normal; overflow-x: auto; white-space: pre;">In your code, you have the following dataframes: <span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">{</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">'df2'</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">,</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">'df'</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">}</span>. I do
not know which one is causing the problem here; I will use the name
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df2</span> as an example.
I suspect that you used indirect chained indexing of a dataframe.
First, you likely created a series using something like:
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">series</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">=</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df2</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[...]</span><span style="background-color: white;"> </span>
This made a copy of the data contained in the dataframe. Next, you
indexed that copy
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">series</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[</span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white;"> </span>
This had no effect on the original dataframe. If your goal is to
modify the value of the original dataframe, try something like the
following instead:
<span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">df2</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">.</span><span style="background-color: white; color: #0011aa; text-decoration-color: rgb(0, 17, 170);">loc</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">[...,</span><span style="background-color: white; color: #101010; text-decoration-color: rgb(16, 16, 16);"> </span><span style="background-color: white; color: darkmagenta; text-decoration-color: rgb(139, 0, 139);">"x"</span><span style="background-color: white; color: #783114; text-decoration-color: rgb(120, 49, 20);">]</span><span style="background-color: white;"> </span>
</pre></div>
</div>
</div>
</div>
</div>
André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-56445115769220826812022-09-19T13:42:00.004-03:002022-09-19T13:50:59.752-03:00New milestone: friendly/friendly-traceback version 0.6 (and why not 1.0)<p>Just a few minutes ago, @isidentical <a href="https://twitter.com/isidentical/status/1571886462194286593" target="_blank">tweeted that PyPy 3.9 had implemented the new enhanced tracebacks that are going to be part of cPython 3.11</a>. Of course, I had to <a href="https://twitter.com/andre_roberge/status/1571890014988169216" target="_blank">reply to show that friendly/friendly-traceback have been able to do the same with all cPython version starting with 3.6.1</a>.</p><p>A few hours before this tweet, I had bumped the minor version number of both friendly and friendly-traceback from 0.5 to 0.6. I always keep them in sync; previously, friendly-traceback was at 0.5.63 and friendly was at 0.5.42.</p><p>friendly builds on friendly-traceback and is the package you want to install as an end-user. If you're just interested at retrieving the data produced by friendly-traceback and format it your own way, as do <a href="https://www.hackinscience.org/">https://www.hackinscience.org/</a> and <a href="https://futurecoder.io/">https://futurecoder.io/</a>, then you only need to install friendly-traceback. Both these websites have been making use of friendly-traceback quite successfully for quite a while. From that point of view, friendly-traceback is really mature enough to be considered as being a 1.0 version. However, other than always including more cases being covered, I have some interesting new additions planned, which makes me postpone giving it a 1.0 version number.</p><p>Quite a bit has been done since version 0.5. In particular, Tamil and Russian have been added as supported language. The syntax highlighting of the traceback location has been improved in friendly. Support for a new project, <a href="https://github.com/friendly-traceback/friendly_idle">friendly_idle</a> has been added. I've <a href="https://aroberge.blogspot.com/2022/06/friendly-idle.html" target="_blank">previously described</a>. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgT-QMvOi6A4kZx6e6SY1xbDLvfzaPmzu13kaFbESBgP-wQVMuQBV1Kei0N-EnMZuA7BG0XxHNW_3xMzVxsEDCgUQNHv5i41YcZ5Mv9ViZv3oby3Jcln0oChYfHDIELokv1lp6CGM_CKR1NCnsm_LVbh1ljKnUFUqs1zDDGzs-qo_XTaKxuBv0" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="855" data-original-width="961" height="569" src="https://blogger.googleusercontent.com/img/a/AVvXsEgT-QMvOi6A4kZx6e6SY1xbDLvfzaPmzu13kaFbESBgP-wQVMuQBV1Kei0N-EnMZuA7BG0XxHNW_3xMzVxsEDCgUQNHv5i41YcZ5Mv9ViZv3oby3Jcln0oChYfHDIELokv1lp6CGM_CKR1NCnsm_LVbh1ljKnUFUqs1zDDGzs-qo_XTaKxuBv0=w640-h569" width="640" /></a></div><br />Note that, when some new highlighted code is shown with friendly_idle, the highlighting done on previous line of codes is removed: this is by design, to help focus the attention on the latest area with problems. <p></p><p>I could say more about friendly ... but, why don't you try it out by yourself and see what you think. Feedback is always appreciated!</p><p><br /></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-37968801051303959032022-06-17T16:28:00.000-03:002022-06-17T16:28:02.158-03:00friendly_idle is done!<p><b>friendly_idle is done!</b></p><p>I've found a better solution for the remaining issue I had mentioned in the previous blog post.</p><p>I also found a fix for an "annoyance" mentioned by Raymond Hettinger on Twitter!</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjPMskE_1g3NtX61dI8Ip42mRK0U6_U5r5fRwnquNkpkiwBRwUHUDhMY1XAdMGIq5ADENvsWU5YSEnJ5QINVbol6JG5_Banu2p07E6daHb0cOYu0zhm37xY2eg5m4Qrqvf0JOPpp0_RntYuTKNMKtcRnnZ6goR0l5s7JKOXh1lrlfzIRPr9P3c" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="467" data-original-width="924" height="324" src="https://blogger.googleusercontent.com/img/a/AVvXsEjPMskE_1g3NtX61dI8Ip42mRK0U6_U5r5fRwnquNkpkiwBRwUHUDhMY1XAdMGIq5ADENvsWU5YSEnJ5QINVbol6JG5_Banu2p07E6daHb0cOYu0zhm37xY2eg5m4Qrqvf0JOPpp0_RntYuTKNMKtcRnnZ6goR0l5s7JKOXh1lrlfzIRPr9P3c=w640-h324" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">I could have changed the version to 1.0 ... but decided to wait until I get more feedback from users.</div><br /><br /><p></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-81307263142436346462022-06-14T08:10:00.001-03:002022-06-14T08:10:53.605-03:00Friendly IDLE<p><a href="https://github.com/friendly-traceback/friendly_idle" target="_blank">friendly_idle</a> is now available. This is just a quick announcement. Eventually I plan to write a longer blog post explaining how I use import hooks to patch IDLE and to provide seamless support for friend/friendly_traceback. Before I incorporated "partial" support for IDLE within <a href="https://friendly-traceback.github.io/docs/index.html" target="_blank">friendly</a>, I had released a package named friendly_idle ... but this is really a much better version.</p><p><br /></p><p>When you launch it from a terminal, the only clue you get that this is not your regular IDLE is from the window title.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjToJZKOe90K1-_KMHp3vQq-LSoqZnyaDFuSmanJnIrRWwFyoY_pJe5IWQtZNYOiR87oM6_O3jFhUNy2eQg_LHgK3NAzOuC-p_DLSuNN7KWTPWQCranAMtJv5cBG5Z-_T5YzOgG4mInMRQhu67KzsAuc4WLNz7DIuPtqbk1Qe5MHUSuiMQGhZE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="227" data-original-width="659" height="221" src="https://blogger.googleusercontent.com/img/a/AVvXsEjToJZKOe90K1-_KMHp3vQq-LSoqZnyaDFuSmanJnIrRWwFyoY_pJe5IWQtZNYOiR87oM6_O3jFhUNy2eQg_LHgK3NAzOuC-p_DLSuNN7KWTPWQCranAMtJv5cBG5Z-_T5YzOgG4mInMRQhu67KzsAuc4WLNz7DIuPtqbk1Qe5MHUSuiMQGhZE=w640-h221" width="640" /></a></div><p><br /></p>Since Python 3.10 (and backported to Python 3.8.10 and 3.9.5), IDLE provide support for sys.excepthook() (<a href="https://docs.python.org/3/whatsnew/3.10.html#idle-and-idlelib" target="_blank">see announcement</a>). Actually, in the announcement, it is <b><u>not</u></b> pointed out that this is only <b><u>partial</u></b> support: exceptions raised because of syntax errors cannot be captured by user-defined exception hooks. However, fear not, friendly_idle is perfectly capable to help you when your code has some syntax errors.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgg5KFosqD4V-jT6SuK9FQVdL4SwSHMh-_DRuN8xCGPhn3CTTGfoNawfYdt8mTUsCvyttfoeCAKEnT___-ulgwYzYGmY_9BLfQDIJHIgvBTlWQ5r4fkmMW9t2H8alRWTXuQmhWCc0KtsJTQcS_aYN0ECQH4eFSz_SvcHXeS0oumMXgyokiUN3k" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="249" data-original-width="647" height="246" src="https://blogger.googleusercontent.com/img/a/AVvXsEgg5KFosqD4V-jT6SuK9FQVdL4SwSHMh-_DRuN8xCGPhn3CTTGfoNawfYdt8mTUsCvyttfoeCAKEnT___-ulgwYzYGmY_9BLfQDIJHIgvBTlWQ5r4fkmMW9t2H8alRWTXuQmhWCc0KtsJTQcS_aYN0ECQH4eFSz_SvcHXeS0oumMXgyokiUN3k=w640-h246" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">And, of course, it can also do so for runtime errors.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjyS5GdrSnng-0zVdchWXSx70Nd3klp-qgYuS5yvakalS9uwjS1Xq4nMfcSf7GZqUe2iZF6PLCRGjuAhSFv2tF9YzI-TBVekgl_RpQDmqbHUV-QqNpr36vp-v5NkJr38exavPShsdxr6f_Z00m5-MkvkfsETCVlaepZlorQYY6lB_6zu-LpwX4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="728" data-original-width="717" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEjyS5GdrSnng-0zVdchWXSx70Nd3klp-qgYuS5yvakalS9uwjS1Xq4nMfcSf7GZqUe2iZF6PLCRGjuAhSFv2tF9YzI-TBVekgl_RpQDmqbHUV-QqNpr36vp-v5NkJr38exavPShsdxr6f_Z00m5-MkvkfsETCVlaepZlorQYY6lB_6zu-LpwX4=w629-h640" width="629" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;">The same is true for code run from a file as well:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjx46nB93mA16taYcsquV5GJtd4nMHQP1oUVd1pADh3WmtsuNUbpJ5MTQA2rRJkH4XWOBf7NaJr4sJm7fATUhkOXScYg8MC8cXcpJf3zHOfDnUHJjGbE-UavuBpsuzLuvWpnoY8D-6_3hyMPxpplcbM9cHs4-88V5hOvndnnZEoJcBFaBRS-0o" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="365" data-original-width="953" height="246" src="https://blogger.googleusercontent.com/img/a/AVvXsEjx46nB93mA16taYcsquV5GJtd4nMHQP1oUVd1pADh3WmtsuNUbpJ5MTQA2rRJkH4XWOBf7NaJr4sJm7fATUhkOXScYg8MC8cXcpJf3zHOfDnUHJjGbE-UavuBpsuzLuvWpnoY8D-6_3hyMPxpplcbM9cHs4-88V5hOvndnnZEoJcBFaBRS-0o=w640-h246" width="640" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;">If the code in a file contains some syntax error, friendly_idle is often much more helpful than IDLE. Here's an example from IDLE:</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiBSRWHiv6MDIi6ZNKke5RpJOoZGzW1hfDJ2sq-CDQrJV9tbr2UeCOrYevq2e5lstgmVYX8uXdmYqegfxyZYKPrOqgl6VTcotwPBwHbxJRZpQFKHVtD9XGbJZdemK9XeIaWr120V_KVnZNy5jLuiUw4mLckYm5CkWaivyb0dPNp_tTtyKSZi1Q" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="374" data-original-width="618" height="388" src="https://blogger.googleusercontent.com/img/a/AVvXsEiBSRWHiv6MDIi6ZNKke5RpJOoZGzW1hfDJ2sq-CDQrJV9tbr2UeCOrYevq2e5lstgmVYX8uXdmYqegfxyZYKPrOqgl6VTcotwPBwHbxJRZpQFKHVtD9XGbJZdemK9XeIaWr120V_KVnZNy5jLuiUw4mLckYm5CkWaivyb0dPNp_tTtyKSZi1Q=w640-h388" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">And the same example run using friendly_idle</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgq9k-sR82DxT2sJzxO9YkxoGo20IWRiryMXFh0WwrbARnhD0SblufSntlK7ZnAz3_7t0lfxtaX0OHcDE6vO22WT-oHzRRp-sAnNHL0TD_yN6yj8tWT1_7ou4JY8Bk7x1OKT598g214wsK1MggFONpBkXb9F7ZyFYmxFi1hLoNrFLLUkxBz-Go" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="864" data-original-width="920" height="600" src="https://blogger.googleusercontent.com/img/a/AVvXsEgq9k-sR82DxT2sJzxO9YkxoGo20IWRiryMXFh0WwrbARnhD0SblufSntlK7ZnAz3_7t0lfxtaX0OHcDE6vO22WT-oHzRRp-sAnNHL0TD_yN6yj8tWT1_7ou4JY8Bk7x1OKT598g214wsK1MggFONpBkXb9F7ZyFYmxFi1hLoNrFLLUkxBz-Go=w640-h600" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">Unfortunately, the tkinter errorbox does not use a monospace font (assumed by friendly/friendly_traceback for the formatting), and does not allow customization. I might have to figure out how to create my own dialog, hopefully with support for monospace font and colour highlighting. If anyone has some experience doing this, feel free to contact me! ;-)</div><br /><br /><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><br /><p></p><p><br /></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-59481297780007351262022-06-11T08:20:00.001-03:002022-06-11T11:13:58.879-03:00Nicer arithmetic with Python<p>Beginning programmers are often surprised by floating point arithmetic inaccuracies. If they use Python, many will often write posts saying that Python is "broken" when the see results as follows:</p>
<pre>>>> 0.1 + 0.2
0.30000000000000004
</pre>
<p>This particular result is not limited to Python. In fact, it is so common that there exists a site with a name inspired by this example (<a href="http://0.30000000000000004.com/" target="_blank">0.30000000000000004.com/</a>), devoted to explaining the origin of this puzzling result, followed by examples from many programming languages.</p><p>Python provides some alternatives to standard floating point operations. For example, one can use the <a href="https://docs.python.org/3/library/decimal.html" target="_blank">decimal module</a> to perform fixed point arithmetic operations. Here's an example.</p>
<pre>>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 7
>>> Decimal(0.1) + Decimal(0.2)
Decimal('0.3000000')
>>> print(_)
0.3000000
</pre>
<p>While one can set the precision (number of decimals) with which operations are performed, printed values can carry extra zeros: 0.3000000 does not look as "nice" as 0.3.</p><p>Another alternative included with Python's standard library is the <a href="https://docs.python.org/3/library/fractions.html" target="_blank">fractions module</a>: it provides support for rational number arithmetic.</p>
<pre>>>> from fractions import Fraction
>>> Fraction("0.1") + Fraction("0.2")
Fraction(3, 10)
>>> print(_)
3/10
</pre>
<p>However, the fractions module can yield some surprising results if one does not use string arguments to represent floats, as was mentioned by Will McGugan (of Rich and Textual fame) in <a href="https://twitter.com/willmcgugan/status/1530194972229640195" target="_blank">a recent tweet</a>.</p>
<pre>>>> from fractions import Fraction as F
>>> F("0.1")
Fraction(1, 10)
>>> F(0.1)
Fraction(3602879701896397, 36028797018963968)
</pre>
<p>In the second case, 0.1 is a float which means that it carries some intrinsic inaccuracy.
For the first case, <a href="https://github.com/python/cpython/blob/1190b6372139f1cd0fda8875fd618f327da8b64d/Lib/fractions.py#L111" target="_blank">some parsing is done by Python</a> to determine the number of decimal places to use before converting the result into a rational number. A similar result can be achieved using the limit_denominator method of the Fraction class:</p>
<pre>>>> F(0.1).limit_denominator(10)
Fraction(1, 10)
</pre>
<p>In fact, we do not have to be as restrictive in the limitation imposed on the denominator to achieve the same result</p>
<pre>>>> F(0.1).limit_denominator(1_000_000_000)
Fraction(1, 10)
</pre>
<p>While we can achieve some "more intuitive" results for floating point arithmetic using special modules from
Python, the notation that one has to use is not as simple as "0.1 + 0.2".
As Raymond Hettinger often says: "There has to be a better way."</p><h2 style="text-align: left;">Using ideas</h2><p>As readers of this blog already know, I created a <a href="https://github.com/aroberge/ideas" target="_blank">Python package named ideas</a> to facilitate the creation of import hooks and to enable easy experimentation with modified Python syntax. ideas comes with its own console that support modified Python syntax. It can also be used with IPython (and thus with Jupyter notebooks).</p><p>Using ideas, one can "instruct" python to perform rational arithmetic. For example, suppose I have a Python file containing the following:</p>
<pre># simple_math.py
a = 0.2 + 0.1
b = 0.2 + 1/10
c = 2/10 + 1/10
print(a, b, c)</pre>
<p>I can run this with Python, getting the expected "unintuitive" result:</p>
<pre>> py simple_math.py
0.30000000000000004 0.30000000000000004 0.30000000000000004
</pre>
<p>Alternatively, using ideas, I can execute this file using rational arithmetic:</p>
<pre>> ideas simple_math -a rational_math
3/10 3/10 3/10
</pre>
<p>Using a different import hook, I can have the result shown with floating point notation.</p>
<pre>> ideas simple_math -a nicer_floats
0.3 0.3 0.3
</pre>
<p>
Instead of executing a script, let's use the ideas console instead, starting with "nicer_float"
</p>
<pre>ideas> 0.1 + 0.2
0.3
ideas> 1/10 + 2/10
0.3
</pre>
For "nicer_float", I've also adopted the Pyret's notation: floating-point number immediately preceded by "~"
are treated as "approximate" floating points i.e. with the regular inaccuracy.
<pre>ideas> ~0.1 + 0.2
0.30000000000000004
</pre>
<p>
And, as mentioned before, I can use ideas with IPython. Here's a very brief example
</p>
<pre>IPython 8.0.0b1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from ideas.examples import rational_math
In [2]: hook = rational_math.add_hook()
The following initializing code from ideas is included:
from fractions import Fraction
In [3]: 0.1 + 0.2
Out[3]: Fraction(3, 10)
</pre>
<h2 style="text-align: left;">Final thoughts</h2>
<p>
Given how confusing floating point arithmetic is to beginners, I think it would be nice if Python had an easy built-in way to switch modes and do calculations as done with ideas in the above examples. However, I doubt very much that this will ever happen. Fortunately, as demonstrated above, it is possible to use import hooks and modified interactive console to achieve this result.</p>
André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-82987683906790404582022-05-13T19:40:00.001-03:002022-05-16T16:18:25.543-03:00Python 🐍 fun with emojis<p>At EuroSciPy in 2018, <a href="https://www.youtube.com/watch?v=Wtm7Iy-wEUI&t=52m43s" target="_blank">Marc Garcia gave a lightning talk</a> which started by pointing out that scientific Python programmers like to alias everything, such as</p><pre>import numpy as np
import pandas as pd</pre>
<p>and suggested that they perhaps would prefer to use emojis, such as</p>
<pre>import pandas as 🐼</pre>
<p>However, Python does not support emojis as code, so the above line cannot be used.</p><p>A year prior, <a href="https://github.com/tacaswell" target="_blank">Thomas A Caswell</a> had <a href="https://github.com/python/cpython/pull/1686" target="_blank">created a pull request</a> for CPython that would have made this possible. This code would have allowed the use of emojis in all environments, including in a Python REPL and even in Jupyter notebooks. Unsurprisingly, this was rejected.</p><p>Undeterred, <a href="https://github.com/gahjelle" target="_blank">Geir Arne Hjelle</a> created a project called <a href="https://github.com/gahjelle/pythonji" target="_blank">pythonji </a>(available on Pypi) which enabled the use of emojis in Python code, but in a much more restricted way. With pythonji, one can run modules ending with 🐍 instead of .py from a terminal. However, such modules cannot be imported, nor can emojis be used in a terminal.</p><p>When I learned about this attempt by Geir Arne Hjelle from a <a href="https://twitter.com/driscollis/status/1525095802972127233" target="_blank">tweet by Mike Driscoll</a>, I thought it would be a fun little project to implement with ideas. Below, I use the same basic example included in the original pythonji project.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgAErm2gYZABXOdlYCLq7OACfQGwwH82HlPGmrHf-H9-BuKMrqV8Q3atZIDtiJ8qmMFaxzZxZdpwfjbfZekHkyc6hdscPiDjR76zXzk-7oGzg581W3Q0lBybzKOkcUrv1ndWPPNN_Kpp2JkCN8ymyjjLuAPP_TqD3KAwMLOUrHx8p9XBhEP5qE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="756" data-original-width="1082" height="448" src="https://blogger.googleusercontent.com/img/a/AVvXsEgAErm2gYZABXOdlYCLq7OACfQGwwH82HlPGmrHf-H9-BuKMrqV8Q3atZIDtiJ8qmMFaxzZxZdpwfjbfZekHkyc6hdscPiDjR76zXzk-7oGzg581W3Q0lBybzKOkcUrv1ndWPPNN_Kpp2JkCN8ymyjjLuAPP_TqD3KAwMLOUrHx8p9XBhEP5qE=w640-h448" width="640" /></a></div><br />As you can see, it works in ideas' console, when importing module. It can also work when running the 🐍 file as source - but leaving the extension out.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg8WJdrWboa3B3Tk2Yrwmsn-isGB0Z0nIE9Npuv4FBUq9ADYtXHGm-uktHz_l-AcKhdEaqfun5cB0y2ETvmOtnG7vm3tAe6e00luCC00Dix_wAcUmJC8E-OewXivlCjyqcFM07wGttdlUT4Y3_dcZzaNRSw12ZhRqxO6_4nUA5gk4V8qHQk-Tg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="262" data-original-width="1082" height="154" src="https://blogger.googleusercontent.com/img/a/AVvXsEg8WJdrWboa3B3Tk2Yrwmsn-isGB0Z0nIE9Npuv4FBUq9ADYtXHGm-uktHz_l-AcKhdEaqfun5cB0y2ETvmOtnG7vm3tAe6e00luCC00Dix_wAcUmJC8E-OewXivlCjyqcFM07wGttdlUT4Y3_dcZzaNRSw12ZhRqxO6_4nUA5gk4V8qHQk-Tg=w640-h154" width="640" /></a></div><br /><br /><p></p><p>And, it works in Jupyter notebooks too!</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgFpCo6efClIu-HoyYQMXkffKjWQZxDYefU1PWc9fGu2eTp2nrIhtky4PrXhfiQsSBnqVdpp6RGVqflB3a6ACmGSiW3NZTSU8lvx43d9besp9FJ8wm8TWOVNp7WIQtOZKdXiwTxumbhAokSWfvT9jdpYWiy6-ubYpTtTb1TpH7cTwZfPzgMCgs" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="536" data-original-width="1155" height="298" src="https://blogger.googleusercontent.com/img/a/AVvXsEgFpCo6efClIu-HoyYQMXkffKjWQZxDYefU1PWc9fGu2eTp2nrIhtky4PrXhfiQsSBnqVdpp6RGVqflB3a6ACmGSiW3NZTSU8lvx43d9besp9FJ8wm8TWOVNp7WIQtOZKdXiwTxumbhAokSWfvT9jdpYWiy6-ubYpTtTb1TpH7cTwZfPzgMCgs=w640-h298" width="640" /></a></div><br />All of this without any need to modify CPython's source code!<p></p><p><span style="font-size: x-large;">😉</span></p><p><br /></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-36387859386739921802022-04-10T16:06:00.002-03:002022-04-12T16:26:26.883-03:00Natural syntax for units in Python<p>In the past week, there has been an interesting discussion on Python-ideas about <a href="https://mail.python.org/archives/list/python-ideas@python.org/thread/JDYXM3AH3ESOL7L6ALPRZOOURL3ZLRHP/" target="_blank">Natural support for units in Python</a>. As I have taught introductory courses in Physics for about 20 of the 30 years of my academic career, I am used to stressing the importance of using units correctly, but had never had the need to explore what kind of support for units was available in Python. I must admit to have been pleasantly surprised by many existing libraries.</p><p>In this blog post, I will give a very brief overview of parts of the discussion that took, and is still taking place, on Python-ideas about this topic. I will then give a very brief introduction to two existing libraries that provide support for units, before showing some actual code inspired by the Python-ideas discussion.</p><p>But first, putting my Physics teacher hat on, let me show you some <u><i>partial</i></u> Python code that I find extremely satisfying, and which contains a line that is almost guaranteed to horrify programmers everywhere, as it <i>seemingly</i> reuse the variable "m" with a completely different meaning.</p>
<pre class="language-python-repl" style="text-align: left;"><code>>>> g = 9.8[m/s^2]
>>> m = 80[kg]
>>> weight = m * g
>>> weight
<Quantity(784.0, 'kilogram * meter / second ** 2')>
>>> tolerance = 1.e-12[N]
>>> abs(weight - 784[N]) < tolerance
True
</code></pre>
<h3 style="text-align: left;">Discussion on Python-ideas</h3><p>The discussion on Python-ideas essentially started with the suggestion that "<i>it would be nice if Python's syntax supported units</i>". That is, if you could basically do something like:</p>
<pre><code>length = 1m + 3cm
# or even
length = 1m 3cm
</code></pre>
<p>and it just worked as "expected". Currently, identifiers in Python cannot start with a number, and writing "3cm" is a SyntaxError. So, in theory, one could add support for this type of construct without causing any backward incompatibility.</p><p>While I never thought of it before, as I use Python as a hobby, I consider the idea of supporting handling units correctly to be an absolute requirement for any scientific calculations. Much emphasis is being made on adding type information to ensure correctness: to my mind, adding *unit* information to ensure correctness is even more important than adding type information.</p><p>During the course of the discussion on Python-ideas, other possible suggestions were made, some of which are actually supported by at least a couple of existing Python libraries. These suggestions included constructs like the following:</p>
<pre class="language-python"><code>length = 1*m + 3*cm
speed = 4*m / 1*s # or speed = 4 * m / s
length = m(1) + cm(3)
speed = m_s(4)
length = 1_m + 3_cm
speed = 4_m_s
length = 1[m] + 3[cm]
speed = 4[m/s]
length = 1"m" + 3"m"
speed = 4"m/s"
density = 1.0[kg/m**3]
density = 1.0[kg/m3]
# No one suggested something like the following
density = 1.0[kg/m^3]</code></pre>
<p>I will come back to looking at potential new syntax for units, as it currently my main interest in this topic. But first, I want to highlight one other main point of the discussion on Python-ideas, namely: <i>Should the units be defined globally for an entire application, or locally according to the standard Python scopes?</i></p><p>My first thought was "of course, it should follow Python's normal scopes". </p><p>Thinking of the opposite argument, what happen if one uses units other than S.I. units in different module, including those from external libraries? Take for example "mile", and have a look at <a href="https://en.wikipedia.org/wiki/Mile" target="_blank">its Wikipedia entry</a>. If one uses units with the same name but different values in different parts of an application, any pretense of using quantities with units to ensure accuracy goes out the window. Furthermore, many units libraries make it possible for users to define they own custom units. What happens if the same name is used for different custom units in different modules, with variables or functions using variables with units in one module are used in a second module?</p><p>Still, as long as libraries do not, or cannot change unit definitions globally, and if they provide clear and well-documented access to the units they use, then the normal Python scopes would likely be the best choice.</p><p>[For a detailed discussion of these two points of view, have a look at the thread on Python-ideas mentioned above. There doesn't seem to be a consensus as to what the correct approach should be.]</p><h3 style="text-align: left;">A brief look at two unit libraries</h3><p>There are many unit libraries available on Pypi. After a brief look at many of them, I decided to focus on only two: <a href="https://docs.astropy.org/en/stable/units/index.html" target="_blank">astropy.units</a> and <a href="https://pint.readthedocs.io/en/stable/" target="_blank">pint</a>. These seemed to be the most complete ones currently available, with source code and good supporting documentation available.</p><p>I will first look at an example that shows how equivalent description of units are easily handled in both of them. First, I use the units module from astropy:</p>
<pre class="language-python-repl" style="text-align: left;"><code>>>> from astropy import units as u
>>> p1 = 1 * u.N / u.m**2
>>> p1
<Quantity 1. N / m2>
>>> p2 = 1 * u.Pa
>>> p1 == p2
True
</code></pre>
<p>Next, doing the same with pint.</p>
<pre class="language-python-repl" style="text-align: left;"><code>>>> import pint
>>> u = pint.UnitRegistry()
>>> p1 = 1 * u.N / u.m**2
>>> p1
<Quantity(1.0, 'newton / meter ** 2')>
>>> p2 = 1 * u.Pa
>>> p1 == p2
True
</code></pre>
<p>In astropy, all the units are defined in a single module. Instead of prefacing the units with the name of the module, one can import units directly</p>
<pre class="language-python-repl" style="text-align: left;"><code>>>> from astropy.units import m, N, Pa
>>> p1 = 1 * N / m**2
>>> p2 = 1 * Pa
>>> p1 == p2
True
</code></pre>
<p>The same cannot be done with pint.</p><h3 style="text-align: left;">A custom syntax for units</h3><p>As I was reading posts from the discussion on Python-ideas, I was thinking that it might be fun to come up with a way to "play" with some code written in a more user-friendly syntax for units. After reading the following, written by Matt del Valle, I decided that I should definitely do it.</p><blockquote><p style="box-sizing: border-box; color: #212529; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 14px; margin-bottom: 1rem; margin-top: 0px; white-space: pre-wrap;"><i>My personal preference for adding units to python would be to make
instances of all numeric classes subscriptable, with the implementation
being roughly equivalent to:</i></p><p style="box-sizing: border-box; color: #212529; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 14px; margin-bottom: 1rem; margin-top: 0px; white-space: pre-wrap;">def __getitem__(self, unit_cls: type[T]) -> T:
return unit_cls(self)</p><p style="box-sizing: border-box; color: #212529; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 14px; margin-bottom: 1rem; margin-top: 0px; white-space: pre-wrap;"><i>We could then discuss the possibility of adding some implementation of
units to the stdlib. For example:</i></p><p style="box-sizing: border-box; color: #212529; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 14px; margin-bottom: 1rem; margin-top: 0px; white-space: pre-wrap;">from units.si import km, m, N, Pa</p><p style="box-sizing: border-box; color: #212529; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 14px; margin-bottom: 1rem; margin-top: 0px; white-space: pre-wrap;">3[km] + 4[m] == 3004[m] # True
5[N]/1[m**2] == 5[Pa] # True</p></blockquote><p>My first thought was to create a custom package building from and depending on astropy.units, as I had looked at it before looking at pint and found it to have everything one might need. However, as I read its <a href="https://github.com/astropy/astropy/blob/main/LICENSE.rst" target="_blank">rather unusual license</a>, I decided that I should take another approach: I chose to simply add a new example to my <a href="https://aroberge.github.io/ideas/docs/html/index.html" target="_blank">ideas library,</a> making it versatile enough so that it could be used with any unit library that uses the standard Python notation for multiplication, division and power of units, which both pint and astropy do. Note that my ideas library has been created to facilitate quick experiments and is not meant to be used in production code.</p><p>First, here's an example that mimics the example given by Matt del Valle above, with what I think is an even nicer (more compact) notation.</p>
<pre class="language-python-repl" style="text-align: left;"><code>python -m ideas -t easy_units
Ideas Console version 0.0.29. [Python version: 3.9.10]
>>> from astropy.units import km, m, N, Pa
>>> 3[km] + 4[m] == 3004[m]
True
>>> 5[N/m^2] == 5[Pa]
True
</code></pre>
<p>In addition to allowing '**' for powers of units (not shown above), I chose to also recognize as equivalent the symbol '^' which is more often associated with exponentiation outside of the (Python) programming world.</p><p>Let's do essentially the same example using pint instead, and follow it with a few additional lines to illustrate further.</p>
<pre class="language-python-repl" style="text-align: left;"><code>Ideas Console version 0.0.29. [Python version: 3.9.10]
>>> import pint
>>> unit = pint.UnitRegistry()
>>> 3[km] + 4[m] == 3004[m]
True
>>> 5[N/m^2] == 5[Pa]
True
>>> pressure = 5[N/m^2]
>>> pressure
<Quantity(5.0, 'newton / meter ** 2')>
>>> pressure = 5[N/m*m]
>>> pressure
<Quantity(5.0, 'newton / meter ** 2')>
</code></pre>
<p>In the last example, I made sure that "N/m*m" did not follow the regular left-to-right order of operation which might have resulted in unit cancellation as we first divide and then multiply by meters.</p><h4 style="text-align: left;">A look at some details</h4><p>Using ideas with a "verbose" mode (-v or --verbose), one can see how the source is transformed prior to its execution. Furthermore, in the case of easy_units, sometime a "prefix" is "extracted" from the code, ensuring that the correct names are used. Here's a very quick look.</p>
<pre class="language-python-repl" style="text-align: left;"><code>python -m ideas -t easy_units -v
Ideas Console version 0.0.29. [Python version: 3.9.10]
>>> import pint
>>> un = pint.UnitRegistry()
===========Prefix============
un.
-----------------------------
>>> pressure = 5[N/m^2]
===========Transformed============
pressure = 5 * un.N/(un.m**2)
-----------------------------
>>> pressure
<Quantity(5.0, 'newton / meter ** 2')>
</code></pre>
<h3 style="text-align: left;">Conclusion</h3><div>Prior to reading the discussion on Python-ideas, I was only vaguely aware of the existence of some units libraries available in Python, and had no idea about their potential usefulness. Many unit libraries are, in my opinion, much less user-friendly than astropy and pint. Still, I do find the requirements to add explicit multiplication symbols to be more tedious and much less readable than the alternative that I have shown. While introducing a syntax like the one I have shown would not cause any backward incompatibilities, I doubt very much that anything like it would be added to Python, as it would likely be considered to be too specific to niche applications. I find this unfortunate ... However, I know that I can use ideas in my own projects if I ever want to use units together with a friendlier syntax.</div><div><br /></div><div>I wrote the easy_units module in just a few hours. It is likely to contain some bugs [1], and is most definitely written as a quick hack not following the best practice. If you do try it, and find some bugs, feel free to file an issue; don't bother looking at the code. ;-)</div><div><br /></div><div>[1] Indeed, I found and fixed a couple while writing this post.</div>
André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-64817882178067310292022-02-08T17:21:00.002-04:002022-02-08T17:21:37.013-04:00Friendly-traceback and IPython: update<p>In my <a href="https://aroberge.blogspot.com/2022/02/friendly-traceback-trying-to-stay-ahead.html">previous post</a>, I mentioned that, unlike IPython, friendly/friendly-traceback included values of relevant objects in a traceback. As I wrote in the update, Alex Hall pointed out that one could get this information by using a verbose mode in IPython. Here is the previous example when using the verbose mode.</p><p><br /></p><p></p><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjTSJ6f-fxc_3L-qwJ3xvFIfMeFHGUNvByFKDzeJ_dQgCKOhkJs7HsMVRGlrni3rwEcy2UrXqOu6SyUrSnlgIooGueVPMDqg1_-Amz46GvJu9A7F5LVZnw-r1CR286YmQ2kyHpdGwUIeuxRr9Y5-A4ZBiQYk9uQ4UNeIz-SuZ0qQF8MYfgZuhY" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="931" data-original-width="757" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEjTSJ6f-fxc_3L-qwJ3xvFIfMeFHGUNvByFKDzeJ_dQgCKOhkJs7HsMVRGlrni3rwEcy2UrXqOu6SyUrSnlgIooGueVPMDqg1_-Amz46GvJu9A7F5LVZnw-r1CR286YmQ2kyHpdGwUIeuxRr9Y5-A4ZBiQYk9uQ4UNeIz-SuZ0qQF8MYfgZuhY=w520-h640" width="520" /></a></div>In (1) I enabled the verbose mode. In (3), we see its effect. (2) is a reminder of the highlighting when it spans many lines. Regarding the highlighting, here's what I had in the previous blog post:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEinbkr27xrtrMaJH2cZewSPwgW1lm6NqDG5RC0WKfgT5ifu_dGmdTS6-q_GHcDEe40q_WZHBWXKBivQs8_HXW7GAWP19UMKRhtTxmT6wIh25XyWmR6WGp7t_AdMA949Dxr-u2_TjIm-sx51upoExPpK4ragt42IQ_rdXIyWmDFKB2ioriF1sOU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1122" data-original-width="677" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEinbkr27xrtrMaJH2cZewSPwgW1lm6NqDG5RC0WKfgT5ifu_dGmdTS6-q_GHcDEe40q_WZHBWXKBivQs8_HXW7GAWP19UMKRhtTxmT6wIh25XyWmR6WGp7t_AdMA949Dxr-u2_TjIm-sx51upoExPpK4ragt42IQ_rdXIyWmDFKB2ioriF1sOU=w387-h640" width="387" /></a></div><div><br /></div>Alex Hall (yes, him again), the author of stack_data used by both IPython and friendly-traceback, suggested that perhaps a better way would be to have a common indentation. This is what I implemented next:<div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh22nBHiwWUXz91damh9yiaPqcd6PZuKM_9qWQB6FYDKWyaQ1j3-qBAItjbjMzuhvtfflC2x_ZJksf_bU9MoMvqFHXaU4tVBajM2lIygqJGfnhmhtb8w1nTgCjZhT_MP8dWU1JQo8NdLWCWRtDBVyTI5ixltOfw9hCzSW4Ti41HzNz1WqxumYA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1122" data-original-width="771" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEh22nBHiwWUXz91damh9yiaPqcd6PZuKM_9qWQB6FYDKWyaQ1j3-qBAItjbjMzuhvtfflC2x_ZJksf_bU9MoMvqFHXaU4tVBajM2lIygqJGfnhmhtb8w1nTgCjZhT_MP8dWU1JQo8NdLWCWRtDBVyTI5ixltOfw9hCzSW4Ti41HzNz1WqxumYA=w440-h640" width="440" /></a></div><br /><br />In my code, this is done in a rather convoluted way. Following a suggestion by Alex, I implemented a change in stack_data itself which yields the correct result, at least when using carets (^) as marker for the location. If Alex can confirm that it works for stack_data in all cases, this new way of highlighting consecutive lines would likely be automatically incorporated into IPython.</div><div><br /></div><div>The reason I go into all these datails is as follows: I'm <b>really</b> interested in getting feedback from users so as to make friendly/friendly-traceback even more useful. So, don't be shy! :-)<br /><br /><br /><p></p></div>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-81083548959877965572022-02-04T20:44:00.003-04:002022-02-08T17:22:33.293-04:00Friendly-traceback: trying to stay ahead of IPython<p>UPDATE: <i>Alex Hall pointed out that IPython can display the values of variables in the highlighted sections using <b>%xmode verbose</b>. He also suggested a different highlighting strategy when the problematic code spans multiple lines. I go into more details about these two issues in a <a href="https://aroberge.blogspot.com/2022/02/friendly-traceback-and-ipython-update.html" target="_blank">future blog post.</a></i></p><p>======</p><p>I'm writing this blog post in the hope that some people will be encouraged to test friendly/friendly-traceback with IPython/Jupyter and make suggestions as to how it could be even more useful.</p><p>However, before you read any further...</p><p><b>Important clarification</b>: IPython is a professionally developed program which is thoroughly tested, and is an essential tool for thousands of Python programmers. By contrast, friendly/friendly-traceback is mostly done by a hobbyist (myself) and is not nearly as widely used nor as reliable as IPython. Any comparison I make below should be taken in stride. Still, I can't help but draw your attention to this <a href="https://twitter.com/Mbussonn/status/1481267070838968324" target="_blank">recent tweet from Matthias Bussonnier</a>, the IPython project leader:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgKbooVOch8V6ofg1YBqjxnY3FSZQmKBZpYSAeMqrRG5Amb8knS5dfeoZaA37nRdVl7gfpXT0L4qcUC9KmpEaTNtf1sijIVaXJOH-NeI4cCssAgTeaEUgy7JGhUumFw_4P4VQy54Q9pJSl34Onvc4EtBYthjPjrnP7zALH_MfE3f5dPnTTVyiI" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="248" data-original-width="841" height="188" src="https://blogger.googleusercontent.com/img/a/AVvXsEgKbooVOch8V6ofg1YBqjxnY3FSZQmKBZpYSAeMqrRG5Amb8knS5dfeoZaA37nRdVl7gfpXT0L4qcUC9KmpEaTNtf1sijIVaXJOH-NeI4cCssAgTeaEUgy7JGhUumFw_4P4VQy54Q9pJSl34Onvc4EtBYthjPjrnP7zALH_MfE3f5dPnTTVyiI=w640-h188" width="640" /></a></div><br /><br /><p></p><p>I don't believe that friendly/friendly-traceback is mature and stable enough to become part of IPython's distribution. However, it is because of this endorsement that I decided to see what I could do to improve friendly/friendly-traceback's integration with IPython.</p><h3 style="text-align: left;">IPython news</h3><p><a href="https://ipython.readthedocs.io/en/stable/whatsnew/version8.html" target="_blank">The recent release of IPython</a> included <a href="https://ipython.readthedocs.io/en/stable/whatsnew/version8.html#traceback-improvements" target="_blank">many traceback improvements</a>. One of these changes, shown with the screen capture below:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgswiicDqHTeLOrUQ7iXAiwRV9N4MNbzCKo5kbMEtJbYPkZUEjUToffmZXcIG7Vqv147M_0u8FacQrGzXrfd76irK3G3KIdFv-n3kAA-8_6ICVHoTCQkU6DCVVQyYWDvug3d_87RCNhIOl_-L92cGsUaAPno4ycLJrd2cM8FFPZZX-IjxJKP2c" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1130" data-original-width="1087" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEgswiicDqHTeLOrUQ7iXAiwRV9N4MNbzCKo5kbMEtJbYPkZUEjUToffmZXcIG7Vqv147M_0u8FacQrGzXrfd76irK3G3KIdFv-n3kAA-8_6ICVHoTCQkU6DCVVQyYWDvug3d_87RCNhIOl_-L92cGsUaAPno4ycLJrd2cM8FFPZZX-IjxJKP2c=w616-h640" width="616" /></a></div><br /><br /><p></p><p>is something that I am happy to have implemented many months ago <a href="https://aroberge.blogspot.com/2021/07/friendlier-tracebacks-in-repls.html" target="_blank">as mentioned in this blog post</a>. I have no reason to believe that my idea was the impetus for this change in IPython's formatting of tracebacks. Still, I think it validates my initial idea.</p><p>However, there have been other changes introduced in this latest IPython release, such as using colour instead of ^^ to highlight the location of the code causing a traceback is something that I had done only for IDLE but not for other environments such as IPython/Jupyter. So, I felt that I had to catch up with what IPython has implemented and, if possible, do even better. Of course, I must recognize that this work is greatly facilitated since I use Alex Hall's excellent <a href="https://github.com/alexmojaki/stack_data" target="_blank">stack_data</a> (as well as some other of his packages on which stack_data depends) in friendly-traceback: stack_data is now used by IPython to generate these tracebacks. So, in principle, there is no reason why I shouldn't be able to implement similar features in friendly/friendly-traceback.</p><p>Again, I must note that the way I use stack_data is a bit hackish, and definitely not as elegant as it is used within IPython. </p><p>Enough of a preamble, time to provide some actual examples.</p><h3 style="text-align: left;">First example</h3><div>Consider the following module which will generate a traceback when imported in IPython:</div><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhp0fUbzQd_AExnnYqPAgQfpp-5kgNzvtrBt5gHFKmaUn0Rd1wLTFE_tz8RKyln8GY-dja1pgxYvnqa0xkjIgfB5Lksez9s_5uNU7eXZTSeul_xRQ0Uc4FnVkE0WTaN_bt2h_7CqwsQbkwA6t91DeOYA6ewKt9vGqb21FWcjKpN2skAX2nvfvQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="269" data-original-width="610" height="141" src="https://blogger.googleusercontent.com/img/a/AVvXsEhp0fUbzQd_AExnnYqPAgQfpp-5kgNzvtrBt5gHFKmaUn0Rd1wLTFE_tz8RKyln8GY-dja1pgxYvnqa0xkjIgfB5Lksez9s_5uNU7eXZTSeul_xRQ0Uc4FnVkE0WTaN_bt2h_7CqwsQbkwA6t91DeOYA6ewKt9vGqb21FWcjKpN2skAX2nvfvQ" width="320" /></a></div><br /><br /><p></p><p>Here is the result:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgnaq01ALd8SxaF0jCLqhMMExySVkqt-xUrI_TQXXRs-E0P6IrIilMzmOOdL3lItwR0ZinHhptavQjH2WpYjbUTIXsF2MYjnduMGeH1TLP4Wgg2wqpduNDL5GzhjRDjKgcGBQWLwveza5xSAJe-s3vzfzfFqQeAd46f-1E0El9n_JufyYgoo8s" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="741" data-original-width="758" height="624" src="https://blogger.googleusercontent.com/img/a/AVvXsEgnaq01ALd8SxaF0jCLqhMMExySVkqt-xUrI_TQXXRs-E0P6IrIilMzmOOdL3lItwR0ZinHhptavQjH2WpYjbUTIXsF2MYjnduMGeH1TLP4Wgg2wqpduNDL5GzhjRDjKgcGBQWLwveza5xSAJe-s3vzfzfFqQeAd46f-1E0El9n_JufyYgoo8s=w640-h624" width="640" /></a></div><br />We can see not only the lines of code that caused the traceback, but actually the specific parts of each line that caused a problem. Notice how the display jumps from line 6 to line 8: this is because line 7 is an empty line. Such empty lines are removed to reduce the vertical space require for the display.<p></p><p>I could replicate this example using the friendly console but, instead, I will use the specific IPython integration to see what else we could do at this point. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjpMifneemxAOdeYW1hTUgwmH5A0TrLYkLHZNAtt7A00Oq_LTDQraTB-w7t2JRPBM4k5lskBguEDpqB92O2at3SoC38nocywmVevb7cQN4dJbtNrhl-KQoZmvukHrXA6yFv7aZt6BqwIBxWBDdYIOSEnjD2LFmvM9dRBZ8CnASqXs_Cq63gU1k" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="581" data-original-width="1003" height="370" src="https://blogger.googleusercontent.com/img/a/AVvXsEjpMifneemxAOdeYW1hTUgwmH5A0TrLYkLHZNAtt7A00Oq_LTDQraTB-w7t2JRPBM4k5lskBguEDpqB92O2at3SoC38nocywmVevb7cQN4dJbtNrhl-KQoZmvukHrXA6yFv7aZt6BqwIBxWBDdYIOSEnjD2LFmvM9dRBZ8CnASqXs_Cq63gU1k=w640-h370" width="640" /></a></div><br />We see a traceback that is somewhat similar to a standard CPython traceback, but with an additional hint at the end which gives us an additional clue as to what the cause of the error might be. Friendly/friendly-traceback can give more information about what() a particular exception means in general, why() it might have happened in this particular case and where() it occurred:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj1cCxm1DcOdG3M6HwT6l-YxzzTCbW8yIJyU67d1wayQpJRfT1DVme6ChoPcRISc4G4-xJQXk1_VIycA9rMspilbE3HPABTYe7Pp56FBvrddXuVA3a_0x5Ag8iqb4qTJkKUHA5_OFIxTjBwJbIaujt8YSdFp6nkob1M6PLUtCWe6KFkI9fBpsA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="878" data-original-width="1205" height="466" src="https://blogger.googleusercontent.com/img/a/AVvXsEj1cCxm1DcOdG3M6HwT6l-YxzzTCbW8yIJyU67d1wayQpJRfT1DVme6ChoPcRISc4G4-xJQXk1_VIycA9rMspilbE3HPABTYe7Pp56FBvrddXuVA3a_0x5Ag8iqb4qTJkKUHA5_OFIxTjBwJbIaujt8YSdFp6nkob1M6PLUtCWe6KFkI9fBpsA=w640-h466" width="640" /></a></div><br />By design, the information provided by where() only focus on the beginning and the end of the traceback, so as to not overwhelm beginners with often irrelevant steps in between. However, notice that in addition to the highlighted parts (new feature!), we also see the values of some objects from these highlighted regions.<p></p><p>Until recently, this was all the information that one could get. However, it is now possible to get more details, in a way similar to that provided by IPython, but with the addition of the values of various objects. (Note that the syntax shown below to obtain this information is subject to change; it is just a proof of concept.)</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhcQTXjzbbxEPWfnzkO34sY7BkYheMyRHq127UPrGbygJpPGr88FjZrThxhUvfj1SGB1t6OSOPdhDfmSNjFJ5Nc-trf_xa9ItajFcCeQRuazyUdyG1fEB3CdHIoYwrQjSEiDpBTXXx6hoBUbHi5KGg_P9ZRj-3Qt7v01Z-8uVvIhHIjOGWwJu4" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1070" data-original-width="729" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEhcQTXjzbbxEPWfnzkO34sY7BkYheMyRHq127UPrGbygJpPGr88FjZrThxhUvfj1SGB1t6OSOPdhDfmSNjFJ5Nc-trf_xa9ItajFcCeQRuazyUdyG1fEB3CdHIoYwrQjSEiDpBTXXx6hoBUbHi5KGg_P9ZRj-3Qt7v01Z-8uVvIhHIjOGWwJu4=w437-h640" width="437" /></a></div><br />Other than the different colour chosen for highlighting (both IPython and this example are done in a Windows Terminal), I also chose to ensure that one line per frame was highlighted, such as the line "import example1". <b>Do you think this is a good choice, or should I do like IPython?</b><div>Finally, I included line 7 which is an empty line, so that beginners (my original target audience) might better recognize their own code instead of seeing a more vertically compressed version. If more than one blank line would be included, they would be replaced by "(...)" indicating that some lines</div><div>were skipped.<br /><p></p><p>If the highlighting is not adequate, it can be changed by using either named colours (converted to lowercase with spaces removed) or hexadecimal values; the name of the function and its arguments are subject to change:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3fSaBNkZRYk0hiYihnEcAcfHNFly8gHS1epLi1GayrcSV18_95YefdIEPPMPsXMstjvXi8uB0TIkwuScgUkkgEoCTuHssbW_2a6pcYJeU1XzINOGlFshE6Nh1jPMeXCOxdof9xGcniREsFj4f355DiK10JP-qWeuvKhDIb3BUYCg2M3CXTQo" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1059" data-original-width="840" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3fSaBNkZRYk0hiYihnEcAcfHNFly8gHS1epLi1GayrcSV18_95YefdIEPPMPsXMstjvXi8uB0TIkwuScgUkkgEoCTuHssbW_2a6pcYJeU1XzINOGlFshE6Nh1jPMeXCOxdof9xGcniREsFj4f355DiK10JP-qWeuvKhDIb3BUYCg2M3CXTQo=w507-h640" width="507" /></a></div><br />Any such change is written to a settings file so that it is remembered for future sessions. Those that prefer traditional Python's notation with ^^ can do so by using None as an argument:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhRWq06vlfiaLaEgHdFUYyZGeR4HO6ilOEnJRLrCRDEdicJ4pmNCZSEJwvsi_lSAUVwcobu62QnIFwFkP5Rmyk0_83JaTdO5LiCq0eB7dOTrBN42O7iCK5BS4CX-IwbpS-0biQE59JpcHOWRng90QIJJ3BAbdMwpAxGxeeIc59UJJi5t_hKQxk" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1212" data-original-width="764" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEhRWq06vlfiaLaEgHdFUYyZGeR4HO6ilOEnJRLrCRDEdicJ4pmNCZSEJwvsi_lSAUVwcobu62QnIFwFkP5Rmyk0_83JaTdO5LiCq0eB7dOTrBN42O7iCK5BS4CX-IwbpS-0biQE59JpcHOWRng90QIJJ3BAbdMwpAxGxeeIc59UJJi5t_hKQxk=w403-h640" width="403" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Finally, one can go back to the defaults by specifying no argument:</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiip3OP1-97_ZpPx0IDn84-3AKGfAYrmw0Sf3EwPVmk3EytTdWhEIeojI75ZmaYpcNLrBeQUGr1BzMDspS68XJS43RbOKMlhGnfU4WzluGyRB8iNNfwpZcSggKFKBTkIX04zDIflxu1b4qL4ICRNKwqBt2WwqekXi-wB0mpiaJa6s8NoOJCjQQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1134" data-original-width="709" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEiip3OP1-97_ZpPx0IDn84-3AKGfAYrmw0Sf3EwPVmk3EytTdWhEIeojI75ZmaYpcNLrBeQUGr1BzMDspS68XJS43RbOKMlhGnfU4WzluGyRB8iNNfwpZcSggKFKBTkIX04zDIflxu1b4qL4ICRNKwqBt2WwqekXi-wB0mpiaJa6s8NoOJCjQQ=w400-h640" width="400" /></a></div><br /><br /></div><h3 style="clear: both; text-align: left;">Example 2</h3><div class="separator" style="clear: both; text-align: left;"><br /></div>In the previous example, all highlighting regions were part of a single line. However, sometimes the code at fault will spill over two lines. Here's how IPython does its highlighting:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjeGQJ0wxRAFLwWKNkyycETTXs6rESOoTw0qGm4Ze9H0M_MaKp-8bTVEGL1big4AUpelPEp2kpwDcDhnYkjS2wyfu7cpLY3RCfbB7sBl-eefcGejIwHpr3gNeoShifkBSIUwAYej2DBjI4sNMNMOzTGHyh0-oH-B78VxdnA25sc-lK2ZPhBK7Y" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="862" data-original-width="757" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEjeGQJ0wxRAFLwWKNkyycETTXs6rESOoTw0qGm4Ze9H0M_MaKp-8bTVEGL1big4AUpelPEp2kpwDcDhnYkjS2wyfu7cpLY3RCfbB7sBl-eefcGejIwHpr3gNeoShifkBSIUwAYej2DBjI4sNMNMOzTGHyh0-oH-B78VxdnA25sc-lK2ZPhBK7Y=w563-h640" width="563" /></a></div><br />Instead of highlighting each line from the beginning, I chose to not highlight the indentation; <b>is this a better choice?</b><br /><br /><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiw9DCP7ZnWavfRAZpCa8W7MUznomb-YpHqOmEUejXiZtl3AEfh5wU3bxqd8wR7MSr9NNnpVDaIJjgpv9k6p-A0eG3m2cqiVezSrUysZbySDRG-kAy2JvonCgpl3iGVGQIZIzPl3gRniBVb_-oJxjyCRbwkpgFB5xkO8HzwX7oZX2PmJ7Mfl58" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1122" data-original-width="677" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEiw9DCP7ZnWavfRAZpCa8W7MUznomb-YpHqOmEUejXiZtl3AEfh5wU3bxqd8wR7MSr9NNnpVDaIJjgpv9k6p-A0eG3m2cqiVezSrUysZbySDRG-kAy2JvonCgpl3iGVGQIZIzPl3gRniBVb_-oJxjyCRbwkpgFB5xkO8HzwX7oZX2PmJ7Mfl58=w387-h640" width="387" /></a></div><br /><br /><p></p><h3 style="text-align: left;">Jupyter notebook</h3><p><br /></p><p>When IPython is used in a Jupyter notebook (or lab), I chose yet again a different way to present the result. First, let's have a look at a simple example using the Jupyter default.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh7rYdIr40SeTBglNz_gYNGzuyD3F6wx7kYt50Wc2pLfVW06AsyIvJ_ZhyobYYCdRBkOukCnl9xYmQLEAoZe-sc8682WzpRZ5FTAf5t1j7Hb4i9yNbj70PCdLiKD0hkYB7JI7KhUJyKK3-7Z8wnGyDzdtwFdlJuUmcaytO8egwKza71bQ2P9ys" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="562" data-original-width="596" height="602" src="https://blogger.googleusercontent.com/img/a/AVvXsEh7rYdIr40SeTBglNz_gYNGzuyD3F6wx7kYt50Wc2pLfVW06AsyIvJ_ZhyobYYCdRBkOukCnl9xYmQLEAoZe-sc8682WzpRZ5FTAf5t1j7Hb4i9yNbj70PCdLiKD0hkYB7JI7KhUJyKK3-7Z8wnGyDzdtwFdlJuUmcaytO8egwKza71bQ2P9ys=w640-h602" width="640" /></a></div><br /><br /><p></p><p>In this example, only two frames are highlighted. Let's see the result, using friendly.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgsEqtVzKKEDKOoafa7WAwWrYSqIvTENGX6vV-iYWIBitPEf4Sthn98aV5dq5xxWRnXmr0jQRAUT8qObLYqg5HmabDCrSEjI8WxRfa1IjYHYdY1TLA9yhHtxAEkmKD5ycSinJVgALeGKAFcbJQECDntGHV4l7cGlm1ZwunMyP95aN7pdwSEDcE" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="414" data-original-width="821" height="322" src="https://blogger.googleusercontent.com/img/a/AVvXsEgsEqtVzKKEDKOoafa7WAwWrYSqIvTENGX6vV-iYWIBitPEf4Sthn98aV5dq5xxWRnXmr0jQRAUT8qObLYqg5HmabDCrSEjI8WxRfa1IjYHYdY1TLA9yhHtxAEkmKD5ycSinJVgALeGKAFcbJQECDntGHV4l7cGlm1ZwunMyP95aN7pdwSEDcE=w640-h322" width="640" /></a></div>We get a basic error message with a button to click if we want to have more details.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjnkBi9DWIaxeXYX28imNokrRiQPe9xWZ4q7h064H4lKHdsBOcU5JOL5AESq_K0ei0EaKDMEc9LajVByhnFXB39YQuJypZohdRjSuw2T6-sNCIVxKv69bom_5jRDrI4AMA8U2DFy4tBdSmQkCzJ6NAnV4kvjCSwYKZOsubBLAz6kZH2Xq6vejE" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="493" data-original-width="817" height="386" src="https://blogger.googleusercontent.com/img/a/AVvXsEjnkBi9DWIaxeXYX28imNokrRiQPe9xWZ4q7h064H4lKHdsBOcU5JOL5AESq_K0ei0EaKDMEc9LajVByhnFXB39YQuJypZohdRjSuw2T6-sNCIVxKv69bom_5jRDrI4AMA8U2DFy4tBdSmQkCzJ6NAnV4kvjCSwYKZOsubBLAz6kZH2Xq6vejE=w640-h386" width="640" /></a></div><br /><br />We already have seen the output of what() and why() before; this time let's just click on where():<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEijruTKCIWLFsUKFTolcVvezBLijgraT2ewDheaXVzZjmq0sPtXLD9E7fADdW-q4eWht_e9oKoMy1fggKiyGVMyMHZ1H0pT-8YHEd-MbPvAhWhRPZRBNlXcKvSHWakoxBbqI_onj8-1sbXWbCDOj0KOBSQfUtPj7H54xByw1vrPWjti_WEq32E" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="616" data-original-width="572" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEijruTKCIWLFsUKFTolcVvezBLijgraT2ewDheaXVzZjmq0sPtXLD9E7fADdW-q4eWht_e9oKoMy1fggKiyGVMyMHZ1H0pT-8YHEd-MbPvAhWhRPZRBNlXcKvSHWakoxBbqI_onj8-1sbXWbCDOj0KOBSQfUtPj7H54xByw1vrPWjti_WEq32E=w595-h640" width="595" /></a></div><br /><br /><p></p><p>Since we only had two frames in the traceback, where() gives us all the relevant information.</p><p>What happens if we have more than two frames in the traceback? First, let's give an example with the Jupyter default.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiJot0qwH6UK78FWyIEJFa80UhBAP8BczGuZWxqcMt_gORXVHY8cbwr0oG1NQvepIlUnfxMxIW-lUDTlHgLHvSEDHMGZjYwejkNoOBv4cyh4Rn_E0xijZVPmzgsv6JBJ4iXPDgErnoxzDWBuV-bvuoaIAr4TKKCUq2qIIf-0BSlsxOiEX7r2UQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="714" data-original-width="709" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEiJot0qwH6UK78FWyIEJFa80UhBAP8BczGuZWxqcMt_gORXVHY8cbwr0oG1NQvepIlUnfxMxIW-lUDTlHgLHvSEDHMGZjYwejkNoOBv4cyh4Rn_E0xijZVPmzgsv6JBJ4iXPDgErnoxzDWBuV-bvuoaIAr4TKKCUq2qIIf-0BSlsxOiEX7r2UQ=w635-h640" width="635" /></a></div><br /><br /><p></p><p>What happens if we use friendly in this case? Below I show the result after clicking "more"</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEih41gAGC2DvuGgolzsvDoypmxB6dXiKi14dwWY0lYt7VM97qswOD6WEKn8toXs81mhuj4WzguKtYJs_D56ba9GGCyqpmDO1G648e999SF-qlFsA6pz1aZPxRWZLBfzAb08Bptn8gmXP3-y77dvkKmNY6rNeh0HwhR0nCLRQ_ZgPRMAe-KbdW8" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="613" data-original-width="815" height="482" src="https://blogger.googleusercontent.com/img/a/AVvXsEih41gAGC2DvuGgolzsvDoypmxB6dXiKi14dwWY0lYt7VM97qswOD6WEKn8toXs81mhuj4WzguKtYJs_D56ba9GGCyqpmDO1G648e999SF-qlFsA6pz1aZPxRWZLBfzAb08Bptn8gmXP3-y77dvkKmNY6rNeh0HwhR0nCLRQ_ZgPRMAe-KbdW8=w640-h482" width="640" /></a></div><br />An additional button has appeared. Note that this is something new that I just did earlier today (before writing this blog post). It is quite possible that there might be bugs if you try it.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi8dnUr8wqYflZ3QpfLKR0kzFKGgGO6vlxroA6Sel-eXbAtahwzM26Xn8WjipR6AX2pAIsBEVaMEisn20sa408Hh0snh-iaVgeRATSWAiFqHIrixMvwu99EbmIhngRotGOGxfdHiVJH7gCaa4IDwf50Wik1-x_mnGo1uA-nOPiwG6tfjAAPoxU" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="734" data-original-width="578" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEi8dnUr8wqYflZ3QpfLKR0kzFKGgGO6vlxroA6Sel-eXbAtahwzM26Xn8WjipR6AX2pAIsBEVaMEisn20sa408Hh0snh-iaVgeRATSWAiFqHIrixMvwu99EbmIhngRotGOGxfdHiVJH7gCaa4IDwf50Wik1-x_mnGo1uA-nOPiwG6tfjAAPoxU=w504-h640" width="504" /></a></div><br /><h3 style="text-align: left;">Conclusion</h3><p></p><p>These new features are simple proofs of concept that have not been thoroughly tested. If you read this far, and hopefully tried it on your own, I would really appreciate getting your feedback regarding the choices I made and any improvement you might be able to suggest.</p></div>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-66553185013892358502022-01-05T18:06:00.000-04:002022-01-05T18:06:33.535-04:00Python 101: enabling a restricted subset of Python<p>I decided to submit to the the Ideas category of Discuss-Python a proposal which I have summarized as follows:</p><p><span style="background-color: white; color: #222222; font-family: Arial, sans-serif; font-size: 15.008px;"><i>Summary: I propose that a new compile time directive be available to restrict the Python syntax to a strict subset. This would facilitate the teaching of Python to beginners as well as the work of people that write tools intended to help beginners learning Python.</i></span></p><p>Here is <a href="https://discuss.python.org/t/python-101-enabling-a-restricted-subset-of-python/12924" target="_blank">a link to that post</a>.</p><p>This is something I have been thinking about for more than a year but always hesitated to submit. It is now done ... feel free to comment <a href="https://discuss.python.org/t/python-101-enabling-a-restricted-subset-of-python/12924" target="_blank">over there</a>.</p><p>Comments posted on this blog about this particular topic will be deleted so that the discussion can take place at a single location.</p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-23301205411099161312021-12-28T14:48:00.000-04:002021-12-28T14:48:11.012-04:00New milestone for friendly: version 0.5<p> Friendly (previously at 0.4.41) and friendly-traceback (previously at 0.4.111) are now at version 0.5. The <a href="https://friendly-traceback.github.io/docs/index.html" target="_blank">joint documentation for both projects</a> has not yet been updated. In addition to the many new cases added for which friendly/friendly-traceback can help with, which includes close to 400 test cases, I am very excited to report to three new important features</p><p></p><ol style="text-align: left;"><li>Getting help when a traceback is generated before friendly is imported</li><li>Not having to set non-default configurations each time friendly is used</li><li>The addition of two new languages.</li></ol><h2 style="text-align: left;">1. Getting help after the fact</h2><div><br /></div><div>Let's start with the first. Previously, if one wanted help from friendly/friendly-traceback, it had either to be used to run a program, via something like "python -m friendly user_program.py", or it had to be imported and installed (either implicitly or explicitly) before any other code was executed. This still works as before and is the best way to use friendly.</div><div><br /></div><div>Now, it can be imported *after* a traceback has been generated, and can provide its usual help when using:</div><div><ul style="text-align: left;"><li>IPython in a terminal</li><li>Jupyter notebooks, and Jupyter lab</li><li>Mu</li><li>Programs run with cPython using "python -i user_program.py"</li><li>Code entered in a cPython terminal, with the caveat that it only works in a limited fashion for some SyntaxErrors but almost never for run time errors.</li><ul><li>The same when using pypy with the exception that using languages other than English may yield some undesirable results.</li></ul><li>Code saved in files and run from IDLE (Python 3.10 and possibly later versions of Python 3.9) -- but excluding SyntaxErrors</li><li>Code entered in IDLE's shell - but excluding SyntaxErrors.</li></ul><div>Before explaining the origin of the (different) limitations when using cPython's interactive interpreter or IDLE, let me show the results using IPython, both for SyntaxErrors and run time errors starting with a very unlikely example of SyntaxError</div></div><div><br /></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLQV0AXXrnfEPbRb-SbJvN2PNLKjbFOkWEHj5XFrEFHhNTCKfXnGNxsEwUaTMKX27qpfSJw6OVAEAwrWu90ssnCv-39JZj-X37P2eGImyD1i3KLpohxZ0yplVU1ceUPjeGrJrC4A/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="856" data-original-width="936" height="366" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLQV0AXXrnfEPbRb-SbJvN2PNLKjbFOkWEHj5XFrEFHhNTCKfXnGNxsEwUaTMKX27qpfSJw6OVAEAwrWu90ssnCv-39JZj-X37P2eGImyD1i3KLpohxZ0yplVU1ceUPjeGrJrC4A/w400-h366/image.png" width="400" /></a></div>Of course, we can ask for more details</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4akdV__zeGDokHWRN0tWlFdrIHnD2lrZbKpzDX-mFDZZOuZFi7Fmlfp9NdKYulK_oXmWis8j-QpESyzHcISlcTOPM8hC6kI2zVfmS4MJ-3SxfVvhJrofbnTpsfColJwxRy8vYxw/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="468" data-original-width="984" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4akdV__zeGDokHWRN0tWlFdrIHnD2lrZbKpzDX-mFDZZOuZFi7Fmlfp9NdKYulK_oXmWis8j-QpESyzHcISlcTOPM8hC6kI2zVfmS4MJ-3SxfVvhJrofbnTpsfColJwxRy8vYxw/w400-h190/image.png" width="400" /></a></div><br />Instead of a SyntaxError, let's see an example of a run time error.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9_mP2v7KqH6f2WQQDAtN2xTq6pasVMZZLVcZ5XbVpFDCiQA_fKsDX4sy9Fyn7murBFyp3v8mcfgBRXVe5anFQRg3gh6-6yUWHAMp9Ea6Qcy_H7KhcsybAtqqbEQ2Wf0t4BaZs1w/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="665" data-original-width="1124" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9_mP2v7KqH6f2WQQDAtN2xTq6pasVMZZLVcZ5XbVpFDCiQA_fKsDX4sy9Fyn7murBFyp3v8mcfgBRXVe5anFQRg3gh6-6yUWHAMp9Ea6Qcy_H7KhcsybAtqqbEQ2Wf0t4BaZs1w/w400-h236/image.png" width="400" /></a></div><br />Again, it just works. :-)</div><div><br /></div><div>Moving on to SyntaxErrors with the cPython interpreter. Let's use the same example as above, with Python 3.10.1:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZy1ubgtamGv3c6LYGl0o87OfBVfN30V_TZ8NejoE2ftIxEb9ANc2mzdOJ9LSk7aGI5RU_tb1f_E9_2EWAGo8wNI1TUKWisQD3OM3qy7ySMMU466IEOanX6Roqu4Qp0WO1aTHLTQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1025" data-original-width="1124" height="365" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZy1ubgtamGv3c6LYGl0o87OfBVfN30V_TZ8NejoE2ftIxEb9ANc2mzdOJ9LSk7aGI5RU_tb1f_E9_2EWAGo8wNI1TUKWisQD3OM3qy7ySMMU466IEOanX6Roqu4Qp0WO1aTHLTQ/w400-h365/image.png" width="400" /></a></div><br />This works. However, let's have a more detailed look at the information available:</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdD_QCJLIiqcowdRiobu053_a5EgNYLLdg1i9r3NrNirw272xscPLp_T3MG1R3t69eRouRsOdD9tKrMuVr_khHscrEjUvrG84oquy8D26CeIpKy-yMRarvoxlFImENK9oKnPIz8g/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="324" data-original-width="1124" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdD_QCJLIiqcowdRiobu053_a5EgNYLLdg1i9r3NrNirw272xscPLp_T3MG1R3t69eRouRsOdD9tKrMuVr_khHscrEjUvrG84oquy8D26CeIpKy-yMRarvoxlFImENK9oKnPIz8g/w400-h115/image.png" width="400" /></a></div><br /><br /></div><div>Python does not store the content of the code entered in the interpreter; for SyntaxErrors, it does include the very last line of code where the error was located. This will not work in other situations where a statement spans multiple lines; in some cases, if the error message is precise enough, friendly might still be able to guess the cause of the error.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX0yrF8pYoZ_c0gDVSYeBeDo_yTREORq_0p2YT9s0BBEayDh6JKOlR8ybDEsSwRATteVqoZo_FaX2L9cCOIVecx3-gKfLmiVoiF5Dwck6uvPS5JInkKx8i-KiDTVXDZ62cwtYNWQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="681" data-original-width="1124" height="243" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjX0yrF8pYoZ_c0gDVSYeBeDo_yTREORq_0p2YT9s0BBEayDh6JKOlR8ybDEsSwRATteVqoZo_FaX2L9cCOIVecx3-gKfLmiVoiF5Dwck6uvPS5JInkKx8i-KiDTVXDZ62cwtYNWQ/w400-h243/image.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvj_Sbd9M3akFAp7fqcm4FK1KwTQi7tzhnXRNuqiQnWjkS8IfEQjTLilg8PFeUeh1I77re7euXlMqk9IJmUlTVCOHn1RXyYFFdw5TTVTVMkR6H1D_sc8r-flJ9JpCPfXjCHKaWVg/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="796" data-original-width="1124" height="284" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvj_Sbd9M3akFAp7fqcm4FK1KwTQi7tzhnXRNuqiQnWjkS8IfEQjTLilg8PFeUeh1I77re7euXlMqk9IJmUlTVCOHn1RXyYFFdw5TTVTVMkR6H1D_sc8r-flJ9JpCPfXjCHKaWVg/w400-h284/image.png" width="400" /></a></div><br />By contrast, friendly does store the entire code entered in its interpreter.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheyJBs5AhRFjlgDcr7bVHVfH3hBLb4b08yK3yIVsYHA5vbWIiJa8qBmWsHg47sy44gdjwdgeKbra8Y7BwbHmlGl9Lax7uYUMusZ6kTdYk2V8Db2ACzWsS5ZHjVBAF4xhJ_QAnyPg/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="520" data-original-width="1124" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheyJBs5AhRFjlgDcr7bVHVfH3hBLb4b08yK3yIVsYHA5vbWIiJa8qBmWsHg47sy44gdjwdgeKbra8Y7BwbHmlGl9Lax7uYUMusZ6kTdYk2V8Db2ACzWsS5ZHjVBAF4xhJ_QAnyPg/w400-h185/image.png" width="400" /></a></div><br /><br /></div><div>Let's have a look at a run time error with cPython.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6Kvl3vEw4LZQctdxsvXuIJErXT19Ul10d_GYVmxlDc2IOvimg9fBuSoTImxcJoKlKser_BhUL_MRlWbkXvDWI_99Z7PJOtfVRcb3irONvjfVYoiIsG6twkgDARe5GnUhlR5dt_Q/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="303" data-original-width="1124" height="108" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6Kvl3vEw4LZQctdxsvXuIJErXT19Ul10d_GYVmxlDc2IOvimg9fBuSoTImxcJoKlKser_BhUL_MRlWbkXvDWI_99Z7PJOtfVRcb3irONvjfVYoiIsG6twkgDARe5GnUhlR5dt_Q/w400-h108/image.png" width="400" /></a></div><br />Notice how the traceback contains no information about the code in the file(s) named "<stdin>".</div><div>Let's see what information we can get from friendly.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVfcWfMmhnOD1VpLLIVNUhskJWn54aApgrB-q91lp7jT5KvuJlKZAzZzSovB5O3qjpeYARxH5eYvRmmtOEYz_nY_EL6BUyT34bMA5Hp2EdafWS7jXLZHrqtbjQkvn9tfGkr-8M3A/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="983" data-original-width="1124" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVfcWfMmhnOD1VpLLIVNUhskJWn54aApgrB-q91lp7jT5KvuJlKZAzZzSovB5O3qjpeYARxH5eYvRmmtOEYz_nY_EL6BUyT34bMA5Hp2EdafWS7jXLZHrqtbjQkvn9tfGkr-8M3A/w400-h350/image.png" width="400" /></a></div><br /><br /></div><div>If you use friendly, you would never see the log message (1) as it is something that is enabled by default on my computer. Note that, in spite of not having access to the exact code that produced the exception, in this case friendly is still able to provide some help. This information is similar to what is available with Python 3.10+; however, you can use friendly with Python 3.6 and still get the same information!</div><div>Of course, it is better still if you use friendly from the start:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGa7-mhZilPU7Gcf7MVttzGz5Psx25FuASv-7LJmCxertVIzhzOta2qv-HTO7BDXz5zSC7RJKDdQZLVimv3Z-M9OVGSTolFCvEO4yYXQe8bIPFGjF1QxuwpwGAsqbIJsVvPSpIbg/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="719" data-original-width="1124" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGa7-mhZilPU7Gcf7MVttzGz5Psx25FuASv-7LJmCxertVIzhzOta2qv-HTO7BDXz5zSC7RJKDdQZLVimv3Z-M9OVGSTolFCvEO4yYXQe8bIPFGjF1QxuwpwGAsqbIJsVvPSpIbg/w400-h256/image.png" width="400" /></a></div><br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCbs-52toBCZZ1lxwXcprYnasx0Fk_JMEevWheKPVaEWhnWvnAOKkzqprlR0afzbyP9MJH-Lv59tZleyjUKlSrUDdvUYnxyNxaRAYGiYr8qUGBI4MSre_G6LZbfxUE__vCbGgZfw/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1064" data-original-width="1124" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCbs-52toBCZZ1lxwXcprYnasx0Fk_JMEevWheKPVaEWhnWvnAOKkzqprlR0afzbyP9MJH-Lv59tZleyjUKlSrUDdvUYnxyNxaRAYGiYr8qUGBI4MSre_G6LZbfxUE__vCbGgZfw/w400-h378/image.png" width="400" /></a></div><br /><br /></div><div>Let's now have a look at IDLE. Recently, IDLE has added support for custom exception hook. Instead of requiring the use of its own console when using IDLE, friendly can make use of this new capability of IDLE to provide help with run time errors - but not SyntaxErrors as those are handled in a peculiar way by IDLE.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCKF2trq_dmuOVhPhOlMT55Kyhui_NEzuTJ_7gkfhDkGUmeuroXdReq8qMy2LGv3nRVFA4OpU-i_0lak9WBNCakJRD5kxHm3pEWq3aCiGyaWUbAFjIvZOab88NPjMCHNjIx09h2g/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="733" data-original-width="961" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCKF2trq_dmuOVhPhOlMT55Kyhui_NEzuTJ_7gkfhDkGUmeuroXdReq8qMy2LGv3nRVFA4OpU-i_0lak9WBNCakJRD5kxHm3pEWq3aCiGyaWUbAFjIvZOab88NPjMCHNjIx09h2g/w400-h305/image.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQtx1drlLyAuCTxXJQ-urEOHUloE0fIUC3tBSWwQS9OmJM5GQQXujUPe0tzt1hfrW33GGxkzEGIfsPhdovrd-3AqkffNoHSspkehW4zPMsW24QR3Fqihyphenhyphen3dBciMKr4ttD4R-ettQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="438" data-original-width="961" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQtx1drlLyAuCTxXJQ-urEOHUloE0fIUC3tBSWwQS9OmJM5GQQXujUPe0tzt1hfrW33GGxkzEGIfsPhdovrd-3AqkffNoHSspkehW4zPMsW24QR3Fqihyphenhyphen3dBciMKr4ttD4R-ettQ/w400-h183/image.png" width="400" /></a></div><br />For this type of error, trying to use friendly after the fact yields very little useful information.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy4NWclHGR0xPDYJM7HtGfRkY_DqyUdOkHm3GUpGiQLfW_8xRAVGekbSj17kr-MFV659FZFokZPllV0-6KIlwj_bMGK-OLs-ewU36jTdZQpXCExworkPJWeJA6rnW5gMnsETJAMg/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1030" data-original-width="961" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy4NWclHGR0xPDYJM7HtGfRkY_DqyUdOkHm3GUpGiQLfW_8xRAVGekbSj17kr-MFV659FZFokZPllV0-6KIlwj_bMGK-OLs-ewU36jTdZQpXCExworkPJWeJA6rnW5gMnsETJAMg/w373-h400/image.png" width="373" /></a></div><br />For SyntaxErrors, the situation is even worse: IDLE does not make any information available.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPJHTMhfzNizNZ5RTYC9dv3DaJ7PgrgImXM2ROWtkOU9J8BPl60c7i_rRDBaD-lj2S4DBv0f3OPJ3N710sLHGbKLzlrI9njxYX6fnEMgYIgJ8lhQLQvvUhoJrCkXr49ucyFEWzYw/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1030" data-original-width="961" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPJHTMhfzNizNZ5RTYC9dv3DaJ7PgrgImXM2ROWtkOU9J8BPl60c7i_rRDBaD-lj2S4DBv0f3OPJ3N710sLHGbKLzlrI9njxYX6fnEMgYIgJ8lhQLQvvUhoJrCkXr49ucyFEWzYw/w373-h400/image.png" width="373" /></a></div><br />Note that <a href="https://bugs.python.org/issue45560" target="_blank">this is a bug which I did report</a>.</div><div><br /></div><h3 style="text-align: left;">1.a) A possible improvement to fix the problem with cPython</h3><div>If you look at the tracebacks from IDLE for runtime errors, you will see "files" with names like </div><div>"<pyshell#4>": each code block entered by the user is saved in such a "file", each file having a different name. IDLE works around a limitation of Python's linecache module to store the content of these files so that they can be retrieved and analyzed. By contrast, cPython shows the code entered by a user as belonging to files with identical names "<stdin>" whose content can never be retrieved.</div><div><br /></div><div>For code executed using exec("code"), the content is shown to belong to a file named "<string>" whose content is also not available. If cPython were to store the code in files whose named included a different integer each time, like IDLE does, then it could be retrieved by programs like friendly and provide additional help. <a href="https://mail.python.org/archives/list/python-ideas@python.org/thread/CMM5ZV7GJPAR7KLEECETZP3SNFLCIKHQ/#CMM5ZV7GJPAR7KLEECETZP3SNFLCIKHQ" target="_blank">This was suggested on Python-ideas for code run using exec</a>, but got not traction, even though related questions are often asked on StackOverflow.</div><div><br /></div><div>Moving on ...</div><div><br /></div><h2 style="text-align: left;">2. Friendly saves settings</h2><div>Previously, each time that friendly was used, it started with default values for the preferred language (French and English only in previous versions), color scheme (light or dark, depending on the background of the terminal or other application), formatter type, etc.</div><div><br /></div><div>Now, friendly saves the values specified which are then used by default when a new session starts. For the language choice, this is a global settings, that carries in all environments. For other settings, friendly (at least on Windows) can determine if it is running in a PowerShell terminal or an old-fashion cmd, in a Visual Studio Code terminal, in a PyCharm terminal, if it is run with IPython, or in a Jupyter notebook, etc. Here's an example of adjusting the background color so that the information provided by friendly blends in better.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0l70aEy1vb58oblhJVpneyxqqK8POea_TZqHYUFStzn01wSYLrfKnvWisHzpKJFMXOgkNRTV0Dt6L_ln5H2eAgqDvFNrI5V6wNPLKWO0F519AKYhHWUJTG4CqxJsZHwB8EUV20A/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1097" data-original-width="1124" height="390" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0l70aEy1vb58oblhJVpneyxqqK8POea_TZqHYUFStzn01wSYLrfKnvWisHzpKJFMXOgkNRTV0Dt6L_ln5H2eAgqDvFNrI5V6wNPLKWO0F519AKYhHWUJTG4CqxJsZHwB8EUV20A/w400-h390/image.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Friendly only includes two different color scheme: one that is designed to work with a white (or similar) background and another with a black (or similar) background. Anyone working with terminals (or notebooks) with background colors that do not work well with either of the two existing color schemes is welcome to provide different color schemes to be added to friendly.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">So far, I have only tested this with Windows. Mac and Linux users are encouraged to try it out and see if their different environments can be detected correctly so that friendly can work well in all the environment they use it.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><h2 style="text-align: left;">3. New languages</h2></div><div>In addition to English and French, friendly is available in Spanish (approximately 99% of the translation is done, as I keep adding new information) and about 10% has been translated into Italian.<br /><br /></div><h2 style="text-align: left;">Conclusion</h2><div>There is much more I could write about new but smaller additions to friendly since version 0.4. However, this blog post is already too long and this will have to wait until later - perhaps after I update the <a href="https://friendly-traceback.github.io/docs/index.html" target="_blank">existing documentation.</a></div><div><br /></div><p></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-22449193949579694362021-11-20T08:45:00.004-04:002021-11-20T15:00:54.303-04:00Friendly-traceback en español<p> Friendly and Friendly-traceback are now partially available in Spanish thanks to the work of Mrtín René (https://github.com/martinvilu).</p><p>You can have a look at the Spanish translations in context for <a href="https://friendly-traceback.github.io/docs/syntax_tracebacks_es.html" target="_blank">SyntaxErrors</a> and for <a href="https://friendly-traceback.github.io/docs/tracebacks_es.html" target="_blank">other exceptions</a>.</p><p>If you are interested in contributing to translations, please <a href="https://github.com/friendly-traceback/friendly-traceback/discussions/75" target="_blank">join this discussion</a> and have a look at this <a href="https://poeditor.com/join/project?hash=yC6iMTgLn5" target="_blank">online collaborative site</a>.</p><p><br /></p><p>Update: Someone just volunteered to help with the Italian translation. Note that there are more than 600 pieces of text to translate and that more volunteers can help!</p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-49489242020698458032021-10-23T08:50:00.003-03:002021-10-24T15:34:42.751-03:00Forgot to set up a custom exception hook? Perhaps it is not too late.<p>There are many custom exception hook that seek to improve upon the standard one from Python. The very first one is Python's <a href="https://docs.python.org/3/library/cgitb.html" target="_blank">cgitb</a> module, but there are many others including, in no particular order, <a href="https://github.com/albertz/py_better_exchook/" target="_blank">better_exchook</a>, <a href="http://infi.traceback" target="_blank">infi.traceback</a>, <a href="https://rich.readthedocs.io/en/latest/traceback.html" target="_blank">the traceback submodule of Rich</a>, the different but similarly named <a href="https://github.com/laurb9/rich-traceback" target="_blank">rich-traceback</a>, IPython's <a href="https://github.com/ipython/ipython/blob/master/IPython/core/ultratb.py" target="_blank">ultratb module</a>, <a href="https://github.com/Qix-/better-exceptions" target="_blank">better-exceptions</a>, <a href="https://github.com/cknd/stackprinter" target="_blank">stackprinter</a>, <a href="https://github.com/onelivesleft/PrettyErrors/" target="_blank">pretty-errors</a>, <a href="https://github.com/skorokithakis/tbvaccine" target="_blank">tbvaccine</a>, <a href="https://github.com/alexmojaki/stack_data" target="_blank">stack_data</a>, and likely many more others, including my own <a href="https://friendly-traceback.github.io/docs/index.html" target="_blank">friendly/friendly-traceback</a>.</p><p>To use any of them, they have to be either installed as a custom <b><span style="color: #cc0000; font-family: courier;">sys.excepthook</span></b> <b>before </b>an exception occurred or explicitly written in an <b><span style="color: #cc0000; font-family: courier;">except </span></b>block to process the information. They normally get the relevant information from <span style="color: #cc0000; font-family: courier;"><b>sys.exc_info()</b></span>.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqVZJZZlbXSIWEeFSBim_OTcBNo4Et7HvHLXn1yvwj3dR5HIe5NrBU9niaCjgyOEBFi0oMuzA7PCeQpakWfAciAU1xFdXysQjMC1hx-v8WZ20-QjbO0g7Gs9nYDQuIODqVbNDj6A/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="277" data-original-width="1419" height="78" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqVZJZZlbXSIWEeFSBim_OTcBNo4Et7HvHLXn1yvwj3dR5HIe5NrBU9niaCjgyOEBFi0oMuzA7PCeQpakWfAciAU1xFdXysQjMC1hx-v8WZ20-QjbO0g7Gs9nYDQuIODqVbNDj6A/w400-h78/image.png" width="400" /></a></div><br />What happens if we try to access this information <b>after </b>a normal traceback has be printed?<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR2FqNDqDE6GDrk77Y3Be7zw5jBQK_IaFzR_WwZGXRGwMZJPMFjpJLTCIJzUN9TMHzc8UqOOiRo61V2-Ll8jaRPUp-VsI-MEZ8DlLXbYogf_Q8pxEHtD5Z0iTjGSi_pAZmYXaCDQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="282" data-original-width="1302" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR2FqNDqDE6GDrk77Y3Be7zw5jBQK_IaFzR_WwZGXRGwMZJPMFjpJLTCIJzUN9TMHzc8UqOOiRo61V2-Ll8jaRPUp-VsI-MEZ8DlLXbYogf_Q8pxEHtD5Z0iTjGSi_pAZmYXaCDQ/w400-h86/image.png" width="400" /></a></div><br />Too late: no information is available.<p></p><p>At least, that's what I thought until very recently. Let's see what we can do using the same problematic code in IPython (just to be different) and using friendly after the fact.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirJA5xky5n3A4eOIL2zaq0VfiKBs3g83tzkTgO8nRA4WnZU5ZZufj2z8psE-1Av6UBj1tQXw5716lvUyXhs0qqyy5VgQLXTHynIoDk1m7AMktGBGCkSvKRAW7OjPSWEyOW8Q9FFA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="736" data-original-width="987" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirJA5xky5n3A4eOIL2zaq0VfiKBs3g83tzkTgO8nRA4WnZU5ZZufj2z8psE-1Av6UBj1tQXw5716lvUyXhs0qqyy5VgQLXTHynIoDk1m7AMktGBGCkSvKRAW7OjPSWEyOW8Q9FFA/w400-h300/image.png" width="400" /></a></div><p><br /></p>As it turns out, the individual content of the tuple obtained from <span style="color: #cc0000; font-family: courier;"><b>sys.exc_info()</b></span> before a traceback is printed are available as individual items: <b><span style="color: #cc0000; font-family: courier;">sys.last_type</span></b>, <span style="color: #cc0000; font-family: courier;"><b>sys.last_value</b></span>, and <span style="color: #cc0000; font-family: courier;"><b>sys.last_traceback</b></span>. As explaine in <a href="https://docs.python.org/3/library/sys.html#sys.last_type" target="_blank">sys</a>:<div><br /></div><div><p style="background-color: white; color: #222222; font-family: "Lucida Grande", Arial, sans-serif; font-size: 16px; hyphens: auto; line-height: 1.4; margin-top: 0px;"></p><blockquote><p style="background-color: white; color: #222222; font-family: "Lucida Grande", Arial, sans-serif; font-size: 16px; hyphens: auto; line-height: 1.4; margin-top: 0px;"><i>These three variables are not always defined; they are set when an exception is not handled and the interpreter prints an error message and a stack traceback.</i><span style="background-color: transparent;"> </span></p></blockquote><blockquote><p style="background-color: white; color: #222222; font-family: "Lucida Grande", Arial, sans-serif; font-size: 16px; hyphens: auto; line-height: 1.4; margin-top: 0px;"><i>...</i></p><p style="background-color: white; color: #222222; font-family: "Lucida Grande", Arial, sans-serif; font-size: 16px; hyphens: auto; line-height: 1.4; margin-bottom: 0px;"><i>The meaning of the variables is the same as that of the return values from <a class="reference internal" href="https://docs.python.org/3/library/sys.html#sys.exc_info" style="color: #0072aa; text-decoration-line: none;" title="sys.exc_info"><code class="xref py py-func docutils literal notranslate" style="background-color: transparent; border-radius: 3px; font-family: monospace, monospace; font-size: 15.44px; padding: 0px 1px;"><span class="pre" style="hyphens: none;">exc_info()</span></code></a> above.</i></p></blockquote><p style="background-color: white; color: #222222; font-family: "Lucida Grande", Arial, sans-serif; font-size: 16px; hyphens: auto; line-height: 1.4; margin-bottom: 0px;"></p><p></p><p>Friendly now uses this knowledge: when friendly is imported, it now checks to see if an exception has occurred previously resulting in a printed traceback. If so, it attempts to make use of the information available to help users. This has now been tested with IPython, Jupyter, Colab, Mu, etc. It also works with recent versions of IDLE [1] and the normal CPython interpreter [2].<br /><br /></p><p>I have not checked ... but I'd be curious to hear if any other "traceback enhancer" can be used in this way. If you know of any, please let me know.</p><p><br /></p><p>[1] Excluding SyntaxErrors for now; see this <a href="https://bugs.python.org/issue45560" target="_blank">bug report</a>.</p><p>[2] With limited success since the information about the code entered is temporarily saved in a file named "<stdin>" whose content can never be retrieved.</p></div>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-36123707378442316602021-07-24T09:19:00.007-03:002021-07-24T09:20:45.120-03:00New interactive friendly tracebacks for Jupyter<p><i> Summary: I have a draft version of a new and arguably better way to display information from friendly for Jupyter labs/notebooks. For now, it only works with the default light theme.</i></p><p><br /></p><p>The following screen captures illustrate a new way to display the information from <a href="https://friendly-traceback.github.io/docs/index.html" target="_blank">friendly </a>inside Jupyter lab/notebooks. Instead of having to type some commands, the user can click on buttons. Initially only the error message is shown.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRrtbLAAvFn1hmRsGw-wcrDCaZMNCoWgF8U4PXC-845bRRIo2SdGxkAjaprJkbng16ByoMcCBM1T8g67fara3_W5mKx8LF5JtTZNHWs6OI3Z-y-AsRnQcmqs8GwvUdbOTN7mN1IQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="580" data-original-width="1010" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRrtbLAAvFn1hmRsGw-wcrDCaZMNCoWgF8U4PXC-845bRRIo2SdGxkAjaprJkbng16ByoMcCBM1T8g67fara3_W5mKx8LF5JtTZNHWs6OI3Z-y-AsRnQcmqs8GwvUdbOTN7mN1IQ/w400-h230/image.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Clicking on "More ..." reveals the friendly traceback (notice how the traditional file names are replaced by references to code blocks) and a few more buttons.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSyQnnUpLjwhW1QwKw5q-1gcwWzSkP0Ik8xhhRCZnkJUnASBDMIPdxwx23Mni1JCtS1xTG9WzElw4IRb3nhx7tUY2olw60nEa5mDEiw5UFYW808D8OD7VVAjkjbn4mtdF1Dsuq8A/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="681" data-original-width="945" height="289" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSyQnnUpLjwhW1QwKw5q-1gcwWzSkP0Ik8xhhRCZnkJUnASBDMIPdxwx23Mni1JCtS1xTG9WzElw4IRb3nhx7tUY2olw60nEa5mDEiw5UFYW808D8OD7VVAjkjbn4mtdF1Dsuq8A/w400-h289/image.png" width="400" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">One can click on individual buttons to show or hide the desired information.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEXbhjo5BNApQ-N77ptbnhv1inlM1HzHDOYS0HTWgcMOyERhlJstWHPzuBcmmlt0QPYN6IMR6PVfoqfN-gGgn9sKq3ELAolUyuYKyNQdsqUjhfnTK9R7HILrzc45H4W-6lR43VnA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="801" data-original-width="1053" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEXbhjo5BNApQ-N77ptbnhv1inlM1HzHDOYS0HTWgcMOyERhlJstWHPzuBcmmlt0QPYN6IMR6PVfoqfN-gGgn9sKq3ELAolUyuYKyNQdsqUjhfnTK9R7HILrzc45H4W-6lR43VnA/w400-h304/image.png" width="400" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;">Using the default formatter, instead of this new one, works somewhat acceptably with the dark theme.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQd1Y7lpUVOiQeBJZZ1qQBdpUMlW9DhokNrmq1SzWq8WAq1XbEL0Z5BOiWp_xuDHJiX4BsbfDH_A5z4BxJ-khqdAb9_gAmiVXTST-UBMh_UVaG5AgBkVzDyUDIxYTrZnv2_upg5g/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1076" data-original-width="1147" height="375" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQd1Y7lpUVOiQeBJZZ1qQBdpUMlW9DhokNrmq1SzWq8WAq1XbEL0Z5BOiWp_xuDHJiX4BsbfDH_A5z4BxJ-khqdAb9_gAmiVXTST-UBMh_UVaG5AgBkVzDyUDIxYTrZnv2_upg5g/w400-h375/image.png" width="400" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;">Most unfortunately, the new interactive display just does not pick up the dark theme appropriately; I have not been able to figure out why.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj416KsDMuIYM-kcjAjuyuCkOR9XowF6yWJ94sCD7K2bMKrdp3xSecEB6Chbq_endcrDbj0sXYRvBuVqJSUuvQPSZ_f8iiqMkB9mWPHkPgxc5ekzr-b9deCQjhI0gJC9JwqgPXTFA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="886" data-original-width="1006" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj416KsDMuIYM-kcjAjuyuCkOR9XowF6yWJ94sCD7K2bMKrdp3xSecEB6Chbq_endcrDbj0sXYRvBuVqJSUuvQPSZ_f8iiqMkB9mWPHkPgxc5ekzr-b9deCQjhI0gJC9JwqgPXTFA/w400-h352/image.png" width="400" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;">Ideally, I would like to be able to use the custom dark theme that I have created to work with Rich, but have not figured out how to do it yet.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUAbHHKnN31mCDBiUJs6LLtaqyfS5xD1UWof5E8HQkIr29RobaadZK3sOvebPoevMEroc7aVKYNx5AH6SXTkJvHYgDmmGJMDuslKCgR3akKBuUjMcMj12339g9pWgbBEo2Qbyqdg/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1176" data-original-width="1148" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUAbHHKnN31mCDBiUJs6LLtaqyfS5xD1UWof5E8HQkIr29RobaadZK3sOvebPoevMEroc7aVKYNx5AH6SXTkJvHYgDmmGJMDuslKCgR3akKBuUjMcMj12339g9pWgbBEo2Qbyqdg/w390-h400/image.png" width="390" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;">Anyone having some insights as to how to fix these problems should not hesitate to comment here or on the <a href="https://github.com/friendly-traceback/friendly/issues/5" target="_blank">Github issue</a>.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><br /><p></p><p><br /></p><p><br /></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-55717493133383787112021-07-21T13:33:00.001-03:002021-07-21T13:33:21.572-03:00Friendly-traceback 0.4 and PyConAu<p> Just a quick update ...</p><p>After over 150 commits, most of which included new traceback cases explained by Friendly-traceback, it is time to go from version 0.3 to 0.4. There are too many changes to mention in this blog post and the <a href="https://friendly-traceback.github.io/docs/index.html">documentation</a> needs to be updated. </p><p>The version change is also to allow me to switch gear and focus on <a href="https://2021.pycon.org.au/program/ekjr9k/" target="_blank">preparing a talk for the next PyConAu</a>. I'm hoping that this can be done reasonably quickly (say, within a week) so that I can have the time to write a proper blog post about all the new features of Friendly-traceback.</p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-72423749141804575182021-07-12T15:03:00.001-03:002021-07-12T15:03:54.356-03:00Friendlier tracebacks in REPLs (including Jupyter)<p><b>Traceback</b>: <i>Determination of origin; the process of tracing something back to its source</i>.</p><p>I have been working towards release 0.4 of <a href="https://friendly-traceback.github.io/docs/index.html" target="_blank">friendly/friendly_traceback</a> as well as preparing for a talk at the upcoming PyconAU. Those familiar with PyconAU might be interested to note that I plan to include an example related to the <a href="https://2021.pycon.org.au/culture/" target="_blank">flipfloperator</a>. :-)</p><p>In this preparatory work, I have been revisiting almost all aspects of friendly/friendly-traceback, making various improvements. Unfortunately, this means that many of the screenshots included in the documentation will have to be updated.</p><p>In this blog post, I want to illustrate some of the changes that I have finished implementing with regards to the traceback themselves. By this, I mean the sequence of calls that were done and <b>not </b>the message, such as "IndexError: list index out of range". I will look at the same simple example using a variety of interpreters. Since this is a simple example, the traceback is not going to be very long ... but I want you to imagine situations where it could be much longer.</p><p>First, let me introduce this example using the CPython REPL:</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8pWSBQ0jwbzw-k7A9Yu0dMtr1ib-YOPdxXhVdlz6HqZ7EnOXlgc8yY22ts6Ura0awe2EiDorJIKuDHGih4re-zs5huJ980fWL1qNpfiQqz8aTSkgyLWl7nhyphenhyphenQfl4r338PB4SCwA/s581/python_repl.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="336" data-original-width="581" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8pWSBQ0jwbzw-k7A9Yu0dMtr1ib-YOPdxXhVdlz6HqZ7EnOXlgc8yY22ts6Ura0awe2EiDorJIKuDHGih4re-zs5huJ980fWL1qNpfiQqz8aTSkgyLWl7nhyphenhyphenQfl4r338PB4SCwA/s320/python_repl.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">We see a reference to a "file" named "<stdin>" which appears many times. While we have some line numbers indicated, CPython does not tell us anything about the content of these lines. Thus, it could be difficult to <i><b>trace back</b></i> the source of the error.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The situation is slightly better with IDLE, as it keeps track of different "<pyshell> files" and show us the relevant lines of code.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWsxWDuCaoryVy8xJky25r5N9oM5z-CDE8TIbHl9Lew-93lgLhrdf4oIwi2ghCl59foPCYUWHxm-bAeVxFBj2odLkPdyeDIcNO_iy2_O1niT26CJjwdpWIQipr3bPC9fCwHXxKJQ/s754/idle_repl.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="401" data-original-width="754" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWsxWDuCaoryVy8xJky25r5N9oM5z-CDE8TIbHl9Lew-93lgLhrdf4oIwi2ghCl59foPCYUWHxm-bAeVxFBj2odLkPdyeDIcNO_iy2_O1niT26CJjwdpWIQipr3bPC9fCwHXxKJQ/w400-h213/idle_repl.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Up until recently, this was the strategy I was using by default with the friendly console. Here we see it running within IDLE.</div><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWIFtJnsxYSZ9aEgO9DW75Qn6S87zX3abtYO-CBbxfqRzAwP1kLbh6UglLijGQrSLTI93e0sUuSddlFPc5AjQyt83joHpwjKBn5yBCxAiJNKB_VD_f6bBoXFVn1sHru7hnJWOAGQ/s1145/friendly_idle.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="678" data-original-width="1145" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWIFtJnsxYSZ9aEgO9DW75Qn6S87zX3abtYO-CBbxfqRzAwP1kLbh6UglLijGQrSLTI93e0sUuSddlFPc5AjQyt83joHpwjKBn5yBCxAiJNKB_VD_f6bBoXFVn1sHru7hnJWOAGQ/w640-h378/friendly_idle.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">For longer traceback, the user might have difficulty identifying the block of code corresponding to each "<friendly-console> file". Fortunately, friendly includes the function where() which can provide some help in this situation.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg919A4BVIcqcO3Dasb5v7ruBoB_6WJAl3X8aNGclK-u1lDoDmD2hLjHIE7NJGKR1Y-RrKlhm6CYsWd4PlJLiaHhews5cx4rr_XwEwLaqVVR_Mro7IbO2N08R-G4M24c4-1Euywg/s981/friendly_idle_where.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="577" data-original-width="981" height="376" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg919A4BVIcqcO3Dasb5v7ruBoB_6WJAl3X8aNGclK-u1lDoDmD2hLjHIE7NJGKR1Y-RrKlhm6CYsWd4PlJLiaHhews5cx4rr_XwEwLaqVVR_Mro7IbO2N08R-G4M24c4-1Euywg/w640-h376/friendly_idle_where.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">For perhaps a slightly better approach, we can look at what IPython does.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEZvT5ujwU_gM1tITWMtqPfoqVP3rPuhidIDNn88bmBKogyboNCJ209DGEoPGN8BXR3Mu78PMNd0wdBAOCG8iZylH2mCcvnKiNlR9Jh-uW9E0VqjDAcpboK-ZiRiqieWdDaCuv6w/s1073/ipython_repl.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="792" data-original-width="1073" height="472" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEZvT5ujwU_gM1tITWMtqPfoqVP3rPuhidIDNn88bmBKogyboNCJ209DGEoPGN8BXR3Mu78PMNd0wdBAOCG8iZylH2mCcvnKiNlR9Jh-uW9E0VqjDAcpboK-ZiRiqieWdDaCuv6w/w640-h472/ipython_repl.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Each code block is identified by a number in brackets, [1], [2], etc.. The traceback includes a weird name of the form "<ipython-input-N-...>". By looking closely at enough examples, we can conclude that the "N" correspond to the digit between the square brackets in a code block.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Moving on to Jupyter (lab). Up until fairly recently, Jupyter lab (and notebooks), which are based on IPython, were using "filenames" of the form "<ipython-input-N-...>". However, this seems to have been changed recently, and we now have "real" filenames ... with names that do not contain any clue about their origin. [<a href="https://github.com/ipython/ipykernel/issues/716" target="_blank">I filed an issue suggesting an improvement.</a>]</div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzBY8XvsVWFbLI_EpuAOaOjLJIeVBcSWzY7WX17GFxoT0HeAK7XqgQLWD_LPxQBe3DHPLYjnUXm3MVpJ6yTy9DLI32XL6yM30Sua-wNv0hvX5gjGP2tZ-wz7PgECGLyWxSMC4t-Q/s889/jupyter_lab.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="518" data-original-width="889" height="372" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzBY8XvsVWFbLI_EpuAOaOjLJIeVBcSWzY7WX17GFxoT0HeAK7XqgQLWD_LPxQBe3DHPLYjnUXm3MVpJ6yTy9DLI32XL6yM30Sua-wNv0hvX5gjGP2tZ-wz7PgECGLyWxSMC4t-Q/w640-h372/jupyter_lab.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div>However, it is possible to do better. Here's how friendly now shows this information within Jupyter lab.<div><br /></div><div><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrkRLZ8By2rVcOW12JGA0lUpJuR-SeQE_B6e_7Gvooy8l9tyFRbmP4MiVGHWbUURkqNjs6lYZZzWDuWkkm8XT-FjB_-YMJioy4RihGcCb1Ktk5YewFEd1fF2zcpt6qP9tnkCrIcg/s949/friendly_jupyter.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="586" data-original-width="949" height="396" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrkRLZ8By2rVcOW12JGA0lUpJuR-SeQE_B6e_7Gvooy8l9tyFRbmP4MiVGHWbUURkqNjs6lYZZzWDuWkkm8XT-FjB_-YMJioy4RihGcCb1Ktk5YewFEd1fF2zcpt6qP9tnkCrIcg/w640-h396/friendly_jupyter.png" width="640" /></a></div><div><br /></div><div><br /></div><div>Instead of a "File" we see mention of "Code block" which might be less confusing, especially to beginners that are starting with Jupyter and never dealt with Python code in separate files before.</div><div><br /></div><div>Of course, this being friendly, we can show much more than simply the traceback. Here's the complete information available:</div><div><br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ2Ya2AMzwPux1qUXysPklt_FHTTFQpk0QntB7NZe8DhrC3pNGeHE3rnQ1uqGWMHNKVoj_xqGlzM1CrB6xeSVCVYalRJ-EhYjZK98Ohbq6lobuTbg7vu1hhKfX89lIUC2_2Dl58g/s1036/friendly_jupyter_explain.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1036" data-original-width="841" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ2Ya2AMzwPux1qUXysPklt_FHTTFQpk0QntB7NZe8DhrC3pNGeHE3rnQ1uqGWMHNKVoj_xqGlzM1CrB6xeSVCVYalRJ-EhYjZK98Ohbq6lobuTbg7vu1hhKfX89lIUC2_2Dl58g/w520-h640/friendly_jupyter_explain.png" width="520" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">To properly format tracebacks with "Code block" instead of "File", I had to monkey patch pygment's lexer.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Since I thought that such numbered code blocks were more informative than fake filenames like "<friendly-console>", I decided to adopt them by default in friendly's REPL:</div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjB_9OqYpLbRTZUIKA-arkTWL4jZW3eHHcNWImKfkERsAUE0jvZBqcUXvZPvM84LiKdGCZYUp9GD0x0Z3wcNw5n8qznC-DfarijFAzTDdckcVUCccq1J1JwhB23nmxSn_di95VeKg/s972/friendly_ipython_prompt.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="773" data-original-width="972" height="508" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjB_9OqYpLbRTZUIKA-arkTWL4jZW3eHHcNWImKfkERsAUE0jvZBqcUXvZPvM84LiKdGCZYUp9GD0x0Z3wcNw5n8qznC-DfarijFAzTDdckcVUCccq1J1JwhB23nmxSn_di95VeKg/w640-h508/friendly_ipython_prompt.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">This new REPL style is also available in friendly.idle (not shown in this post).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Finally, here's an example showing a mixture of code from files and code blocks.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNNjHDxQm6IQDRVlKsaAuj19lHvFGN8yZttWQn30UhO39PFncy-kg26XqsY9ihw0-T8YR8xz21bOXxGlBbz4tAwZSWAUJhoWOk__SezFr2-hNqVfUezRHxlUiw9Ve1j8KJ_sGtAQ/s970/friendly_ipython_prompt1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="381" data-original-width="970" height="252" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNNjHDxQm6IQDRVlKsaAuj19lHvFGN8yZttWQn30UhO39PFncy-kg26XqsY9ihw0-T8YR8xz21bOXxGlBbz4tAwZSWAUJhoWOk__SezFr2-hNqVfUezRHxlUiw9Ve1j8KJ_sGtAQ/w640-h252/friendly_ipython_prompt1.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><span>If you have any suggestion for possible improvements to friendly/friendly-traceback, please do not hesitate to let me know by </span><a href="https://github.com/friendly-traceback/friendly-traceback/issues" target="_blank">filing an issue</a>. I am especially interested to hearing from teachers/mentors that work with beginners.</div><br /><div class="separator" style="clear: both; text-align: center;"><span style="text-align: left;">. </span></div><p><br /></p><p><br /></p></div>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-33482896178590075352021-07-08T11:12:00.000-03:002021-07-08T11:12:28.020-03:00friendly-traceback is back!<p> A few months ago, <a href="https://aroberge.blogspot.com/2021/03/friendly-traceback-will-have-new-name.html" target="_blank">I mentioned that friendly-traceback had been renamed friendly</a>. As friendly/friendly-traceback evolved, the number of dependencies increased. For some third-party projects that use friendly/friendly-traceback, such as <a href="https://futurecoder.io/" target="_blank">futurecoder</a> and <a href="https://www.hackinscience.org/" target="_blank">HackInScience</a>, many of these dependencies are simply redundant and represent a performance hit. For this reason, I have split friendly/friendly-traceback into two separate projects, and created a new "organization" on Github [1] with separate repositories: one for<a href="https://github.com/friendly-traceback/friendly-traceback" target="_blank"> friendly-traceback</a>, and a separate one for <a href="https://github.com/friendly-traceback/friendly" target="_blank">friendly</a> (which has friendly-traceback as a dependency). I even temporarily added a third project (friendly_idle) but folded it back into friendly as the extra burden of maintaining a relatively small project with lots of duplication was too much work for very little benefit.</p><p>Much progress has been done with these projects since the last minor release (0.3) and I am in the process of doing a major update to <a href="https://friendly-traceback.github.io/docs/index.html" target="_blank">the documentation</a> prior to the 0.4 release; friendly-traceback is currently at version 0.3.154.</p><p>If you are a user of friendly/friendly-traceback, you likely need to do "pip install friendly". If you want to use the information provided by friendly/friendly-traceback into your own project, you likely only need to do "pip install friendly-back".</p><p><br /></p><p>[1] This change meant going back to zero stars on Github! </p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-7642577329423628392021-03-14T08:30:00.000-03:002021-03-14T08:30:02.156-03:00Friendly version 0.3 has been released<p><a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/index.html" target="_blank">Friendly</a> version 0.3 has been released. This version also marks the official name change from the former friendly-traceback.</p><p>Before I started working on Friendly, I assumed that to do custom exception handling, one simply had to redefine sys.excepthook. However, I since found out that this does not work in many environments, including all those based on IPython (which include Jupyter notebooks). Even Python's IDLE, at least up until Python version 3.10.0a5, does not work with a simple replacement of sys.excepthook.</p><p>Additionally, creating coloured output varies depending on the programming environment. Thankfully, this can be done in most environments simply by using <a href="https://github.com/willmcgugan/rich">Rich</a> -- sometimes supplemented by a bit of additional code.</p><p>As a result, friendly includes various custom modules so that it can work with the following:</p><p></p><ul style="text-align: left;"><li>IDLE (see <a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/idle.html">this </a>and the following 2 pages)</li><li>Mu (see four pages starting <a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/mu_about.html">here</a>)</li><li><a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/jupyter.html" target="_blank">Jupyter notebooks and Jupyter Lab</a> (which also work with <a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/colab.html">Google colab</a>)</li><li><a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/ipython_repl.html">IPython console</a></li><li>VS Code (either running from the terminal or with embedded notebooks)</li></ul><div>Friendly has now over 200 test cases. Most of these are not unit tests, but instead are integration tests: given code X that raises exception Y (including message Z), ensure that the output is as expected. For example, suppose that after module M is imported, we try to use attribute A containing a typo. Such a base scenario might need 2 test cases to over all possibilities: whether we can find an attribute that is a similar word, likely indicating a typo, or not. Here's an example of what one can get in the first scenario:</div><div><br /></div><div><div style="background-color: #0c0c0c; display: inline-block; font-family: 'Cascadia Mono',monospace; font-size: 12pt; padding: 4px; white-space: pre;"><span style="color: #cccccc;">>>> import math<br />>>> a = math.sine(1)<br /><br />Traceback (most recent call last):<br /> File "<friendly-console:2>", line 1, in <module><br /> a = math.sine(1)<br />AttributeError: module 'math' has no attribute 'sine'<br /><br /> Did you mean one of the following: `sin, sinh, asin`?<br /></span></div></div><div><br /></div><div>Friendly included a "hint" (Did you mean one of the...). Further information can be obtained by using what(), why(), where(), etc., as described in previous posts.</div><div><br /></div><div>[Note to self: for the above example, the hint should only include the most likely candidate, namely 'sin', leaving the other choices for the longer explanation done with "why()", as was done for other type of exceptions.]</div><div><br /></div><div>Currently, about half of the test cases are examples of SyntaxError. I suspect that this will continue to be the case as work on friendly continues and the total number of test cases continues to grow. I would not be surprised if the total number of test cases were to double by the time version 1.0 is ready. My goal for version 1.0 is to try to cover all possible exception cases that can occur when making mistakes while trying to use code from Python's standard library.</div><p></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-46679102608734650942021-03-06T12:58:00.001-04:002021-03-06T12:58:33.238-04:00Going back in history<p>Imagine that you wish to run a program that takes a long time to run. Just in case somethings goes wrong, you decide to use friendly-traceback (soon to be renamed...) in interactive mode to run it. This turns out to be a good decision:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6PHldszCMBZ_XNGhG0DrJt8HvTO83BYaNmTqPncebDVCj132UHmAW9640-VIkcwTxaEWSPUSnUm6g858_bj8viVTtUpobFqz5AjdrQp423AIGM_SH6m9ShiOY0gHQG6s3zPi8zQ/s1856/history1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="479" data-original-width="1856" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6PHldszCMBZ_XNGhG0DrJt8HvTO83BYaNmTqPncebDVCj132UHmAW9640-VIkcwTxaEWSPUSnUm6g858_bj8viVTtUpobFqz5AjdrQp423AIGM_SH6m9ShiOY0gHQG6s3zPi8zQ/w640-h166/history1.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">Time to explore what might be the problem, and where exactly things might have gone wrong.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ2IXHf1m_sEsuL-wT2cPhJ8iW2-AMJlOYLRLGWGOXXo26JZo8EuL6kpJHto6e1P4ekKzUJ1FDQrIIbsN_xGUUKafmMecZXt-6e2BgZa4v0n4e9Vh-hryskU65YH0VlMFgS4hquA/s1856/history2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="466" data-original-width="1856" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ2IXHf1m_sEsuL-wT2cPhJ8iW2-AMJlOYLRLGWGOXXo26JZo8EuL6kpJHto6e1P4ekKzUJ1FDQrIIbsN_xGUUKafmMecZXt-6e2BgZa4v0n4e9Vh-hryskU65YH0VlMFgS4hquA/w640-h160/history2.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">Ooops ... a silly typo. Easy enough to correct:</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwHdLCLgHLGWWjWsHGzeIY-x413CB7UCRneI-QO5fUuNJgrBrf3cuL8euPdk3MAdIN9ftMyb28dSpF437zei_DYtIFQY3R-LrtlISrNLkWjzJangiPowb0kB9XOimDFfR0dSzj_Q/s1856/history3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="349" data-original-width="1856" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwHdLCLgHLGWWjWsHGzeIY-x413CB7UCRneI-QO5fUuNJgrBrf3cuL8euPdk3MAdIN9ftMyb28dSpF437zei_DYtIFQY3R-LrtlISrNLkWjzJangiPowb0kB9XOimDFfR0dSzj_Q/w640-h120/history3.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">Unfortunately, that did not work: Friendly-traceback, like all Python programs designed to handle exceptions, only capture the last one.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">This has happened to me so many times; granted, it was always with short programs so that I could easily recreate the original exception. However, I can only imagine how frustrating it might be for beginners encountering this situation.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><h3 style="clear: both; text-align: left;">A solution</h3><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Fortunately, you are using the latest version of Friendly-traceback, the one that records exceptions that were captured, and allows you to discard the last one recorded (rinse, and repeat as often as needed), thus going back in history.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8_c3Ir3f7M_CHmRRnrrGeL5R7O0FKCChV_Qregv0YqTb_T66YJ4QIXXmDsW__OSceWNVAoNn1gd7eO06l7pmvZ5arTcESQi6rrzvAr7eeggDVKEFP_bhrGOsSR7jAS2kG0VzX5Q/s1856/history4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="293" data-original-width="1856" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8_c3Ir3f7M_CHmRRnrrGeL5R7O0FKCChV_Qregv0YqTb_T66YJ4QIXXmDsW__OSceWNVAoNn1gd7eO06l7pmvZ5arTcESQi6rrzvAr7eeggDVKEFP_bhrGOsSR7jAS2kG0VzX5Q/w640-h102/history4.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Now that we are set, we can explore further to determine what might have gone wrong.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCoeL_2YeIWrSYVmds94nVSYrw3AbeG6SovnSRe4nrcBZsMDScHOIkRE1JyMhXIphvp0aYD04kWcY8W6gcD38_0gXqoFofjoySwFdIvfXABSyEF-S8yOrCE5Zw6BMTTdQR8aDz3Q/s1856/history5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="921" data-original-width="1856" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCoeL_2YeIWrSYVmds94nVSYrw3AbeG6SovnSRe4nrcBZsMDScHOIkRE1JyMhXIphvp0aYD04kWcY8W6gcD38_0gXqoFofjoySwFdIvfXABSyEF-S8yOrCE5Zw6BMTTdQR8aDz3Q/w640-h318/history5.png" width="640" /></a></div><br /><p><br /></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-76087254760248354292021-03-05T15:56:00.000-04:002021-03-05T15:56:17.771-04:00Friendly-traceback will have a new name<p>tl; dr: I plan to change the name from <span style="color: #990000; font-family: courier;"><b>friendly_traceback</b></span> to <span style="color: #990000; font-family: courier;"><b>friendly</b></span>.</p><span><a name='more'></a></span><p><br /></p><p> When I started working on Friendly-traceback, I had a simple goal in mind:</p><blockquote><p>Given <b>an error message</b> in a Python <b>traceback</b>, parse it and reformulate it into something easier to understand by beginners and that could be easily translated into languages other than English.</p></blockquote>
<p>A secondary goal was to help users learn how to decipher a normal Python
traceback and use the information <strong>provided by Python</strong>
to understand what went wrong and how to fix it. </p><p>Early on, I quickly realised that this would not be helpful when users are faced with arguably the most frustrating error message of them all: </p><p><b></b></p><blockquote><b><span style="color: #990000; font-family: courier;">SyntaxError: invalid syntax</span></b></blockquote><p></p><p>Encouraged by early adopters, I then began a quest to go much beyond simply interpreting a given error message, and trying to find a more specific cause of a given traceback. As Friendly-traceback was able to provide more and more information to users, I was faced with the realisation that too much information presented all at once could be counter-productive. Thus, it was broken down and could be made available in a console by asking <b><span style="color: #990000; font-family: courier;">what(), where(), why()</span></b>, etc. If Friendly-traceback does not recognize a given error message, one can now simply type <span style="color: #990000; font-family: courier;"><b>www()</b></span> [<i>name subject to change</i>] and an Internet search for that specific message will be done using the default web browser.</p><p>By default, Friendly-traceback uses a custom exception hook to replace <span style="color: #990000; font-family: courier;"><b>sys.excepthook</b></span>: this definitely works with a standard Python interpreter. However, it does not work with IPython, Jupyter notebooks, IDLE (at least, not for Python 3.9 and older), etc. So, custom modules now exist and users have to write:</p><p></p><ul style="text-align: left;"><li><b><span style="color: #990000; font-family: courier;">from friendly_traceback.idle import ...</span></b></li><li><span style="color: #990000; font-family: courier;"><b>from friendly_traceback.jupyter import ...</b></span></li><li><span style="color: #990000; font-family: courier;"><b>from friendly_traceback.ipython import ...</b></span></li><li><span style="color: #990000; font-family: courier;"><b>from friendly_traceback.mu import ...</b></span></li><li><span style="font-family: courier;"><b><span style="color: #990000;">from friendly_traceback import ...</span> </b></span> <span style="color: #38761d;"># generic case</span></li></ul><div>To run a program from a terminal requires to write:</div><div><br /></div><div><span style="color: #990000; font-family: courier;"><b>python -m friendly_traceback my_program.py</b></span> [<i>additional options</i>]</div><div><br /></div><div>All of these are rather long to type ...</div><div><br /></div><div>In addition to tracebacks, I have been thinking of including Python warnings, and in particular <span style="color: #990000; font-family: courier;"><b>SyntaxWarnings</b></span>. </div><div><br /></div><div>Along the same lines, when using the "friendly" console, I have added some exprimental warnings, such as those shown below.</div><div><br /></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYRFbKBcowfO3bTYMzHyibJenrZ6EnaJzgSI7UYnaCzrLE9LPK5xHYst_ggOrc-GE5xM-KzvhyI4P5Lk0XYk_P4pQv5qXiCmDI3xfgtPNy-anK_brFK0jbWkxRM7xiU1dl0KqZrg/s1077/warnings_examples.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="378" data-original-width="1077" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYRFbKBcowfO3bTYMzHyibJenrZ6EnaJzgSI7UYnaCzrLE9LPK5xHYst_ggOrc-GE5xM-KzvhyI4P5Lk0XYk_P4pQv5qXiCmDI3xfgtPNy-anK_brFK0jbWkxRM7xiU1dl0KqZrg/w640-h225/warnings_examples.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div>I do not know if these warnings will be part of future versions of Friendly-traceback. What I do know, is that I want to consider incorporating things other than traceback that might be useful to beginners and/or to non-English speakers.<p></p><p>Back to the name change. I have typed "friendly_traceback" many, many times. It is long and annoying to type. When I work at a console, I often do:</p><p><span style="color: #990000; font-family: courier;"><b></b></span></p><blockquote><span style="color: #990000; font-family: courier;"><b>import friendly_traceback as ft</b></span></blockquote><p></p><p>and proceed from there.</p><p>I suspect that not too many potential users would be fond of friendly_traceback as a name. Furthermore, I wonder how convenient it is to type a name with an underscore character when using a non-English keyboard. Finally, whenever I write about Friendly-traceback, it is an hyphen that is used between the two names, and not an underscore character: one more possible source of confusion.</p><p>For all these reasons, I plan to soon change the name to be simply "friendly". This will almost certainly be done as the version number will increase from 0.2.xy to 0.3.0 ... which is going to happen "soon".</p><p>Such a name change will mean a major editing job to the <a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/index.html" target="_blank">extensive documentation</a> which currently includes 76 screenshots, most of which have "friendly_traceback" in them. This means that they will all have to be redone. Of course, the most important work to be done will be changing the source code itself; however, this should be fairly easy to do with a global search/replace.</p><p></p><div><br /></div><div><br /></div><p></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-7121252420690700312021-02-27T11:42:00.004-04:002021-02-27T11:51:12.528-04:00Friendly-traceback: testing with Real Python<p><a href="https://realpython.com/" target="_blank">Real Python</a> is an excellent learning resource for beginning and intermediate Python programmers that want to learn more about various Python related topics. Most of the resources of RealPython are behind a paywall, but there are many articles available for free. One of the free articles, <a href="https://realpython.com/invalid-syntax-python/" target="_blank">Invalid Syntax in Python: Common Reasons for SyntaxError</a>, is a good overview of possible causes of syntax errors when using Python. The Real Python article shows code raising exceptions due to syntax errors and provides some explanation for each case.</p><p>In this blog post, I reproduce the cases covered in the Real Python article and show the information provided by Friendly-traceback. Ideally, <b><u>you should read this blog post side by side with the Real Python article</u></b>, as I mostly focus on showing screen captures, with very little added explanation or background.</p><p>If you want to follow along using Friendly-traceback, make sure that you use version 0.2.34 or newer.</p><h2 style="text-align: left;">Missing comma: first example from the article</h2><div>The article starts off showing some code leading to this rather terse and uninformative traceback.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvdmBg9OAbipmjop6OBu_ePqNaW7ASejXujmlvVwqfU-V8NDlaj1GCA3aJMPBUz8fAzCYVUf4QszFiM8FlO5q0Toj5_V1veEVttvKPVyaWRj6_F22e4Kz0rVLMOG5HGILjHXbTRg/s1040/syntax_error1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="169" data-original-width="1040" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvdmBg9OAbipmjop6OBu_ePqNaW7ASejXujmlvVwqfU-V8NDlaj1GCA3aJMPBUz8fAzCYVUf4QszFiM8FlO5q0Toj5_V1veEVttvKPVyaWRj6_F22e4Kz0rVLMOG5HGILjHXbTRg/w640-h104/syntax_error1.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Since the code is found in a file, we use <b><span style="color: #cc0000;">python -m friendly_traceback theofficefacts.py</span></b> to run it and obtain the following.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-vB1yuj0BmWEOhJnIYY0cY7MV2Keth_YruPWzCRt6I5F4_v-NvKbKSG0mL5zrIdobzyNLk-ZK45RECPW1PAf7GsaY4tJ1vaRKIpFlOuJ4QcQ6jwu55fY1fYSOG1biQNCGsrATdg/s1040/syntax_error1a.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="904" data-original-width="1040" height="556" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-vB1yuj0BmWEOhJnIYY0cY7MV2Keth_YruPWzCRt6I5F4_v-NvKbKSG0mL5zrIdobzyNLk-ZK45RECPW1PAf7GsaY4tJ1vaRKIpFlOuJ4QcQ6jwu55fY1fYSOG1biQNCGsrATdg/w640-h556/syntax_error1a.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h2 style="clear: both; text-align: left;">Misusing the Assignment Operator (=)</h2><div>We only show one example here, as the others mentioned in the article would be redundant. We remind you for one last time that, if you are not doing so, you should really look at the Real Python article at the same time as you go through this rather terse blog post.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYsyyD4JGExbfmQCPXVz-ejIrzReIymRLC7CKY_76Ou81MgyYvO1P1cET-CuHJ2A4rKqdKleQw-_OxwbG4lJbByXcfkhSAX3Us-mNtpIXg5IPIwpfE2glP9Vl685jfwIXsDhUlww/s1040/syntax_error2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="282" data-original-width="1040" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYsyyD4JGExbfmQCPXVz-ejIrzReIymRLC7CKY_76Ou81MgyYvO1P1cET-CuHJ2A4rKqdKleQw-_OxwbG4lJbByXcfkhSAX3Us-mNtpIXg5IPIwpfE2glP9Vl685jfwIXsDhUlww/w640-h174/syntax_error2.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Friendly traceback provides a "hint" right after the traceback. We can get more information by asking <b><span style="color: #cc0000;">why()</span></b>.<div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioM9VdEds8RLwbm25Pp5am8eJrrvDnJiRInlZ2vfUpDC5mtBe4VNANENO6aoVe05n1IwosC0eGG8iKnjVcifSzFOOGQuj3AnmvwVHX8Mr_cD3hFfwVecNPXbN6Mjp47I4siZw0cA/s1040/syntax_error2a.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="296" data-original-width="1040" height="182" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioM9VdEds8RLwbm25Pp5am8eJrrvDnJiRInlZ2vfUpDC5mtBe4VNANENO6aoVe05n1IwosC0eGG8iKnjVcifSzFOOGQuj3AnmvwVHX8Mr_cD3hFfwVecNPXbN6Mjp47I4siZw0cA/w640-h182/syntax_error2a.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h2 style="clear: both; text-align: left;">Misspelling, Missing, or Misusing Python Keywords</h2><div>Identifying misspelled keywords was actually inspired by that article from Real Python.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0BxWI2OvlCMxTWYRLfhinn4EIeTP_Uk5LEveZywc4lHrboP2sXMVXhzoJ6Bsj2IkPW3sXAag4G2vm-4QE1VNCtC0XU_Paeu5LvUKVnQDquq2G3ZCZ_ptA8xGa169PWOGwIz8hBg/s1040/syntax_error3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="268" data-original-width="1040" height="164" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0BxWI2OvlCMxTWYRLfhinn4EIeTP_Uk5LEveZywc4lHrboP2sXMVXhzoJ6Bsj2IkPW3sXAag4G2vm-4QE1VNCtC0XU_Paeu5LvUKVnQDquq2G3ZCZ_ptA8xGa169PWOGwIz8hBg/w640-h164/syntax_error3.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4XGkwMoghK-ZvcJZy3yMHqxWHXxKFy9pOMKSFjryWkwkjkMMRs0nu85wsEpVztfKeJQEXBKtMVsgbMN8FqD94WkZeb5-CuJo1vAHmQu7nmq9KsjM96c4LJyh3fn9KzsyrkhPW2w/s1040/syntax_error3a.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="295" data-original-width="1040" height="182" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4XGkwMoghK-ZvcJZy3yMHqxWHXxKFy9pOMKSFjryWkwkjkMMRs0nu85wsEpVztfKeJQEXBKtMVsgbMN8FqD94WkZeb5-CuJo1vAHmQu7nmq9KsjM96c4LJyh3fn9KzsyrkhPW2w/w640-h182/syntax_error3a.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Note that Friendly-traceback identifies "for" as being the most likely misspelled keyword, but gives other possible valid choices.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Friendly-traceback can also identify when <b><span style="color: #cc0000;">break </span></b>(and <b><span style="color: #cc0000;">return</span></b>) are used outside a loop.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuGP336EmQcaE3nlCnqkE6uv2Z1UPeuVlZir8itlR4oJXZAFaRM0HJvM1VHBuNNkgZiAyT0YFUArF2aWv3QHctQ77A0mJFcTcUMo2MnEF36Ld4Yr0Fj21YVbvB1eEKYxcS1nD-Nw/s1040/syntax_error4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="295" data-original-width="1040" height="182" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuGP336EmQcaE3nlCnqkE6uv2Z1UPeuVlZir8itlR4oJXZAFaRM0HJvM1VHBuNNkgZiAyT0YFUArF2aWv3QHctQ77A0mJFcTcUMo2MnEF36Ld4Yr0Fj21YVbvB1eEKYxcS1nD-Nw/w640-h182/syntax_error4.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6ezKK3tRJBJnAOhwuc93Ys6Ro2b54bnv4nsNYEanT9AhcVpAqTAMvIW6vRcrtDKSVq89brDUdO7jF7bFKTYM40VJDmkLTHoKnkcILlXuiJHHkoKqbwl3ZOEAAVRpBK-7n2o37rA/s1040/syntax_error4a.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="120" data-original-width="1040" height="74" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6ezKK3tRJBJnAOhwuc93Ys6Ro2b54bnv4nsNYEanT9AhcVpAqTAMvIW6vRcrtDKSVq89brDUdO7jF7bFKTYM40VJDmkLTHoKnkcILlXuiJHHkoKqbwl3ZOEAAVRpBK-7n2o37rA/w640-h74/syntax_error4a.png" width="640" /></a></div><div><br /></div><div>To the English reader, Friendly-traceback might seem to add very little useful information. However, keep in mind that all this additional information can be translated. If you read the following and do not understand what "boucle" means, then you might get an idea of the some of the challenges faced by non-English speakers when using Python.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3WA5U1HTAtsZz7YYvBsHjNQhHf6F32lN-FE6xYYQcN6iJ44m8Dvg3yYoRVU9Z2DgTFg-ONOIpAcI4Dr9zx8PnvJaKyZC5m_rb0Th685LQI-lhlO-rnxz6CEt6iCYbDAxNM7BYxw/s1040/syntax_error4b.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="148" data-original-width="1040" height="92" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3WA5U1HTAtsZz7YYvBsHjNQhHf6F32lN-FE6xYYQcN6iJ44m8Dvg3yYoRVU9Z2DgTFg-ONOIpAcI4Dr9zx8PnvJaKyZC5m_rb0Th685LQI-lhlO-rnxz6CEt6iCYbDAxNM7BYxw/w640-h92/syntax_error4b.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">In some other cases, like the example given in the Real Python article, Friendly-traceback can identify a <b>missing </b>keyword.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA7amL5kfmuTKfX8xi-UuRbTinWsLA1iaGYnHkIQV56l_7Jiah0Nh17mCkZ010JdfRg2bAuUEqNE0vCIFcRhAas5576kSM5DB0BDnh9BAv-7lt5yCKr33ZetqoD2UA_-2lRpPRA/s1040/syntax_error4c.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="449" data-original-width="1040" height="276" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibA7amL5kfmuTKfX8xi-UuRbTinWsLA1iaGYnHkIQV56l_7Jiah0Nh17mCkZ010JdfRg2bAuUEqNE0vCIFcRhAas5576kSM5DB0BDnh9BAv-7lt5yCKr33ZetqoD2UA_-2lRpPRA/w640-h276/syntax_error4c.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">As long as there is only one instance of "<b><span style="color: #cc0000;">in</span></b>" missing, Friendly-traceback can identify it properly.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqihp26SBXJ_4oOxy8okJC5KolBHezfA3FPjj4NF0d-Z_6d5prUqRdMbgquMt8L0VuX_jT2wpcz6wr8PUf2JxzBC3KRlFkfmI-_bmRJ5FnzqLvG4c8byQHR-mDRB5rslA-s9lJrQ/s1040/syntax_error4d.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="493" data-original-width="1040" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqihp26SBXJ_4oOxy8okJC5KolBHezfA3FPjj4NF0d-Z_6d5prUqRdMbgquMt8L0VuX_jT2wpcz6wr8PUf2JxzBC3KRlFkfmI-_bmRJ5FnzqLvG4c8byQHR-mDRB5rslA-s9lJrQ/w640-h304/syntax_error4d.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Finally, two more cases where a Python keyword is not used properly.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRY8m1pL5veTD0Rx247iSwQA4_v1WdaCFa_HnflBxcRynzuO-7CqIMrIzzJY4gxjiTak86okNLCAcfx7sy_-08dWpkVXmHeiOys7qzWyDbH5MhNxGt81yF_CEdJohjKv4atRcY_g/s1040/syntax_error5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="381" data-original-width="1040" height="234" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRY8m1pL5veTD0Rx247iSwQA4_v1WdaCFa_HnflBxcRynzuO-7CqIMrIzzJY4gxjiTak86okNLCAcfx7sy_-08dWpkVXmHeiOys7qzWyDbH5MhNxGt81yF_CEdJohjKv4atRcY_g/w640-h234/syntax_error5.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsJWBGKgJnNYdrd8G3yRGLXKs7lvG2_qvAgUWseL9VXLJtkPvLQG-dhvO8imz5EP5cgVEwoRrJBaVuqmDyzdsAqgfvT29nO-51Q4QJibIrdK9_fpRSwaAmHZrnPNNKUcKGIoBMWg/s1040/syntax_error6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="350" data-original-width="1040" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsJWBGKgJnNYdrd8G3yRGLXKs7lvG2_qvAgUWseL9VXLJtkPvLQG-dhvO8imz5EP5cgVEwoRrJBaVuqmDyzdsAqgfvT29nO-51Q4QJibIrdK9_fpRSwaAmHZrnPNNKUcKGIoBMWg/w640-h216/syntax_error6.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h2 style="clear: both; text-align: left;">Missing Parentheses, Brackets, and Quotes</h2><div>Five examples taken from the Real Python article offered without additional comments.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiycH1VyOQ_142LE00QFeYKQOP_FCiM7ovT-IDCrI5p4T-YFCkJtlHV8tdCUEchTOKi_0kNHaGbhYakFcb_7keKeisrCjGiECaFqw6eGpoijW8RTfcES6NyUcDycR_z7dNHhJudHA/s1040/syntax_error7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="415" data-original-width="1040" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiycH1VyOQ_142LE00QFeYKQOP_FCiM7ovT-IDCrI5p4T-YFCkJtlHV8tdCUEchTOKi_0kNHaGbhYakFcb_7keKeisrCjGiECaFqw6eGpoijW8RTfcES6NyUcDycR_z7dNHhJudHA/w640-h256/syntax_error7.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZtL2RbcT4R_CftfNOu3tVS-6JkkG1hQpG2ytGkkM8ZiMs8n_oEto1mIS57CrSACrciljmeVu4HGZMUOGZVih2hQuUg3zFP8Zp-eZfHYLAlCJD3Gz5dzdrY89hOUtnSUoDJ-dtyg/s1040/syntax_error8.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="383" data-original-width="1040" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZtL2RbcT4R_CftfNOu3tVS-6JkkG1hQpG2ytGkkM8ZiMs8n_oEto1mIS57CrSACrciljmeVu4HGZMUOGZVih2hQuUg3zFP8Zp-eZfHYLAlCJD3Gz5dzdrY89hOUtnSUoDJ-dtyg/w640-h236/syntax_error8.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN9_UeoQIMgZI2i5E8SZwXHSjigekBy1EbmmwOF4WKKyFFe0k9EGW_YG4XEJAXDnFu4p69Az21ezRGlsWtE_Zr-z3zYFsgZRdynJQRAQGc1OoUXQ8x5NFbPl-nIphcKecx_KWPRg/s1040/syntax_error9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="495" data-original-width="1040" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgN9_UeoQIMgZI2i5E8SZwXHSjigekBy1EbmmwOF4WKKyFFe0k9EGW_YG4XEJAXDnFu4p69Az21ezRGlsWtE_Zr-z3zYFsgZRdynJQRAQGc1OoUXQ8x5NFbPl-nIphcKecx_KWPRg/w640-h304/syntax_error9.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPo_6S4W3OlH7Na5tsONuHFq_YAPBn908R7cQOW-EgHAVs1koPR_y1eaK5EjN99XHaTh8oOB1YcD-GPzrlSqVvCQchvQiofAu-wuQ_aevcTlOJnr5FomgRncRzndvrX2EtoxB7YA/s1040/syntax_error10.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="759" data-original-width="1040" height="468" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPo_6S4W3OlH7Na5tsONuHFq_YAPBn908R7cQOW-EgHAVs1koPR_y1eaK5EjN99XHaTh8oOB1YcD-GPzrlSqVvCQchvQiofAu-wuQ_aevcTlOJnr5FomgRncRzndvrX2EtoxB7YA/w640-h468/syntax_error10.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil6BdpaN8Az7xk41SIbNd3W2I7XsV9hkAqptE1DoLVTLEAYkgBmXUdkcrWXZxgy54RgaKG-ytQO6HRUcU5DvV5HQZuUd-EYZxkFSjNEtRxxNRmIQ9j3c7my4C2K7nOl-2_fQF1nQ/s1040/syntax_error11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="889" data-original-width="1040" height="548" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil6BdpaN8Az7xk41SIbNd3W2I7XsV9hkAqptE1DoLVTLEAYkgBmXUdkcrWXZxgy54RgaKG-ytQO6HRUcU5DvV5HQZuUd-EYZxkFSjNEtRxxNRmIQ9j3c7my4C2K7nOl-2_fQF1nQ/w640-h548/syntax_error11.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><h2 style="text-align: left;">Mistaking Dictionary Syntax</h2></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-p_aqQFZi7RLFwaGd19lXaT_3IlvPQzXTbu1UOsIzVdQkGFBEXxi4z2hg1mOI6hHCA1iVIRoEyay_-bYepAGSAwNNmsM9JAbSQfmke-UfI1tDBTOcJo_FCzxKIUPo5m2nL12Rtw/s1040/syntax_error12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="371" data-original-width="1040" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-p_aqQFZi7RLFwaGd19lXaT_3IlvPQzXTbu1UOsIzVdQkGFBEXxi4z2hg1mOI6hHCA1iVIRoEyay_-bYepAGSAwNNmsM9JAbSQfmke-UfI1tDBTOcJo_FCzxKIUPo5m2nL12Rtw/w640-h228/syntax_error12.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><h2 style="text-align: left;">Using the Wrong Indentation</h2><div style="text-align: left;">Real Python gives many examples. They would all be handled correctly by Friendly-traceback in a similar way as the single example we decided to use for this post.</div><h3 style="text-align: left;"><a class="headerlink" href="https://realpython.com/invalid-syntax-python/#using-the-wrong-indentation" title="Permanent link"></a></h3></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjcHjQMjVGVLXR0UeoD64pgX_ZMwtaAMwYnvqFztpJcxg_W-uL7ZBFdtuFZisbu8RXvScEvxA2cHshmWL_ropfFskULAxu2SqqVGUmLB8WS5Z-Rgl1Zfa4ng2Ih3vieFP_BNiEPw/s1040/syntax_error13.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="733" data-original-width="1040" height="452" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjcHjQMjVGVLXR0UeoD64pgX_ZMwtaAMwYnvqFztpJcxg_W-uL7ZBFdtuFZisbu8RXvScEvxA2cHshmWL_ropfFskULAxu2SqqVGUmLB8WS5Z-Rgl1Zfa4ng2Ih3vieFP_BNiEPw/w640-h452/syntax_error13.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><h2 style="text-align: left;">Defining and Calling Functions</h2></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg98wq3FWLsX1pOt110VPdrnRO3_fN20-QhRqzc2JIxGk_398uUYWgoPUlnhpWjC253euPZvC9aZa3iCXeLiX7LgiyXWzz3fpgEW4QjzhtoXkWYIAdL12WeU6RhYzW7ecPB5NJrHg/s1040/syntax_error14.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="388" data-original-width="1040" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg98wq3FWLsX1pOt110VPdrnRO3_fN20-QhRqzc2JIxGk_398uUYWgoPUlnhpWjC253euPZvC9aZa3iCXeLiX7LgiyXWzz3fpgEW4QjzhtoXkWYIAdL12WeU6RhYzW7ecPB5NJrHg/w640-h238/syntax_error14.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPqd_o9yQcXRobPbl0ItktnpGLvTCCHIbbg6KUT3CjWSKsdBmkyf_7zCJ4wcrV4HvO7SwBs22ZoW8z13hZsmo5ynl25wuQ1lh0Jy3cEaXFwq53SyqGFOcpIrQI8J9SGijtF4uxqw/s1040/syntax_error15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="789" data-original-width="1040" height="486" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPqd_o9yQcXRobPbl0ItktnpGLvTCCHIbbg6KUT3CjWSKsdBmkyf_7zCJ4wcrV4HvO7SwBs22ZoW8z13hZsmo5ynl25wuQ1lh0Jy3cEaXFwq53SyqGFOcpIrQI8J9SGijtF4uxqw/w640-h486/syntax_error15.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><h2 style="text-align: left;">Changing Python Versions</h2><div style="text-align: left;"><br /></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6MDhuGnH7vjBAAe8Z2X98ecoUCcNT9YkpVXJT-HGiXTxBAXErDlhMLavxw3rHiZlR8ZSLbeoolWvDR-eGCQvXfVpkS3lkhzQNxsij-ZyExX_5NpKc8HfWa_LcnIhc0snryRV_AA/s1040/syntax_error16.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="527" data-original-width="1040" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6MDhuGnH7vjBAAe8Z2X98ecoUCcNT9YkpVXJT-HGiXTxBAXErDlhMLavxw3rHiZlR8ZSLbeoolWvDR-eGCQvXfVpkS3lkhzQNxsij-ZyExX_5NpKc8HfWa_LcnIhc0snryRV_AA/w640-h324/syntax_error16.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Friendly-traceback requires Python version 3.6 or newer. Not shown here is that it can recognize that the walrus operator, :=, is not valid before Python version 3.8 and give an appropriate message.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><h2 style="clear: both; text-align: left;">Last example: TypeError result of a syntax error.</h2><div><span style="text-align: left;">Let's look at the last example in the Real Python article.</span></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-NBcSaHOAMybQ84lVtsZuoU5wWEnizVdoHwKuU_x3hKQljYL64XWtMHLt1PO6nyn8-US0m28_keAhvjfjd8O_IrvBMMOxpLpJOg1xFqb2wAxycJxtZy5cBcsqc-xjG_rLuP_mIQ/s1040/syntax_error17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="502" data-original-width="1040" height="308" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-NBcSaHOAMybQ84lVtsZuoU5wWEnizVdoHwKuU_x3hKQljYL64XWtMHLt1PO6nyn8-US0m28_keAhvjfjd8O_IrvBMMOxpLpJOg1xFqb2wAxycJxtZy5cBcsqc-xjG_rLuP_mIQ/w640-h308/syntax_error17.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The explanation given by Friendly-traceback might seem weird "the object (1, 2) was meant to be a function ...". Often one might have assigned a name to that object, which leads to an explanation that should be seen as more reasonable.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglp3CAaNlVQajaU8-J-OAcIfhkM0dGq9CwrYSMP5c0ZuvVbNxTMBOPg2z8oepGH4V23FlcnEZDfwL82jPWHzH9sIvr7b1Fhd7vDV9l7084Nin0TUvRlBY5vi9Ln9ZzHujOKIPZmQ/s1040/syntax_error17a.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="527" data-original-width="1040" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglp3CAaNlVQajaU8-J-OAcIfhkM0dGq9CwrYSMP5c0ZuvVbNxTMBOPg2z8oepGH4V23FlcnEZDfwL82jPWHzH9sIvr7b1Fhd7vDV9l7084Nin0TUvRlBY5vi9Ln9ZzHujOKIPZmQ/w640-h324/syntax_error17a.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS9ic_adYx6FLdElLRfqFLt2iawMnRUbv7-gLLxZ5ELKk7ciS-eHYYJB5yvRsFe3aDHieu1F3UZHs2IsvEsqfJgSDISk5IFaE-aMiO4iPw1rS7tbg_TyoCkwoXXs2EylWEEyrwvg/s1040/syntax_error17b.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="236" data-original-width="1040" height="146" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS9ic_adYx6FLdElLRfqFLt2iawMnRUbv7-gLLxZ5ELKk7ciS-eHYYJB5yvRsFe3aDHieu1F3UZHs2IsvEsqfJgSDISk5IFaE-aMiO4iPw1rS7tbg_TyoCkwoXXs2EylWEEyrwvg/w640-h146/syntax_error17b.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The explanation of looking for a "missing comma" when this TypeError is raised was actually added following a suggestion by S. de Menten in the recent contest I held for Friendly-traceback.</div><br /><h2 style="text-align: left;">There is more ...</h2></div><div>Friendly-traceback includes<a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/syntax_tracebacks_en_3.8.html" target="_blank"> many more cases</a> that those shown above and mentioned in the Real Python article. However, it is limited in that it can only identify the cause of syntax errors there is a single word or symbol used incorrectly or if the error message provided by Python is more informative than the dreaded <b><span style="color: #cc0000;">SyntaxError: invalid syntax</span></b>.</div>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-73454046893121797042021-02-18T08:33:00.002-04:002021-02-18T08:33:27.503-04:00My wish for Python 4<p>I love Python. </p><p>A few years after I started using it, I saw someone writing about it and using the phrase "it fits my brain": this is very much how I feel ... at least, for simple straightforward code that doesn't rely on weird metaclass constructs, or even with the added distraction of type annotations. [Yes, type annotations can be extremely useful, but they do not (currently) "fit my brain".]</p><p>I am extremely grateful to the many volunteers that work constantly to improve Python. Thanks to their efforts, Python keeps growing. I see most of the growth as positives: more users, more applications in a growing number of fields. In many universities, Python has displaced languages such as Scheme and (thankfully!) Java as the first language that students learn. From *my* limited point of view, there is a small negative in that this growth of Python includes a growth in its syntax: when I first encountered Python (version 2.3), it had a comparatively very simple syntax which meant that it was easier to learn (in spite of some warts that were fixed in the 2 to 3 transition) compared with the latest Python version (3.10). </p><p>I am well aware that Python doesn't use semantic version numbers: code written for version 3.x can be incompatible with code written for version 3.y. [This was also the case for the 2.x series.] As a decision has been made to use two-digit minor version numbers, there is no apparent need to think of a version 4 of Python: improvements can continue for many years while keeping 3 as the main version. However, I wish there could be a version 4 - as I describe below.</p><p>Note that when I think of Python, I do not think of a specific implementation (such as CPython), but I think of Python as the language. I do not consider "implementation details" such as the Global Interpreter Lock (GIL) as part of the language. Yes, it would be useful if the main Python implementation could make better use of multi-core CPUs. However, that is not something that is relevant for the purpose of this post.</p><p>So, what is my wish for Python 4? ... </p><p>Above all, going from the last 3.x version (let's call it 3.14...) to 4.0 should be done seamlessly: code written for version 3.14 should run as is in Python 4.0.</p><p>I would like for Python 4 to get inspired by Racket and introduce "<a href="https://docs.racket-lang.org/guide/more-hash-lang.html" target="_blank">dialects</a>". Python could even borrow the notation used by Racket (#lang dialect) as a top directive <b>in a given module</b> to specify the dialect used in that module. Unlike Racket, I would limit the number of possible dialects to 4.</p><p>The main dialect would not need to be specified: it would simply be the standard Python that everyone knows and loves (or not). It would continue evolving, changing slightly as it goes from version 4.x to 4.y.</p><p>A second dialect would be an "<b>experimental</b>" dialect. This dialect could be use to introduce some new syntax with no guarantee whatsoever of backward (or forward) compatibility. It would allow people to experiment with proposed new syntactic constructs before deciding to incorporate them (or not) in the main dialect. I honestly think that this would help reduce some friction in the Python community as changes are proposed and adopted. The main benefit of such a dialect would be more social than technical.</p><p>A third dialect would be a "<b>beginner</b>" dialect. The goal of the <b>beginner </b>dialect would be to make it easier to learn <b>basic programming concepts</b> as opposed to learning a <i>quirky</i> syntax to express these concepts. This beginner dialect would be, in version 4.0, a strict subset of the main dialect. It would not include type annotations, and it might perhaps also exclude the new pattern matching syntax and other syntactic constructs. For example, using the keyword <b><span style="color: #cc0000; font-family: courier;">is</span></b> might only be limited to checking if the object is one of the three singletons (<span style="color: #cc0000; font-family: courier;"><b>None, True, False</b></span>); other uses of <b><span style="color: #cc0000; font-family: courier;">is</span></b> could and should be done with "<span style="color: #cc0000; font-family: courier;"><b>== id(thing)</b></span>". Based on feedback from educators, it might <b>perhaps </b>make sense to <b>eventually</b> introduce a few additional keywords and constructs not available from the main dialect, such as:</p><p></p><ul style="text-align: left;"><li>Having <span style="color: #cc0000; font-family: courier;"><b>nobreak</b></span> as a keyword equivalent to <span style="color: #cc0000; font-family: courier;"><b>else</b></span> in loops.</li><li>Having <span style="color: #cc0000; font-family: courier;"><b>function</b></span> as a keyword equivalent to <span style="color: #cc0000; font-family: courier;"><b>lambda.</b></span></li><li>Having <span style="color: #cc0000; font-family: courier;"><b>imported</b></span> as a keyword with a meaning equivalent to <span style="color: #cc0000;"><b><span style="font-family: courier;">not __name__ == "__main__</span>"</b></span>.</li><li>Having <b><span style="color: #cc0000; font-family: courier;">repeat</span></b> as a keyword as is available in <a href="http://jython.tobiaskohn.ch/" target="_blank">TygerJython</a> and <a href="https://reeborg.ca/index_en.html" target="_blank">Reeborg's World</a> for the construct <span style="color: #cc0000; font-family: courier;"><b>repeat nb_steps:</b></span>. It might perhaps be also useful to have <span style="color: #cc0000; font-family: courier;"><b>repeat forever:</b></span> as equivalent to Python's <span style="color: #cc0000; font-family: courier;"><b>while True:</b></span> . [<a href="https://aroberge.github.io/avantpy/docs/html/repeat.html" target="_blank">Other possible uses of this keyword are described here.</a>]</li></ul><div>Finally, a fourth dialect would be a "<b>static</b>" dialect. This fourth dialect would always be using a syntax strictly compatible with the main dialect. In this dialect, some dynamical features of Python (such as the possibility to change the type of the object specified by a given name) would not be available so that some optimizations could be applied to increase the execution speed. I am sure that experts would be able to suggest other restrictions that could be used to greatly increase the execution speed. I think that such a dialect would be one that would generate the most enthusiastic response from Python users.</div><div><br /></div><div>That being said, I doubt very much that I'll ever see Python adopting these ideas. However, it is sometimes nice to dream ...</div><p></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-58645113274291302252021-02-11T11:34:00.002-04:002021-02-12T08:00:26.729-04:00Friendly-traceback's www function<p>Today, I saw some write up that Friendly-traceback was discussed on the <a href="https://pythonbytes.fm/episodes/show/220/what-why-and-where-of-friendly-errors-in-python" target="_blank">PythonBytes podcast</a>. A comment made during that podcast suggested that it would be useful if an internet search could be performed, perhaps using a function named www. (Another name was mentioned). So, of course I immediately <a href="https://github.com/aroberge/friendly-traceback/issues/185" target="_blank">created an issue</a> ... and implemented a first version of this function.</p><p><br /></p>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com0tag:blogger.com,1999:blog-9266717.post-37818680518121473002021-02-04T18:13:00.001-04:002021-02-05T06:18:11.048-04:00Python's tug of war between beginner-friendly features and support for advanced users<p>Python is my favourite programming language. Since I discovered it in 2004, programming in Python became my favourite hobby. I've tried to learn a few other languages and have never found one as friendly to beginners as Python. As readers of this blog know, these days I am <a href="https://aroberge.github.io/friendly-traceback-docs/docs/html/" target="_blank">particularly interested in tracebacks</a>, and I am likely paying more attention than most to Python's improvements in this area: Python is becoming more and more precise in the information that it provides to the users when something goes wrong. For example, consider these 2 examples from Python 3.7</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1A9N-cJiprrYoKuQJXNtZQ6vZhTO4DQ5-3h6HgIOuk_8nZUmQykBRJm2woznoM7CEmVF0qxd5yzPqXKgAUF3nvF24ALSnhY0kYMsK1ltcA_OuZFdK__WRcBJLewktmngMJQ5_fQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="805" data-original-width="1181" height="436" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1A9N-cJiprrYoKuQJXNtZQ6vZhTO4DQ5-3h6HgIOuk_8nZUmQykBRJm2woznoM7CEmVF0qxd5yzPqXKgAUF3nvF24ALSnhY0kYMsK1ltcA_OuZFdK__WRcBJLewktmngMJQ5_fQ/w640-h436/image.png" width="640" /></a></div><p></p><p>Given the message when we try to assign a value to None, we might have expected to see the same when trying to assign a value to the keyword "pass"; instead we get a not so useful "invalid syntax". Of course, if you've been reading this blog before, you won't be surprised that Friendly-traceback can provide a bit more useful information in this case.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWydAMe_fugtZgcp2UxVnZEbsCWuXavCUo9Ws2usfIiEr2bPUUb3vrw1XYMD6WFQUgxFYl5VsrSZCe4iRNHyQcva8IG6fRiKjSnDKlQ60ExUzDyTmfwnUT5OwsyXSbp6kPnj0kwQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="452" data-original-width="1248" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWydAMe_fugtZgcp2UxVnZEbsCWuXavCUo9Ws2usfIiEr2bPUUb3vrw1XYMD6WFQUgxFYl5VsrSZCe4iRNHyQcva8IG6fRiKjSnDKlQ60ExUzDyTmfwnUT5OwsyXSbp6kPnj0kwQ/w640-h232/image.png" width="640" /></a></div><br />However, this is not the point of this post... Let's see what kind of information Python 3.8 gives us for the first case.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXeSM3-6WweyvTV7lCRuYdWcAB8X1sIt5qd7ptTnEKQ023D6LRLvEVyB9_7pyQGlu9ADd9Svwh_oKc1BV7_aRx3hk7eripG4uO-YogZbsjz3URPPdBWqEiB3ZjU0kIPYEziJm_GA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="396" data-original-width="1198" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXeSM3-6WweyvTV7lCRuYdWcAB8X1sIt5qd7ptTnEKQ023D6LRLvEVyB9_7pyQGlu9ADd9Svwh_oKc1BV7_aRx3hk7eripG4uO-YogZbsjz3URPPdBWqEiB3ZjU0kIPYEziJm_GA/w640-h212/image.png" width="640" /></a></div><br />As you can see, it is much more precise: this is a definite improvement.<p></p><p>Let's have a look at another case, using Python 3.8 again:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSo10MdaaL-qRgD-AwrVE0JaLt1ojzNkRc9C9lln7BQ0rc-bGKKuBsKLsIM5iYf9b-uI3cZc5yHnHKeptrjRdqUEsc5HyPSXs8E67WxBpY9EWGgdXFyxkxLuhKnTCnPYGabJAP9g/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="527" data-original-width="1177" height="286" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSo10MdaaL-qRgD-AwrVE0JaLt1ojzNkRc9C9lln7BQ0rc-bGKKuBsKLsIM5iYf9b-uI3cZc5yHnHKeptrjRdqUEsc5HyPSXs8E67WxBpY9EWGgdXFyxkxLuhKnTCnPYGabJAP9g/w640-h286/image.png" width="640" /></a></div><br />Again, the dreaded "invalid syntax". However, this has been significantly improve with the latest Python version, released yesterday.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP50q-3QL4a1ROcsNWsB6kUx_TQK-djgysK2oTzzlqAZJupPEn-aPYggPrReOAzVwo8iB3BJlwidMGKAkN7WkAa8FUKqbOV6a3I8AeOYQ8waq5hQB5AF-A6LkTsEYypSrfH_dulQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="505" data-original-width="1305" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP50q-3QL4a1ROcsNWsB6kUx_TQK-djgysK2oTzzlqAZJupPEn-aPYggPrReOAzVwo8iB3BJlwidMGKAkN7WkAa8FUKqbOV6a3I8AeOYQ8waq5hQB5AF-A6LkTsEYypSrfH_dulQ/w640-h248/image.png" width="640" /></a></div><br />Again, much better error messages which will be so much more useful for beginners that do not use Friendly-traceback [ even though they should! ;-) ]<p></p><p>There has been a few other similar improvements in the latest release ... but this one example should suffice to illustrate the work done to make Python even friendlier to beginners. However, this is unfortunately not the whole story.</p><p>To make Python useful to advanced users having to deal with large code base, Python has introduced "optional" type annotations. This is certainly something that the vast majority of professional programmers find useful - unlike hobbyists like me. Let me illustrate this by an example inspired from a <a href="https://twitter.com/joepolitz/status/1357402738464755712">Twitter post I saw today</a>. First, I'll use Python 3.8:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8BOVrmaQ4iQXp5TFcmKcGge5tyXzXfc7DjiQLE2x85A5fI0WK9TF1toKjulBZ0Q6PXBvwQIsY125jXJTL_yhutDFbrn3PSA8LhwLCvk-YKskp9QCLh0XhpOKXoWE_61tZqun1rw/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="317" data-original-width="825" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8BOVrmaQ4iQXp5TFcmKcGge5tyXzXfc7DjiQLE2x85A5fI0WK9TF1toKjulBZ0Q6PXBvwQIsY125jXJTL_yhutDFbrn3PSA8LhwLCvk-YKskp9QCLh0XhpOKXoWE_61tZqun1rw/w640-h246/image.png" width="640" /></a></div><br />If you know Python and are not actively using type annotations, you likely will not be surprised by the above. Now, what happens if we try to do the same thing with Python 3.9+<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwx8LFrxT1aMIHfBu_-9lYjekUIVnd-VBofqq6NpbJyiV156DdDfwoAB7rpewuGOcjvL6EDrywARoAiW-U3yabt1U34r1P4ZMlDblmfz7T_ACRFy-yakvuoYJJ9MhIblfjvLytAA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="220" data-original-width="1248" height="112" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwx8LFrxT1aMIHfBu_-9lYjekUIVnd-VBofqq6NpbJyiV156DdDfwoAB7rpewuGOcjvL6EDrywARoAiW-U3yabt1U34r1P4ZMlDblmfz7T_ACRFy-yakvuoYJJ9MhIblfjvLytAA/w640-h112/image.png" width="640" /></a></div><br /><br /></div>No exceptions are raised! Imagine you are a beginner having written the above code: you would certainly not expect an error then when doing the following immediately after:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGo2v33JXQGHhRyDB6tDg2560Tf0kfYrbQfjx8PkGZ5zCyZAYnxH4YoM_01-WmqUUZZlZiTIr6Ffl2hrE7GLG26fYNEH7A1iGzakQFZrc4hK6CZ8zLinCL6FxLbMmHZWqEUa3Bgw/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="301" data-original-width="1228" height="156" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGo2v33JXQGHhRyDB6tDg2560Tf0kfYrbQfjx8PkGZ5zCyZAYnxH4YoM_01-WmqUUZZlZiTIr6Ffl2hrE7GLG26fYNEH7A1iGzakQFZrc4hK6CZ8zLinCL6FxLbMmHZWqEUa3Bgw/w640-h156/image.png" width="640" /></a></div><br />Unfortunately, Friendly-traceback cannot (yet!) provide any help with this.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4Nwj-LKuEfEjaUoxImf3INSWYyU6SsJLg625aqRHCJAs15dSRljmcvyjQTt67RVPvjdA3H5gDrffsQ_tYBVePBpfyrV8tI36sR66854nOpqggFHDkj1E7Vxfcl2Iij2fzHQiRog/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="872" data-original-width="1419" height="394" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4Nwj-LKuEfEjaUoxImf3INSWYyU6SsJLg625aqRHCJAs15dSRljmcvyjQTt67RVPvjdA3H5gDrffsQ_tYBVePBpfyrV8tI36sR66854nOpqggFHDkj1E7Vxfcl2Iij2fzHQiRog/w640-h394/image.png" width="640" /></a></div><br /><br />EDIT: this might be even more confusing.<div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAvlHYAenoQOD_FaAxbfm29aQi86q1ItxlOZ0_HJ6yT7FJYz9HddvOxL36-ZXZ3ueX8veNKmvbbHNqos-DSO2luyLI4kyvtNAFgd5yFBLE735qN15lhsD_6kflTToWxt35UbNK7Q/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="339" data-original-width="1096" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAvlHYAenoQOD_FaAxbfm29aQi86q1ItxlOZ0_HJ6yT7FJYz9HddvOxL36-ZXZ3ueX8veNKmvbbHNqos-DSO2luyLI4kyvtNAFgd5yFBLE735qN15lhsD_6kflTToWxt35UbNK7Q/w640-h198/image.png" width="640" /></a></div><br />/EDIT<br /><br /><p></p><p>Eventually, I'll make use of the following to provide some potentially useful information.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh80NWT7xzYAXoAqw586jyxuD9FFko8zBg0553JUC-BQCkR4i3uRolv4GcZQla9itKCP8kboXAoDOQnQfa_mCLQQ-iWtm8x1juLLphsSKFt5xA8IKpgzw0dfzMoH-ZAVHjZFsBAGQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="192" data-original-width="710" height="109" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh80NWT7xzYAXoAqw586jyxuD9FFko8zBg0553JUC-BQCkR4i3uRolv4GcZQla9itKCP8kboXAoDOQnQfa_mCLQQ-iWtm8x1juLLphsSKFt5xA8IKpgzw0dfzMoH-ZAVHjZFsBAGQ/w400-h109/image.png" width="400" /></a></div><br />Ideally, I would really, really like if it were possible to have truly "optional" type annotation, and a way to turn them off (and make their use generate an exception). Alas, I gather that this will never be the case, which I find most unfortunate.<p></p></div>André Robergehttp://www.blogger.com/profile/08131391818998844540noreply@blogger.com7