<?xml version="1.0"?>
<rdf:RDF
	xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:foaf="http://xmlns.com/foaf/0.1/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns="http://purl.org/rss/1.0/"
>
<channel rdf:about="http://planetpython.org/">
	<title>Planet Python</title>
	<link>http://planetpython.org/</link>
	<description>Planet Python - http://planetpython.org/</description>

	<items>
		<rdf:Seq>
			<rdf:li rdf:resource="https://www.thepythoncodingstack.com/p/python-oop-mindset-you-store-data-and-you-do-stuff-with-data" />
			<rdf:li rdf:resource="https://realpython.com/quizzes/openrouter-api/" />
			<rdf:li rdf:resource="https://blog.scikit-learn.org/updates/update-array-api" />
			<rdf:li rdf:resource="https://lucumr.pocoo.org/2026/3/5/theseus/" />
			<rdf:li rdf:resource="https://blog.jetbrains.com/?post_type=ai&amp;p=685326" />
			<rdf:li rdf:resource="https://www.pythonmorsels.com/custom-comprehensions/" />
			<rdf:li rdf:resource="https://blog.jetbrains.com/ai/2026/03/cursor-joined-the-acp-registry-and-is-now-live-in-your-jetbrains-ide/" />
			<rdf:li rdf:resource="https://realpython.com/openrouter-api/" />
			<rdf:li rdf:resource="tag:blog.glyph.im,2026-03-03:/2026/03/what-is-code-review-for.html" />
			<rdf:li rdf:resource="https://sethmlarson.dev/pip-relative-dependency-cooling-with-crontab?utm_campaign=rss" />
			<rdf:li rdf:resource="https://pycoders.com/issues/724" />
			<rdf:li rdf:resource="https://mathspp.com/blog/til/install-jupyter-with-uv" />
			<rdf:li rdf:resource="https://www.djangoproject.com/weblog/2026/mar/03/security-releases/" />
			<rdf:li rdf:resource="https://realpython.com/courses/init-py/" />
			<rdf:li rdf:resource="https://realpython.com/quizzes/duck-typing-python/" />
			<rdf:li rdf:resource="https://pybit.es/?p=15332" />
			<rdf:li rdf:resource="https://www.pythonmorsels.com/when-are-classes-used/" />
			<rdf:li rdf:resource="https://snarky.ca/rss/69a5da1f6fe6ab00015dc2d0" />
			<rdf:li rdf:resource="https://mathspp.com/blog/til/multiline-input-in-the-repl" />
			<rdf:li rdf:resource="https://realpython.com/ydata-profiling-eda/" />
			<rdf:li rdf:resource="https://mathspp.com/blog/remove-extra-spaces" />
			<rdf:li rdf:resource="https://realpython.com/quizzes/ydata-profiling-eda/" />
			<rdf:li rdf:resource="https://realpython.com/quizzes/pandas-dataframe/" />
			<rdf:li rdf:resource="https://pythonbytes.fm/episodes/d4444cd8-0ed5-4c82-8a59-4fa40653074d" />
			<rdf:li rdf:resource="https://discuss.tryton.org/c/news/discuss.tryton.org-topic-9121" />
		</rdf:Seq>
	</items>
</channel>

<item rdf:about="https://www.thepythoncodingstack.com/p/python-oop-mindset-you-store-data-and-you-do-stuff-with-data">
	<title>The Python Coding Stack: You Store Data and You Do Stuff With Data • The OOP Mindset</title>
	<link>https://www.thepythoncodingstack.com/p/python-oop-mindset-you-store-data-and-you-do-stuff-with-data</link>
	<content:encoded>&lt;p&gt;Learning &lt;em&gt;how&lt;/em&gt; to use a tool can be challenging. But learning &lt;em&gt;why&lt;/em&gt; you should use that tool&amp;#8211;and &lt;em&gt;when&lt;/em&gt;&amp;#8211;is sometimes even more challenging.&lt;/p&gt;&lt;p&gt;In this post, I&amp;#8217;ll discuss the OOP mindset&amp;#8211;or, let&amp;#8217;s say, one way of viewing the object-oriented paradigm. I debated with myself whether to write this article here on &lt;em&gt;The Python Coding Stack&lt;/em&gt;. Most articles here are aimed at the &amp;#8220;intermediate-ish&amp;#8221; Python programmer, whatever &amp;#8220;intermediate&amp;#8221; means. Most readers may feel they already &lt;em&gt;understand&lt;/em&gt; the ethos and philosophy of OOP. I know I keep discovering new perspectives from time to time. So here&amp;#8217;s a perspective I&amp;#8217;ve been exploring in courses I ran recently.&lt;/p&gt;&lt;p&gt;As you can see, I decided to write this post. At worst, it serves as revision for some readers, perhaps a different route towards understanding why we (sometimes) define classes and (always) use objects in Python. And beginners read these articles, too!&lt;/p&gt;&lt;p&gt;If you feel you&amp;#8217;re an OOP Pro, go ahead and skip this post. Or just read it anyway. It&amp;#8217;s up to you. You can always catch up with anything you may have missed from &lt;a href=&quot;https://www.thepythoncodingstack.com/archive&quot;&gt;The Python Coding Stack&amp;#8217;s Archive&lt;/a&gt; instead &amp;#8211; around 120 full-length articles and counting!&lt;/p&gt;&lt;p&gt;&lt;em&gt;This post is inspired by the introduction to OOP in Chapter 7 in &lt;a href=&quot;https://thepythoncodingplace.com/the-python-coding-book/&quot;&gt;The Python Coding Book &amp;#8211; The Relaxed and Friendly Programming Book for Beginners&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;p&gt;Meet Mark, a market seller. He sets up a stall in his local market and sells tea, coffee, and home-made biscuits (aka cookies for those in North America).&lt;/p&gt;&lt;p&gt;I&amp;#8217;ll write two and a half versions of the code Mark could use to keep track of his stock and sales. The first one&amp;#8211;that&amp;#8217;s the half&amp;#8211;is not very pretty. Don&amp;#8217;t write code like this. But it will serve as a starting point for the main theme of this post. The second version is a stepping stone towards the third.&lt;/p&gt;&lt;h2&gt;&lt;strong&gt;First (Half) Version &amp;#8226; Not Pretty&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Mark is also learning Python in his free time. He starts writing some code:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!E89x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!E89x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5eac796e-cd41-4866-b921-657d182f9e06_1200x294.png&quot; width=&quot;1200&quot; height=&quot;294&quot; src=&quot;src&quot; /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;All code blocks are available in text format at the end of this article &amp;#8226; #1&lt;/div&gt;&lt;p&gt;He was planning to write some functions to update the stock, change the cost price and selling price, and deal with a sale from his market stall.&lt;/p&gt;&lt;p&gt;But he stopped.&lt;/p&gt;&lt;p&gt;We&amp;#8217;ll also stop here with this version.&lt;/p&gt;&lt;p&gt;Mark is a Python beginner, but even he knew this was not the best way to store the relevant data for each product he sells.&lt;/p&gt;&lt;p&gt;Four separate objects? Not ideal. Mark knows that the data in these four lists are linked to each other. The first items in each list belong together, and so on. But he&amp;#8217;ll need to manually ensure he maintains these links in the code he writes. Not impossible, but it&amp;#8217;s asking for trouble. So many things can go wrong. And he&amp;#8217;ll have a tough time writing the code, making all those links in every line he writes.&lt;/p&gt;&lt;p&gt;Mark knows what the link is between the various values. But the computer program doesn&amp;#8217;t. The computer program sees four separate data structures, unrelated to each other. You can try writing an &lt;code&gt;update_stock()&lt;/code&gt; function with this version to see the challenges, the manual work needed to connect data across separate structures.&lt;/p&gt;&lt;p&gt;Let&amp;#8217;s move on.&lt;/p&gt;&lt;h2&gt;&lt;strong&gt;Second Version &amp;#8226; Dictionary and Functions&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Mark learnt about dictionaries. They&amp;#8217;re a great way to group data together:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!X257!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!X257!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F831a258f-5694-4ba3-b70d-be8b2306f0e2_1200x840.png&quot; width=&quot;1200&quot; height=&quot;840&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#2&lt;/div&gt;&lt;p&gt;Now there&amp;#8217;s one data structure that contains the three products he sells. This structure contains three other data structures, each containing all the relevant data for each product.&lt;/p&gt;&lt;p&gt;The data are structured into these dictionaries to show what belongs where. In this version, the Python program &amp;#8220;knows&amp;#8221; which data items belong together. Each dictionary contains related values.&lt;/p&gt;&lt;p&gt;Well done, Mark! He understood the need to organise the data into a sensible structure. This is not the only combination of nested data structures Mark could use, but this is a valid option. Certainly better than the first version!&lt;/p&gt;&lt;p&gt;He can write some functions now. First up is &lt;code&gt;update_stock()&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!rNJj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!rNJj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F58ba20a8-f2c9-47ab-b913-8ec95bb402b3_1200x462.png&quot; width=&quot;1200&quot; height=&quot;462&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#3&lt;/div&gt;&lt;p&gt;Let&amp;#8217;s ignore the fact that Mark is accessing the global variable &lt;code&gt;products&lt;/code&gt; within the function. We&amp;#8217;ll have a chat with Mark about this.&lt;/p&gt;&lt;p&gt;Still, there&amp;#8217;s now a function called &lt;code&gt;update_stock()&lt;/code&gt;. It needs the product name as an argument. It also needs the stock amount to increase (or decrease if the value is negative).&lt;/p&gt;&lt;p&gt;Let&amp;#8217;s look at another function Mark wrote:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!hZ4n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!hZ4n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03fe16a7-0577-453a-b56e-017ecda88be0_1200x840.png&quot; width=&quot;1200&quot; height=&quot;840&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#4&lt;/div&gt;&lt;p&gt;This function also needs the product name as one of its arguments. It also needs a second argument: the quantity sold.&lt;/p&gt;&lt;p&gt;Mark wrote other functions, but I&amp;#8217;ll keep this section brief so I won&amp;#8217;t show them. However, many of Mark&amp;#8217;s functions have a few things in common:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;They require the product name as one of the arguments. This makes sense since the operations Mark needs to carry out depend on which product he&amp;#8217;s dealing with.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;They make changes to the data in one of the inner dictionaries defined within &lt;code&gt;products&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Some of them return data. Others don&amp;#8217;t.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Great. Mark is happy with his effort.&lt;/p&gt;&lt;p&gt;He structured the data sensibly so it&amp;#8217;s well organised. Separately, he wrote functions that use data from those data structures&amp;#8211;the inner dictionaries in &lt;code&gt;products&lt;/code&gt; and sometimes make changes to the data in those data structures.&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Your call&amp;#8230;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;The Python Coding Place offers something for everyone:&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&amp;#8226; a super-personalised one-to-one 6-month mentoring option&lt;/em&gt;&lt;br /&gt;&lt;em&gt;$ 4,750&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&amp;#8226; individual one-to-one sessions&lt;/em&gt;&lt;br /&gt;&lt;em&gt;$ 125&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&amp;#8226; a self-led route with access to 60+ hrs of exceptional video courses and a support forum&lt;/em&gt;&lt;br /&gt;&lt;em&gt;$ 400&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;a href=&quot;https://thepythoncodingplace.com?utm_source=the-python-coding-stack&quot;&gt;Which The Python Coding Place student are you?&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;&lt;strong&gt;Third Version &amp;#8226; Class and Objects&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Mark&amp;#8217;s code stores data. The nested dictionaries in &lt;code&gt;products&lt;/code&gt; deal with the storage part. His code also does stuff* with the data through the functions he wrote.&lt;/p&gt;&lt;p&gt;&lt;em&gt;*stuff is not quite a Python technical term, in case you&amp;#8217;re wondering. But it&amp;#8217;s quite suitable here, I think!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Lots of programs store data and do stuff with data.&lt;/p&gt;&lt;p&gt;Object-oriented programming takes these two separate tasks&amp;#8211;storing data and doing stuff with data&amp;#8211;and combines them into a single &amp;#8220;unit&amp;#8221;. In OOP, this &amp;#8220;unit&amp;#8221; is the &lt;em&gt;object&lt;/em&gt;. Objects are at the centre of the OOP paradigm, which is why OOP is called OOP, after all!&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Mark&amp;#8217;s progression from the first version to the second relied on structuring the data into units&amp;#8211;the nested dictionary structure.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The progression from the second version to the third takes this a step further and structures the data &lt;em&gt;and&lt;/em&gt; the functions that act on those data into a single unit: the object.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This structure means that the data for each object is contained within the object, and the actions performed are also contained within that object. Everything is self-contained within the object.&lt;/p&gt;&lt;p&gt;Let&amp;#8217;s build a &lt;code&gt;Product&lt;/code&gt; class for Mark. But let&amp;#8217;s do this step by step, using the code from the second version and gradually morphing it into object-oriented code. This will help us follow the transition.&lt;/p&gt;&lt;p&gt;First, let&amp;#8217;s create the class:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!z1i5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!z1i5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5143cf31-32cd-4d87-b117-a0ff79ed70f6_1200x210.png&quot; width=&quot;1200&quot; height=&quot;210&quot; src=&quot;src&quot; /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#5&lt;/div&gt;&lt;p&gt;Nothing much to see so far. But this is the shell that will contain all the instructions for creating this &amp;#8220;unit,&amp;#8221; including all the relevant data and functionality.&lt;/p&gt;&lt;p&gt;You can already create an &lt;em&gt;instance&lt;/em&gt; of this class &amp;#8211; that&amp;#8217;s another way of saying &lt;em&gt;an object created from this class&lt;/em&gt;:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!-uje!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!-uje!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25d3e1e5-a426-4591-ba68-a3eb22390703_1200x336.png&quot; width=&quot;1200&quot; height=&quot;336&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#6&lt;/div&gt;&lt;p&gt;Whereas &lt;code&gt;Product&lt;/code&gt; represents the class, &lt;code&gt;Product()&lt;/code&gt; is an instance of the class. Note the parentheses. There&amp;#8217;s only one &lt;code&gt;Product&lt;/code&gt;, but you can create many instances using &lt;code&gt;Product()&lt;/code&gt; .&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;Bundling In the Functionality &amp;#8226; Methods&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Now, let me take a route I recently explored for the first time while teaching OOP in a beginners&amp;#8217; course. It&amp;#8217;s only subtly different from my &amp;#8220;usual&amp;#8221; teaching route, but I think it helped me appreciate the topic from a distinct perspective. So, here we go.&lt;/p&gt;&lt;p&gt;Let&amp;#8217;s copy and paste the functions from version two directly into this class. Warning: This won&amp;#8217;t work. We&amp;#8217;ll need to make some changes. But let&amp;#8217;s use this as a starting point:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!-Iap!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!-Iap!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F67de610b-ab76-4c8f-9467-13f3cb72a693_1272x1260.png&quot; width=&quot;1272&quot; height=&quot;1260&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#7&lt;/div&gt;&lt;p&gt;To keep you on your toes, we change the terminology here. These are functions. However, when they&amp;#8217;re defined within a class definition, we call them &lt;em&gt;methods&lt;/em&gt;. But they&amp;#8217;re still functions. Everything you know about functions applies equally to methods.&lt;/p&gt;&lt;p&gt;You&amp;#8217;ve seen that each function in version two needed to know which product it was dealing with. That&amp;#8217;s why the first parameter is &lt;code&gt;product_name&lt;/code&gt;. You used &lt;code&gt;product_name&lt;/code&gt; to fetch the correct values when you needed the selling price, the cost price, or the number of items in stock.&lt;/p&gt;&lt;p&gt;However, now that we&amp;#8217;re in the OOP domain, the object will contain all the data it needs&amp;#8211;you&amp;#8217;ll add the data-related code soon. You don&amp;#8217;t need to fetch the data from anywhere else, just from the object &lt;em&gt;itself&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Therefore, the functions defined within the class &amp;#8211; the methods &amp;#8211; no longer need the product name as the first argument. Instead, they need the entire object &lt;em&gt;itself&lt;/em&gt; since this object contains all the data the method needs about the object.&lt;/p&gt;&lt;p&gt;We just need a parameter name to describe the object &lt;em&gt;itself&lt;/em&gt;. How about &lt;code&gt;self&lt;/code&gt;?&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!F7-U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!F7-U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F19d503ca-5374-4ea9-a497-ccf98f0a4fad_1200x378.png&quot; width=&quot;1200&quot; height=&quot;378&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#8&lt;/div&gt;&lt;p&gt;The method signatures now include &lt;code&gt;self&lt;/code&gt; as the first parameter and whatever else the methods need as the remaining parameters.&lt;/p&gt;&lt;p&gt;Incidentally, although you can technically name this first parameter anything you want, there&amp;#8217;s a strong convention to always use &lt;code&gt;self&lt;/code&gt;. So it&amp;#8217;s best to stick with &lt;code&gt;self&lt;/code&gt;!&lt;/p&gt;&lt;p&gt;The methods are the tools you use to bundle functionality into the object. Let&amp;#8217;s pause on working on the methods for now and shift our attention to how to store the data, which also needs to be bundled into the object.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;Bundling In the Data &amp;#8226; Data Attributes&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Let&amp;#8217;s get back to the code that creates an instance of the &lt;code&gt;Product&lt;/code&gt; class:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!ELDx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!ELDx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb77c2438-d948-4408-abd0-cd2eef82a455_1200x252.png&quot; width=&quot;1200&quot; height=&quot;252&quot; src=&quot;src&quot; /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#9&lt;/div&gt;&lt;p&gt;The expression &lt;code&gt;Product()&lt;/code&gt; does a few things behind the scenes. First, it creates a new blank object. Then it initialises this object. To initialise the object, Python needs to &amp;#8220;do stuff&amp;#8221;. Therefore, it needs a method. But not just any method. A special method:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!Tw81!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!Tw81!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50668299-736e-48a0-aaad-eb07baaf231f_1200x420.png&quot; width=&quot;1200&quot; height=&quot;420&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#10&lt;/div&gt;&lt;p&gt;You can&amp;#8217;t choose the name of this method. It must be &lt;code&gt;.__init__()&lt;/code&gt;. But it&amp;#8217;s still a method. It still takes &lt;code&gt;self&lt;/code&gt; as the first parameter since it still needs access to the object itself. This method creates variables that are attached to the object. You can think of the dot as attaching &lt;code&gt;name&lt;/code&gt; to &lt;code&gt;self&lt;/code&gt; and so on for the others. We tend not to call these variables &amp;#8211; more new terminology just for the OOP paradigm &amp;#8211; they&amp;#8217;re &lt;em&gt;data attributes&lt;/em&gt;. They&amp;#8217;re object attributes that store data.&lt;/p&gt;&lt;p&gt;For now, these data attributes contain default values: the empty string for the &lt;code&gt;.name&lt;/code&gt; data attribute and &lt;code&gt;0&lt;/code&gt; for the others. However, when you create an object, you often want to supply some or all of the data that the object needs. Often, you want to pass this information when you create the object:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!1OUE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!1OUE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fb4f53a-64a7-4719-a7e6-9b4e658372a6_1200x252.png&quot; width=&quot;1200&quot; height=&quot;252&quot; src=&quot;src&quot; /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#11&lt;/div&gt;&lt;p&gt;However, when you run the code, you get an error:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Traceback (most recent call last):
  ...
    a_product = Product(&amp;#8221;Coffee&amp;#8221;, 1.98, 3.2, 100)
TypeError: Product.__init__() takes 1 positional 
    argument but 5 were given&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The error message mentions &lt;code&gt;Product.__init__()&lt;/code&gt;. Note how you don&amp;#8217;t explicitly use &lt;code&gt;.__init__()&lt;/code&gt; in the expression to create a &lt;code&gt;Product&lt;/code&gt; object. However, Python calls this special method behind the scenes. And when it does, it complains about the arguments you passed:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;&quot;Product.__init__() takes 1 positional argument...&quot;&lt;/code&gt; is the first part of the error message. Makes sense, since you have &lt;code&gt;self&lt;/code&gt; as the one and only parameter within the definition of the &lt;code&gt;.__init__()&lt;/code&gt; special method.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;&quot;...but 5 [arguments] were given&quot;&lt;/code&gt;, the error message goes on to say. Wait, why 5? You pass four objects when you create &lt;code&gt;Product()&lt;/code&gt;: the string &lt;code&gt;&quot;Coffee&quot;&lt;/code&gt;, the floats &lt;code&gt;1.98&lt;/code&gt; and &lt;code&gt;3.2&lt;/code&gt;, and the integer &lt;code&gt;100&lt;/code&gt;. Python can&amp;#8217;t count, it seems?&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Not quite. When Python calls a method, it automatically passes the object as the first argument. You don&amp;#8217;t see this. You don&amp;#8217;t need to do anything about it. It happens behind the scenes. So, soon after &lt;code&gt;Product()&lt;/code&gt; creates a blank new instance, it calls &lt;code&gt;.__init__()&lt;/code&gt; and passes the object as the first argument to &lt;code&gt;.__init__()&lt;/code&gt;, the one that&amp;#8217;s assigned to the parameter &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Recall that methods are functions. So, Python automatically passes the object as the first argument so that these functions (methods) have access to the object.&lt;/p&gt;&lt;p&gt;That&amp;#8217;s why the error message says that five arguments were passed: the object itself and the four remaining arguments.&lt;/p&gt;&lt;p&gt;This tells you that &lt;code&gt;.__init__()&lt;/code&gt; needs five parameters. The first is &lt;code&gt;self&lt;/code&gt;, which is the first parameter in these methods. Let&amp;#8217;s add the remaining four:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!KdEU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!KdEU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F606da463-e4b2-4120-b097-0974a5d66597_1218x378.png&quot; width=&quot;1218&quot; height=&quot;378&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#12&lt;/div&gt;&lt;p&gt;The code no longer raises an error since the number of arguments passed to &lt;code&gt;Product()&lt;/code&gt; when you create the object &amp;#8211; including the object itself, which is implied &amp;#8211; matches the number of parameters in &lt;code&gt;.__init__()&lt;/code&gt;. However, you want to shift the data into the data attributes you created earlier. These data attributes are the storage devices attached to the object. You want the data to be stored there:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!Ee1j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!Ee1j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2650aea2-eed0-4cb0-a601-db93fd0c7b5f_1218x378.png&quot; width=&quot;1218&quot; height=&quot;378&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#13&lt;/div&gt;&lt;p&gt;The parameters are used only to transfer the data from the method call to the data attributes. From now on, the data are stored in the data attributes, which are attached to the object.&lt;/p&gt;&lt;p&gt;Note that the data attribute names don&amp;#8217;t have to match the parameter names. But why bother coming up with different names? Might as well use the same ones!&lt;/p&gt;&lt;p&gt;Now, you can create any object you wish, each having its own data:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!02LF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!02LF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21b7ea38-19c2-44ef-bf4e-a0d2dc968ecb_1200x546.png&quot; width=&quot;1200&quot; height=&quot;546&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#14&lt;/div&gt;&lt;p&gt;Here&amp;#8217;s the output:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Coffee
Tea
3.2
2.25&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Every instance of the &lt;code&gt;Product&lt;/code&gt; class will have a &lt;code&gt;.name&lt;/code&gt;, &lt;code&gt;.cost_price&lt;/code&gt;, &lt;code&gt;.selling_price&lt;/code&gt;, and &lt;code&gt;.stock&lt;/code&gt;. But each instance will have its own values for those data attributes. Each object is distinct from the others. The data is self-contained within the object.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;Back to the Functionality &amp;#8226; Methods&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Let&amp;#8217;s look at the class so far, which still has code pasted from version two earlier:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!7mVZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!7mVZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c425b1e-6ff3-477c-aca7-9ade1211e4ae_1272x1512.png&quot; width=&quot;1272&quot; height=&quot;1512&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#15&lt;/div&gt;&lt;p&gt;Let&amp;#8217;s focus on &lt;code&gt;.update_stock()&lt;/code&gt; first. Note how in my writing style guide, I use a leading dot when writing method names, such as &lt;code&gt;.update_stock()&lt;/code&gt;. That&amp;#8217;s because they&amp;#8217;re also attributes of the object. To call a method, you call it through the object, such as &lt;code&gt;a_product.update_stock(30)&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Recall that Python will pass the object &lt;em&gt;itself&lt;/em&gt; as the first argument to the method. That&amp;#8217;s the argument assigned to &lt;code&gt;self&lt;/code&gt;. You then pass &lt;code&gt;30&lt;/code&gt; to the parameter &lt;code&gt;stock_increment&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;But this means that this method can only act on an object that already exists, that already has the data attributes it needs. You no longer need to check whether the product exists. If you&amp;#8217;re calling this method, you&amp;#8217;re calling it on a product. Checks on existence will happen elsewhere in your code.&lt;/p&gt;&lt;p&gt;So, all that&amp;#8217;s left is to increment the stock for this object. The current stock is stored in the &lt;code&gt;.stock&lt;/code&gt; data attribute &amp;#8211; the data attribute that&amp;#8217;s connected to the object. But you passed the object to &lt;code&gt;.update_stock()&lt;/code&gt;, so you can access &lt;code&gt;self.stock&lt;/code&gt; from within the method:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!N97H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!N97H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2683369b-d547-42c0-b821-dfd60661d0e9_1200x252.png&quot; width=&quot;1200&quot; height=&quot;252&quot; src=&quot;src&quot; /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#16&lt;/div&gt;&lt;p&gt;And that&amp;#8217;s it. Everything is self-contained within the object. The method acts on the object and modifies one of the object&amp;#8217;s data attributes.&lt;/p&gt;&lt;p&gt;How about the &lt;code&gt;.sell()&lt;/code&gt; method?&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!qOzq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!qOzq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6213da54-d72a-46bf-a868-b6affd44dfeb_1272x630.png&quot; width=&quot;1272&quot; height=&quot;630&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#17&lt;/div&gt;&lt;p&gt;Again, you can remove the validation to check that the product exists. A method is called on a &lt;code&gt;Product&lt;/code&gt; object, so it exists. This method then uses three of the object&amp;#8217;s data attributes to perform its task.&lt;/p&gt;&lt;p&gt;Note that you can add similar validation in &lt;code&gt;.update_stock()&lt;/code&gt; to ensure the stock doesn&amp;#8217;t dip below &lt;code&gt;0&lt;/code&gt;. However, &lt;code&gt;stock_increment&lt;/code&gt; can be negative to enable reducing the stock. You could even use &lt;code&gt;.update_stock()&lt;/code&gt; within &lt;code&gt;.sell()&lt;/code&gt; if you wish.&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;p&gt;Ah, what about the biscuits? You can still use a list of products, but this time the list contains &lt;code&gt;Product&lt;/code&gt; objects:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!YJBc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!YJBc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd689a1-d478-47f5-a966-cce06885dee5_1200x378.png&quot; width=&quot;1200&quot; height=&quot;378&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#18&lt;/div&gt;&lt;p&gt;Or, if you prefer, you can use a dictionary:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!jUm2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!jUm2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf5c65c-133b-4286-859e-8bb9793df9cd_1200x336.png&quot; width=&quot;1200&quot; height=&quot;336&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#19&lt;/div&gt;&lt;p&gt;Let&amp;#8217;s trial out this dictionary and the &lt;code&gt;Product&lt;/code&gt; class:&lt;/p&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!BaYC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!BaYC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbdafbc9b-c2d7-4d73-a0da-9bba48c2d376_1200x756.png&quot; width=&quot;1200&quot; height=&quot;756&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;#20&lt;/div&gt;&lt;p&gt;In this basic use case, you use the item input by the user to fetch the corresponding &lt;code&gt;Product&lt;/code&gt; object from the &lt;code&gt;products&lt;/code&gt; dictionary. You assign this object to the variable name &lt;code&gt;product&lt;/code&gt;. Then you can call its methods, such as &lt;code&gt;.sell()&lt;/code&gt;, and access its data attributes, such as &lt;code&gt;.name&lt;/code&gt; and &lt;code&gt;.stock&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Here&amp;#8217;s the output from this code, including sample user inputs:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Enter item: coffee
Enter quantity of Coffee sold: 3
The income from this sale is &amp;#163;9.60
The profit from this sale is &amp;#163;3.66
Remaining Coffee units: 97&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;Final Words&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;The aim of this article is not to provide a comprehensive and exhaustive walkthrough of OOP. I wrote elsewhere about OOP. You can start from &lt;em&gt;&lt;a href=&quot;https://thepythoncodingplace.com/the-python-coding-book/&quot;&gt;The Python Coding Book&lt;/a&gt;&lt;/em&gt; and then read the seven-part series &lt;em&gt;&lt;a href=&quot;https://www.thepythoncodingstack.com/p/a-magical-tour-through-object-oriented&quot;&gt;A Magical Tour Through Object-Oriented Programming in Python &amp;#8226; Hogwarts School of Codecraft and Algorithmancy&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;You can learn about the syntax and the mechanics of defining and using classes. And that&amp;#8217;s important if you want to write classes. But just as importantly, you need to adopt an OOP mindset. A central point is how OOP bundles data and functionality into a single unit, the object, and how everything stems from that structure.&lt;/p&gt;&lt;p&gt;In this post, you saw how data is bundled into the object through data attributes. And you bundled the high-level functionality that matters for your object through the methods &lt;code&gt;.update_stock()&lt;/code&gt; and &lt;code&gt;.sell()&lt;/code&gt;. Mark will need more of these methods, as you can imagine.&lt;/p&gt;&lt;p&gt;However, the object also includes low-level functionality. What should Python do when it needs to print the object? How about if it needs to add it to another object? Should that be possible? Should the object be considered &lt;code&gt;False&lt;/code&gt; under any circumstance, say? These operations are defined by an object&amp;#8217;s special methods, also known as dunder methods. I proposed thinking of these as &amp;#8220;plumbing methods&amp;#8221; recently: &lt;a href=&quot;https://www.thepythoncodingstack.com/p/pythons-plumbing-is-not-as-flashy&quot;&gt;&amp;#8220;Python&amp;#8217;s Plumbing&amp;#8221; Is Not As Flashy as &amp;#8220;Magic Methods&amp;#8221; &amp;#8226; But Is It Better?&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Therefore, there&amp;#8217;s plenty of high-level and low-level functionality bundled within the object. And the data, of course.&lt;/p&gt;&lt;p&gt;Here&amp;#8217;s another relatively recent post you may enjoy in case you missed it when it was published: &lt;a href=&quot;https://www.thepythoncodingstack.com/p/my-life-the-autobiography-of-a-python-object&quot;&gt;My Life &amp;#8226; The Autobiography of a Python Object&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;In summary, OOP structures data and functionality into a single unit &amp;#8211; the object. Then, your code is oriented around this object.&lt;/p&gt;&lt;div class=&quot;pullquote&quot;&gt;&lt;p&gt;&lt;em&gt;Do you want to master Python one article at a time? Then don&amp;#8217;t miss out on the articles in The Club which are exclusive to premium subscribers here on The Python Coding Stack&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;button-wrapper&quot;&gt;&lt;a class=&quot;button primary&quot; href=&quot;https://www.thepythoncodingstack.com/subscribe&quot;&gt;&lt;span&gt;Subscribe now&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class=&quot;captioned-image-container&quot;&gt;&lt;a class=&quot;image-link image2 is-viewable-img&quot; target=&quot;_blank&quot; href=&quot;https://substackcdn.com/image/fetch/$s_!X8ae!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg&quot;&gt;&lt;div class=&quot;image2-inset&quot;&gt;&lt;img src=&quot;https://substackcdn.com/image/fetch/$s_!X8ae!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0c839d9c-76df-411e-982e-25c9d1762b66_3024x4032.jpeg&quot; width=&quot;622&quot; height=&quot;829.190934065934&quot; src=&quot;src&quot; /&gt;&lt;div class=&quot;image-link-expand&quot;&gt;&lt;div class=&quot;pencraft pc-display-flex pc-gap-8 pc-reset&quot;&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container restack-image&quot;&gt;&lt;/button&gt;&lt;button tabindex=&quot;0&quot; type=&quot;button&quot; class=&quot;pencraft pc-reset pencraft icon-container view-image&quot;&gt;&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&quot;https://www.pexels.com/photo/bamboo-sticks-tied-together-in-bundles-5607157/&quot;&gt;gomed fashion&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Code in this article uses Python 3.14&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;The code images used in this article are created using &lt;a href=&quot;https://snappify.cello.so/f4AsFrwgwov&quot;&gt;Snappify&lt;/a&gt;.&lt;/em&gt; &lt;em&gt;[Affiliate link]&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;a href=&quot;https://www.thepythoncodingstack.com/subscribe&quot;&gt;Join&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt;&lt;a href=&quot;https://www.thepythoncodingstack.com/subscribe&quot;&gt; The Club&lt;/a&gt;&lt;/strong&gt;&lt;em&gt;, the exclusive area for paid subscribers for &lt;a href=&quot;https://www.thepythoncodingstack.com/s/the-club&quot;&gt;more Python posts&lt;/a&gt;, videos, a members&amp;#8217; forum, and more.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;button-wrapper&quot;&gt;&lt;a class=&quot;button primary&quot; href=&quot;https://www.thepythoncodingstack.com/subscribe&quot;&gt;&lt;span&gt;Subscribe now&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;You can also support this publication by making a &lt;a href=&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;&gt;one-off contribution of any amount you wish&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p class=&quot;button-wrapper&quot;&gt;&lt;a class=&quot;button primary&quot; href=&quot;https://buy.stripe.com/00g3de2iGdgg4gg7su&quot;&gt;&lt;span&gt;Support The Python Coding Stack&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;For more Python resources, you can also visit&lt;/em&gt; &lt;em&gt;&lt;a href=&quot;https://realpython.com?utm_source=the-python-coding-stack&quot;&gt;Real Python&lt;/a&gt;&amp;#8212;you may even stumble on one of my own articles or courses there!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Also, are you interested in technical writing? You&amp;#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at&lt;/em&gt; &lt;em&gt;&lt;a href=&quot;http://stephengruppetta.com/breaking-the-rules&quot;&gt;Breaking the Rules&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;And you can find out more about me at&lt;/em&gt; &lt;em&gt;&lt;a href=&quot;https://stephengruppetta.com?utm_source=the-python-coding-stack&quot;&gt;stephengruppetta.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Further reading related to this article&amp;#8217;s topic:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://thepythoncodingplace.com/the-python-coding-book/&quot;&gt;The Python Coding Book&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://www.thepythoncodingstack.com/p/a-magical-tour-through-object-oriented&quot;&gt;A Magical Tour Through Object-Oriented Programming in Python &amp;#8226; Hogwarts School of Codecraft and Algorithmancy&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://www.thepythoncodingstack.com/p/my-life-the-autobiography-of-a-python-object&quot;&gt;My Life &amp;#8226; The Autobiography of a Python Object&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;Appendix: Code Blocks&lt;/h2&gt;&lt;h5&gt;Code Block #1&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;products = [&quot;Coffee&quot;, &quot;Tea&quot;, &quot;Biscuits&quot;]
cost_price = [1.98, 0.85, 3.1]
selling_price = [3.2, 2.25, 5]
stock = [100, 200, 25]
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #2&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;products = {
    &quot;Coffee&quot;: {
        &quot;cost_price&quot;: 1.98,
        &quot;selling_price&quot;: 3.2,
        &quot;stock&quot;: 100,
    },
    &quot;Tea&quot;: {
        &quot;cost_price&quot;: 0.85,
        &quot;selling_price&quot;: 2.25,
        &quot;stock&quot;: 200,
    },
    &quot;Biscuits&quot;: {
        &quot;cost_price&quot;: 3.1,
        &quot;selling_price&quot;: 5,
        &quot;stock&quot;: 25,
    },
}
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #3&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...
def update_stock(product_name, stock_increment):
    if product_name not in products:
        raise KeyError(f&quot;Product {product_name} not found&quot;)
    products[product_name][&quot;stock&quot;] += stock_increment
    # We'll need to have a chat with Mark about the issues
    # with accessing global variables from functions.
    # Let's let it pass here!
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #4&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...
def sell(product_name, quantity):
    if product_name not in products:
        raise KeyError(f&quot;Product {product_name} not found&quot;)
    if quantity &amp;lt;= 0:
        raise ValueError(&quot;Quantity must be greater than zero&quot;)
    if products[product_name][&quot;stock&quot;] &amp;lt; quantity:
        raise ValueError(&quot;Not enough stock&quot;)
        
    products[product_name][&quot;stock&quot;] -= quantity
    selling_price = products[product_name][&quot;selling_price&quot;]
    cost_price = products[product_name][&quot;cost_price&quot;]

    income = selling_price * quantity
    profit = (selling_price - cost_price) * quantity

    return income, profit
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #5&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    ...
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #6&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    ...
    

a_product = Product()
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #7&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    # Just pasting the functions from Version 2 here for now.
    # This still doesn't work. It's a work-in-progress
    def update_stock(product_name, stock_increment):
        if product_name not in products:
            raise KeyError(f&quot;Product {product_name} not found&quot;)
        products[product_name][&quot;stock&quot;] += stock_increment
        # We'll need to have a chat with Mark about the issues
        # with accessing global variables from functions.
        # Let's let it pass here!

    def sell(product_name, quantity):
        if product_name not in products:
            raise KeyError(f&quot;Product {product_name} not found&quot;)
        if quantity &amp;lt;= 0:
            raise ValueError(&quot;Quantity must be greater than zero&quot;)
        if products[product_name][&quot;stock&quot;] &amp;lt; quantity:
            raise ValueError(&quot;Not enough stock&quot;)

        products[product_name][&quot;stock&quot;] -= quantity
        selling_price = products[product_name][&quot;selling_price&quot;]
        cost_price = products[product_name][&quot;cost_price&quot;]

        income = selling_price * quantity
        profit = (selling_price - cost_price) * quantity

        return income, profit
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #8&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    def update_stock(self, stock_increment):
        # rest of code pasted from version two (for now)

    def sell(self, quantity):
        # rest of code pasted from version two (for now)
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #9&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...

a_product = Product()
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #10&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    # We'll make improvements to this method soon
    def __init__(self):
        self.name = &quot;&quot;
        self.cost_price = 0
        self.selling_price = 0
        self.stock = 0
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #11&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...

a_product = Product(&quot;Coffee&quot;, 1.98, 3.2, 100)
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #12&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    def __init__(self, name, cost_price, selling_price, stock):
        self.name = &quot;&quot;
        self.cost_price = 0
        self.selling_price = 0
        self.stock = 0
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #13&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    def __init__(self, name, cost_price, selling_price, stock):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price
        self.stock = stock
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #14&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...

a_product = Product(&quot;Coffee&quot;, 1.98, 3.2, 100)
another_product = Product(&quot;Tea&quot;, 0.85, 2.25, 200)

print(a_product.name)
print(another_product.name)

print(a_product.selling_price)
print(another_product.selling_price)
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #15&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;class Product:
    def __init__(self, name, cost_price, selling_price, stock):
        self.name = name
        self.cost_price = cost_price
        self.selling_price = selling_price
        self.stock = stock

    # Just pasting the functions from Version 2 here for now.
    # This still doesn't work. It's a work-in-progress
    def update_stock(self, stock_increment):
        if product_name not in products:
            raise KeyError(f&quot;Product {product_name} not found&quot;)
        products[product_name][&quot;stock&quot;] += stock_increment
        # We'll need to have a chat with Mark about the issues
        # with accessing global variables from functions.
        # Let's let it pass here!

    def sell(self, quantity):
        if product_name not in products:
            raise KeyError(f&quot;Product {product_name} not found&quot;)
        if quantity &amp;lt;= 0:
            raise ValueError(&quot;Quantity must be greater than zero&quot;)
        if products[product_name][&quot;stock&quot;] &amp;lt; quantity:
            raise ValueError(&quot;Not enough stock&quot;)

        products[product_name][&quot;stock&quot;] -= quantity
        selling_price = products[product_name][&quot;selling_price&quot;]
        cost_price = products[product_name][&quot;cost_price&quot;]

        income = selling_price * quantity
        profit = (selling_price - cost_price) * quantity

        return income, profit
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #16&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...
    def update_stock(self, stock_increment):
        self.stock += stock_increment
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #17&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...
    def sell(self, quantity):
        if quantity &amp;lt;= 0:
            raise ValueError(&quot;Quantity must be greater than zero&quot;)
        if self.stock &amp;lt; quantity:
            raise ValueError(&quot;Not enough stock&quot;)

        self.stock -= quantity
        income = self.selling_price * quantity
        profit = (self.selling_price - self.cost_price) * quantity

        return income, profit
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #18&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...
products = [
    Product(&quot;Coffee&quot;, 1.98, 3.2, 100),
    Product(&quot;Tea&quot;, 0.85, 2.25, 200),
    Product(&quot;Biscuits&quot;, 3.1, 5, 25),
]
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #19&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;products = {
    &quot;Coffee&quot;: Product(&quot;Coffee&quot;, 1.98, 3.2, 100),
    &quot;Tea&quot;: Product(&quot;Tea&quot;, 0.85, 2.25, 200),
    &quot;Biscuits&quot;: Product(&quot;Biscuits&quot;, 3.1, 5, 25),
}
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5&gt;Code Block #20&lt;/h5&gt;&lt;pre&gt;&lt;code&gt;&lt;code&gt;# ...

# I've kept this demo code brief and simple, so it lacks
# verification and robustness. You can add this if you wish!

item = input(&quot;Enter item: &quot;).title()
qty = int(input(f&quot;Enter quantity of {item} sold: &quot;))

product = products[item]
income, profit = product.sell(qty)

print(f&quot;The income from this sale is &amp;#163;{income:.2f}&quot;)
print(f&quot;The profit from this sale is &amp;#163;{profit:.2f}&quot;)

print(f&quot;Remaining {product.name} units: {product.stock}&quot;)
&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;For more Python resources, you can also visit&lt;/em&gt; &lt;em&gt;&lt;a href=&quot;https://realpython.com?utm_source=the-python-coding-stack&quot;&gt;Real Python&lt;/a&gt;&amp;#8212;you may even stumble on one of my own articles or courses there!&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;Also, are you interested in technical writing? You&amp;#8217;d like to make your own writing more narrative, more engaging, more memorable? Have a look at&lt;/em&gt; &lt;em&gt;&lt;a href=&quot;http://stephengruppetta.com/breaking-the-rules&quot;&gt;Breaking the Rules&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;And you can find out more about me at&lt;/em&gt; &lt;em&gt;&lt;a href=&quot;https://stephengruppetta.com?utm_source=the-python-coding-stack&quot;&gt;stephengruppetta.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-05T22:58:15+00:00</dc:date>
</item>
<item rdf:about="https://realpython.com/quizzes/openrouter-api/">
	<title>Real Python: Quiz: How to Use the OpenRouter API to Access Multiple AI Models via Python</title>
	<link>https://realpython.com/quizzes/openrouter-api/</link>
	<content:encoded>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of
&lt;a href=&quot;https://realpython.com/openrouter-api/&quot;&gt;How to Use the OpenRouter API to Access Multiple AI Models via Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By completing this quiz, you&amp;rsquo;ll review how OpenRouter provides a unified routing layer, how to call multiple providers from a single Python script, how to switch models without changing your code, and how to compare outputs.&lt;/p&gt;
&lt;p&gt;It also reinforces practical skills for making API requests in Python, handling authentication, and processing responses. For deeper guidance, review the tutorial linked above.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-05T12:00:00+00:00</dc:date>
</item>
<item rdf:about="https://blog.scikit-learn.org/updates/update-array-api/">
	<title>scikit-learn: Update on array API adoption in scikit-learn</title>
	<link>https://blog.scikit-learn.org/updates/update-array-api/</link>
	<content:encoded>&lt;div&gt;
  &lt;img src=&quot;https://blog.scikit-learn.org/assets/images/posts_images/array-api-scikit-learn-2026-feature.png&quot; alt=&quot;&quot; /&gt;
  

  
  
  
  

  

  

Author:  
      &lt;a href=&quot;https://github.com/lucyleeow&quot; rel=&quot;me noopener noreferrer&quot;&gt;&lt;img src=&quot;https://blog.scikit-learn.org/assets/images/author_images/lucyliu.jpeg&quot; alt=&quot;Author Icon&quot; class=&quot;orcid-icon&quot; /&gt;Lucy Liu&lt;/a&gt;
     

&lt;br /&gt;&lt;br /&gt;

&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Note: this blog post is a cross-post of a &lt;a href=&quot;https://labs.quansight.org/blog/array-api-scikit-learn-2026&quot;&gt;Quansight Labs blog post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://data-apis.org/&quot;&gt;Consortium for Python Data API Standards&lt;/a&gt;
developed the &lt;a href=&quot;https://data-apis.org/array-api/&quot;&gt;Python array API standard&lt;/a&gt;
to define a consistent interface for array libraries, specifing core
operations, data types, and behaviours. This enables ‘array-consuming’
libraries (such as scikit-learn) to write array-agnostic code that can
be run on any array API compliant backend. Adopting array API support in scikit-learn
means that users can pass arrays from any array API compliant library to
functions that have been converted to be array-agnostic. This is useful because it
allows users to take advantage of array library features, such as hardware
acceleration, most notably via GPUs.&lt;/p&gt;

&lt;p&gt;Indeed, GPU support in scikit-learn has been of interest for a long time - 11 years
ago, we added an entry to our FAQ page explaining that we had no plans to add GPU
support in the near future due to the software dependencies and platform specific
issues it would introduce. By relying on the array API standard, however, these
concerns can now be avoided.&lt;/p&gt;

&lt;p&gt;In this blog post, I will provide an update to the array API adoption work in
scikit-learn, since it’s initial introduction in version 1.3 two years ago.
Thomas Fan’s &lt;a href=&quot;https://labs.quansight.org/blog/array-api-support-scikit-learn&quot;&gt;blog post&lt;/a&gt;
provides details on the status when array API support was initially added.&lt;/p&gt;

&lt;h2 id=&quot;current-status&quot;&gt;Current status&lt;/h2&gt;

&lt;p&gt;Since the introduction of array API support in version 1.3 of scikit-learn,
several key developments have followed.&lt;/p&gt;

&lt;h3 id=&quot;vendoring-array-api-compat-and-array-api-extra&quot;&gt;Vendoring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-compat&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-extra&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Scikit-learn now vendors both
&lt;a href=&quot;https://data-apis.org/array-api-compat/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-compat&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://data-apis.org/array-api-extra/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-extra&lt;/code&gt;&lt;/a&gt;.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-compat&lt;/code&gt; is a wrapper around common array libraries (e.g., PyTorch,
CuPy, JAX) that bridges gaps to ensure compatibility with the standard. It
enables adoption of backwards incompatible changes while still allowing array
libraries time to adopt the standard slowly. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-extra&lt;/code&gt; provides array
functions not included in the standard but deemed useful for array-consuming
libraries.&lt;/p&gt;

&lt;p&gt;We chose to vendor these now much more mature libraries in order to avoid the
complexity of conditionally handling optional dependencies throughout the
codebase. This approach also follows precedent, as SciPy also vendors these
packages.&lt;/p&gt;

&lt;h3 id=&quot;array-libraries-supported&quot;&gt;Array libraries supported&lt;/h3&gt;

&lt;p&gt;Scikit-learn currently supports CuPy ndarrays, PyTorch tensors (testing
against all devices: ‘cpu’, ‘cuda’, ‘mps’ and ‘xpu’) and NumPy arrays. JAX
support is also on the horizon. The main focus of this work is addressing
in-place mutations in the codebase. Follow
&lt;a href=&quot;https://github.com/scikit-learn/scikit-learn/pull/29647&quot;&gt;PR #29647&lt;/a&gt; for
updates.&lt;/p&gt;

&lt;p&gt;Beyond these libraries, scikit-learn also tests against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-strict&lt;/code&gt;, a
reference implementation that strictly adheres to the array API specification.
The purpose of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-strict&lt;/code&gt; is to help automate compliance checks for
consuming libraries and to enable development and testing of array
API functionality without the need for GPU or other specialized hardware.
Array libraries that conform to the standard and pass the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-tests&lt;/code&gt; suite
should be accepted by scikit-learn and SciPy, without any additional modifications
from maintainers.&lt;/p&gt;

&lt;h3 id=&quot;estimators-and-metrics-with-array-api-support&quot;&gt;Estimators and metrics with array API support&lt;/h3&gt;

&lt;p&gt;The full list of metrics and estimators that now support array API can be
found in our
&lt;a href=&quot;https://scikit-learn.org/dev/modules/array_api.html&quot;&gt;Array API support&lt;/a&gt;
documentation page. The majority of high impact metrics have now been
converted to be array API compatible. Many transformers are also now
supported, notably &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LabelBinarizer&lt;/code&gt; which is widely used internally and
simplifies other conversions.&lt;/p&gt;

&lt;p&gt;Conversion of estimators is much more complicated as it often involves
benchmarking different variations of code or consensus gathering on
implementation choices. It generally requires many months of work by several
maintainers. Nonetheless, support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LogisticRegression&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GaussianNB&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GaussianMixture&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ridge&lt;/code&gt; (and family: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RidgeCV&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RidgeClassifier&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RidgeClassifierCV&lt;/code&gt;), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Nystroem&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PCA&lt;/code&gt; has been added. Work on
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GaussianProcessRegressor&lt;/code&gt; is also underway (follow at
&lt;a href=&quot;https://github.com/scikit-learn/scikit-learn/pull/33096&quot;&gt;PR #33096&lt;/a&gt;).&lt;/p&gt;

&lt;h3 id=&quot;handling-mixed-array-namespaces-and-devices&quot;&gt;Handling mixed array namespaces and devices&lt;/h3&gt;

&lt;p&gt;scikit-learn takes a unique approach among ‘array-consuming’ libraries by
supporting mixed array namespace and device inputs. This design choice enables
the framework to handle the practical complexities of end-to-end machine
learning pipelines.&lt;/p&gt;

&lt;p&gt;String-valued class labels are common in classification tasks and enable users
to work with interpretable categories rather than integer codes. NumPy is
currently the only array library with string array support, meaning that any
workflow involving both GPU-accelerated computation and string labels
necessarily involves mixed array type inputs.&lt;/p&gt;

&lt;p&gt;Mixed array input support also enables flexible pipeline workflows. Pipelines
provide significant value by chaining preprocessing steps and estimators into
reusable workflows that prevent data leakage and ensure consistent
preprocessing. However, they have an intentional design limitation: pipeline
steps can transform feature arrays (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X&lt;/code&gt;) but cannot modify target arrays
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt;). Allowing mixed array inputs means a pipeline can include a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FunctionTransformer&lt;/code&gt; step that moves feature data from CPU to GPU to leverage
hardware acceleration, while allowing the target array, which cannot be
modified, to remain on CPU.&lt;/p&gt;

&lt;p&gt;For example, mixed array inputs enable a pipeline where string classification
features are encoded on CPU (as only NumPy supports string arrays), converted
to torch CUDA tensors, then passed to the array API-compatible
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RidgeClassifier&lt;/code&gt; for GPU-accelerated computation:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;functools&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;partial&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn.linear_model&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RidgeClassifier&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn.pipeline&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make_pipeline&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionTransformer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TargetEncoder&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;make_pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Encode string categories with average target values
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;TargetEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Convert feature array `X` to Torch CUDA device
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;FunctionTransformer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;asarray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dtype&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;float32&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cuda&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RidgeClassifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;solver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;svd&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Work on adding mixed array type inputs for metrics and estimators is underway
and expected to progress quickly. This work includes developing a robust
testing framework, including for pipelines using mixed array types (follow
&lt;a href=&quot;https://github.com/scikit-learn/scikit-learn/pull/32755&quot;&gt;PR #32755&lt;/a&gt; for details).&lt;/p&gt;

&lt;p&gt;Finally, we have also revived our work to support the ability to fit and
predict on different namespaces/devices. This allows users to train models on
GPU hardware but deploy predictions on CPU hardware, optimizing costs and
accommodating different resource availability between training and production
environments. Follow
&lt;a href=&quot;https://github.com/scikit-learn/scikit-learn/pull/33076&quot;&gt;PR #33076&lt;/a&gt; for
details.&lt;/p&gt;

&lt;h2 id=&quot;challenges&quot;&gt;Challenges&lt;/h2&gt;

&lt;p&gt;The challenges of array API adoption remain largely unchanged from when this
work began. These are also common to other array-consuming libraries, with a
notable addition: the need to handle array movement between namespaces and
devices to support mixed array type inputs.&lt;/p&gt;

&lt;h3 id=&quot;array-api-standard-is-a-subset-of-numpys-api&quot;&gt;Array API Standard is a subset of NumPy’s API&lt;/h3&gt;

&lt;p&gt;The array API standard only includes widely-used functions implemented across
most array libraries, meaning many NumPy functions are absent. When such a
function is encountered while adding array API support, we have the following
options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;add the function to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-extra&lt;/code&gt; - this allows other array-consuming
libraries to benefit and allows sharing of maintenance burden, but is only
relevant for more widely used functions&lt;/li&gt;
  &lt;li&gt;add our own implementation in scikit-learn - these functions live in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sklearn/utils/_array_api.py&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;check if SciPy implements an array API compatible version of the function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quantile&lt;/code&gt; function illustrates this decision-making process. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quantile&lt;/code&gt;
is not included in the standard as it is not widely used (outside of
scikit-learn) and while it is implemented in most array libraries, the
set of quantile methods supported and their APIs vary. Currently, scikit-learn maintains its
own array API compatible version that supports both weights and NaNs, but due
to the maintenance burden we decided to investigate alternatives. SciPy has an
array API compatible implementation, but it did not support weights. We thus
investigated adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quantile&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;array-api-extra&lt;/code&gt;; however, during this
effort, SciPy decided to add weight support. Thus, we ultimately decided to
transition to the SciPy implementation once our minimum SciPy version allows.&lt;/p&gt;

&lt;h3 id=&quot;compiled-code&quot;&gt;Compiled code&lt;/h3&gt;

&lt;p&gt;Many performance-critical parts of scikit-learn are written using compiled
code extensions in Cython, C or C++. These directly access the underlying
memory buffers of NumPy arrays and are thus restricted to CPU.&lt;/p&gt;

&lt;p&gt;Metrics and estimators, with compiled code, handle this in one of two ways:
convert arrays to NumPy first or maintain two parallel branches of code, one
for NumPy (compiled) and one for other array types (array API compatible).
When performance is less critical or array API conversion provides no gains
(e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;confusion_matrix&lt;/code&gt;), we convert to NumPy. When performance gains are
significant, we accept the maintenance burden of dual code paths. This was the case for
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LogisticRegression&lt;/code&gt; and the extensive process required for making such implementation
decisions can be seen in the
&lt;a href=&quot;https://github.com/scikit-learn/scikit-learn/pull/32644&quot;&gt;PR #32644&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;unspecified-behaviour-in-the-standard&quot;&gt;Unspecified behaviour in the standard&lt;/h3&gt;

&lt;p&gt;The array API standard intentionally leaves some function behaviors
unspecified, permitting implementation differences across array libraries. For
example, the order of unique elements is not specified for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unique_*&lt;/code&gt;
functions and as of NumPy version 2.3, some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unique_*&lt;/code&gt; functions no longer
return sorted values. This will require code amendments in cases where sorted
output was relied upon.&lt;/p&gt;

&lt;p&gt;Similarly, NaN handing is also unspecified for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sort&lt;/code&gt;; however, in this case, all
array libraries currently supported by scikit-learn follow NumPy’s NaN
semantics, placing NaNs at the end. This consistency eliminates the need for
special handling code, though comprehensive testing remains essential when
adding support for new array libraries.&lt;/p&gt;

&lt;h3 id=&quot;device-transfer&quot;&gt;Device transfer&lt;/h3&gt;

&lt;p&gt;Mixed array namespace and device inputs necessitates conversion of arrays
between different namespaces and devices. This presented a number of
considerations and challenges.&lt;/p&gt;

&lt;p&gt;The array API standard adopted DLPack as the recommended
&lt;a href=&quot;https://data-apis.org/array-api/latest/design_topics/data_interchange.html#data-interchange&quot;&gt;data interchange&lt;/a&gt;
protocol. This protocol is widely implemented in array libraries and offers an
efficient, C ABI compatible protocol for array conversion. While this provided
us with an easy way to implement these transfers, there were limitations.
Cross-device transfer capability was only introduced in DLPack v1, released in
September 2024. This meant that only the latest PyTorch and CuPy versions have
support for DLPack v1. Moreover, not all array libraries have adopted support
yet. We therefore implemented a ‘manual’ fallback; however, this requires
conversion via NumPy when the transfer involves two non-NumPy arrays.
Additionally, there are no DLPack tests in
&lt;a href=&quot;https://github.com/data-apis/array-api-tests&quot;&gt;array-api-tests&lt;/a&gt;, a testing
suite to verify standard compliance, leaving DLPack implementation bugs easier
to overlook. Despite these challenges, scikit-learn will benefit from future
improvements, such as addition of a C-level API for DLPack exchange that
bypasses Python function calls, offering significant benefit for GPU
applications.&lt;/p&gt;

&lt;p&gt;Beyond the technical considerations, there were also user interface
considerations. How should we inform users that these conversions, which incur
memory and performance cost, are occurring? We decided against warnings, which
risk being ignored or becoming a nuisance, and to instead clearly document this
behaviour. Additionally, different devices have different data type limitations;
for example, Apple MPS only supports float32. How best to handle these differences
when performing conversions while ensuring users are informed of precision
impacts is an ongoing consideration.&lt;/p&gt;

&lt;h2 id=&quot;a-quick-benchmark&quot;&gt;A quick benchmark&lt;/h2&gt;

&lt;p&gt;Array API support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ridge&lt;/code&gt; regression was added in version 1.5, enabling
GPU-accelerated linear models in scikit-learn. Combined with support of
several transformers, this allows for complete preprocessing and estimation
pipelines on GPU.&lt;/p&gt;

&lt;p&gt;The following benchmark shows the use of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxAbsScaler&lt;/code&gt; transformed
followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ridge&lt;/code&gt; regression using randomly generated data with 500,000
samples and 300 features. The benchmarks were run on AMD Ryzen Threadripper
2970WX CPU, NVIDIA Quadro RTX 8000 GPU and Apple M4 GPU (Metal 3).&lt;/p&gt;

&lt;p&gt;The figure below shows the performance speed up on CuPy, Torch CPU and Torch
GPU relative to NumPy.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://blog.scikit-learn.org/assets/images/posts_images/array_api_timings.png&quot; alt=&quot;Benchmarking of MaxAbsScaler/Ridge pipeline&quot; title=&quot;Benchmarking of MaxAbsScaler/Ridge pipeline&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Performance speedup relative to NumPy across different backends.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The observed speedups are representative of performance gains achievable with
sufficiently large datasets on datacenter-grade GPUs for linear
algebra-intensive workloads. Mobile GPUs, such as those in laptops, would
typically yield more modest improvements.&lt;/p&gt;

&lt;p&gt;Note that scikit-learn’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ridge&lt;/code&gt; regressor currently only supports ‘svd’
solver. We selected this solver for initial implementation as it exclusively
uses standard-compliant functions available across all backends and is the
most stable solver. Support for the ‘cholesky’ solver is also underway (see
details in &lt;a href=&quot;https://github.com/scikit-learn/scikit-learn/pull/29318&quot;&gt;PR #29318&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;looking-forward&quot;&gt;Looking forward&lt;/h2&gt;

&lt;p&gt;As of version 1.8, array API support is still in experimental mode and thus
not enabled by default. However, we welcome early adopters and interested
users to try it and report any
&lt;a href=&quot;https://github.com/scikit-learn/scikit-learn/issues&quot;&gt;issues&lt;/a&gt;. See
&lt;a href=&quot;https://scikit-learn.org/dev/modules/array_api.html#enabling-array-api-support&quot;&gt;our documentation&lt;/a&gt;
for details on enabling array API support.&lt;/p&gt;

&lt;p&gt;Before removing experimental status, we would like to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;develop a system for automatically documenting functions and classes that
support array API, potentially with the ability to add relevant details&lt;/li&gt;
  &lt;li&gt;mixed array type input support&lt;/li&gt;
  &lt;li&gt;support fit and predict on different hardware by allowing conversion of
fitted estimators between namespaces/devices using utility functions&lt;/li&gt;
  &lt;li&gt;improved testing, in particular for the new mixed array type
functionalities&lt;/li&gt;
  &lt;li&gt;improved documentation, including adding an example to our gallery&lt;/li&gt;
  &lt;li&gt;decide on the minimal dependency versions required&lt;/li&gt;
  &lt;li&gt;get real world user feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alongside these infrastructure and framework improvements, we look forward to
adding support for more estimators. These improvements will deliver
production-ready GPU support and flexible deployment options to scikit-learn
users. We welcome community involvement through testing and feedback
throughout this development phase.&lt;/p&gt;

&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements&lt;/h2&gt;

&lt;p&gt;Work on array API in scikit-learn has been a combined effort from many
contributors. This work was partly funded by CZI and NASA Roses.&lt;/p&gt;

&lt;p&gt;I would like to thank &lt;a href=&quot;https://github.com/ogrisel&quot;&gt;Olivier Grisel&lt;/a&gt;,
&lt;a href=&quot;https://github.com/betatim&quot;&gt;Tim Head&lt;/a&gt; and
&lt;a href=&quot;https://github.com/ev-br&quot;&gt;Evgeni Burovski&lt;/a&gt; for helping me with my array API
questions.&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-05T00:00:00+00:00</dc:date>
</item>
<item rdf:about="https://lucumr.pocoo.org/2026/3/5/theseus/">
	<title>Armin Ronacher: AI And The Ship of Theseus</title>
	<link>https://lucumr.pocoo.org/2026/3/5/theseus/</link>
	<content:encoded>&lt;p&gt;Because code gets cheaper and cheaper to write, this includes
re-implementations.  I mentioned recently that I had an AI port one of my
libraries to another language and it ended up choosing a different
design for that implementation.  In many ways, the functionality was the same,
but the path it took to get there was different.  The way that port worked was
by going via the test suite.&lt;/p&gt;
&lt;p&gt;Something related, but different, &lt;a href=&quot;https://github.com/chardet/chardet/issues/327#issuecomment-4005195078&quot;&gt;happened with
chardet&lt;/a&gt;.
The current maintainer reimplemented it from scratch by only pointing it to the
API and the test suite.  The motivation: enabling relicensing from LGPL to MIT.
I personally have a horse in the race here because I too wanted chardet to be
under a non-GPL license for many years.  So consider me a very biased person in
that regard.&lt;/p&gt;
&lt;p&gt;Unsurprisingly, that new implementation caused a stir.  In particular, Mark
Pilgrim, the original author of the library, objects to the new implementation
and considers it a derived work.  The new maintainer, who has maintained it for
the last 12 years, considers it a new work and instructs his coding agent to do
precisely that.  According to author, validating with JPlag, the new
implementation is distinct.  If you actually consider how it works, that&amp;#8217;s not
too surprising.  It&amp;#8217;s significantly faster than the original implementation,
supports multiple cores and uses a fundamentally different design.&lt;/p&gt;
&lt;p&gt;What I think is more interesting about this question is the consequences of
where we are.  Copyleft code like the GPL heavily depends on copyrights and
friction to enforce it.  But because it&amp;#8217;s fundamentally in the open, with or
without tests, you can trivially rewrite it these days.  I myself have been
intending to do this for a little while now with some other GPL libraries.  In
particular I started a re-implementation of readline a while ago for similar
reasons, because of its GPL license.  There is an obvious moral question here,
but that isn&amp;#8217;t necessarily what I&amp;#8217;m interested in.  For all the GPL software
that might re-emerge as MIT software, so might be proprietary abandonware.&lt;/p&gt;
&lt;p&gt;For me personally, what is more interesting is that we might not even be able
to copyright these creations at all.  A court still might rule that all
AI-generated code is in the public domain, because there was not enough human
input in it.  That&amp;#8217;s quite possible, though probably not very likely.&lt;/p&gt;
&lt;p&gt;But this all causes some interesting new developments we are not necessarily
ready for.  Vercel, for instance, happily &lt;a href=&quot;https://just-bash.dev/&quot;&gt;re-implemented
bash&lt;/a&gt; with Clankers but &lt;a href=&quot;https://x.com/cramforce/status/2027155457597669785&quot;&gt;got visibly
upset&lt;/a&gt; when someone
re-implemented Next.js in the same way.&lt;/p&gt;
&lt;p&gt;There are huge consequences to this.  When the cost of generating code goes down
that much, and we can re-implement it from test suites alone, what does that
mean for the future of software?  Will we see a lot of software re-emerging
under more permissive licenses?  Will we see a lot of proprietary software
re-emerging as open source?  Will we see a lot of software re-emerging as
proprietary?&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s a new world and we have very little idea of how to navigate it.  In the
interim we will have some fights about copyrights but I have the feeling very
few of those will go to court, because everyone involved will actually be
somewhat scared of setting a precedent.&lt;/p&gt;
&lt;p&gt;In the GPL case, though, I think it warms up some old fights about copyleft vs
permissive licenses that we have not seen in a long time.  It probably does not
feel great to have one&amp;#8217;s work rewritten with a Clanker and one&amp;#8217;s authorship
eradicated.  Unlike the &lt;a href=&quot;https://en.wikipedia.org/wiki/Ship_of_Theseus&quot;&gt;Ship of
Theseus&lt;/a&gt;, though, this seems more
clear-cut: if you throw away all code and start from scratch, even if the end
result behaves the same, it&amp;#8217;s a new ship.  It only continues to carry the name.
Which may be another argument for why authors should hold on to trademarks
rather than rely on licenses and contract law.&lt;/p&gt;
&lt;p&gt;I personally think all of this is exciting.  I&amp;#8217;m a strong supporter of putting
things in the open with as little license enforcement as possible.  I think
society is better off when we share, and I consider the GPL to run against that
spirit by restricting what can be done with it.  This development plays into my
worldview.  I understand, though, that not everyone shares that view, and I
expect more fights over the emergence of slopforks as a result.  After all, it
combines two very heated topics, licensing and AI, in the worst possible way.&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-05T00:00:00+00:00</dc:date>
</item>
<item rdf:about="">
	<title>PyCharm</title>
	<link></link>
	<content:encoded>&lt;p&gt;Cursor is now available as an AI agent inside JetBrains IDEs through the &lt;a href=&quot;https://www.jetbrains.com/acp/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Agent Client Protocol&lt;/a&gt;. Select it from the agent picker, and it has full access to your project.&lt;/p&gt;



&lt;p&gt;If you&amp;#8217;ve spent any time in the AI coding space, you already know &lt;a href=&quot;https://cursor.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Cursor&lt;/a&gt;. It has been one of the most requested additions to the ACP Registry.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What you get&lt;/h2&gt;



&lt;p&gt;Cursor is known for its AI-native, agentic workflows. JetBrains IDEs are valued for deep code intelligence – refactoring, debugging, code quality checks, and the tooling professionals rely on at scale. ACP brings the two together.&lt;/p&gt;



&lt;p&gt;You can now use Cursor&amp;#8217;s agentic capabilities directly inside your JetBrains IDE – within the workflows and features you already use.&amp;nbsp;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;A growing open ecosystem&lt;/h2&gt;



&lt;p&gt;Cursor joins a growing list of agents available through ACP in JetBrains IDEs. Every new addition to the ACP Registry means you have more choice – while still working inside the IDE you already rely on. You get access to frontier models from major providers, including OpenAI, Anthropic, Google, and now also Cursor.&lt;/p&gt;



&lt;p&gt;This is part of our open ecosystem strategy. Plug in the agents you want and work in the IDE you love – without getting locked into a single solution.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote&quot;&gt;
&lt;p&gt;&lt;/p&gt;
&lt;cite&gt;Cursor is focused on building the best way to build software with AI. By integrating Cursor with JetBrains IDEs, we&amp;#8217;re excited to provide teams with powerful agentic capabilities in the environments where they&amp;#8217;re already working.&lt;br /&gt;&lt;br /&gt;– Jordan Topoleski, COO at Cursor&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Get started&lt;/h2&gt;



&lt;p&gt;You need version 2025.3.2 or later of your JetBrains IDE with the &lt;em&gt;AI Assistant&lt;/em&gt; plugin enabled. From there, open the agent selector, select &lt;em&gt;Install from&lt;/em&gt; &lt;em&gt;ACP Registry…&lt;/em&gt;, install Cursor, and start working. You &lt;strong&gt;don’t need a JetBrains AI subscription&lt;/strong&gt; to use Cursor as an AI agent.&lt;/p&gt;



&lt;div class=&quot;wp-block-embed__wrapper&quot;&gt;

&lt;/div&gt;



&lt;p&gt;The ACP Registry keeps growing, and many agents have already joined it – with more on the way. Try it today with Cursor and experience agent-driven development inside your JetBrains IDE. For more information about the Agent Client Protocol, see &lt;a href=&quot;https://blog.jetbrains.com/ai/2025/12/bring-your-own-ai-agent-to-jetbrains-ides/&quot;&gt;our original announcement&lt;/a&gt; and the &lt;a href=&quot;https://blog.jetbrains.com/ai/2026/01/acp-agent-registry/&quot;&gt;blog post on the ACP Agent Registry support&lt;/a&gt;.&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-04T16:41:00+00:00</dc:date>
</item>
<item rdf:about="https://www.pythonmorsels.com/custom-comprehensions/">
	<title>Python Morsels: Invent your own comprehensions in Python</title>
	<link>https://www.pythonmorsels.com/custom-comprehensions/</link>
	<content:encoded>&lt;p&gt;Python doesn't have tuple, &lt;code&gt;frozenset&lt;/code&gt;, or &lt;code&gt;Counter&lt;/code&gt; comprehensions, but you can invent your own by passing a generator expression to any iterable-accepting callable.&lt;/p&gt;


&lt;div&gt;
  
  &lt;p&gt;
  &lt;strong&gt;Table of contents&lt;/strong&gt;
  &lt;ol&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/custom-comprehensions/#generator-expressions-pair-nicely-with-iterable-accepting-callables&quot; target=&quot;_blank&quot;&gt;Generator expressions pair nicely with iterable-accepting callables&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/custom-comprehensions/#tuple-comprehensions&quot; target=&quot;_blank&quot;&gt;Tuple comprehensions&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/custom-comprehensions/#frozenset-comprehensions&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;frozenset&lt;/code&gt; comprehensions&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/custom-comprehensions/#counter-comprehensions&quot; target=&quot;_blank&quot;&gt;Counter comprehensions&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/custom-comprehensions/#aggregate-with-reducer-functions&quot; target=&quot;_blank&quot;&gt;Aggregate with reducer functions&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/custom-comprehensions/#invent-your-own-comprehensions-with-generator-expressions&quot; target=&quot;_blank&quot;&gt;Invent your own comprehensions with generator expressions&lt;/a&gt;&lt;/li&gt;
  
  &lt;/ol&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
  
    &lt;h2&gt;Generator expressions pair nicely with iterable-accepting callables&lt;/h2&gt;
    
      
        &lt;p&gt;Generator expressions work &lt;em&gt;really&lt;/em&gt; nicely with &lt;a href=&quot;https://www.pythonmorsels.com/any-and-all/&quot; target=&quot;_blank&quot;&gt;Python's &lt;code&gt;any&lt;/code&gt; and &lt;code&gt;all&lt;/code&gt; functions&lt;/a&gt;:&lt;/p&gt;

      
        &lt;div class=&quot;codehilite&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      
        &lt;p&gt;In fact, I rarely see &lt;code&gt;any&lt;/code&gt; and &lt;code&gt;all&lt;/code&gt; used &lt;em&gt;without&lt;/em&gt; a generator expression passed to them.&lt;/p&gt;

      
        &lt;p&gt;Note that generator expressions are made with parentheses:&lt;/p&gt;

      
        &lt;div class=&quot;codehilite&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;generator object &amp;lt;genexpr&amp;gt; at 0x74c535589b60&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      
        &lt;p&gt;But when a generator expression is the &lt;em&gt;sole&lt;/em&gt; argument passed into a function:&lt;/p&gt;

      
        &lt;div class=&quot;codehilite&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      
        &lt;p&gt;The double set of parentheses (one to form the generator expression and one for the function call) can be turned into just a single set of parentheses:&lt;/p&gt;

      
        &lt;div class=&quot;codehilite&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      
        &lt;p&gt;This special allowance was added to Python's syntax because it's &lt;em&gt;very&lt;/em&gt; common to see generator expressions passed in as the sole argument to specific functions.&lt;/p&gt;

      
        &lt;p&gt;Note that passing &lt;strong&gt;generator expressions&lt;/strong&gt; into &lt;a href=&quot;https://www.pythonmorsels.com/iterable/&quot; target=&quot;_blank&quot;&gt;iterable&lt;/a&gt;-accepting functions and classes makes something that &lt;em&gt;looks&lt;/em&gt; a bit like a &lt;strong&gt;custom comprehension&lt;/strong&gt;.
Every iterable-accepting function/class is a comprehension-like tool waiting to happen.&lt;/p&gt;

      
    
  
    &lt;h2&gt;Tuple comprehensions&lt;/h2&gt;
    
      &lt;p&gt;Python does not have tuple …&lt;/p&gt;
    
  
&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;https://www.pythonmorsels.com/custom-comprehensions/&quot; target=&quot;_blank&quot;&gt;Read the full article: https://www.pythonmorsels.com/custom-comprehensions/&lt;/a&gt;&lt;/h3&gt;</content:encoded>
	<dc:date>2026-03-04T15:30:00+00:00</dc:date>
</item>
<item rdf:about="https://blog.jetbrains.com/ai/2026/03/cursor-joined-the-acp-registry-and-is-now-live-in-your-jetbrains-ide/">
	<title>PyCharm: Cursor Joined the ACP Registry and Is Now Live in Your JetBrains IDE</title>
	<link>https://blog.jetbrains.com/ai/2026/03/cursor-joined-the-acp-registry-and-is-now-live-in-your-jetbrains-ide/</link>
	<dc:date>2026-03-04T15:28:01+00:00</dc:date>
</item>
<item rdf:about="https://realpython.com/openrouter-api/">
	<title>Real Python: How to Use the OpenRouter API to Access Multiple AI Models via Python</title>
	<link>https://realpython.com/openrouter-api/</link>
	<content:encoded>&lt;div&gt;&lt;p&gt;One of the quickest ways to call multiple AI models from a single Python script is to use OpenRouter’s API, which acts as a unified routing layer between your code and multiple AI providers. By the end of this guide, you’ll access models from several providers through one unified API, as shown in the image below:&lt;/p&gt;
&lt;a href=&quot;https://files.realpython.com/media/open-router-multiple-AI-models-screenshot.adfb5097125a.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/open-router-multiple-AI-models-screenshot.adfb5097125a.png&quot; width=&quot;1527&quot; height=&quot;773&quot; alt=&quot;Open Router Running Multiple AI Models&quot; /&gt;&lt;/a&gt;OpenRouter Unified API Running Multiple AI Models

&lt;p&gt;This convenience matters because the AI ecosystem is highly fragmented: each provider exposes its own API, authentication scheme, rate limits, and model lineup. Working with multiple providers often requires additional setup and integration effort, especially when you want to experiment with different models, compare outputs, or evaluate trade-offs for a specific task.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://openrouter.ai/&quot;&gt;OpenRouter&lt;/a&gt; gives you access to thousands of models from leading providers such as OpenAI, Anthropic, Mistral, Google, and Meta. You switch between them without changing your application code.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Get Your Code:&lt;/strong&gt; &lt;a href=&quot;https://realpython.com/bonus/openrouter-api-code/&quot; class=&quot;alert-link&quot;&gt;Click here to download the free sample code&lt;/a&gt; that shows you how to use the OpenRouter API to access multiple AI models via Python.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;container border rounded text-wrap-pretty my-3&quot;&gt;

  &lt;p class=&quot;my-3&quot;&gt;&lt;strong&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “How to Use the OpenRouter API to Access Multiple AI Models via Python” quiz. You’ll receive a score upon completion to help you track your learning progress:&lt;/p&gt;

  &lt;hr /&gt;

  &lt;div class=&quot;row my-3&quot;&gt;
    &lt;div class=&quot;col-xs-12 col-sm-4 col-md-3 align-self-center&quot;&gt;

      &lt;a href=&quot;https://realpython.com/quizzes/openrouter-api/&quot; tabindex=&quot;-1&quot;&gt;
        &lt;div class=&quot;embed-responsive embed-responsive-16by9&quot;&gt;

            &lt;img class=&quot;card-img-top m-0 p-0 embed-responsive-item rounded&quot; alt=&quot;How to Use the OpenRouter API to Access Multiple AI Models via Python&quot; src=&quot;https://files.realpython.com/media/How-To-Use-OpenRouter-to-Access-Multiple-AI-Models-via-Python_Watermarked-2.7cb51570b2ac.jpg&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;


          &lt;div class=&quot;card-img-overlay d-flex align-items-center&quot;&gt;
            &lt;div class=&quot;mx-auto&quot;&gt;
              &lt;span class=&quot;text-light&quot;&gt;&lt;span class=&quot;icon baseline scale2x&quot;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/a&gt;

    &lt;/div&gt;
    &lt;div class=&quot;col&quot;&gt;
      &lt;div class=&quot;mt-3 d-md-none&quot;&gt;&lt;/div&gt; 
      &lt;p class=&quot;small text-muted mb-0&quot;&gt;&lt;strong&gt;Interactive Quiz&lt;/strong&gt;&lt;/p&gt;
      &lt;a href=&quot;https://realpython.com/quizzes/openrouter-api/&quot; class=&quot;stretched-link&quot;&gt;&lt;span class=&quot;my-0 h4&quot;&gt;How to Use the OpenRouter API to Access Multiple AI Models via Python&lt;/span&gt;&lt;/a&gt; 
      &lt;p class=&quot;text-muted mb-0 small&quot;&gt;Test your Python skills with OpenRouter: learn unified API access, model switching, provider routing, and fallback strategies.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#prerequisites&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before diving into OpenRouter, you should be comfortable with Python fundamentals like importing &lt;a href=&quot;https://realpython.com/ref/glossary/module/&quot; class=&quot;ref-link&quot;&gt;modules&lt;/a&gt;, working with &lt;a href=&quot;https://realpython.com/ref/glossary/dictionary/&quot; class=&quot;ref-link&quot;&gt;dictionaries&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/ref/builtin-exceptions/&quot; class=&quot;ref-link&quot;&gt;handling exceptions&lt;/a&gt;, and using environment &lt;a href=&quot;https://realpython.com/ref/glossary/variable/&quot; class=&quot;ref-link&quot;&gt;variables&lt;/a&gt;. If you’re familiar with these basics, the first step is authenticating with OpenRouter’s API.&lt;/p&gt;
&lt;h2 id=&quot;step-1-connect-to-openrouters-api&quot;&gt;Step 1: Connect to OpenRouter’s API&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#step-1-connect-to-openrouters-api&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before using OpenRouter, you need to create an account and generate an &lt;a href=&quot;https://realpython.com/ref/glossary/api/&quot; class=&quot;ref-link&quot;&gt;API&lt;/a&gt; key. Some models require prepaid credits for access, but you can start with free access to test the API and confirm that everything is working.&lt;/p&gt;
&lt;p&gt;To generate an API key:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an account at &lt;a href=&quot;https://openrouter.ai/&quot;&gt;OpenRouter.ai&lt;/a&gt; or sign in if you already have an account.&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;Keys&lt;/em&gt; from the dropdown menu and create an &lt;a href=&quot;https://openrouter.ai/settings/keys&quot;&gt;API key&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Fill in the name, something like &lt;em&gt;OpenRouter Testing&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Leave the remaining defaults and click &lt;em&gt;Create&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Copy the generated key and keep it secure. In a moment, you’ll store it as an environment variable rather than embedding it directly in your code.&lt;/p&gt;
&lt;p&gt;To call multiple AI models from a single &lt;a href=&quot;https://realpython.com/run-python-scripts/&quot;&gt;Python script&lt;/a&gt;, you’ll use OpenRouter’s API. You’ll use the &lt;a href=&quot;https://realpython.com/python-requests/&quot;&gt;&lt;code&gt;requests&lt;/code&gt; library&lt;/a&gt; to make HTTP calls, which gives you full control over the API interactions without requiring a specific &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_development_kit&quot;&gt;SDK&lt;/a&gt;. This approach works with any HTTP client and keeps your code simple and transparent.&lt;/p&gt;
&lt;p&gt;First, create a new directory for your project and set up a &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;virtual environment&lt;/a&gt;. This isolates your project dependencies from your system Python installation:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;Shell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;mkdir&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;openrouter-project/
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;openrouter-project/
&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;python&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-m&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;venv&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;venv/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;Now, you can activate the virtual environment:&lt;/p&gt;
&lt;ul class=&quot;nav nav-tabs justify-content-end js-platform-widget-tabs&quot;&gt;

  &lt;li class=&quot;nav-item mb-0 js-platform-widget-tab-windows&quot;&gt;
    &lt;a class=&quot;nav-link link-unstyled text-body active small&quot; id=&quot;windows-tab-1&quot; href=&quot;https://realpython.com/atom.xml#windows-1&quot;&gt;&lt;span class=&quot;icon baseline text-muted mr-1&quot;&gt;&lt;/span&gt;Windows&lt;/a&gt;
  &lt;/li&gt;




  &lt;li class=&quot;nav-item mb-0 js-platform-widget-tab-linuxmacos&quot;&gt;
    &lt;a class=&quot;nav-link link-unstyled text-body small&quot; id=&quot;macos-tab-1&quot; href=&quot;https://realpython.com/atom.xml#linux-macos-1&quot;&gt;&lt;span class=&quot;icon baseline text-muted&quot;&gt;&lt;/span&gt;&lt;span class=&quot;icon baseline text-muted mr-1&quot;&gt;&lt;/span&gt;Linux + macOS&lt;/a&gt;
  &lt;/li&gt;

&lt;/ul&gt;
&lt;div class=&quot;tab-content mt-2 mb-0 js-platform-widget-content&quot;&gt;
&lt;div class=&quot;tab-pane fade show active&quot; id=&quot;windows-1&quot;&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;Windows PowerShell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;PS&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;venv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scripts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;activate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;/div&gt;
&lt;div class=&quot;tab-pane fade &quot; id=&quot;linux-macos-1&quot;&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;Shell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You should see &lt;a href=&quot;https://realpython.com/ref/stdlib/venv/&quot; class=&quot;ref-link&quot;&gt;(&lt;code&gt;venv&lt;/code&gt;)&lt;/a&gt; in your terminal prompt when it’s active. Now you’re ready to install the &lt;code&gt;requests&lt;/code&gt; package for conveniently &lt;a href=&quot;https://realpython.com/python-requests/&quot;&gt;making HTTP calls&lt;/a&gt;:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;Shell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp gp-VirtualEnv&quot;&gt;(venv)&lt;/span&gt; &lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;python&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-m&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pip&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;/div&gt;&lt;h2&gt;&lt;a href=&quot;https://realpython.com/openrouter-api/?utm_source=realpython&amp;utm_medium=rss&quot;&gt;Read the full article at https://realpython.com/openrouter-api/ »&lt;/a&gt;&lt;/h2&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-04T14:00:00+00:00</dc:date>
</item>
<item rdf:about="https://blog.glyph.im/2026/03/what-is-code-review-for.html">
	<title>Glyph Lefkowitz: What Is Code Review For?</title>
	<link>https://blog.glyph.im/2026/03/what-is-code-review-for.html</link>
	<content:encoded>&lt;h2 id=&quot;humans-are-bad-at-perceiving&quot;&gt;Humans Are Bad At Perceiving&lt;/h2&gt;
&lt;p&gt;Humans are not particularly good at catching bugs.  For one thing, we get tired
easily.  &lt;a href=&quot;https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/&quot;&gt;There is some science on this, indicating that humans can’t even
maintain enough concentration to review more than about 400 lines of code at a
time.&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We have existing terms of art, in various fields, for the ways in which the
human perceptual system fails to register stimuli. Perception fails when humans
are distracted, tired, overloaded, or merely improperly engaged.&lt;/p&gt;
&lt;p&gt;Each of these has implications for the fundamental limitations of code review
as an engineering practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Inattentional_blindness&quot;&gt;Inattentional
  Blindness&lt;/a&gt;: you won’t
  be able to reliably find bugs that you’re &lt;em&gt;not&lt;/em&gt; looking for.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Repetition_blindness&quot;&gt;Repetition Blindness&lt;/a&gt;:
  you won’t be able to reliably find bugs that you &lt;em&gt;are&lt;/em&gt; looking for, if they
  keep occurring.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.asisonline.org/security-management-magazine/articles/2013/05/vigilance-fatigue/&quot;&gt;Vigilance
  Fatigue&lt;/a&gt;:
  you won’t be able to reliably find either kind of bugs, if you have to keep
  being alert to the presence of bugs all the time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;and, of course, the distinct but related &lt;a href=&quot;https://www.ibm.com/think/topics/alert-fatigue&quot;&gt;Alert
  Fatigue&lt;/a&gt;: you won’t even be
  able to reliably &lt;em&gt;evaluate&lt;/em&gt; reports of &lt;em&gt;possible&lt;/em&gt; bugs, if there are too many
  false positives.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;never-send-a-human-to-do-a-machines-job&quot;&gt;Never Send A Human To Do A Machine’s Job&lt;/h2&gt;
&lt;p&gt;When you need to catch a category of error in your code reliably, you will need
a deterministic tool to evaluate — and, thanks to our old friend “alert
fatigue” above — ideally, to also remedy that type of error.  These tools will
relieve the need for a human to make the same repetitive checks over and over.
None of them are perfect, but:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;to catch logical errors, use automated tests.&lt;/li&gt;
&lt;li&gt;to catch formatting errors, use autoformatters.&lt;/li&gt;
&lt;li&gt;to catch common mistakes, use linters.&lt;/li&gt;
&lt;li&gt;to catch common security problems, use a security scanner.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don’t blame reviewers for missing these things.&lt;/p&gt;
&lt;p&gt;Code review should not be how you catch bugs.&lt;/p&gt;
&lt;h2 id=&quot;what-is-code-review-for-then&quot;&gt;What Is Code Review For, Then?&lt;/h2&gt;
&lt;p&gt;Code review is for three things.&lt;/p&gt;
&lt;p&gt;First, code review is for catching &lt;em&gt;process failures&lt;/em&gt;.  If a reviewer &lt;em&gt;has&lt;/em&gt;
noticed a few bugs of the same type in code review, that’s a sign that that
type of bug is probably getting &lt;em&gt;through&lt;/em&gt; review more often than it’s getting
caught.  Which means it’s time to figure out a way to deploy a tool or a test
into CI that will &lt;em&gt;reliably&lt;/em&gt; prevent that class of error, without requiring
reviewers to be vigilant to it any more.&lt;/p&gt;
&lt;p&gt;Second — and this is actually its &lt;em&gt;more important&lt;/em&gt; purpose — code review is a
tool for &lt;em&gt;acculturation&lt;/em&gt;.  Even if you already have good tools, good processes,
and good documentation, new members of the team won’t necessarily &lt;em&gt;know&lt;/em&gt; about
those things.  Code review is an opportunity for older members of the team to
introduce newer ones to existing tools, patterns, or areas of responsibility.
If you’re building an observer pattern, you might not realize that the codebase
you’re working in already has an existing idiom for doing that, so you wouldn’t
even think to search for it, but someone else who has worked more with the code
might know about it and help you avoid repetition.&lt;/p&gt;
&lt;p&gt;You will notice that I carefully avoided saying “junior” or “senior” in that
paragraph.  Sometimes the newer team member is actually more senior.  But also,
the acculturation goes both ways.  This is the third thing that code review is
for: &lt;em&gt;disrupting&lt;/em&gt; your team’s culture and avoiding stagnation.  If you have new
talent, a fresh perspective can &lt;em&gt;also&lt;/em&gt; be an extremely valuable tool for
building a healthy culture.  If you’re new to a team and trying to build
something with an observer pattern, and this codebase has no tools for that,
but your &lt;em&gt;last&lt;/em&gt; job did, and it used one from an open source library, that is a
good thing to point out in a review as well.  It’s an opportunity to spot areas
for improvement to culture, as much as it is to spot areas for improvement to
process.&lt;/p&gt;
&lt;p&gt;Thus, code review should be as hierarchically flat as possible.  If the goal of
code review were to spot bugs, it would make sense to reserve the ability to
review code to only the most senior, detail-oriented, rigorous engineers in the
organization.  But most teams already know that that’s a recipe for
brittleness, stagnation and bottlenecks.  Thus, even though we &lt;em&gt;know&lt;/em&gt; that not
everyone on the team will be equally good at spotting bugs, it is very common
in most teams to allow anyone past some fairly low minimum seniority bar to do
reviews, often as low as “everyone on the team who has finished onboarding”.&lt;/p&gt;
&lt;h2 id=&quot;oops-surprise-this-post-is-actually-about-llms-again&quot;&gt;Oops, Surprise, This Post Is Actually About LLMs Again&lt;/h2&gt;
&lt;p&gt;Sigh.  I’m as disappointed as you are, but there are no two ways about it: LLM
code generators are everywhere now, and we need to talk about how to deal with
them.  Thus, an important corollary of this understanding that code review is a
&lt;em&gt;social activity&lt;/em&gt;, is that LLMs are not &lt;em&gt;social actors&lt;/em&gt;, thus you cannot rely
on code review to inspect their output.&lt;/p&gt;
&lt;p&gt;My own &lt;em&gt;personal&lt;/em&gt; preference would be to eschew their use entirely, but in the
spirit of harm reduction, if you’re going to use LLMs to generate code, you
need to remember the ways in which LLMs are not like human beings.&lt;/p&gt;
&lt;p&gt;When you relate to a human colleague, you will expect that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;you can make decisions about what to focus on based on their level of
   experience and areas of expertise to know what problems to focus on; from a
   late-career colleague you might be looking for bad habits held over from
   legacy programming languages; from an earlier-career colleague you might be
   focused more on logical test-coverage gaps,&lt;/li&gt;
&lt;li&gt;and, they will learn from repeated interactions so that you can gradually
   focus less on a specific type of problem once you have seen that they’ve
   learned how to address it,&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With an LLM, by contrast, while errors can certainly be &lt;em&gt;biased&lt;/em&gt; a bit by the
prompt from the engineer and pre-prompts that might exist in the repository, the
types of errors that the LLM will make are somewhat more uniformly distributed
across the experience range.&lt;/p&gt;
&lt;p&gt;You will still find supposedly extremely sophisticated LLMs making &lt;a href=&quot;https://arxiv.org/html/2407.07064v2&quot;&gt;extremely
common mistakes&lt;/a&gt;, specifically because
they &lt;em&gt;are&lt;/em&gt; common, and thus appear frequently in the training data.&lt;/p&gt;
&lt;p&gt;The LLM also can’t really learn.  An intuitive response to this problem is to
simply continue adding more and more instructions to its pre-prompt, treating
&lt;em&gt;that&lt;/em&gt; text file as its “memory”, but that &lt;a href=&quot;https://github.com/anthropics/claude-code/issues/2766&quot;&gt;just doesn’t work, and probably
never will&lt;/a&gt;.  The
problem — “&lt;a href=&quot;https://research.trychroma.com/context-rot&quot;&gt;context rot&lt;/a&gt;” is
somewhat fundamental to the nature of the technology.&lt;/p&gt;
&lt;p&gt;Thus, code-generators must be treated more adversarially than you would a human
code review partner.  When you notice it making errors, you &lt;em&gt;always&lt;/em&gt; have to
add tests to a mechanical, deterministic harness that will evaluates the code,
because the LLM cannot meaningfully learn from its mistakes outside a very
small context window in the way that a human would, so giving it &lt;em&gt;feedback&lt;/em&gt; is
unhelpful.  Asking it to just generate the code again still requires you to
review it &lt;em&gt;all&lt;/em&gt; again, and as we have previously learned, you, a human, cannot
review more than 400 lines at once.&lt;/p&gt;
&lt;h2 id=&quot;to-sum-up&quot;&gt;To Sum Up&lt;/h2&gt;
&lt;p&gt;Code review is a social process, and you should treat it as such.  When you’re
reviewing code from humans, share knowledge and encouragement as much as you
share bugs or unmet technical requirements.&lt;/p&gt;
&lt;p&gt;If you must reviewing code from an LLM, strengthen your automated code-quality
verification tooling and make sure that its agentic loop will fail on its own
when those quality checks fail immediately next time. Do not fall into the trap
of appealing to its feelings, knowledge, or experience, because it doesn’t have
any of those things.&lt;/p&gt;
&lt;p&gt;But for both humans &lt;em&gt;and&lt;/em&gt; LLMs, do not fall into the trap of thinking that your
code review process is catching your bugs.  That’s not its job.&lt;/p&gt;
&lt;h2 id=&quot;acknowledgments&quot;&gt;Acknowledgments&lt;/h2&gt;
&lt;p class=&quot;update-note&quot;&gt;Thank you to &lt;a href=&quot;https://glyph.twistedmatrix.com/pages/patrons.html&quot;&gt;my patrons&lt;/a&gt; who are supporting my writing on
this blog.  If you like what you’ve read here and you’d
like to read more of it, or you’d like to support my &lt;a href=&quot;https://github.com/glyph/&quot;&gt;various open-source
endeavors&lt;/a&gt;, you can &lt;a href=&quot;https://glyph.twistedmatrix.com/pages/patrons.html&quot;&gt;support my work as a
sponsor&lt;/a&gt;!&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-04T05:24:00+00:00</dc:date>
</item>
<item rdf:about="https://sethmlarson.dev/pip-relative-dependency-cooling-with-crontab?utm_campaign=rss">
	<title>Seth Michael Larson: Relative “Dependency Cooldowns” in pip v26.0 with crontab</title>
	<link>https://sethmlarson.dev/pip-relative-dependency-cooling-with-crontab?utm_campaign=rss</link>
	<content:encoded>&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;WARNING:&lt;/strong&gt; Most of this blog post is
    a hack, everyone should probably just
    wait for relative dependency cooldowns to come
    to a future version of pip.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!-- more --&gt;

&lt;p&gt;pip v26.0 &lt;a href=&quot;https://ichard26.github.io/blog/2026/01/whats-new-in-pip-26.0/#excluding-distributions-by-upload-time&quot;&gt;added support for the &lt;code&gt;--uploaded-prior-to&lt;/code&gt; option&lt;/a&gt;.
This new option enables implementing “&lt;a href=&quot;https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns&quot;&gt;dependency cooldowns&lt;/a&gt;”, a technique
described by &lt;a href=&quot;https://yossarian.net&quot;&gt;William Woodruff&lt;/a&gt;, that provides simple but effective
protections for the &lt;a href=&quot;https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns#fn:filippo:~:text=Approx%2E%20Window%20of%20Opportunity&quot;&gt;relatively short attack-window time&lt;/a&gt;
of malware published to public software repositories. This brings the
reaction time to malware back within the realm of humans, who sometimes need to
execute manual triage processes to take down malware from PyPI.&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;So if you set &lt;code&gt;--uploaded-prior-to&lt;/code&gt; to 7 days before this post
was published, February 25th, you'd do so like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;python -m pip install \
  --uploaded-prior-to=2026-02-25 \
  urllib3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But this is only an absolute date, and we have to remember
to set the option on each call to &lt;code&gt;pip install&lt;/code&gt;? That seems like
a lot of work!&lt;/p&gt;

&lt;p&gt;Dependency cooldowns work best when the policy can be set in a
&lt;a href=&quot;https://nesbitt.io/2026/03/04/package-managers-need-to-cool-down.html&quot;&gt;global configuration file to a relative value&lt;/a&gt;
like “7 days”.  The “window” of acceptable packages is then automatically updating over time
without needing to set a new absolute value constantly. “Set-and-forget”-style.&lt;/p&gt;

&lt;p&gt;uv allows &lt;a href=&quot;https://docs.astral.sh/uv/guides/scripts/#improving-reproducibility&quot;&gt;setting a relative value&lt;/a&gt; via &lt;code&gt;--exclude-newer&lt;/code&gt;,
but pip &lt;a href=&quot;https://github.com/pypa/pip/issues/13674&quot;&gt;doesn't support relative ranges yet&lt;/a&gt;. I mostly use pip and still wanted to test this feature today, so I
created a little hack to update my user &lt;code&gt;pip.conf&lt;/code&gt; configuration file
on a regular basis instead. Here's what my &lt;code&gt;pip.conf&lt;/code&gt; file looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[install]
uploaded-prior-to = 2026-02-25
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And below is the entire Python script doing the updating. Quick reminder that
I only tested this on my own system, your mileage
may vary, do not use in production.&lt;/p&gt;

&lt;div class=&quot;codehilite&quot;&gt;
&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# License: MIT&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;re&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Parse the command line options.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pip_conf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abspath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expanduser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;days&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Load the existing pip.conf file.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pip_conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pip_conf_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;FileNotFoundError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Could not find pip.conf file at: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pip_conf&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Update the existing uploaded-prior-to value.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;uploaded_prior_to_re&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;^uploaded-prior-to\s*=\s*2[0-9]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{3}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-[0-9]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-[0-9]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;$&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MULTILINE&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uploaded_prior_to_re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pip_conf_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Could not find uploaded-prior-to option in pip.conf under [install]&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new_uploaded_prior_to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%Y-%m-&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pip_conf_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;uploaded_prior_to_re&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;uploaded-prior-to = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_uploaded_prior_to&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pip_conf_data&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Write the new uploaded-prior-to&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# value to pip.conf&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pip_conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pip_conf_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The script takes two parameters, your &lt;code&gt;pip.conf&lt;/code&gt; file you want
to update (typically &lt;code&gt;~/.config/pip/pip.conf&lt;/code&gt; on Linux) and
an integer number of days. I used 14 in my cron example below.&lt;/p&gt;

&lt;p&gt;Simple right? I installed and &lt;code&gt;chmod u+X&lt;/code&gt;-ed the script in my &lt;code&gt;/usr/local/bin&lt;/code&gt; directory
and then added to my crontab using &lt;code&gt;crontab -u (USERNAME) -e&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;0 * * * * (/usr/local/bin/pip-dependency-cooldown /home/sethmlarson/.config/pip/pip.conf 14)  2&amp;gt;&amp;amp;1 | logger -t pip-dependency-cooldown
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This pattern will run the script once per hour and update the value
of &lt;code&gt;uploaded-prior-to&lt;/code&gt; to the new day. Now I only receive packages that
were published 14 or more days ago by default when running &lt;code&gt;pip install&lt;/code&gt; without any other options.&lt;/p&gt;

&lt;p&gt;Stay tuned for more about dependency cooldowns for Python installers
once pip supports relative values.&lt;/p&gt;
&lt;br /&gt;&lt;hr /&gt;&lt;p&gt;Thanks for keeping RSS alive! ♥&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-04T00:00:00+00:00</dc:date>
</item>
<item rdf:about="https://pycoders.com/issues/724">
	<title>PyCoder’s Weekly: Issue #724: Unit Testing Performance, Ordering, FastAPI, and More (March 3, 2026)</title>
	<link>https://pycoders.com/issues/724</link>
	<content:encoded>&lt;p&gt; &lt;span&gt;#724 – MARCH 3, 2026&lt;/span&gt;&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/issues/724/feed&quot;&gt;View in Browser »&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://pycoders.com&quot;&gt;&lt;img alt=&quot;The PyCoder&amp;rsquo;s Weekly Logo&quot; src=&quot;https://cdn.pycoders.com/37bdf31dc645f968ffb90196e5d38ff5&quot; /&gt;&lt;/a&gt;&lt;/p&gt; &lt;hr /&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16079/feed&quot; target=&quot;_blank&quot;&gt;Unit Testing: Catching Speed Changes&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; This second post in a series covers how to use unit testing to ensure the performance of your code. This post talks about catching differences in performance after code has changed.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16079/feed&quot; target=&quot;_blank&quot;&gt;ITAMAR TURNER-TRAURING&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16066/feed&quot; target=&quot;_blank&quot;&gt;Lexicographical Ordering in Python&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Python lexicographically orders tuples, strings, and all other sequences, comparing element-by-element. Learn what this means when you compare values or sort.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16066/feed&quot; target=&quot;_blank&quot;&gt;TREY HUNNER&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16051/feed&quot; target=&quot;_blank&quot;&gt;A Cheaper Heroku? See for Yourself&lt;/a&gt;&lt;/h3&gt; &lt;a href=&quot;https://pycoders.com/link/16051/feed&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://cdn.pycoders.com/4373666f0f4efd84fc5ff93afe6c4c06&quot; alt=&quot;alt&quot; /&gt;&lt;/a&gt; &lt;p&gt; Is PaaS too expensive for your Django app? We built a comparison calculator that puts the &lt;a href=&quot;https://pycoders.com/link/16051/feed&quot; target=&quot;_blank&quot;&gt;fully-managed hosting options head-to-head →&lt;/a&gt;&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16051/feed&quot; target=&quot;_blank&quot;&gt;JUDOSCALE&lt;/a&gt;&lt;/span&gt; &lt;span&gt;sponsor&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16076/feed&quot; target=&quot;_blank&quot;&gt;Start Building With FastAPI&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Learn how to build APIs with FastAPI in Python, including Pydantic models, HTTP methods, CRUD operations, and interactive documentation.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16076/feed&quot; target=&quot;_blank&quot;&gt;REAL PYTHON&lt;/a&gt;&lt;/span&gt; &lt;span&gt;course&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16078/feed&quot; target=&quot;_blank&quot;&gt;PEP 743: Add Py_OMIT_LEGACY_API to the Python C API (Rejected)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16078/feed&quot; target=&quot;_blank&quot;&gt;PYTHON.ORG&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16053/feed&quot; target=&quot;_blank&quot;&gt;DjangoCon US 2026 (Chicago) Call for Proposals Open&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16053/feed&quot; target=&quot;_blank&quot;&gt;DJANGOCON.US&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;h2&gt;Python Jobs&lt;/h2&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16056/feed&quot; target=&quot;_blank&quot;&gt;Python + AI Content Specialist (Anywhere)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;&lt;a href=&quot;https://pycoders.com/link/16056/feed&quot; target=&quot;_blank&quot;&gt;Real Python&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://www.pythonjobshq.com?utm_source=newsletter&amp;utm_campaign=pycoders724&amp;utm_medium=feed&quot; target=&quot;_blank&quot;&gt;More Python Jobs &amp;gt;&amp;gt;&amp;gt;&lt;/a&gt;&lt;/p&gt; &lt;h2&gt;Articles &amp;amp; Tutorials&lt;/h2&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16074/feed&quot; target=&quot;_blank&quot;&gt;Serving Private Files With Django and S3&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Django’s &lt;code&gt;FileField&lt;/code&gt; and &lt;code&gt;ImageField&lt;/code&gt; are good at storing files, but on their own they don’t let you control access. Serving files from S3 just makes this more complicated. Learn how to secure a file behind your login wall.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16074/feed&quot; target=&quot;_blank&quot;&gt;RICHARD TERRY&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16072/feed&quot; target=&quot;_blank&quot;&gt;FastAPI Error Handling: Types, Methods, and Best Practices&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; FastAPI provides various error-handling mechanisms to help you build robust applications. With built-in validation models, exceptions, and custom exception handlers, you can build robust and scalable FastAPI applications.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16072/feed&quot; target=&quot;_blank&quot;&gt;HONEYBADGER.IO&lt;/a&gt; • Shared by Addison Curtis&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16058/feed&quot; target=&quot;_blank&quot;&gt;CLI Subcommands With Lazy Imports&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Python 3.15 will support lazy imports, meaning modules don&amp;rsquo;t get pulled in until they are needed. This can be particularly useful with Command Line Interfaces where a subcommand doesn&amp;rsquo;t need everything to be useful.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16058/feed&quot; target=&quot;_blank&quot;&gt;BRETT CANNON&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16061/feed&quot; target=&quot;_blank&quot;&gt;How the Self-Driving Tech Stack Works&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; A technical guide to how self-driving cars actually work. CAN bus protocols, neural networks, sensor fusion, and control system with open source implementations, most of which can be accessed through Python.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16061/feed&quot; target=&quot;_blank&quot;&gt;CARDOG&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16057/feed&quot; target=&quot;_blank&quot;&gt;Managing Shared Data Science Code With Git Submodules&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Learn how to manage shared code across projects using Git submodules. Prevent version drift, maintain reproducible workflows, and support team collaboration with practical examples.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16057/feed&quot; target=&quot;_blank&quot;&gt;CODECUT.AI&lt;/a&gt; • Shared by &lt;a href=&quot;https://pycoders.com/link/16077/feed&quot; target=&quot;_blank&quot;&gt;Khuyen Tran&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16071/feed&quot; target=&quot;_blank&quot;&gt;Datastar: Modern Web Dev, Simplified&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Talk Python interviews Delaney Gillilan, Ben Croker, and Chris May about the Datastar framework, a library that combines the concepts of HTMX, Alpine, and more.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16071/feed&quot; target=&quot;_blank&quot;&gt;TALK PYTHON&lt;/a&gt;&lt;/span&gt; &lt;span&gt;podcast&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16065/feed&quot; target=&quot;_blank&quot;&gt;Introducing the Zen of DevOps&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Inspired by the Zen of Python, Tibo has written a Zen of DevOps, applying similar ideas from your favorite language to the world of servers and deployment.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16065/feed&quot; target=&quot;_blank&quot;&gt;TIBO BEIJEN&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16054/feed&quot; target=&quot;_blank&quot;&gt;Stop Using Pickle Already. Seriously, Stop It!&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; Python&amp;rsquo;s Pickle is insecure by design, so using it in public facing code is highly problematic. This article explains why and suggests alternatives.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16054/feed&quot; target=&quot;_blank&quot;&gt;MICHAL NAZAREWICZ&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16070/feed&quot; target=&quot;_blank&quot;&gt;Raw+DC: The ORM Pattern of 2026?&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; After 25+ years championing ORMs, Michael has switched to raw database queries paired with Python dataclasses. This post explains why.&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16070/feed&quot; target=&quot;_blank&quot;&gt;MICHAEL KENNEDY&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;h2&gt;Projects &amp;amp; Code&lt;/h2&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16080/feed&quot; target=&quot;_blank&quot;&gt;InvenTree: OSS Inventory Management System&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16080/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/INVENTREE&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16067/feed&quot; target=&quot;_blank&quot;&gt;marimo-jupyter-extension: Integrate Marimo Into JupyterLab&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16067/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/MARIMO-TEAM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16075/feed&quot; target=&quot;_blank&quot;&gt;py2many: Transpiler of Python to Many Other Languages&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16075/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/PY2MANY&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16068/feed&quot; target=&quot;_blank&quot;&gt;ptapplot: Make Pressure Tap Plots&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16068/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/PAULENORMAN&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16055/feed&quot; target=&quot;_blank&quot;&gt;django-bolt: Rust-Powered API Framework for Django&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16055/feed&quot; target=&quot;_blank&quot;&gt;GITHUB.COM/FARHANALIRAZA&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;h2&gt;Events&lt;/h2&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16060/feed&quot; target=&quot;_blank&quot;&gt;Weekly Real Python Office Hours Q&amp;amp;A (Virtual)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; March 4, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16060/feed&quot; target=&quot;_blank&quot;&gt;REALPYTHON.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16052/feed&quot; target=&quot;_blank&quot;&gt;Python Unplugged on PyTV&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; March 4 to March 5, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16052/feed&quot; target=&quot;_blank&quot;&gt;JETBRAINS.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16063/feed&quot; target=&quot;_blank&quot;&gt;Canberra Python Meetup&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; March 5, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16063/feed&quot; target=&quot;_blank&quot;&gt;MEETUP.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16069/feed&quot; target=&quot;_blank&quot;&gt;Sydney Python User Group (SyPy)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; March 5, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16069/feed&quot; target=&quot;_blank&quot;&gt;SYPY.ORG&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16062/feed&quot; target=&quot;_blank&quot;&gt;PyDelhi User Group Meetup&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; March 7, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16062/feed&quot; target=&quot;_blank&quot;&gt;MEETUP.COM&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;div&gt; &lt;h3&gt;&lt;a href=&quot;https://pycoders.com/link/16073/feed&quot; target=&quot;_blank&quot;&gt;PyConf Hyderabad 2026&lt;/a&gt;&lt;/h3&gt; &lt;p&gt; March 14 to March 16, 2026&lt;br /&gt; &lt;span&gt;&lt;a href=&quot;https://pycoders.com/link/16073/feed&quot; target=&quot;_blank&quot;&gt;PYCONFHYD.ORG&lt;/a&gt;&lt;/span&gt; &lt;/p&gt; &lt;/div&gt; &lt;hr /&gt; &lt;p&gt;Happy Pythoning!&lt;br /&gt;This was PyCoder&amp;rsquo;s Weekly Issue #724.&lt;br /&gt;&lt;a href=&quot;https://pycoders.com/issues/724/feed&quot;&gt;View in Browser »&lt;/a&gt;&lt;/p&gt; &lt;img src=&quot;https://pycoders.com/issues/724/open/feed&quot; width=&quot;1&quot; height=&quot;1&quot; alt=&quot;alt&quot; /&gt; 
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Subscribe to 🐍 PyCoder&amp;rsquo;s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week &lt;a href=&quot;https://pycoders.com/?utm_source=pycoders&amp;utm_medium=feed&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-03T19:30:00+00:00</dc:date>
</item>
<item rdf:about="https://mathspp.com/blog/til/install-jupyter-with-uv">
	<title>Rodrigo Girão Serrão: TIL #140 – Install Jupyter with uv</title>
	<link>https://mathspp.com/blog/til/install-jupyter-with-uv</link>
	<content:encoded>&lt;img alt=&quot;&quot; src=&quot;https://mathspp.com/images/4/7/2/5/1/472518cf58596ba2575cbf434bdb1ffdb9d6d3d6-thumbnail.webp&quot; /&gt;
                                &lt;p&gt;Today I learned how to install jupyter properly while using uv to manage tools.&lt;/p&gt;

&lt;h2 id=&quot;running-a-jupyter-notebook-server-or-jupyter-lab&quot;&gt;Running a Jupyter notebook server or Jupyter lab&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#running-a-jupyter-notebook-server-or-jupyter-lab&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To run a Jupyter notebook server with uv, you can run the command&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ uvx jupyter notebook&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly, if you want to run Jupyter lab, you can run&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ uvx jupyter lab&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both work, but uv will kindly present a message explaining how it's actually doing you a favour, because it &lt;em&gt;guessed&lt;/em&gt; what you wanted.
That's because &lt;code&gt;uvx something&lt;/code&gt; usually looks for a package named “something” with a command called “something”.&lt;/p&gt;
&lt;p&gt;As it turns out, the command &lt;code&gt;jupyter&lt;/code&gt; comes from the package &lt;code&gt;jupyter-core&lt;/code&gt;, not from the package &lt;code&gt;jupyter&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;installing-jupyter&quot;&gt;Installing Jupyter&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#installing-jupyter&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you're running Jupyter notebooks often, you can install the notebook server and Jupyter lab with&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ uv tool install --with jupyter jupyter-core&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;why-uv-tool-install-jupyter-fails&quot;&gt;Why &lt;code&gt;uv tool install jupyter&lt;/code&gt; fails&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#why-uv-tool-install-jupyter-fails&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Running &lt;code&gt;uv tool install jupyter&lt;/code&gt; fails because the package &lt;code&gt;jupyter&lt;/code&gt; doesn't provide any commands by itself.&lt;/p&gt;
&lt;h3 id=&quot;why-uv-tool-install-jupyter-core-doesn-t-work&quot;&gt;Why &lt;code&gt;uv tool install jupyter-core&lt;/code&gt; doesn't work&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#why-uv-tool-install-jupyter-core-doesn-t-work&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The command &lt;code&gt;uv tool install jupyter-core&lt;/code&gt; looks like it works because it installs the command &lt;code&gt;jupyter&lt;/code&gt; correctly.
However, if you use &lt;code&gt;--help&lt;/code&gt; you can see that you don't have access to the subcommands you need:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ uv tool install jupyter-core
...
Installed 3 executables: jupyter, jupyter-migrate, jupyter-troubleshoot
$ jupyter --help
...
Available subcommands: book migrate troubleshoot&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's because the subcommands &lt;code&gt;notebook&lt;/code&gt; and &lt;code&gt;lab&lt;/code&gt; are from the package &lt;code&gt;jupyter&lt;/code&gt;.
The solution?
Install &lt;code&gt;jupyter-core&lt;/code&gt; &lt;em&gt;with&lt;/em&gt; the additional dependency &lt;code&gt;jupyter&lt;/code&gt;, which is what the command &lt;code&gt;uv tool install --with jupyter jupyter-core&lt;/code&gt; does.&lt;/p&gt;
&lt;h2 id=&quot;other-usages-of-jupyter&quot;&gt;Other usages of Jupyter&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#other-usages-of-jupyter&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The uv documentation has a &lt;a href=&quot;https://docs.astral.sh/uv/guides/integration/jupyter/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot; class=&quot;external-link no-image&quot;&gt;page dedicated exclusively to the usage of uv with Jupyter&lt;/a&gt;, so check it out for other use cases of the uv and Jupyter combo!&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-03T15:16:00+00:00</dc:date>
</item>
<item rdf:about="https://www.djangoproject.com/weblog/2026/mar/03/security-releases/">
	<title>Django Weblog: Django security releases issued: 6.0.3, 5.2.12, and 4.2.29</title>
	<link>https://www.djangoproject.com/weblog/2026/mar/03/security-releases/</link>
	<content:encoded>&lt;p&gt;In accordance with &lt;a class=&quot;reference external&quot; href=&quot;https://docs.djangoproject.com/en/stable/internals/security/&quot;&gt;our security release policy&lt;/a&gt;, the Django team
is issuing releases for
&lt;a class=&quot;reference external&quot; href=&quot;https://docs.djangoproject.com/en/stable/releases/6.0.3/&quot;&gt;Django 6.0.3&lt;/a&gt;,
&lt;a class=&quot;reference external&quot; href=&quot;https://docs.djangoproject.com/en/stable/releases/5.2.12/&quot;&gt;Django 5.2.12&lt;/a&gt;, and
&lt;a class=&quot;reference external&quot; href=&quot;https://docs.djangoproject.com/en/stable/releases/4.2.29/&quot;&gt;Django 4.2.29&lt;/a&gt;.
These releases address the security issues detailed below. We encourage all
users of Django to upgrade as soon as possible.&lt;/p&gt;
&lt;div class=&quot;section&quot; id=&quot;s-cve-2026-25673-potential-denial-of-service-vulnerability-in-urlfield-via-unicode-normalization-on-windows&quot;&gt;
&lt;h3&gt;CVE-2026-25673: Potential denial-of-service vulnerability in &lt;tt class=&quot;docutils literal&quot;&gt;URLField&lt;/tt&gt; via Unicode normalization on Windows&lt;/h3&gt;
&lt;p&gt;The &lt;tt class=&quot;docutils literal&quot;&gt;django.forms.URLField&lt;/tt&gt; form field's &lt;tt class=&quot;docutils literal&quot;&gt;to_python()&lt;/tt&gt; method used
&lt;tt class=&quot;docutils literal&quot;&gt;urllib.parse.urlsplit()&lt;/tt&gt; to determine whether to prepend a URL scheme to
the submitted value. On Windows, &lt;tt class=&quot;docutils literal&quot;&gt;urlsplit()&lt;/tt&gt; performs
NFKC normalization (&lt;tt class=&quot;docutils literal&quot;&gt;unicodedata.normalize&lt;/tt&gt;), which can be
disproportionately slow for large inputs containing certain characters.&lt;/p&gt;
&lt;p&gt;&lt;tt class=&quot;docutils literal&quot;&gt;URLField.to_python()&lt;/tt&gt; now uses a simplified scheme detection, avoiding
Unicode normalization entirely and deferring URL validation to the appropriate
layers. As a result, while leading and trailing whitespace is still stripped by
default, characters such as newlines, tabs, and other control characters within
the value are no longer handled by &lt;tt class=&quot;docutils literal&quot;&gt;URLField.to_python()&lt;/tt&gt;. When using the
default &lt;tt class=&quot;docutils literal&quot;&gt;URLValidator&lt;/tt&gt;, these values will continue to raise &lt;tt class=&quot;docutils literal&quot;&gt;ValidationError&lt;/tt&gt;
during validation, but if you rely on custom validators, ensure they do not
depend on the previous behavior of &lt;tt class=&quot;docutils literal&quot;&gt;URLField.to_python()&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This issue has severity &amp;quot;moderate&amp;quot; according to the Django Security Policy.&lt;/p&gt;
&lt;p&gt;Thanks to Seokchan Yoon for the report.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;section&quot; id=&quot;s-cve-2026-25674-potential-incorrect-permissions-on-newly-created-file-system-objects&quot;&gt;
&lt;h3&gt;CVE-2026-25674: Potential incorrect permissions on newly created file system objects&lt;/h3&gt;
&lt;p&gt;Django's file-system storage and file-based cache backends used the process
&lt;tt class=&quot;docutils literal&quot;&gt;umask&lt;/tt&gt; to control permissions when creating directories. In multi-threaded
environments, one thread's temporary umask change can affect other threads'
file and directory creation, resulting in file system objects being created
with unintended permissions.&lt;/p&gt;
&lt;p&gt;Django now applies the requested permissions via &lt;tt class=&quot;docutils literal&quot;&gt;os.chmod()&lt;/tt&gt; after
&lt;tt class=&quot;docutils literal&quot;&gt;os.mkdir()&lt;/tt&gt;, removing the dependency on the process-wide umask.&lt;/p&gt;
&lt;p&gt;This issue has severity &amp;quot;low&amp;quot; according to the Django Security Policy.&lt;/p&gt;
&lt;p&gt;Thanks to Tarek Nakkouch for the report.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;section&quot; id=&quot;s-affected-supported-versions&quot;&gt;
&lt;h3&gt;Affected supported versions&lt;/h3&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;Django main&lt;/li&gt;
&lt;li&gt;Django 6.0&lt;/li&gt;
&lt;li&gt;Django 5.2&lt;/li&gt;
&lt;li&gt;Django 4.2&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;section&quot; id=&quot;s-resolution&quot;&gt;
&lt;h3&gt;Resolution&lt;/h3&gt;
&lt;p&gt;Patches to resolve the issue have been applied to Django's
main, 6.0, 5.2, and 4.2 branches.
The patches may be obtained from the following changesets.&lt;/p&gt;
&lt;div class=&quot;section&quot; id=&quot;s-cve-2026-25673-potential-denial-of-service-vulnerability-in-urlfield-via-unicode-normalization-on-windows-1&quot;&gt;
&lt;h4&gt;CVE-2026-25673: Potential denial-of-service vulnerability in &lt;tt class=&quot;docutils literal&quot;&gt;URLField&lt;/tt&gt; via Unicode normalization on Windows&lt;/h4&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/951ffb3832cd83ba672c1e3deae2bda128eb9cca&quot;&gt;main branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/b1444d9acf43db9de96e0da2b4737ad56af0eb76&quot;&gt;6.0 branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/4d3c184686626d224d9a87451410ecf802b41f7c&quot;&gt;5.2 branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/b3e8ec8cc310489fe80174b14b11edb970d682ea&quot;&gt;4.2 branch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;section&quot; id=&quot;s-cve-2026-25674-potential-incorrect-permissions-on-newly-created-file-system-objects-1&quot;&gt;
&lt;h4&gt;CVE-2026-25674: Potential incorrect permissions on newly created file system objects&lt;/h4&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/019e44f67a8dace67b786e2818938c8691132988&quot;&gt;main branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/264d5c70ef3281a8869cb2ad45a3a52d5adbe790&quot;&gt;6.0 branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/b07ed2a1e445efde54fc64cb8c37e0f4f7fe53e5&quot;&gt;5.2 branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;On the &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/django/django/commit/54b50bf7d6dcbf02d4c01f853627cc9299d4934d&quot;&gt;4.2 branch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;section&quot; id=&quot;s-the-following-releases-have-been-issued&quot;&gt;
&lt;h3&gt;The following releases have been issued&lt;/h3&gt;
&lt;ul class=&quot;simple&quot;&gt;
&lt;li&gt;Django 6.0.3 (&lt;a class=&quot;reference external&quot; href=&quot;https://www.djangoproject.com/download/6.0.3/tarball/&quot;&gt;download Django 6.0.3&lt;/a&gt; |
&lt;a class=&quot;reference external&quot; href=&quot;https://www.djangoproject.com/download/6.0.3/checksum/&quot;&gt;6.0.3 checksums&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Django 5.2.12 (&lt;a class=&quot;reference external&quot; href=&quot;https://www.djangoproject.com/download/5.2.12/tarball/&quot;&gt;download Django 5.2.12&lt;/a&gt; |
&lt;a class=&quot;reference external&quot; href=&quot;https://www.djangoproject.com/download/5.2.12/checksum/&quot;&gt;5.2.12 checksums&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Django 4.2.29 (&lt;a class=&quot;reference external&quot; href=&quot;https://www.djangoproject.com/download/4.2.29/tarball/&quot;&gt;download Django 4.2.29&lt;/a&gt; |
&lt;a class=&quot;reference external&quot; href=&quot;https://www.djangoproject.com/download/4.2.29/checksum/&quot;&gt;4.2.29 checksums&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The PGP key ID used for this release is Natalia Bidart: &lt;a class=&quot;reference external&quot; href=&quot;https://github.com/nessita.gpg&quot;&gt;2EE82A8D9470983E&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;section&quot; id=&quot;s-general-notes-regarding-security-reporting&quot;&gt;
&lt;h3&gt;General notes regarding security reporting&lt;/h3&gt;
&lt;p&gt;As always, we ask that potential security issues be reported via private email
to &lt;tt class=&quot;docutils literal&quot;&gt;security&amp;#64;djangoproject.com&lt;/tt&gt;, and not via Django's Trac instance, nor via
the Django Forum. Please see &lt;a class=&quot;reference external&quot; href=&quot;https://www.djangoproject.com/security/&quot;&gt;our security policies&lt;/a&gt; for further information.&lt;/p&gt;
&lt;/div&gt;</content:encoded>
	<dc:date>2026-03-03T14:00:00+00:00</dc:date>
</item>
<item rdf:about="https://realpython.com/courses/init-py/">
	<title>Real Python: What Does Python's __init__.py Do?</title>
	<link>https://realpython.com/courses/init-py/</link>
	<content:encoded>&lt;p&gt;Python&amp;rsquo;s special &lt;code&gt;__init__.py&lt;/code&gt; file marks a directory as a regular Python package and allows you to import its modules. This file runs automatically the first time you import its containing package. You can use it to initialize package-level variables, define functions or classes, and structure the package&amp;rsquo;s namespace clearly for users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this video course, you&amp;rsquo;ll understand that:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A directory &lt;strong&gt;without an &lt;code&gt;__init__.py&lt;/code&gt; file&lt;/strong&gt; becomes a &lt;strong&gt;namespace package&lt;/strong&gt;, which behaves differently from a regular package and may cause slower imports.&lt;/li&gt;
&lt;li&gt;You can use &lt;code&gt;__init__.py&lt;/code&gt; to explicitly define a package&amp;rsquo;s &lt;strong&gt;public API&lt;/strong&gt; by importing specific modules or functions into the package namespace.&lt;/li&gt;
&lt;li&gt;The Python convention of using &lt;strong&gt;leading underscores&lt;/strong&gt; helps indicate to users which objects are intended as &lt;strong&gt;non-public&lt;/strong&gt;, although this convention can still be bypassed.&lt;/li&gt;
&lt;li&gt;Code inside &lt;code&gt;__init__.py&lt;/code&gt; runs &lt;strong&gt;only once&lt;/strong&gt; during the first import, even if you run the import statement multiple times.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding how to effectively use &lt;code&gt;__init__.py&lt;/code&gt; helps you structure your Python packages in a clear, maintainable way, improving usability and namespace management.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-03T14:00:00+00:00</dc:date>
</item>
<item rdf:about="https://realpython.com/quizzes/duck-typing-python/">
	<title>Real Python: Quiz: Duck Typing in Python: Writing Flexible and Decoupled Code</title>
	<link>https://realpython.com/quizzes/duck-typing-python/</link>
	<content:encoded>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of
&lt;a href=&quot;https://realpython.com/duck-typing-python/&quot;&gt;Duck Typing in Python: Writing Flexible and Decoupled Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By working through this quiz, you&amp;rsquo;ll revisit what duck typing is and its pros and cons, how Python uses behavior-based interfaces, how protocols and special methods support it, and what alternatives you can use in Python.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-03T12:00:00+00:00</dc:date>
</item>
<item rdf:about="https://pybit.es/articles/why-building-a-production-rag-pipeline-is-easier-than-you-think/">
	<title>PyBites: Why Building a Production RAG Pipeline is Easier Than You Think</title>
	<link>https://pybit.es/articles/why-building-a-production-rag-pipeline-is-easier-than-you-think/</link>
	<content:encoded>&lt;div id=&quot;buzzsprout-player-18698592&quot;&gt;&lt;/div&gt;



&lt;p class=&quot;&quot;&gt;&lt;strong&gt;Adding AI to legacy code doesn&amp;#8217;t have to be a challenge.&lt;/strong&gt;&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;Many devs are hearing this right now: &lt;em&gt;“We need to add AI to the app.”&lt;/em&gt; &lt;/p&gt;



&lt;p class=&quot;&quot;&gt;And for many of them, panic ensues. &lt;/p&gt;



&lt;p class=&quot;&quot;&gt;The assumption is that you have to rip your existing architecture down to its foundation. You start having nightmares about standing up complex microservices, massive AWS bills, and spending six months learning the intricate math behind vector embeddings.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;It feels like a monumental risk to your stable, production-ready codebase, right?&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;Here&amp;#8217;s the current reality though: adding AI to an existing application doesn&amp;#8217;t actually require a massive rewrite. &lt;/p&gt;



&lt;p class=&quot;&quot;&gt;If you have solid software design fundamentals, integrating a Retrieval-Augmented Generation (RAG) pipeline is entirely within your reach.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;Here&amp;#8217;s how you do it without breaking everything you&amp;#8217;ve already built.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;&lt;strong&gt;Get the Python Stack to do the Heavy Lifting&lt;/strong&gt;&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;You don&amp;#8217;t need to build your AI pipeline from scratch. The Python ecosystem has matured to the point where the hardest parts of a RAG pipeline are already solved for you.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li class=&quot;&quot;&gt;Need to parse massive PDFs? Libraries like &lt;code&gt;docling&lt;/code&gt; handle it practically out of the box. &lt;/li&gt;



&lt;li class=&quot;&quot;&gt;Need to convert text into embeddings and store them? You let the LLM provider handle the embedding math, and you drop the results into a vector database. &lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;&quot;&gt;It&amp;#8217;s not a huge technical challenge in Python. It is just an orchestration of existing, powerful tools.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;&lt;strong&gt;Stop Coding and Start &lt;em&gt;Orchestrating&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;



&lt;p id=&quot;p-rc_44498ea0822ca039-39&quot; class=&quot;&quot;&gt;When a developer builds a RAG pipeline and the AI starts hallucinating, their first instinct is to dive into the code&lt;sup&gt;&lt;/sup&gt;&lt;sup&gt;&lt;/sup&gt;&lt;sup&gt;&lt;/sup&gt;&lt;sup&gt;&lt;/sup&gt;. They try to fix the API calls or mess with the vector search logic.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;Take a step back. The code usually isn&amp;#8217;t the problem.&lt;/p&gt;



&lt;p id=&quot;p-rc_44498ea0822ca039-40&quot; class=&quot;&quot;&gt;The system prompt is the conductor of your entire RAG pipeline. It dictates how the LLM interacts with your vector database. If you&amp;#8217;re getting bad results, you don&amp;#8217;t need to rewrite your Python logic &amp;#8211; you need to refine your prompt through trial and error to strict, data-grounded constraints.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;&lt;strong&gt;Beat Infrastructure Constraints by Offloading&lt;/strong&gt;&lt;/p&gt;



&lt;p id=&quot;p-rc_44498ea0822ca039-41&quot; class=&quot;&quot;&gt;What if your app is hosted on something lightweight, like Heroku, with strict size and memory limits? You might think you need to containerise everything and migrate to a heavier cloud setup.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;Nope! You just need to separate your concerns.&lt;/p&gt;



&lt;p id=&quot;p-rc_44498ea0822ca039-42&quot; class=&quot;&quot;&gt;Indexing documents and generating embeddings is heavy work. Querying is light. Offload the heavy lifting (like storing and searching vectors) entirely to your vector database service (like &lt;em&gt;Weaviate&lt;/em&gt;). This keeps your core app lightweight, so it only acts as the middleman routing the query.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;&lt;strong&gt;We Broke Down Exactly How This Works With Tim Gallati&lt;/strong&gt;&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;We explored the reality of this architecture with Tim Gallati and Pybites AI coach Juanjo on the podcast. Tim had an existing app, &lt;em&gt;Quiet Links&lt;/em&gt;, running on Heroku. &lt;/p&gt;



&lt;p class=&quot;&quot;&gt;In just six weeks with us, he integrated a massive, production-ready RAG pipeline into it, without breaking his existing user experience.&lt;/p&gt;



&lt;p class=&quot;&quot;&gt;If you want to hear the full breakdown of how they architected this integration, listen to the episode using the player above, or at the following links:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=e5XDcFuF1oc&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;YouTube&lt;/a&gt;&lt;/li&gt;



&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://open.spotify.com/episode/4wRqFLQTFmdlA2VaQ00qvj?si=2d1f40b88a4d4b34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Spotify&lt;/a&gt;&lt;/li&gt;



&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://podcasts.apple.com/au/podcast/217-revisiting-quiet-links-with-tim-gallati/id1545551340?i=1000751354767&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;Apple Podcasts&lt;/a&gt;&lt;/li&gt;



&lt;li class=&quot;&quot;&gt;&lt;a href=&quot;https://www.pybitespodcast.com/1501156/episodes/18698592-217-building-a-production-rag-pipeline-is-easier-than-you-think-with-tim-gallati&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;Other&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p class=&quot;&quot;&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-03T09:06:04+00:00</dc:date>
</item>
<item rdf:about="https://www.pythonmorsels.com/when-are-classes-used/">
	<title>Python Morsels: When are classes used in Python?</title>
	<link>https://www.pythonmorsels.com/when-are-classes-used/</link>
	<content:encoded>&lt;p&gt;While you don't often need to make your own classes in Python, they can sometimes make your code reusable and easier to read.&lt;/p&gt;


&lt;div&gt;
  
    &lt;a href=&quot;https://www.pythonmorsels.com/when-are-classes-used/&quot;&gt;&lt;img width=&quot;480&quot; height=&quot;270&quot; src=&quot;https://i.vimeocdn.com/filter/overlay?src0=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F2128583271-ee4c537824962cb8f1e06869b94a6b0dfbea70705a0101448a8638da82a34f9d-d_1920x1080%3F%26region%3Dus&amp;src1=http%3A%2F%2Ff.vimeocdn.com%2Fp%2Fimages%2Fcrawler_play.png&quot; /&gt;&lt;/a&gt;
  
  &lt;p&gt;
  &lt;strong&gt;Table of contents&lt;/strong&gt;
  &lt;ol&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/when-are-classes-used/#using-classes-because-a-framework-requires-it&quot; target=&quot;_blank&quot;&gt;Using classes because a framework requires it&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/when-are-classes-used/#passing-the-same-data-into-multiple-functions&quot; target=&quot;_blank&quot;&gt;Passing the same data into multiple functions&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/when-are-classes-used/#creating-a-custom-class&quot; target=&quot;_blank&quot;&gt;Creating a custom class&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/when-are-classes-used/#using-a-class-to-convey-the-purpose-of-data&quot; target=&quot;_blank&quot;&gt;Using a class to convey the purpose of data&lt;/a&gt;&lt;/li&gt;
  
  &lt;li&gt;&lt;a href=&quot;https://www.pythonmorsels.com/when-are-classes-used/#using-classes-to-improve-readability&quot; target=&quot;_blank&quot;&gt;Using classes to improve readability&lt;/a&gt;&lt;/li&gt;
  
  &lt;/ol&gt;
  &lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
  
    &lt;h2&gt;Using classes because a framework requires it&lt;/h2&gt;
    
      
        &lt;p&gt;Unlike many programming languages, you can accomplish quite a bit in Python without ever making a class.&lt;/p&gt;

      
        &lt;p&gt;So when are classes typically used?&lt;/p&gt;

      
        &lt;p&gt;The most common time to use a class in Python is when &lt;strong&gt;using a library or framework that &lt;em&gt;requires&lt;/em&gt; writing a class&lt;/strong&gt;.&lt;/p&gt;

      
        &lt;p&gt;For example, users of the &lt;a href=&quot;https://www.djangoproject.com&quot; target=&quot;_blank&quot;&gt;Django&lt;/a&gt; web framework need to make a class to allow Django to manage their database tables:&lt;/p&gt;

      
        &lt;div class=&quot;codehilite&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;price&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DecimalField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_digits&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decimal_places&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      
        &lt;p&gt;With Django, each &lt;code&gt;Model&lt;/code&gt; class is used for managing one table in the database.
Each instance of every model represents one row in each database table:&lt;/p&gt;

      
        &lt;div class=&quot;codehilite&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;products.models&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Product: rubber duck&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      
        &lt;p&gt;Many Python programmers create their first class because a library requires them to make one.&lt;/p&gt;

      
    
  
    &lt;h2&gt;Passing the same data into multiple functions&lt;/h2&gt;
    
      &lt;p&gt;But sometimes you may choose …&lt;/p&gt;
    
  
&lt;/div&gt;
&lt;h3&gt;&lt;a href=&quot;https://www.pythonmorsels.com/when-are-classes-used/&quot; target=&quot;_blank&quot;&gt;Read the full article: https://www.pythonmorsels.com/when-are-classes-used/&lt;/a&gt;&lt;/h3&gt;</content:encoded>
	<dc:date>2026-03-02T23:30:00+00:00</dc:date>
</item>
<item rdf:about="https://snarky.ca/state-of-wasi-support-for-cpython-march-2026/">
	<title>Brett Cannon: State of WASI support for CPython: March 2026</title>
	<link>https://snarky.ca/state-of-wasi-support-for-cpython-march-2026/</link>
	<content:encoded>&lt;p&gt;It&amp;amp;aposs been a while since I posted &lt;a href=&quot;https://snarky.ca/state-of-wasi-support-for-cpython-march-2024/&quot;&gt;about WASI support in CPython&lt;/a&gt;! &amp;#x1F605; Up until now, most of the work I have been doing around WASI has been making its maintenance easier for me and other core developers. For instance, the &lt;a href=&quot;https://github.com/python/cpython-devcontainers/&quot;&gt;cpython-devcontainer repo&lt;/a&gt; now provides a &lt;a href=&quot;https://github.com/python/cpython-devcontainers/pkgs/container/wasicontainer&quot;&gt;WASI dev container&lt;/a&gt; so people don&amp;amp;apost have to install the WASI SDK to be productive (e.g. there&amp;amp;aposs a &lt;a href=&quot;https://github.com/python/cpython/tree/main/.devcontainer/wasi&quot;&gt;WASI codespace&lt;/a&gt; now so you can work on WASI entirely from your browser without installing anything). All this work around making development easier not only led to having &lt;a href=&quot;https://devguide.python.org/getting-started/setup-building/#wasi&quot;&gt;WASI instructions in the devguide&lt;/a&gt; and the creation of a &lt;a href=&quot;https://github.com/python/cpython/tree/main/Platforms/WASI&quot;&gt;CLI app&lt;/a&gt;, but also a large expansion of &lt;a href=&quot;https://devguide.python.org/getting-started/setup-building/#using-a-container&quot;&gt;how to use containers to do CPython development&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;But the main reason I&amp;amp;aposm blogging today is that &lt;a href=&quot;https://peps.python.org/pep-0816/&quot;&gt;PEP 816&lt;/a&gt; was accepted! That PEP defines how WASI compatibility will be handled starting with Python 3.15. The key point is that once the first beta for a Python version is reached, the &lt;a href=&quot;https://wasi.dev&quot;&gt;WASI&lt;/a&gt; and &lt;a href=&quot;https://github.com/WebAssembly/wasi-sdk&quot;&gt;WASI SDK&lt;/a&gt; versions that will be supported for the life of that Python version will be locked down. That gives the community a target to build packages for since things built with the WASI SDK are not forwards- or backwards-compatible for linking purposes due to &lt;a href=&quot;https://github.com/WebAssembly/wasi-libc&quot;&gt;wasi-libc&lt;/a&gt; not having any compatibility guarantees.&lt;/p&gt;&lt;p&gt;With that PEP out of the way, the next big items on my &lt;a href=&quot;https://opensource.snarky.ca/Python/WASI/Plans&quot;&gt;WASI todo list&lt;/a&gt; are (in rough order):&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Implement a subcommand to bundle up a WASI build for &lt;a href=&quot;https://github.com/brettcannon/cpython-wasi-build&quot;&gt;distribution&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Write a PEP to &lt;a href=&quot;https://bytecodealliance.zulipchat.com/#narrow/channel/219900-wasi/topic/Platform.20tags.20for.20packages.20targeting.20WASI/&quot;&gt;define a platform tag for wheels&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Implement a subcommand to build &lt;a href=&quot;https://github.com/python/cpython-source-deps&quot;&gt;dependencies for CPython&lt;/a&gt; (e.g. zlib)&lt;/li&gt;&lt;li&gt;Turn on socket support (which requires WASI 0.3 and threading support to be released as I&amp;amp;aposm skipping over WASI 0.2)&lt;/li&gt;&lt;/ol&gt;</content:encoded>
	<dc:date>2026-03-02T19:28:41+00:00</dc:date>
</item>
<item rdf:about="https://mathspp.com/blog/til/multiline-input-in-the-repl">
	<title>Rodrigo Girão Serrão: TIL #139 – Multiline input in the REPL</title>
	<link>https://mathspp.com/blog/til/multiline-input-in-the-repl</link>
	<content:encoded>&lt;img alt=&quot;&quot; src=&quot;https://mathspp.com/images/6/0/8/e/f/608ef20c21ef51b1b926219c3bac4dd78c9eae8c-thumbnail.webp&quot; /&gt;
                                &lt;p&gt;Today I learned how to do multiline input in the REPL using an uncommon combination of arguments for the built-in &lt;code&gt;open&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A while ago &lt;a href=&quot;https://mathspp.com/blog/til/020&quot;&gt;I learned I could use &lt;code&gt;open(0)&lt;/code&gt; to open standard input&lt;/a&gt;.
This unlocks a neat trick that allows you to do multiline input in the REPL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-pycon&quot;&gt;&amp;gt;&amp;gt;&amp;gt; msg = open(0).read()
Hello,
world!
^D
&amp;gt;&amp;gt;&amp;gt; msg
'Hello,\nworld!\n'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The cryptic &lt;code&gt;^D&lt;/code&gt; is &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;D&lt;/kbd&gt;, which means EOF on Unix systems.
If you're on Windows, use &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Z&lt;/kbd&gt;.&lt;/p&gt;
&lt;p&gt;The problem is that if you try to use &lt;code&gt;open(0).read()&lt;/code&gt; again to read more multiline input, you get an exception:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;OSError: [Errno 9] Bad file descriptor&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's because, when you finished reading the first time around, Python closed the file descriptor &lt;code&gt;0&lt;/code&gt;, so you can no longer use it.&lt;/p&gt;
&lt;p&gt;The fix is to set &lt;code&gt;closefd=False&lt;/code&gt; when you use the built-in &lt;code&gt;open&lt;/code&gt;.
With the parameter &lt;code&gt;closefd&lt;/code&gt; set to &lt;code&gt;False&lt;/code&gt;, the underlying file descriptor isn't closed and you can reuse it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-pycon&quot;&gt;&amp;gt;&amp;gt;&amp;gt; msg1 = open(0, closefd=False).read()
Hello,
world!
^D
&amp;gt;&amp;gt;&amp;gt; msg1
'Hello,\nworld!\n'

&amp;gt;&amp;gt;&amp;gt; msg2 = open(0, closefd=False).read()
Goodbye,
world!
^D
&amp;gt;&amp;gt;&amp;gt; msg2
'Goodbye,\nworld!\n'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using &lt;code&gt;open(0, closefd=False)&lt;/code&gt;, you can read multiline input in the REPL &lt;em&gt;repeatedly&lt;/em&gt;.&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-02T14:15:00+00:00</dc:date>
</item>
<item rdf:about="https://realpython.com/ydata-profiling-eda/">
	<title>Real Python: Automate Python Data Analysis With YData Profiling</title>
	<link>https://realpython.com/ydata-profiling-eda/</link>
	<content:encoded>&lt;div&gt;&lt;p&gt;The YData Profiling package generates an exploratory data analysis (EDA) report with a few lines of code. The report provides dataset and column-level analysis, including plots and summary statistics to help you quickly understand your dataset. These reports can be exported to HTML or JSON so you can share them with other stakeholders.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this tutorial, you’ll understand that:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;YData Profiling generates interactive reports&lt;/strong&gt; containing EDA results, including summary statistics, visualizations, correlation matrices, and data quality warnings from DataFrames.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ProfileReport&lt;/code&gt; creates a profile&lt;/strong&gt; you can save with &lt;code&gt;.to_file()&lt;/code&gt; for HTML or JSON export, or display inline with &lt;code&gt;.to_notebook_iframe()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Setting &lt;code&gt;tsmode=True&lt;/code&gt; and specifying a date column with &lt;code&gt;sortby&lt;/code&gt; &lt;strong&gt;enables time series analysis&lt;/strong&gt;, including stationarity tests and seasonality detection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &lt;code&gt;.compare()&lt;/code&gt; method generates side-by-side reports&lt;/strong&gt; highlighting distribution shifts and statistical differences between datasets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get the most out of this tutorial, you’ll benefit from having knowledge of &lt;a href=&quot;https://realpython.com/learning-paths/pandas-data-science/&quot;&gt;pandas&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The examples in this tutorial were tested using Python &lt;a href=&quot;https://realpython.com/python313-new-features/&quot;&gt;3.13&lt;/a&gt;. Additionally, you may need to install &lt;code&gt;setuptools&amp;lt;81&lt;/code&gt; for backward compatibility.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can install this package using &lt;a href=&quot;https://realpython.com/ref/glossary/pip/&quot; class=&quot;ref-link&quot;&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--yellow&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;Shell&lt;/span&gt;
    
    &lt;div class=&quot;noselect&quot;&gt;
      
        &lt;span class=&quot;codeblock__output-toggle&quot; title=&quot;Toggle prompts and output&quot; tabindex=&quot;0&quot;&gt;&lt;span class=&quot;icon baseline js-codeblock-output-on codeblock__header--icon-lower&quot;&gt;&lt;/span&gt;&lt;/span&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;$ &lt;/span&gt;python&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;-m&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;pip&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;install&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;ydata-profiling
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;Once installed, you’re ready to transform any &lt;a href=&quot;https://realpython.com/pandas-dataframe/&quot;&gt;pandas DataFrame&lt;/a&gt; into an interactive report. To follow along, download the example dataset you’ll work with by clicking the link below:&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot;&gt;
&lt;p&gt;&lt;strong&gt;Get Your Code:&lt;/strong&gt; &lt;a href=&quot;https://realpython.com/bonus/ydata-profiling-eda-code/&quot; class=&quot;alert-link&quot;&gt;Click here to download the free sample code&lt;/a&gt; and start automating Python data analysis with YData Profiling.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The following example generates a profiling report from the 2024 flight delay dataset and saves it to disk:&lt;/p&gt;

  &lt;div class=&quot;codeblock__header codeblock--blue&quot;&gt;
    &lt;span class=&quot;mr-2 noselect&quot;&gt;Python&lt;/span&gt;
    &lt;span class=&quot;mr-2&quot;&gt;&lt;code&gt;flight_report.py&lt;/code&gt;&lt;/span&gt;
    &lt;div class=&quot;noselect&quot;&gt;
      
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;codeblock__contents&quot;&gt;
    &lt;div class=&quot;highlight highlight--with-header&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;pandas&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;pd&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ydata_profiling&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProfileReport&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read_csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;flight_data_2024_sample.csv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProfileReport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;flight_report.html&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    
    &lt;button class=&quot;codeblock__copy btn btn-outline-secondary border m-1 px-1 d-hover-only&quot; title=&quot;Copy to clipboard&quot;&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt;&lt;/button&gt;
    
  &lt;/div&gt;

&lt;p&gt;This &lt;a href=&quot;https://realpython.com/ref/glossary/source-code/&quot; class=&quot;ref-link&quot;&gt;code&lt;/a&gt; generates an HTML file containing interactive visualizations, statistical summaries, and data quality warnings:&lt;/p&gt;
&lt;a href=&quot;https://files.realpython.com/media/report-overview.c5b7b1fa2ba4.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/report-overview.c5b7b1fa2ba4.png&quot; width=&quot;2225&quot; height=&quot;995&quot; alt=&quot;Dataset overview displaying statistics and variable types. Statistics include 35 variables, 10,000 observations, and 3.2% missing cells. Variable types: 5 categorical, 23 numeric, 1 DateTime, 6 text.&quot; /&gt;&lt;/a&gt;

&lt;p&gt;You can open the file in any browser to explore your data’s characteristics without writing additional analysis code.&lt;/p&gt;
&lt;p&gt;There are a number of tools available for high-level dataset exploration, but not all are built for the same purpose. The following table highlights a few common options and when each one is a good fit:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Pick&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You want to quickly generate an exploratory report&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ydata-profiling&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generating exploratory data analysis reports with visualizations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want an overview of a large dataset&lt;/td&gt;
&lt;td&gt;&lt;code&gt;skimpy&lt;/code&gt; or &lt;code&gt;df.describe()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Providing fast, lightweight summaries in the console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want to enforce data quality&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pandera&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validating schemas and catching errors in data pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Overall, &lt;a href=&quot;https://docs.profiling.ydata.ai/latest/&quot;&gt;YData Profiling&lt;/a&gt; is best used as an exploratory report creation tool. If you’re looking to generate an overview for a large dataset, using &lt;a href=&quot;https://aeturrell.github.io/skimpy/&quot;&gt;SkimPy&lt;/a&gt; or a built-in &lt;a href=&quot;https://realpython.com/ref/glossary/dataframe/&quot; class=&quot;ref-link&quot;&gt;DataFrame&lt;/a&gt; library method may be more efficient. Other tools, like &lt;a href=&quot;https://pandera.readthedocs.io/en/stable/&quot;&gt;Pandera&lt;/a&gt;, are more appropriate for data validation.&lt;/p&gt;
&lt;p&gt;If YData Profiling looks like the right choice for your use case, then keep reading to learn about its most important features.&lt;/p&gt;
&lt;div class=&quot;container border rounded text-wrap-pretty my-3&quot;&gt;

  &lt;p class=&quot;my-3&quot;&gt;&lt;strong&gt;&lt;span class=&quot;icon baseline&quot;&gt;&lt;/span&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “Automate Python Data Analysis With YData Profiling” quiz. You’ll receive a score upon completion to help you track your learning progress:&lt;/p&gt;

  &lt;hr /&gt;

  &lt;div class=&quot;row my-3&quot;&gt;
    &lt;div class=&quot;col-xs-12 col-sm-4 col-md-3 align-self-center&quot;&gt;

      &lt;a href=&quot;https://realpython.com/quizzes/ydata-profiling-eda/&quot; tabindex=&quot;-1&quot;&gt;
        &lt;div class=&quot;embed-responsive embed-responsive-16by9&quot;&gt;

            &lt;img class=&quot;card-img-top m-0 p-0 embed-responsive-item rounded&quot; alt=&quot;Automate Python Data Analysis With YData Profiling&quot; src=&quot;https://files.realpython.com/media/YData-Profiling-One-Click-EDA_Watermarked.dd7cbf2e0693.jpg&quot; width=&quot;1920&quot; height=&quot;1080&quot; /&gt;


          &lt;div class=&quot;card-img-overlay d-flex align-items-center&quot;&gt;
            &lt;div class=&quot;mx-auto&quot;&gt;
              &lt;span class=&quot;text-light&quot;&gt;&lt;span class=&quot;icon baseline scale2x&quot;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/a&gt;

    &lt;/div&gt;
    &lt;div class=&quot;col&quot;&gt;
      &lt;div class=&quot;mt-3 d-md-none&quot;&gt;&lt;/div&gt; 
      &lt;p class=&quot;small text-muted mb-0&quot;&gt;&lt;strong&gt;Interactive Quiz&lt;/strong&gt;&lt;/p&gt;
      &lt;a href=&quot;https://realpython.com/quizzes/ydata-profiling-eda/&quot; class=&quot;stretched-link&quot;&gt;&lt;span class=&quot;my-0 h4&quot;&gt;Automate Python Data Analysis With YData Profiling&lt;/span&gt;&lt;/a&gt; 
      &lt;p class=&quot;text-muted mb-0 small&quot;&gt;Test your knowledge of YData Profiling, including report creation, customization, performance optimization, time series analysis, and comparisons.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;

&lt;h2 id=&quot;building-a-report-with-ydata-profiling&quot;&gt;Building a Report With YData Profiling&lt;a class=&quot;headerlink&quot; href=&quot;https://realpython.com/atom.xml#building-a-report-with-ydata-profiling&quot; title=&quot;Permanent link&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;/div&gt;&lt;h2&gt;&lt;a href=&quot;https://realpython.com/ydata-profiling-eda/?utm_source=realpython&amp;utm_medium=rss&quot;&gt;Read the full article at https://realpython.com/ydata-profiling-eda/ »&lt;/a&gt;&lt;/h2&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-02T14:00:00+00:00</dc:date>
</item>
<item rdf:about="https://mathspp.com/blog/remove-extra-spaces">
	<title>Rodrigo Girão Serrão: Remove extra spaces</title>
	<link>https://mathspp.com/blog/remove-extra-spaces</link>
	<content:encoded>&lt;img alt=&quot;&quot; src=&quot;https://mathspp.com/images/b/5/e/7/b/b5e7b10fd60c3ca3dfa3940c1d5b4d9a5788ff36-thumbnail.webp&quot; /&gt;
                                &lt;p&gt;Learn how to remove extra spaces from a string using regex, string splitting, a fixed point, and &lt;code&gt;itertools.groupby&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this article you'll learn about three different ways in which you can remove extra spaces from the middle of a string.
That is, you'll learn how to go from a string like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;string = &quot;This is  a   perfectly    normal     sentence.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to a string like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;string = &quot;This is a perfectly normal sentence.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-best-solution-to-remove-extra-spaces-from-a-string&quot;&gt;The best solution to remove extra spaces from a string&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#the-best-solution-to-remove-extra-spaces-from-a-string&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The best solution for this task, which is both readable and performant, uses the regex module &lt;code&gt;re&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;import re

def remove_extra_spaces(string):
    return re.sub(&quot; {2,}&quot;, &quot; &quot;, string)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function &lt;code&gt;sub&lt;/code&gt; can be used to &lt;strong&gt;sub&lt;/strong&gt;stitute a pattern for a replacement you specify.
The pattern &lt;code&gt;&quot; {2,}&quot;&lt;/code&gt; finds runs of 2 or more consecutive spaces and replaces them with a single space.&lt;/p&gt;
&lt;h2 id=&quot;string-splitting&quot;&gt;String splitting&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#string-splitting&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using the string method &lt;code&gt;split&lt;/code&gt; can also be a good approach:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;def remove_extra_spaces(string):
    return &quot; &quot;.join(string.split(&quot; &quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you're using string splitting, you'll want to provide the space &lt;code&gt;&quot; &quot;&lt;/code&gt; as an argument.
If you call &lt;code&gt;split&lt;/code&gt; with no arguments, you'll be splitting on &lt;em&gt;all&lt;/em&gt; whitespace, which is not what you want if you have newlines and other whitespace characters you should preserve.&lt;/p&gt;
&lt;p&gt;This solution is great, except it doesn't work:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;print(remove_extra_spaces(string))
# 'This is  a   perfectly    normal     sentence.'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem is that splitting on the space will produce a list with empty strings:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;print(string.split(&quot; &quot;))
# ['This', 'is', '', 'a', '', '', 'perfectly', '', '', '', 'normal', '', '', '', '', 'sentence.']&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These empty strings will be joined back together and you'll end up with the same string you started with.
For this to work, you'll have to filter the empty strings first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;def remove_extra_spaces(string):
    return &quot; &quot;.join(filter(None, string.split(&quot; &quot;)))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;filter(None, ...)&lt;/code&gt; filters out the &lt;a href=&quot;https://mathspp.com/blog/pydonts/truthy-falsy-and-bool&quot;&gt;Falsy&lt;/a&gt; strings, so that the final joining operation only joins the strings that matter.&lt;/p&gt;
&lt;p&gt;This solution has a problem, though, in that it will completely remove any leading or trailing whitespace, which may or may not be a problem.&lt;/p&gt;
&lt;p&gt;The two solutions presented so far &amp;mdash; using regular expressions and string splitting &amp;mdash; are pretty reasonable.
But they're also boring.
You'll now learn about two other solutions.&lt;/p&gt;
&lt;h2 id=&quot;replacing-spaces-until-you-hit-a-fixed-point&quot;&gt;Replacing spaces until you hit a fixed point&lt;a href=&quot;https://mathspp.com/blog/tags/python.rss#replacing-spaces-until-you-hit-a-fixed-point&quot; class=&quot;toc-anchor after&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can think about the task of removing extra spaces as the task of replacing extra spaces by the empty string.
And if you think about doing string replacements, you should think about the string method &lt;code&gt;replace&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can't do something like &lt;code&gt;string.replace(&quot; &quot;, &quot;&quot;)&lt;/code&gt;, otherwise you'd remove &lt;em&gt;all&lt;/em&gt; spaces, so you have to be a bit more careful:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-py&quot;&gt;def remove_extra_spaces(string):
    while True:
        new_string = string.replace(&quot;  &quot;, &quot; &quot;)
        if new_string == string:
            break
        string = new_string
    return string&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can replace two consecutive spaces by a single space, and you repeat this operation until nothing changes in your string.&lt;/p&gt;
&lt;p&gt;The idea of running a function until its output doesn't change is common enough in maths that they call...&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-02T12:39:00+00:00</dc:date>
</item>
<item rdf:about="https://realpython.com/quizzes/ydata-profiling-eda/">
	<title>Real Python: Quiz: Automate Python Data Analysis With YData Profiling</title>
	<link>https://realpython.com/quizzes/ydata-profiling-eda/</link>
	<content:encoded>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of &lt;a href=&quot;https://realpython.com/ydata-profiling-eda/&quot;&gt;Automate Python Data Analysis With YData Profiling&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By working through this quiz, you&amp;rsquo;ll revisit how to generate and display profile reports in a notebook, export reports to files, add column descriptions, and speed up profiling.&lt;/p&gt;
&lt;p&gt;This quiz focuses on practical YData Profiling tasks such as rendering reports, comparing datasets, and preparing time series data. If you want a deeper walkthrough, review the tutorial linked above.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-02T12:00:00+00:00</dc:date>
</item>
<item rdf:about="https://realpython.com/quizzes/pandas-dataframe/">
	<title>Real Python: Quiz: The pandas DataFrame: Make Working With Data Delightful</title>
	<link>https://realpython.com/quizzes/pandas-dataframe/</link>
	<content:encoded>&lt;p&gt;In this quiz, you&amp;rsquo;ll test your understanding of the
&lt;a href=&quot;https://realpython.com/pandas-dataframe/&quot;&gt;pandas DataFrame&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By working through this quiz, you&amp;rsquo;ll review how to create pandas DataFrames, access and modify columns, insert and sort data, extract values as NumPy arrays, and how pandas handles missing data.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;utm_medium=rss&amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-02T12:00:00+00:00</dc:date>
</item>
<item rdf:about="https://pythonbytes.fm/episodes/show/471/the-orm-pattern-of-2026">
	<title>Python Bytes: #471 The ORM pattern of 2026?</title>
	<link>https://pythonbytes.fm/episodes/show/471/the-orm-pattern-of-2026</link>
	<content:encoded>&amp;lt;strong&amp;gt;Topics covered in this episode:&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;

&amp;lt;ul&amp;gt;
	&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&quot;https://mkennedy.codes/posts/raw-dc-the-orm-pattern-of-2026/?featured_on=pythonbytes&quot;&amp;gt;Raw+DC: The ORM pattern of 2026&amp;lt;/a&amp;gt;?&amp;lt;/strong&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&quot;https://github.com/okken/pytest-check/releases?featured_on=pythonbytes&quot;&amp;gt;pytest-check releases&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&quot;https://dcw.ritviknag.com/en/latest/#&quot;&amp;gt;Dataclass Wizard&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;&amp;lt;a href=&quot;https://github.com/adamghill/sqliteo?featured_on=pythonbytes&quot;&amp;gt;SQLiteo&amp;lt;/a&amp;gt; - “native macOS SQLite browser built for normal people”&amp;lt;/strong&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Extras&amp;lt;/strong&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Joke&amp;lt;/strong&amp;gt;&amp;lt;/li&amp;gt;

&amp;lt;/ul&amp;gt;&amp;lt;a href='https://www.youtube.com/watch?v=tZyf7KtTQVU' style='font-weight: bold;'data-umami-event=&quot;Livestream-Past&quot; data-umami-event-episode=&quot;471&quot;&amp;gt;Watch on YouTube&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;About the show&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Sponsored by us! Support our work through:&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Our &amp;lt;a href=&quot;https://training.talkpython.fm/?featured_on=pythonbytes&quot;&amp;gt;&amp;lt;strong&amp;gt;courses at Talk Python Training&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://courses.pythontest.com/p/the-complete-pytest-course?featured_on=pythonbytes&quot;&amp;gt;&amp;lt;strong&amp;gt;The Complete pytest Course&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.patreon.com/pythonbytes&quot;&amp;gt;&amp;lt;strong&amp;gt;Patreon Supporters&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt;
&amp;lt;strong&amp;gt;Connect with the hosts&amp;lt;/strong&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Michael: &amp;lt;a href=&quot;https://fosstodon.org/@mkennedy&quot;&amp;gt;@mkennedy@fosstodon.org&amp;lt;/a&amp;gt; / &amp;lt;a href=&quot;https://bsky.app/profile/mkennedy.codes?featured_on=pythonbytes&quot;&amp;gt;@mkennedy.codes&amp;lt;/a&amp;gt; (bsky)&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Brian: &amp;lt;a href=&quot;https://fosstodon.org/@brianokken&quot;&amp;gt;@brianokken@fosstodon.org&amp;lt;/a&amp;gt; / &amp;lt;a href=&quot;https://bsky.app/profile/brianokken.bsky.social?featured_on=pythonbytes&quot;&amp;gt;@brianokken.bsky.social&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Show: &amp;lt;a href=&quot;https://fosstodon.org/@pythonbytes&quot;&amp;gt;@pythonbytes@fosstodon.org&amp;lt;/a&amp;gt; / &amp;lt;a href=&quot;https://bsky.app/profile/pythonbytes.fm&quot;&amp;gt;@pythonbytes.fm&amp;lt;/a&amp;gt; (bsky)
Join us on YouTube at &amp;lt;a href=&quot;https://pythonbytes.fm/stream/live&quot;&amp;gt;&amp;lt;strong&amp;gt;pythonbytes.fm/live&amp;lt;/strong&amp;gt;&amp;lt;/a&amp;gt; to be part of the audience. Usually &amp;lt;strong&amp;gt;Monday&amp;lt;/strong&amp;gt; at 11am PT. Older video versions available there too.
Finally, if you want an artisanal, hand-crafted digest of every week of the show notes in email form? Add your name and email to &amp;lt;a href=&quot;https://pythonbytes.fm/friends-of-the-show&quot;&amp;gt;our friends of the show list&amp;lt;/a&amp;gt;, we'll never share it.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Michael #1: &amp;lt;a href=&quot;https://mkennedy.codes/posts/raw-dc-the-orm-pattern-of-2026/?featured_on=pythonbytes&quot;&amp;gt;Raw+DC: The ORM pattern of 2026&amp;lt;/a&amp;gt;?&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;ORMs/ODMs provide great support and abstractions for developers&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;They are not the &amp;lt;em&amp;gt;native&amp;lt;/em&amp;gt; language of agentic AI&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Raw queries are trained 100x+ more than standard ORMs&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Using raw queries at the data access optimizes for AI coding&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Returning some sort of object mapped to the data optimizes for type safety and devs&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Brian #2: &amp;lt;a href=&quot;https://github.com/okken/pytest-check/releases?featured_on=pythonbytes&quot;&amp;gt;pytest-check releases&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;3 merged pull requests&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;8 closed issues&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;at one point got to 0 PR’s and 1 enhancement request&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Now back to 2 issues and 1 PR, but activity means it’s still alive and being used. so cool&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Check out &amp;lt;a href=&quot;https://github.com/okken/pytest-check/blob/main/changelog.md?featured_on=pythonbytes&quot;&amp;gt;changelog&amp;lt;/a&amp;gt; for all mods&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;A lot of changes around supporting mypy
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;I’ve decided to NOT have the examples be fully &amp;lt;code&amp;gt;--strict&amp;lt;/code&amp;gt; as I find it reduces readability
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;See &amp;lt;code&amp;gt;tox.ini&amp;lt;/code&amp;gt; for explanation&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;But src is &amp;lt;code&amp;gt;--strict&amp;lt;/code&amp;gt; clean now, so user tests can be &amp;lt;code&amp;gt;--strict&amp;lt;/code&amp;gt; clean.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Michael #3: &amp;lt;a href=&quot;https://dcw.ritviknag.com/en/latest/#&quot;&amp;gt;Dataclass Wizard&amp;lt;/a&amp;gt;&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Simple, elegant wizarding tools for Python’s&amp;lt;/strong&amp;gt; &amp;lt;code&amp;gt;dataclasses&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Features
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🚀 Fast — code-generated loaders and dumpers&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🪶 Lightweight — pure Python, minimal dependencies&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🧠 Typed — powered by Python type hints&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🧙 Flexible — JSON, YAML, TOML, and environment variables&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🧪 Reliable — battle-tested with extensive test coverage&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://dcw.ritviknag.com/en/latest/#no-inheritance-needed&quot;&amp;gt;No Inheritance Needed&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Brian #4: &amp;lt;a href=&quot;https://github.com/adamghill/sqliteo?featured_on=pythonbytes&quot;&amp;gt;SQLiteo&amp;lt;/a&amp;gt; - “native macOS SQLite browser built for normal people”&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Adam Hill&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;This is a fun tool, built by someone I trust.&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;That trust part is something I’m thinking about a lot in these days of dev+agent built tools&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Some notes on my thoughts when evaluating
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;I know mac rules around installing .dmg files not from the apple store are picky.
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;And I like that&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;But I’m ok with the override when something comes from a dev I trust&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;The contributors are all Adam
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;I’m still not sure how I feel about letting agents do commits in repos&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;There’s “AGENTS” folder and markdown files in the project for agents, so Ad&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Extras&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Michael:&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://lp.jetbrains.com/python-unplugged/?featured_on=pythonbytes&quot;&amp;gt;PyTV Python Unplugged This Week&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.techbuzz.ai/articles/ibm-crashes-11-as-anthropic-threatens-cobol-empire?featured_on=pythonbytes&quot;&amp;gt;IBM Crashes 11% in 4 Hours&amp;lt;/a&amp;gt; - $24 Billion Wiped Out After Anthropic's Claude Code         Threatens the Entire COBOL Consulting Industry&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Loving my &amp;lt;a href=&quot;https://www.amazon.com/dp/B0FJYNVR3R?ref_=ppx_hzsearch_conn_dt_b_fed_asin_title_1&amp;amp;featured_on=pythonbytes&quot;&amp;gt;40” ultrawide monitor&amp;lt;/a&amp;gt; more every day&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://updatest.app?featured_on=pythonbytes&quot;&amp;gt;Updatest&amp;lt;/a&amp;gt; for updating all the mac things&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.reddit.com/r/macapps/comments/1qwkq38/os_thaw_a_fork_of_ice_menu_bar_manager_for_macos/?featured_on=pythonbytes&quot;&amp;gt;Ice has Thawed out&amp;lt;/a&amp;gt; (mac menubar app)&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Joke: &amp;lt;a href=&quot;https://x.com/pr0grammerhum0r/status/2018852032304566331?s=12&amp;amp;featured_on=pythonbytes&quot;&amp;gt;House is read-only&amp;lt;/a&amp;gt;!&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;</content:encoded>
	<dc:date>2026-03-02T08:00:00+00:00</dc:date>
</item>
<item rdf:about="https://discuss.tryton.org/t/tryton-news-march-2026/9121">
	<title>Tryton News: Tryton News March 2026</title>
	<link>https://discuss.tryton.org/t/tryton-news-march-2026/9121</link>
	<content:encoded>&lt;div&gt;  &lt;/div&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;lightbox-wrapper&quot;&gt;&lt;a class=&quot;lightbox&quot; href=&quot;https://discuss-cdn.tryton.org/uploads/default/original/2X/9/9d4232da578af20227f7233128e17af01742fe6e.jpeg&quot; title=&quot;Photo Pexels, cottonbro studio&quot;&gt;&lt;img src=&quot;https://discuss-cdn.tryton.org/uploads/default/optimized/2X/9/9d4232da578af20227f7233128e17af01742fe6e_2_503x500.jpeg&quot; alt=&quot;Boy in White T-shirt Sitting on Chair in Front of Computer&quot; title=&quot;Photo Pexels, cottonbro studio&quot; width=&quot;503&quot; height=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;In the last month we focused on fixing bugs, improving the behaviour of things, speeding-up performance issues - building on the changes from &lt;a href=&quot;https://discuss.tryton.org/t/tryton-release-7-8/&quot;&gt;our last release&lt;/a&gt;. We also added some new features which we would like to introduce to you in this newsletter.&lt;/p&gt;
&lt;p&gt;For an in depth overview of the &lt;a href=&quot;https://bugs.tryton.org/&quot;&gt;Tryton issues please take a look at our issue tracker &lt;/a&gt; or see the issues and merge requests &lt;a href=&quot;https://code.tryton.org/tryton/-/labels&quot;&gt;filtered by label&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;heading--user&quot;&gt;Changes for the User&lt;/h2&gt;
&lt;h3 id=&quot;heading--sales-purchase&quot;&gt;Sales, Purchases and Projects&lt;/h3&gt;
&lt;p&gt;Now we &lt;a href=&quot;https://bugs.tryton.org/14538&quot;&gt;add the web shop URL to sales&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We now add a &lt;a href=&quot;https://bugs.tryton.org/14507&quot;&gt;menu entry for &lt;em&gt;party identifier.&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;heading--accounting&quot;&gt;Accounting, Invoicing and Payments&lt;/h3&gt;
&lt;p&gt;Now we can &lt;a href=&quot;https://code.tryton.org/tryton/-/commit/d859e52b7ba404c95d2ed3c8f0887e0cceb2c1df&quot;&gt;search for shipments on the invoice line&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In UBL we now &lt;a href=&quot;https://bugs.tryton.org/14517&quot;&gt;set &lt;code&gt;BillingReference&lt;/code&gt; to &lt;em&gt;Invoice&lt;/em&gt; and &lt;em&gt;Credit Note&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We now &lt;a href=&quot;https://code.tryton.org/tryton/-/commit/fa6de53a766e1bfb5cfd04009a3f8ba3d04e7ebc&quot;&gt;improve the layout of the &lt;em&gt;invoice credit form&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we enforce the Peppol rule “&lt;a href=&quot;https://www.peppolguide.sg/billing/rules/BR-27/&quot;&gt;[BR-27]-The Item net price (BT-146) shall NOT be negative&lt;/a&gt;”. So we &lt;a href=&quot;https://bugs.tryton.org/14504&quot;&gt;make sure that the &lt;em&gt;unit price&lt;/em&gt; on an &lt;em&gt;invoice line&lt;/em&gt; is not negative&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We now add an &lt;a href=&quot;https://code.tryton.org/tryton/-/commit/e6e5d9c225711c755e8d71ee279c14b8b9cc7d04&quot;&gt;&lt;em&gt;Update Status&lt;/em&gt; button to the Peppol document&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;heading--stock&quot;&gt;Stock, Production and Shipments&lt;/h3&gt;
&lt;p&gt;Now we can &lt;a href=&quot;https://bugs.tryton.org/14424&quot;&gt;charge duties for UPS for &lt;em&gt;buyer&lt;/em&gt; or &lt;em&gt;seller&lt;/em&gt; on import and export defined in the incoterms&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;heading--ui&quot;&gt;User Interface&lt;/h3&gt;
&lt;p&gt;Now we &lt;a href=&quot;https://bugs.tryton.org/14634&quot;&gt;allow to re-order tabs in Sao&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://bugs.tryton.org/14574&quot;&gt;favourites menu we now display a message&lt;/a&gt; how to use it, instead of showing an empty menu.&lt;/p&gt;
&lt;p&gt;We also &lt;a href=&quot;https://bugs.tryton.org/14575&quot;&gt;improve the &lt;em&gt;blank state&lt;/em&gt; of &lt;em&gt;notifications&lt;/em&gt; by showing a message&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we &lt;a href=&quot;https://bugs.tryton.org/13306&quot;&gt;add a button in Sao for closing the search filter&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;heading--system-data-config&quot;&gt;System Data and Configuration&lt;/h3&gt;
&lt;p&gt;Now &lt;a href=&quot;https://code.tryton.org/tryton/-/commit/f1888e0e031352187fb5b24207c55618f3081e61&quot;&gt;we update the required version of &lt;code&gt;python-stdnum&lt;/code&gt; to version &lt;code&gt;2.22&lt;/code&gt;&lt;/a&gt; and introduced new party identifier.&lt;/p&gt;

More &lt;a href=&quot;https://discuss.tryton.org/t/tryton-news-march-2026/9121/1&quot;&gt;(click for more details)&lt;/a&gt;
&lt;h2 id=&quot;heading--new-releases&quot;&gt;New Releases&lt;/h2&gt;
&lt;p&gt;We released bug fixes for the currently maintained &lt;a href=&quot;https://discuss.tryton.org/t/release-process/395&quot;&gt;long term support series&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://code.tryton.org/tryton/-/commits/branch/7.0&quot;&gt;7.0&lt;/a&gt; and &lt;a href=&quot;https://code.tryton.org/tryton/-/commits/branch/6.0&quot;&gt;6.0&lt;/a&gt;, and for the penultimate series &lt;a href=&quot;https://code.tryton.org/tryton/-/commits/branch/7.8&quot;&gt;7.8&lt;/a&gt; and &lt;a href=&quot;https://code.tryton.org/tryton/-/commits/branch/7.6&quot;&gt;7.6&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;heading--security&quot;&gt;Security&lt;/h2&gt;
Please update your systems to take care of a security related bug we found last month. 
&lt;h2 id=&quot;heading--sysadmin&quot;&gt;Changes for the System Administrator&lt;/h2&gt;
&lt;p&gt;Now we also &lt;a href=&quot;https://code.tryton.org/tryton/-/commit/0a667448a4dd1be5561d205ffa56b077cf79b972&quot;&gt;display the &lt;code&gt;create date&lt;/code&gt; and &lt;code&gt;create time&lt;/code&gt; in the error list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We now &lt;a href=&quot;https://bugs.tryton.org/14528&quot;&gt;add &lt;code&gt;basic&lt;/code&gt; authentication for &lt;em&gt;user applications&lt;/em&gt;&lt;/a&gt;. Because in some cases the consumer of the user application may not be able to use the &lt;code&gt;bearer&lt;/code&gt; authentication.&lt;/p&gt;
&lt;p&gt;Now we &lt;a href=&quot;https://bugs.tryton.org/14530&quot;&gt;allow to activate the &lt;em&gt;development mode&lt;/em&gt; with WSGI applications by setting the environment variable &lt;code&gt;TRYTOND_DEV&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Writing compatible HTML for email can be very difficult. &lt;a href=&quot;https://mjml.io/&quot;&gt;MJML&lt;/a&gt; provides a syntax to ease the creation of such emails. So now we &lt;a href=&quot;https://bugs.tryton.org/12597&quot;&gt;support the MJML email format in Tryton&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;heading--developer&quot;&gt;Changes for Implementers and Developers&lt;/h2&gt;
&lt;p&gt;We now add a &lt;a href=&quot;https://bugs.tryton.org/14202&quot;&gt;timestamp field on &lt;code&gt;ModelStorage&lt;/code&gt; for last modified.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we &lt;a href=&quot;https://bugs.tryton.org/4369&quot;&gt;introduce a new type of field for SQL expressions: &lt;code&gt;Field.sql_column(tables, Model)&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We now &lt;a href=&quot;https://bugs.tryton.org/14539&quot;&gt;allow &lt;code&gt;UserError&lt;/code&gt; and &lt;code&gt;UserWarning&lt;/code&gt; exceptions to be raised on evaluating button&lt;/a&gt; inputs.&lt;/p&gt;
&lt;p&gt;Now we &lt;a href=&quot;https://code.tryton.org/tryton/-/commit/646d50f1e24e3070c5cfc7817e1a19ed8202c08d&quot;&gt;replace the extension separator by an underscore in report names used as temporary files&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We now &lt;a href=&quot;https://bugs.tryton.org/14526&quot;&gt;do no longer check for missing parent depends when the &lt;code&gt;One2Many&lt;/code&gt; is readonly&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we &lt;a href=&quot;https://foss.heptapod.net/tryton/tryton/-/merge_requests/2971&quot;&gt;preserve the line numbers when converting doctest files to python files&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Authors: &lt;a class=&quot;mention&quot; href=&quot;https://discuss.tryton.org/u/dave&quot;&gt;@dave&lt;/a&gt; &lt;a class=&quot;mention&quot; href=&quot;https://discuss.tryton.org/u/pokoli&quot;&gt;@pokoli&lt;/a&gt; &lt;a class=&quot;mention&quot; href=&quot;https://discuss.tryton.org/u/udono&quot;&gt;@udono&lt;/a&gt;&lt;/p&gt;
            &lt;p&gt;&lt;small&gt;1 post - 1 participant&lt;/small&gt;&lt;/p&gt;
            &lt;p&gt;&lt;a href=&quot;https://discuss.tryton.org/t/tryton-news-march-2026/9121&quot;&gt;Read full topic&lt;/a&gt;&lt;/p&gt;</content:encoded>
	<dc:date>2026-03-01T07:00:31+00:00</dc:date>
</item>

</rdf:RDF>
