Congratulations!

[Valid RSS] This is a valid RSS feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: https://www.nedbatchelder.com/blog/rss.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <?xml-stylesheet type="text/xsl" href="https://nedbatchelder.com/rssfull2html.xslt" media="screen" ?>
  3.  
  4. <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/">
  5. <channel rdf:about="https://nedbatchelder.com/blog">
  6. <title>Ned Batchelder's blog</title>
  7. <link>https://nedbatchelder.com/blog</link>
  8. <description>Ned Batchelder's personal blog.</description>
  9. <dc:language>en-US</dc:language>
  10. <image rdf:resource="https://nedbatchelder.com/pix/rss-banner.gif"/>
  11. <items>
  12. <rdf:Seq>
  13. <rdf:li resource="https://nedbatchelder.com/blog/202404/try_it_functionclass_coverage_report.html"/><rdf:li resource="https://nedbatchelder.com/blog/202403/is_this_for_autistic_people.html"/><rdf:li resource="https://nedbatchelder.com/blog/202403/does_python_have_pointers.html"/><rdf:li resource="https://nedbatchelder.com/blog/202402/updated_multiparameter_interactive_jupyter_notebook.html"/><rdf:li resource="https://nedbatchelder.com/blog/202402/one_way_to_package_python_code_right_now.html"/><rdf:li resource="https://nedbatchelder.com/blog/202401/i_am_at_liberty.html"/><rdf:li resource="https://nedbatchelder.com/blog/202401/you_probably_dont_need_to_learn_c.html"/><rdf:li resource="https://nedbatchelder.com/blog/202401/randomly_subsetting_test_suites.html"/><rdf:li resource="https://nedbatchelder.com/blog/202312/coveragepy_with_sysmonitoring.html"/><rdf:li resource="https://nedbatchelder.com/blog/202312/realworld_matchcase.html"/>
  14. </rdf:Seq>
  15. </items>
  16. </channel>
  17. <image rdf:about="https://nedbatchelder.com/pix/rss-banner.gif">
  18. <title>Ned Batchelder's blog</title>
  19. <link>https://nedbatchelder.com/blog</link>
  20. <url>https://nedbatchelder.com/pix/rss-banner.gif</url>
  21. </image>
  22. <item rdf:about="https://nedbatchelder.com/blog/202404/try_it_functionclass_coverage_report.html">
  23. <title>Try it: function/class coverage report</title>
  24. <link>https://nedbatchelder.com/blog/202404/try_it_functionclass_coverage_report.html</link>
  25. <dc:date>2024-04-15T16:02:00-04:00</dc:date>
  26. <dc:creator>Ned Batchelder</dc:creator>
  27. <description><![CDATA[<p>I&#8217;ve added experimental function and class coverage reports to coverage.py.
  28. I&#8217;d like <a href="https://github.com/nedbat/coveragepy/issues/780" rel="external noopener">feedback</a>
  29. about whether they behave the way you want them to.</p><p>I haven&#8217;t made a PyPI release.  To try the new reports, install coverage from
  30. GitHub. Be sure to include the hash:</p><blockquote class="code"><pre>$ python3 -m pip install git+https://github.com/nedbat/coveragepy@f10c455b7c8fd26352de#egg=coverage==0.0<br></pre></blockquote><p>Then run coverage and make an HTML report as you usually do.  You should
  31. have two new pages, not linked from the index page (yet).
  32. &#8220;htmlcov/function_index.html&#8221; is the function coverage report, and the classes
  33. are in &#8220;htmlcov/class_index.html&#8221;.</p><p>I had to decide how to categorize nested functions and classes. Inner
  34. functions are not counted as part of their outer functions.  Classes consist of
  35. the executable lines in their methods, but not lines outside of methods, because
  36. those lines run on import.  Each file has an entry in the function report for
  37. all of the lines outside of any function, called &#8220;(no function)&#8221;.  The class
  38. report has &#8220;(no class)&#8221; entries for lines outside of any classes.</p><p>The result should be that every line is part of one function, or the &#8220;(no
  39. function)&#8221; entry, and every line is part of one class, or the &#8220;(no class)&#8221;
  40. entry.  This is what made sense to me, but maybe there&#8217;s a compelling reason to
  41. do it differently.</p><p>The reports have a sortable column for the file name, and a sortable column
  42. for the function or class.  Where functions or classes are nested, the name is a
  43. dotted sequence, but is sorted by only the last component.  Just like the
  44. original file listing page, the new pages can be filtered to focus on areas of
  45. interest.</p><p>You can look at some sample reports:</p><ul>
  46. <li><a href="https://nedbatchelder.com/files/sample_coverage_html_beta">Usual file report</a></li>
  47. <li><a href="https://nedbatchelder.com/files/sample_coverage_html_beta/function_index.html">Function report</a></li>
  48. <li><a href="https://nedbatchelder.com/files/sample_coverage_html_beta/class_index.html">Class report</a></li>
  49. </ul><p>It would be helpful if you could give me
  50. <a href="https://github.com/nedbat/coveragepy/issues/780" rel="external noopener">feedback on the
  51. original issue</a> about some questions:</p><ul>
  52.  
  53. <li>Is it useful to have &#8220;(no function)&#8221; and &#8220;(no class)&#8221; entries or is it just
  54. distracting pedantry?  With the entries, the total is the same as the file
  55. report, but they don&#8217;t seem useful by themselves.</li>
  56.  
  57. <li>Does the handling of nested functions and classes make sense?</li>
  58.  
  59. <li>Should these reports be optional (requested with a switch) or always
  60. produced?</li>
  61.  
  62. <li>Is it reasonable to produce one page with every function? How large does a
  63. project have to get before that&#8217;s not feasible or useful?</li>
  64.  
  65. <li>And most importantly: do these reports help you understand how to improve
  66. your code?</li>
  67.  
  68. </ul><p>This is only in the HTML report for now, but we can do more in the future.
  69. Other <a href="https://github.com/nedbat/coveragepy/issues/780" rel="external noopener">ideas
  70. about improvements</a> are of course welcome.  Thanks!</p>
  71. ]]></description>
  72. </item>
  73. <item rdf:about="https://nedbatchelder.com/blog/202403/is_this_for_autistic_people.html">
  74. <title>Is this for autistic people?</title>
  75. <link>https://nedbatchelder.com/blog/202403/is_this_for_autistic_people.html</link>
  76. <dc:date>2024-03-20T11:46:00-04:00</dc:date>
  77. <dc:creator>Ned Batchelder</dc:creator>
  78. <description><![CDATA[<p>Special Olympics swimming season started this past weekend.  A new athlete
  79. joined us, a young boy I&#8217;ll call Bryan. He asked me a question that has stuck
  80. with me.</p><p>Bryan is 12 or so, with the slightly goofy look of a boy growing into his
  81. body. He has braces on his too-large teeth.  It was his first time swimming with
  82. us, so we needed to show him the locker room, how to get out to the pool, and so
  83. on. He was serious and inquisitive about all of these things that were new to
  84. him.</p><p>We got out on the deck and started stretching with the other athletes, most
  85. of whom don&#8217;t look like Bryan.  They are older and have a variety of
  86. intellectual disabilities. Bryan surveyed the group then turned to me and asked
  87. the question: &#8220;is this for autistic people?&#8221;</p><p>I had only just met Bryan. I didn&#8217;t know his formal diagnosis (or if he even
  88. had one), and I didn&#8217;t know how he thought of himself.  When he asked the
  89. question, I didn&#8217;t know if he was including himself in the category of autistic
  90. people or not, so I wanted to answer carefully.</p><p>Did he mean, &#8220;are autistic people allowed here?&#8221; or, &#8220;is this only for
  91. autistic people?&#8221; Maybe he meant, &#8220;are all of these swimmers autistic?&#8221; or even,
  92. &#8220;will being here mean I am autistic?&#8221;</p><p>I told him that it was for autistic people, that my own son Nat was here and
  93. Nat is autistic.  Bryan accepted this in his sober way and continued on with the
  94. practice.</p><p>Later I talked with his mom about the question and asked her if Bryan
  95. identified as autistic. She said that he did, but it was a recent awareness for
  96. him.  In school he&#8217;s in a typical integrated classroom.</p><p>Bryan did well at the practice, and called me &#8220;Coach Ned.&#8221;  His mom was
  97. really appreciative of the group as a whole and was clearly pleased.</p><p>I&#8217;ve been thinking about Bryan and his question: &#8220;is this for autistic
  98. people?&#8221;  He&#8217;s young, and finding his way in the world in so many ways.  We all
  99. need to figure out who we are, what groups we belong to, where we fit.  We all
  100. encounter difficulties in one way or another working all that out, and it&#8217;s
  101. a life-long process.  Bryan has a lot to work on.  I hope it isn&#8217;t too hard.</p>
  102. ]]></description>
  103. </item>
  104. <item rdf:about="https://nedbatchelder.com/blog/202403/does_python_have_pointers.html">
  105. <title>Does Python have pointers?</title>
  106. <link>https://nedbatchelder.com/blog/202403/does_python_have_pointers.html</link>
  107. <dc:date>2024-03-11T07:27:22-04:00</dc:date>
  108. <dc:creator>Ned Batchelder</dc:creator>
  109. <description><![CDATA[<p>People sometimes ask, &#8220;Does Python have pointers?&#8221;  I hate to be the typical
  110. senior engineer, but this is one of those questions where the answer is, it
  111. depends what you mean by pointer.</p><p>The classic definition of a pointer is: a variable that holds the address
  112. of something else and that you can use to work with that something else.
  113. In very broad pseudo-code, it would be something like this:
  114. </p><blockquote class="code"><pre>myvar = SOMETHING;<br>mypointer = get_address_of(myvar);<br>print(get_value_via_pointer(mypointer));<br>## output is SOMETHING<br></pre></blockquote><p>This is useful because we can use a pointer to refer to data, setting the
  115. pointer in one part of the code with whatever logic we need to decide what data
  116. it should point to. Then elsewhere we can use the pointer without having to know
  117. what it&#8217;s referring to or how the decision was made.  The pointer gives us an
  118. indirection that lets us separate concerns and write more modular code.</p><p>Many programming languages provide a pointer facility like this.  For
  119. example, in C, the get_address_of() operation is ampersand, and the
  120. get_value_via_pointer() operation is star, and our code snippet would be:</p><blockquote class="code"><pre class="c"><span class="kt">int</span><span class="w">&#xA0;</span><span class="n">myvar</span><span class="w">&#xA0;</span><span class="o">=</span><span class="w">&#xA0;</span><span class="mi">17</span><span class="p">;</span><span class="w"></span>
  121. <br><span class="kt">int</span><span class="w">&#xA0;</span><span class="o">*</span><span class="n">mypointer</span><span class="w">&#xA0;</span><span class="o">=</span><span class="w">&#xA0;</span><span class="o">&amp;</span><span class="n">myvar</span><span class="p">;</span><span class="w"></span>
  122. <br><span class="n">print_int</span><span class="p">(</span><span class="o">*</span><span class="n">mypointer</span><span class="p">);</span><span class="w">&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;</span><span class="c1">//&#xA0;outputs&#xA0;17</span>
  123. <br></pre></blockquote><p>Other languages like C++, C#, Go, Rust, Pascal, and even Fortran have similar
  124. capabilities.</p><p>OK, so what about Python?  In one sense, Python doesn&#8217;t have a pointer
  125. concept like this.  You could say that get_address_of() is provided by Python&#8217;s
  126. id() function, since (in CPython at least) it returns the memory address of the
  127. data:</p><blockquote class="code"><pre class="python"><span class="n">myvar</span>&#xA0;<span class="o">=</span>&#xA0;<span class="mi">17</span>
  128. <br><span class="n">mypointer</span>&#xA0;<span class="o">=</span>&#xA0;<span class="nb">id</span><span class="p">(</span><span class="n">myvar</span><span class="p">)</span>&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;**&#xA0;not&#xA0;useful</span>
  129. <br></pre></blockquote><p>But Python has no inverse operation: there&#8217;s no get_value_via_pointer()
  130. that can get you myvar given mypointer.</p><p>So Python doesn&#8217;t have the classic pair of operations to be able to work with
  131. pointers explicitly.  But on the other hand, <em>every</em> variable in Python
  132. is a pointer, because variables in Python are <a href="https://nedbatchelder.com/text/names1.html">names
  133. that refer to objects</a>.</p><p>In Python, our simple example looks like this:</p><blockquote class="code"><pre class="python"><span class="n">myvar</span>&#xA0;<span class="o">=</span>&#xA0;<span class="mi">17</span>
  134. <br><span class="n">mypointer</span>&#xA0;<span class="o">=</span>&#xA0;<span class="n">myvar</span>
  135. <br><span class="nb">print</span><span class="p">(</span><span class="n">mypointer</span><span class="p">)</span>&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;outputs&#xA0;17</span>
  136. <br></pre></blockquote><p>When someone asks, does Python have pointers, perhaps the best answer is: it
  137. doesn&#8217;t have explicit pointers like some other languages, but everything is
  138. implicitly a pointer. So you have the power of pointers to use when you need
  139. them: you can have multiple data structures, then assign a variable to one you
  140. choose, and use the variable later.  You&#8217;ve achieved the separation of &#8220;which
  141. data&#8221; from &#8220;work with the data&#8221; that pointers provide.</p><p>Maybe this is yet another case of
  142. <a href="https://nedbatchelder.com/blog/202301/same_words_different_meanings.html">Same words, different meanings</a>.</p><p>Note:</p><ul>
  143.  
  144. <li>Some languages like C also allow pointer arithmetic to adjust a pointer from
  145. one item in an array to another. Python&#8217;s references don&#8217;t allow for that.</li>
  146.  
  147. <li>Python&#8217;s standard library provides <a rel="external noopener" href="https://docs.python.org/3/library/ctypes.html">ctypes</a>, which
  148. is great for interfacing with native C code, exposing details there including C
  149. pointers.  This does not count as Python having explicit pointers.</li>
  150.  
  151. </ul>
  152. ]]></description>
  153. </item>
  154. <item rdf:about="https://nedbatchelder.com/blog/202402/updated_multiparameter_interactive_jupyter_notebook.html">
  155. <title>Updated multi-parameter interactive Jupyter notebook</title>
  156. <link>https://nedbatchelder.com/blog/202402/updated_multiparameter_interactive_jupyter_notebook.html</link>
  157. <dc:date>2024-02-12T14:21:11-05:00</dc:date>
  158. <dc:creator>Ned Batchelder</dc:creator>
  159. <description><![CDATA[<p>A few years ago I wrote
  160. <a href="https://nedbatchelder.com/blog/201610/multiparameter_jupyter_notebook_interaction.html">Multi-parameter Jupyter notebook interaction</a>  about a
  161. Jupyter notebook. It worked at the time, but when I dusted it off recently, it
  162. didn&#8217;t.  I&#8217;ve renovated it and cleaned it up a little, and now it works
  163. again.</p><p>It&#8217;s a <a rel="external noopener" href="/code/misc/retirement_sample2.ipynb">Jupyter notebook</a> with a simulation of
  164. late-career money flows to figure out possibilities for retirement.  It uses
  165. widgets to give you sliders to adjust parameters to see how the outcome changes.
  166. It also lets you pick one of the parameters to auto-plot with multiple values,
  167. which gives a more visceral way to understand the effect different variables
  168. have.</p><div class="figurep"><a href="https://nedbatchelder.com/code/misc/retirement_sample2.ipynb"><figure><picture><source type="image/webp" srcset="https://nedbatchelder.com/iv/webp/pix/retirement_sliders2.png.webp"><img src="https://nedbatchelder.com/pix/retirement_sliders2.png" alt="Screenshot of the sliders and resulting plot of outcomes" width="1122" height="1456"></picture></figure></a></div><p>You can <a rel="external noopener" href="/code/misc/retirement_sample2.ipynb">get the notebook itself</a> if you like.</p>
  169. ]]></description>
  170. </item>
  171. <item rdf:about="https://nedbatchelder.com/blog/202402/one_way_to_package_python_code_right_now.html">
  172. <title>One way to package Python code right now</title>
  173. <link>https://nedbatchelder.com/blog/202402/one_way_to_package_python_code_right_now.html</link>
  174. <dc:date>2024-02-10T06:50:00-05:00</dc:date>
  175. <dc:creator>Ned Batchelder</dc:creator>
  176. <description><![CDATA[<p>A year or so ago, I couldn&#8217;t find a step-by-step guide to packaging a Python
  177. project that didn&#8217;t get bogged down in confusing options and choices, so I wrote
  178. my own: <a rel="external noopener" href="https://github.com/nedbat/pkgsample">pkgsample</a>.  After I wrote it, I found the
  179. <a rel="external noopener" href="https://packaging.python.org/en/latest/tutorials/packaging-projects/">PyPA Packaging Python Projects tutorial</a>, which is
  180. very good, so I never made a post here about my sample.</p><p>Since then, I&#8217;ve shown my sample to people a number of times, and they liked
  181. it, so I guess it&#8217;s helpful.  Here&#8217;s what I wrote about it back when I first created it:</p><p class="bulletsep">•    •    •</p><p>The Python packaging world is confusing.  There are decades of history and
  182. change.  There are competing tools, with new ones arriving frequently.  I don&#8217;t
  183. want to criticize anyone, let&#8217;s just take it as a fact of life right now.</p><p>But I frequently see questions from people who have written some Python code,
  184. and would like to get it packaged. They have a goal in mind, and it is not to
  185. learn about competing tools, intricate standards, or historical artifacts.
  186. They are fundamentally uninterested in the mechanics of packaging.  They just
  187. want to get their code packaged.</p><p>There are lots of pages out there that try to explain things, but they all
  188. seem to get distracted by the options, asking our poor developer to choose
  189. between alternatives they don&#8217;t understand, with no clear implications.</p><p>I&#8217;m also not criticzing the uninterested developer.  I am that developer!  I
  190. don&#8217;t know what all these things are, or how they compete and overlap: build,
  191. twine, hatch, poetry, flit, wheel, pdm, setuptools, distutils, pep517, shiv,
  192. <a rel="external noopener" href="https://packaging.python.org/en/latest/key_projects/">etc</a>, <a rel="external noopener" href="https://chadsmith.dev/python-packaging/">etc</a>.</p><p>I just want someone to tell me what to do so my code will install on users&#8217;
  193. machines.  Once that works, I can go back to fixing bugs, adding features,
  194. writing docs, and so on.</p><p>So I wrote <a rel="external noopener" href="https://github.com/nedbat/pkgsample">pkgsample</a> to be the instructions I
  195. couldn&#8217;t find.  It&#8217;s simple and stripped down, and does not ask you to make
  196. choices you don&#8217;t care about. It tells you what to do. It gives you one way to
  197. make a simple Python package that works right now. It isn&#8217;t THE way. It&#8217;s A way.
  198. It will probably work for you.</p>
  199. ]]></description>
  200. </item>
  201. <item rdf:about="https://nedbatchelder.com/blog/202401/i_am_at_liberty.html">
  202. <title>I am at liberty</title>
  203. <link>https://nedbatchelder.com/blog/202401/i_am_at_liberty.html</link>
  204. <dc:date>2024-01-30T09:08:00-05:00</dc:date>
  205. <dc:creator>Ned Batchelder</dc:creator>
  206. <description><![CDATA[<p>As of a few weeks ago, I am between gigs.  Riffing on some corporate-speak
  207. from a <a rel="external noopener" href="https://2u.com/newsroom/2u-inc-announces-leadership-transition/">recent press release</a>: &#8220;2U and I have mutually
  208. determined that 2U is laying me off.&#8221;</p><p>I feel OK about it: work was becoming increasingly frustrating, and I have
  209. some severance pay.  2U is in a tough spot as a company so at least these
  210. layoffs seemed like an actual tactic rather than another pointless
  211. please-the-investors move by companies flush with profits and cash.  2U
  212. struggling also makes being laid off a more appealing option than remaining
  213. there after a difficult cut.</p><p><a href="https://edx.org" rel="external noopener">edX</a> was a good run for me. We had a noble
  214. mission: educate the world.  The software was mostly open source
  215. (<a href="http://openedx.org" rel="external noopener">Open edX</a>), which meant our efforts could
  216. power education that we as a corporation didn&#8217;t want to pursue.</p><p>Broadly speaking, my job was to oversee how to do open source well.  I loved
  217. the mission of education combined with the mission of open source.  I loved
  218. seeing the community do things together that edX alone could not. I have many
  219. good friends at 2U and in the community. I hope they can make everything work
  220. out well, and I hope I can do a good job staying in touch with them.</p><p>I don&#8217;t know what my next gig will be.  I like writing software. I like
  221. having developers as my customers. I am good at building community both inside
  222. and outside of companies.  I am good at helping people.  I&#8217;m interested to hear
  223. ideas.</p>
  224. ]]></description>
  225. </item>
  226. <item rdf:about="https://nedbatchelder.com/blog/202401/you_probably_dont_need_to_learn_c.html">
  227. <title>You (probably) don’t need to learn C</title>
  228. <link>https://nedbatchelder.com/blog/202401/you_probably_dont_need_to_learn_c.html</link>
  229. <dc:date>2024-01-24T06:38:49-05:00</dc:date>
  230. <dc:creator>Ned Batchelder</dc:creator>
  231. <description><![CDATA[<p>On <a href="https://hachyderm.io/@nedbat/111789013210403320" rel="external noopener">Mastodon I
  232. wrote</a> that I was tired of people saying, &#8220;you should learn C so you can
  233. understand how a computer really works.&#8221; I got a lot of replies which did not
  234. change my mind, but helped me understand more how abstractions are inescapable
  235. in computers.</p><p>People made a number of claims. C was important because syscalls are defined
  236. in terms of C semantics (they are not).  They said it was good for exploring
  237. limited-resource computers like Arduinos, but most people don&#8217;t program for
  238. those. They said it was important because C is more performant, but Python
  239. programs often offload the compute-intensive work to libraries other people have
  240. written, and these days that work is often on a GPU. Someone said you need it to
  241. debug with strace, then someone said they use strace all the time and don&#8217;t know
  242. C.  Someone even said C was good because it explains why NUL isn&#8217;t allowed in
  243. filenames, but who tries to do that, and why learn a language just for that
  244. trivia?</p><p>I&#8217;m all for learning C if it will be useful for the job at hand, but you can
  245. write lots of great software without knowing C.</p><p>A few people repeated the idea that C teaches you how code &#8220;really&#8221; executes.
  246. But C is an abstract model of a computer, and modern CPUs do all kinds of things
  247. that C doesn&#8217;t show you or explain.  Pipelining, cache misses, branch
  248. prediction, speculative execution, multiple cores, even virtual memory are all
  249. completely invisible to C programs.</p><p>C is an abstraction of how a computer works, and chip makers work hard to
  250. implement that abstraction, but they do it on top of much more complicated
  251. machinery.</p><p>C is far removed from modern computer architectures: there have been 50 years
  252. of innovation since it was created in the 1970&#8217;s.  The gap between C&#8217;s model and
  253. modern hardware is the root cause of famous vulnerabilities like Meltdown and
  254. Spectre, as explained in
  255. <a href="https://dl.acm.org/doi/pdf/10.1145/3212477.3212479" rel="external noopener">C is <i>Not</i> a
  256. Low-level Language</a>.</p><p>C can teach you useful things, like how memory is a huge array of bytes, but
  257. you can also learn that without writing C programs.  People say, C teaches you
  258. about memory allocation.  Yes it does, but you can learn what that means as a
  259. concept without learning a programming language.  And besides, what will Python
  260. or Ruby developers do with that knowledge other than appreciate that their
  261. languages do that work for them and they no longer have to think about it?</p><p>Pointers came up a lot in the Mastodon replies. Pointers underpin concepts in
  262. higher-level languages, but you can
  263. <a href="https://nedbatchelder.com/text/names1.html" rel="external noopener">explain those concepts as
  264. references</a> instead, and skip pointer arithmetic, aliasing, and null pointers
  265. completely.</p><p>A question I asked a number of people: what mistakes are
  266. JavaScript/Ruby/Python developers making if they don&#8217;t know these things (C,
  267. syscalls, pointers)?&#8221;.  I didn&#8217;t get strong answers.</p><p>We work in an enormous tower of abstractions.  I write programs in Python,
  268. which provides me abstractions that C (its underlying implementation language)
  269. does not.  C provides an abstract model of memory and CPU execution which the
  270. computer implements on top of other mechanisms (microcode and virtual memory).
  271. When I made a wire-wrapped computer, I could pretend the signal travelled
  272. through wires instantaneously.  For other hardware designers, that abstraction
  273. breaks down and they need to consider the speed electricity travels.  Sometimes
  274. you need to go one level deeper in the abstraction stack to understand what&#8217;s
  275. going on. Everyone has to find the right layer to work at.</p><p><a href="https://hachyderm.io/@agocke/111791030850237121" rel="external noopener">Andy Gocke said
  276. it well</a>:</p><blockquote><div><p>When you no longer have problems at that layer, that&#8217;s when you can
  277. stop caring about that layer. I don&#8217;t think there&#8217;s a universal level of
  278. knowledge that people need or is sufficient.</p></div></blockquote><p><a href="https://toot.cat/@idlestate/111793957024682587" rel="external noopener">&#8220;like jam or
  279. bootlaces&#8221; made another excellent point</a>:</p><blockquote><div><p>There&#8217;s a big difference between &#8220;everyone should know this&#8221; and
  280. &#8220;someone should know this&#8221; that seems to get glossed over in these kinds of
  281. discussions.</p></div></blockquote><p>C can teach you many useful and interesting things.  It will make you a
  282. better programmer, just as learning any new-to-you language will because it
  283. broadens your perspective.  Some kinds of programming need C, though other
  284. languages like Rust are ably filling that role now too.  C doesn&#8217;t teach you how
  285. a computer really works. It teaches you a common abstraction of how computers
  286. work.</p><p>Find a level of abstraction that works for what you need to do.  When you
  287. have trouble there, look beneath that abstraction. You won&#8217;t be seeing how
  288. things really work, you&#8217;ll be seeing a lower-level abstraction that could be
  289. helpful.  Sometimes what you need will be an abstraction one level up.  Is your
  290. Python loop too slow? Perhaps you need a C loop. Or perhaps you need numpy array
  291. operations.</p><p>You (probably) don&#8217;t need to learn C.</p>
  292. ]]></description>
  293. </item>
  294. <item rdf:about="https://nedbatchelder.com/blog/202401/randomly_subsetting_test_suites.html">
  295. <title>Randomly sub-setting test suites</title>
  296. <link>https://nedbatchelder.com/blog/202401/randomly_subsetting_test_suites.html</link>
  297. <dc:date>2024-01-14T09:39:32-05:00</dc:date>
  298. <dc:creator>Ned Batchelder</dc:creator>
  299. <description><![CDATA[<p>I needed to run random subsets of my test suite to narrow down the cause of
  300. some mysterious behavior.  I didn&#8217;t find an existing tool that worked the way I
  301. wanted to, so I cobbled something together.</p><p>I wanted to run 10 random tests (out of 1368), and keep choosing randomly
  302. until I saw the bad behavior.  Once I had a selection of 10, I wanted to be able
  303. to whittle it down to try to reduce it further.</p><p>I tried a few different approaches, and here&#8217;s what I came up with, two tools
  304. in the coverage.py repo that combine to do what I want:</p><ul> <li>A pytest plugin (<a rel="external noopener" href="https://github.com/nedbat/coveragepy/blob/master/tests/select_plugin.py">select_plugin.py</a>) that
  305. lets me run a command to output the names of the exact tests I want to
  306. run,</li>
  307.  
  308. <li>A command-line tool (<a rel="external noopener" href="https://github.com/nedbat/coveragepy/blob/master/lab/pick.py">pick.py</a>) to select random
  309. lines of text from a file.  For convenience, blank or commented-out lines are
  310. ignored.</li>
  311.  
  312. </ul><p>More details are in the comment at the top of
  313. <a rel="external noopener" href="https://github.com/nedbat/coveragepy/blob/master/lab/pick.py">pick.py</a>, but here&#8217;s a quick example:</p><ol>
  314.  
  315. <li>Get all the test names in tests.txt.  These are pytest &#8220;node&#8221; specifications:
  316.  
  317. <blockquote class="code"><pre>pytest --collect-only | grep :: &gt; tests.txt<br></pre></blockquote>
  318. </li>
  319.  
  320. <li>Now tests.txt has a line per test node.  Some are straightforward:
  321.  
  322. <blockquote class="code"><pre>tests/test_cmdline.py::CmdLineStdoutTest::test_version<br>tests/test_html.py::HtmlDeltaTest::test_file_becomes_100<br>tests/test_report_common.py::ReportMapsPathsTest::test_map_paths_during_html_report<br></pre></blockquote>
  323.  
  324. but with parameterization they can be complicated:
  325.  
  326. <blockquote class="code"><pre>tests/test_files.py::test_invalid_globs[bar/***/foo.py-***]<br>tests/test_files.py::FilesTest::test_source_exists[a/b/c/foo.py-a/b/c/bar.py-False]<br>tests/test_config.py::ConfigTest::test_toml_parse_errors[[tool.coverage.run]\nconcurrency="foo"-not a list]<br></pre></blockquote>
  327.  
  328. </li>
  329.  
  330. <li>Run a random bunch of 10 tests:
  331.  
  332. <blockquote class="code"><pre>pytest --select-cmd="python pick.py sample 10 &lt; tests.txt"<br></pre></blockquote>
  333.  
  334. We&#8217;re using <code>--select-cmd</code> to specify the shell command that
  335. will output the names of tests.  Our command uses <code>pick.py</code>
  336. to select 10 random lines from tests.txt.
  337.  
  338. </li>
  339.  
  340. <li>Run many random bunches of 10, announcing the seed each time:
  341.  
  342. <blockquote class="code"><pre>for seed in $(seq 1 100); do<br>    echo seed=$seed<br>    pytest --select-cmd="python pick.py sample 10 $seed &lt; tests.txt"<br>done<br></pre></blockquote>
  343.  
  344. </li>
  345.  
  346. <li>Once you find a seed that produces the small batch you want, save that batch:
  347.  
  348. <blockquote class="code"><pre>python pick.py sample 10 17 &lt; tests.txt &gt; bad.txt<br></pre></blockquote>
  349.  
  350. </li>
  351.  
  352. <li>Now you can run that bad batch repeatedly:
  353.  
  354. <blockquote class="code"><pre>pytest --select-cmd="cat bad.txt"<br></pre></blockquote>
  355.  
  356. </li>
  357.  
  358. <li>To reduce the bad batch, comment out lines in bad.txt with a hash character,
  359. and the tests will be excluded. Keep editing until you find the small set of
  360. tests you want.</li>
  361.  
  362. </ol><p>I like that this works and I understand it. I like that it&#8217;s based on the
  363. bedrock of text files and shell commands.  I like that there&#8217;s room for
  364. different behavior in the future by adding to how pick.py works.  For example,
  365. it doesn&#8217;t do any bisecting now, but it could be adapted to it.</p><p>As usual, there might be a better way to do this, but this works for me.</p>
  366. ]]></description>
  367. </item>
  368. <item rdf:about="https://nedbatchelder.com/blog/202312/coveragepy_with_sysmonitoring.html">
  369. <title>Coverage.py with sys.monitoring</title>
  370. <link>https://nedbatchelder.com/blog/202312/coveragepy_with_sysmonitoring.html</link>
  371. <dc:date>2023-12-27T18:03:33-05:00</dc:date>
  372. <dc:creator>Ned Batchelder</dc:creator>
  373. <description><![CDATA[<p>New in Python 3.12 is <a rel="external noopener" href="https://docs.python.org/3/library/sys.monitoring.html">sys.monitoring</a>, a
  374. lighter-weight way to monitor the execution of Python programs.
  375. <a href="https://pypi.org/project/coverage/7.4.0" rel="external noopener">Coverage.py 7.4.0</a> now
  376. can optionally use sys.monitoring instead of
  377. <a rel="external noopener" href="https://docs.python.org/3/library/sys.html#sys.settrace">sys.settrace</a>, the facility that has underpinned
  378. coverage.py for nearly two decades.  This is a big change, both in Python and in
  379. coverage.py.  It would be great if you could try it out and provide some
  380. feedback.</p><p>Using sys.monitoring should reduce the overhead of coverage measurement,
  381. often lower than 5%, but of course your timings might be different.  One of the
  382. things I would like to know is what your real-world speed improvements are
  383. like.</p><p>Because the support is still a bit experimental, you need to define an
  384. environment variable to use it: <code>COVERAGE_CORE=sysmon</code>.
  385. Eventually, sys.monitoring will be automatically used where possible, but for
  386. now you need to explicitly request it.</p><p>Some things won&#8217;t work with sys.monitoring: plugins and dynamic contexts
  387. aren&#8217;t yet supported, though eventually they will be.  Execution will be faster
  388. for line coverage, but not yet for branch coverage.  Let me know how it works
  389. for you.</p><p>This has been in the works since at least March.  I hope I haven&#8217;t forgotten
  390. something silly in getting it out the door.</p>
  391. ]]></description>
  392. </item>
  393. <item rdf:about="https://nedbatchelder.com/blog/202312/realworld_matchcase.html">
  394. <title>Real-world match/case</title>
  395. <link>https://nedbatchelder.com/blog/202312/realworld_matchcase.html</link>
  396. <dc:date>2023-12-10T13:21:00-05:00</dc:date>
  397. <dc:creator>Ned Batchelder</dc:creator>
  398. <description><![CDATA[<p>Python 3.10 brought us structural pattern matching, better known as
  399. <b>match/case</b>.  At first glance, it looks like a switch statement from C or
  400. JavaScript, but it&#8217;s very different.</p><p>You can use match/case to match specific literals, similar to how switch
  401. statements work, but their point is to match patterns in the structure of data,
  402. not just values. <a href="https://peps.python.org/pep-0636/" rel="external noopener">PEP 636:
  403. Structural Pattern Matching: Tutorial</a> does a good job explaining the
  404. mechanics, but feels like a toy example.</p><p>Here&#8217;s a real-world use: at work we have a GitHub bot installed as a
  405. webhook.  When something happens in one of our repos, GitHub sends a payload of
  406. JSON data to our bot.  The bot has to examine the decoded payload to decide what
  407. to do.</p><p>These payloads are complex: they are dictionaries with only 6 or 8 keys, but
  408. they are deeply nested, eventually containing a few hundred pieces of data.
  409. Originally we were picking them apart to see what keys and values they had, but
  410. match/case made the job much simpler.</p><p>Here&#8217;s some of the code for determining what to do when we get a &#8220;comment
  411. created&#8221; event:</p><blockquote class="code"><pre class="py"><span class="c1">#&#xA0;Check&#xA0;the&#xA0;structure&#xA0;of&#xA0;the&#xA0;payload:</span>
  412. <br><span class="k">match</span>&#xA0;<span class="n">event</span><span class="p">:</span>
  413. <br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">case</span>&#xA0;<span class="p">{</span>
  414. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="s2">&quot;issue&quot;</span><span class="p">:</span>&#xA0;<span class="p">{</span><span class="s2">&quot;closed_at&quot;</span><span class="p">:</span>&#xA0;<span class="n">closed</span><span class="p">},</span>
  415. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="s2">&quot;comment&quot;</span><span class="p">:</span>&#xA0;<span class="p">{</span><span class="s2">&quot;created_at&quot;</span><span class="p">:</span>&#xA0;<span class="n">commented</span><span class="p">},</span>
  416. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="p">}</span>&#xA0;<span class="k">if</span>&#xA0;<span class="n">closed</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">commented</span><span class="p">:</span>
  417. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;This&#xA0;is&#xA0;a&#xA0;&quot;Close&#xA0;with&#xA0;comment&quot;&#xA0;comment.&#xA0;Don&#39;t&#xA0;do&#xA0;anything&#xA0;for&#xA0;the</span>
  418. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;comment,&#xA0;because&#xA0;we&#39;ll&#xA0;also&#xA0;get&#xA0;a&#xA0;&quot;pull&#xA0;request&#xA0;closed&quot;&#xA0;event&#xA0;at</span>
  419. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;the&#xA0;same&#xA0;time,&#xA0;and&#xA0;it&#xA0;will&#xA0;do&#xA0;whatever&#xA0;we&#xA0;need.</span>
  420. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">pass</span>
  421. <br>
  422. <br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">case</span>&#xA0;<span class="p">{</span><span class="s2">&quot;sender&quot;</span><span class="p">:</span>&#xA0;<span class="p">{</span><span class="s2">&quot;login&quot;</span><span class="p">:</span>&#xA0;<span class="n">who</span><span class="p">}}</span>&#xA0;<span class="k">if</span>&#xA0;<span class="n">who</span>&#xA0;<span class="o">==</span>&#xA0;<span class="n">get_bot_username</span><span class="p">():</span>
  423. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;When&#xA0;the&#xA0;bot&#xA0;comments&#xA0;on&#xA0;a&#xA0;pull&#xA0;request,&#xA0;it&#xA0;causes&#xA0;an&#xA0;event,&#xA0;which</span>
  424. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;gets&#xA0;sent&#xA0;to&#xA0;webhooks,&#xA0;including&#xA0;us.&#xA0;&#xA0;We&#xA0;don&#39;t&#xA0;have&#xA0;to&#xA0;do&#xA0;anything</span>
  425. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;for&#xA0;our&#xA0;own&#xA0;comment&#xA0;events.</span>
  426. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">pass</span>
  427. <br>
  428. <br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">case</span>&#xA0;<span class="p">{</span><span class="s2">&quot;issue&quot;</span><span class="p">:</span>&#xA0;<span class="p">{</span><span class="s2">&quot;pull_request&quot;</span><span class="p">:</span>&#xA0;<span class="n">_</span><span class="p">}}:</span>
  429. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="c1">#&#xA0;The&#xA0;comment&#xA0;is&#xA0;on&#xA0;a&#xA0;pull&#xA0;request.&#xA0;Process&#xA0;it.</span>
  430. <br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="k">return</span>&#xA0;<span class="n">process_pull_request_comment</span><span class="p">(</span><span class="n">event</span><span class="p">)</span>
  431. <br></pre></blockquote><p>The first case matches if the dict has an &#8220;issue&#8221; key containing a dict with
  432. a &#8220;closed_at&#8221; key and also a &#8220;comment&#8221; key containing a dict with a &#8220;created_at&#8221;
  433. key, and if those two leaves in the dict are equal.  Writing out that condition
  434. without match/case would be more verbose and confusing.</p><p>The second case examines the event to see if the bot was the originator of
  435. the event.  This one wouldn&#8217;t have been so hard to write in a different way, but
  436. match/case makes it nicer.</p><p>This is just what match/case is good at: checking patterns in the structure
  437. of data.</p><p>It&#8217;s also interesting to see the bytecode generated.  For that first case, it
  438. looks like this:</p><blockquote class="code"><pre>  2           0 LOAD_GLOBAL              0 (event)<br><br>  3           2 MATCH_MAPPING<br>              4 POP_JUMP_IF_FALSE       67 (to 134)<br>              6 GET_LEN<br>              8 LOAD_CONST               1 (2)<br>             10 COMPARE_OP               5 (&gt;=)<br>             12 POP_JUMP_IF_FALSE       67 (to 134)<br><br>  4          14 NOP<br><br>  5          16 NOP<br><br>  3          18 LOAD_CONST               8 (('issue', 'comment'))<br>             20 MATCH_KEYS<br>             22 POP_JUMP_IF_FALSE       65 (to 130)<br>             24 DUP_TOP<br>             26 LOAD_CONST               4 (0)<br>             28 BINARY_SUBSCR<br><br>  4          30 MATCH_MAPPING<br>             32 POP_JUMP_IF_FALSE       64 (to 128)<br>             34 GET_LEN<br>             36 LOAD_CONST               5 (1)<br>             38 COMPARE_OP               5 (&gt;=)<br>             40 POP_JUMP_IF_FALSE       64 (to 128)<br>             42 LOAD_CONST               9 (('closed_at',))<br>             44 MATCH_KEYS<br>             46 POP_JUMP_IF_FALSE       62 (to 124)<br>             48 DUP_TOP<br>             50 LOAD_CONST               4 (0)<br>             52 BINARY_SUBSCR<br>             54 ROT_N                    7<br>             56 POP_TOP<br>             58 POP_TOP<br>             60 POP_TOP<br>             62 DUP_TOP<br>             64 LOAD_CONST               5 (1)<br>             66 BINARY_SUBSCR<br><br>  5          68 MATCH_MAPPING<br>             70 POP_JUMP_IF_FALSE       63 (to 126)<br>             72 GET_LEN<br>             74 LOAD_CONST               5 (1)<br>             76 COMPARE_OP               5 (&gt;=)<br>             78 POP_JUMP_IF_FALSE       63 (to 126)<br>             80 LOAD_CONST              10 (('created_at',))<br>             82 MATCH_KEYS<br>             84 POP_JUMP_IF_FALSE       61 (to 122)<br>             86 DUP_TOP<br>             88 LOAD_CONST               4 (0)<br>             90 BINARY_SUBSCR<br>             92 ROT_N                    8<br>             94 POP_TOP<br>             96 POP_TOP<br>             98 POP_TOP<br>            100 POP_TOP<br>            102 POP_TOP<br>            104 POP_TOP<br>            106 STORE_FAST               0 (closed)<br>            108 STORE_FAST               1 (commented)<br><br>  6         110 LOAD_FAST                0 (closed)<br>            112 LOAD_FAST                1 (commented)<br>            114 COMPARE_OP               2 (==)<br>            116 POP_JUMP_IF_FALSE       70 (to 140)<br><br> 10         118 LOAD_CONST               0 (None)<br>            120 RETURN_VALUE<br><br>  3     &gt;&gt;  122 POP_TOP<br>        &gt;&gt;  124 POP_TOP<br>        &gt;&gt;  126 POP_TOP<br>        &gt;&gt;  128 POP_TOP<br>        &gt;&gt;  130 POP_TOP<br>            132 POP_TOP<br>        &gt;&gt;  134 POP_TOP<br>            136 LOAD_CONST               0 (None)<br>            138 RETURN_VALUE<br><br>  6     &gt;&gt;  140 LOAD_CONST               0 (None)<br>            142 RETURN_VALUE<br></pre></blockquote><p>That&#8217;s a lot, but you can see roughly what it&#8217;s doing: check if the value is
  439. a mapping (dict) with at least two keys (bytecodes 2–12), then check if
  440. it has the two specific keys we&#8217;ll be examining (18–22).  Look at the
  441. value of the first key, check if it&#8217;s a dict with at least one key
  442. (24–40), etc, and so on.</p><p>Hand-writing these sorts of checks might result in shorter bytecode. For
  443. example, I already know the event value is a dict, since that is what the GitHub
  444. API promise me, so there&#8217;s no need to check it explicitly each time.  But the
  445. Python code would be twistier and harder to get right. I was initially a skeptic
  446. about match/case, but this example shows a clear benefit.</p>
  447. ]]></description>
  448. </item>
  449. </rdf:RDF>
  450.  

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid RSS" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=https%3A//www.nedbatchelder.com/blog/rss.xml

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda