Planet Python
Last update: December 17, 2025 09:44 PM UTC
December 17, 2025
Sebastian Pölsterl
scikit-survival 0.26.0 released
I am pleased to announce that scikit-survival 0.26.0 has been released.
This is a maintainance release that adds support for Python 3.14 and
includes updates to make scikit-survival compatible with new versions
of pandas and osqp.
It adds support for the pandas string dtype,
and copy-on-write, which is going to become the default with pandas 3.
In addition, sksurv.preprocessing.OneHotEncoder
now supports converting columns with the object dtype.
With this release, the minimum supported version are:
| Package | Minimum Version |
|---|---|
| Python | 3.11 |
| pandas | 2.0.0 |
| osqp | 1.0.2 |
Install
scikit-survival is available for Linux, macOS, and Windows and can be installed either
via pip:
pip install scikit-survival
or via conda
conda install -c conda-forge scikit-survival
December 17, 2025 08:26 PM UTC
PyCharm
The Islands theme is now the default look across JetBrains IDEs starting with version 2025.3.This update is more than a visual refresh. It’s our commitment to creating a soft, balanced environment designed to support focus and comfort throughout your workflow. We began introducing the new theme earlier this year, gathering feedback, conducting research, and testing it hands-on with developers […]
December 17, 2025 07:41 PM UTC
Real Python
How to Build the Python Skills That Get You Hired
Build a focused learning plan that helps you identify essential Python skills, assess your strengths, and practice effectively to progress.
December 17, 2025 02:00 PM UTC
Python Morsels
Embrace whitespace
Well placed spaces and line breaks can greatly improve the readability of your Python code.
Table of contents
Whitespace around operators
Compare this:
result = a**2+b**2+c**2
To this:
result = a**2 + b**2 + c**2
I find that second one more readable because the operations we're performing are more obvious (as is the order of operations).
Too much whitespace can hurt readability though:
result = a ** 2 + b ** 2 + c ** 2
This seems like a step backward because we've lost those three groups we had before.
With both typography and visual design, more whitespace isn't always better.
Auto-formatters: both heroes and villains
If you use an auto-formatter …
Read the full article: https://www.pythonmorsels.com/embrace-whitespace/
December 17, 2025 12:00 AM UTC
Armin Ronacher
What Actually Is Claude Code’s Plan Mode?
December 17, 2025 12:00 AM UTC
December 16, 2025
PyCoder’s Weekly
Issue #713: Deprecations, Compression, Functional Programming, and More (Dec. 16, 2025)
December 16, 2025 07:30 PM UTC
Real Python
Exploring Asynchronous Iterators and Iterables
Learn to build async iterators and iterables in Python to handle async operations efficiently and write cleaner, faster code.
December 16, 2025 02:00 PM UTC
Caktus Consulting Group
PydanticAI Agents Intro
In previous posts, we explored function calling and how it enables models to interact with external tools. However, manually defining schemas and managing the request/response loop can get tedious as an application grows. Agent frameworks can help here.
December 16, 2025 01:00 PM UTC
Tryton News
Tryton Release 7.8
We are proud to announce the 7.8 release of Tryton.
This release provides many bug fixes, performance improvements and some fine tuning.
You can give it a try on the demo server, use the docker image or download it here.
As usual upgrading from previous series is fully supported.
Here is a list of the most noticeable changes:
Changes for the User
Client
We added now a drop-down menu to the client containing the user’s notifications. Now when a user clicks on a notification, it is marked as read for this user.
Also we implemented an unread counter in the client and raise a user notification pop-up when a new notification is sent by the server.
Now users can subscribe to a chat of documents by toggling the notification bell-icon.
The chat feature has been activated to many documents like sales, purchases and invoices.
Now we display the buttons that are executed on a selection of records at the bottom of lists.
We now implemented an easier way to search for empty relation fields:
The query Warehouse: = will now return records without a warehouse instead of the former result of records with warehouses having empty names. And the former result can be searched by the following query: "Warehouse.Record Name": =.
Now we interchanged the internal ID by the record name when exporting Many2One and Reference fields to CSV. And the export of One2Many and Many2Many fields is using a list of record names.
We also made it possible to import One2Many field content by using a list of names (like for the Many2Many).
Web
We made the keyboard shortcuts now also working on modals.
Server
On scheduled tasks we now also implemented user notifications.
Each user can now subscribe to be notified by scheduled tasks which generates notifications. Notifications will appear in the client drop-down.
Accounting
On supplier invoice we now made it possible to set a payment reference and to validate it. Per default the Creditor Reference is supported. And on customer invoices Tryton generates a payment reference automatically. It is using the Creditor Reference format by default, and the structured communication for Belgian customers. The payment reference can be validated for defined formats like the “Creditor Reference”. And it can be used in payment rules.
Now we support the Belgian structured communication on invoices, payments and statement rules. And with this the reconciliation process can be automated.
We now implemented when succeeding a group of payments, Tryton now will ask for the clearing date instead of just using today.
Now we store the address of the party in the SEPA mandate instead of using just the first party address.
We now added a button on the accounting category to add or remove multiple products easily.
Customs
Now we support customs agents. They define a party to whom the company is delegating the customs between two countries.
Incoterm
We now added also the old version of Incoterms 2000 because some companies and services are still using it.
Now we allow the modification of the incoterms on the customer shipment as long as it has not yet been shipped.
Product
We now make the list of variants for a product sortable. This is useful for e-commerce if you want to put a specific variant in front.
Now it is possible to set a different list price and gross price per variant without the need for a custom module.
We now made the volume and weight usable in price list formulas. This is useful to include taxes based on such criteria.
Production
Now we made it possible to define phantom bill-of-materials (BOM) to group common inputs or outputs for different BOMs. When used in a production, the phantom BOM is replaced by its corresponding materials.
We now made it possible to define a production as a disassembly. In this case the calculation from the BOM is inverted.
Purchasing
Now we restrict the run of the create purchase wizard from purchase requests which are already purchased.
And also we now restrict to run the create quotation wizard on purchase requests when it is no longer possible to create them.
It is now possible to create a new quotation for a purchase request which already has received one.
Now we made the client to open quotations that have been created by the wizard.
We fine-tuned the supply system: When no supplier can supply on time, the system will now choose the fastest supplier.
Sales
Now we made it possible to encode refunding payments on the sale order.
We allow now to group invoices created for a sale rental with the invoices created for sale orders.
In the sale subscription lines we now implemented a summary column similar to sales.
Stock
We now added two new stock reports that calculates the inventory and turnover of the stock. We find this useful to optimize and fine-tune the order points.
Now we added the support for international shipping to the shipping services: DPD, Sendcloud and UPS.
And now we made Tryton to generate a default shipping description based on the custom categories of the shipped goods (with a fallback to “General Merchandise” for UPS). This is useful for international shipping.
We now implemented an un-split functionality to correct erroneous split moves.
Now we allow to cancel a drop-shipment in state done similar to the other shipment types.
Web Shop
We now define the default Incoterm per web shop to set on the sale orders.
Now we added a status URL to the sales coming from a web shop.
We now added the URL to each product that is published in a web shop.
Now we added a button on sale from the web shop to force an update from the web shop.
We did many improvements to extend our Shopify support:
- Support the credit refunds
- Support of taxes from the shipping product
- Add an option to notify the customers about fulfilment
- Add a set of rules to select the carrier
- Support of product of type “kit”
- Set the “compare-at” price using the non-sale price
- Set the language of the customer to the party
- Add admin URL to each record with a Shopify identifier
New Modules
EDocument Peppol
The EDocument Peppol Module provides the foundation for sending and receiving
electronic documents on the Peppol network.
EDocument Peppol Peppyrus
The EDocument Peppol Peppyrus Module allows sending and receiving electronic
documents on the Peppol network thanks to the free Peppyrus service.
EDocument UBL
The EDocument UBL Module adds electronic documents from UBL.
Sale Rental
The Sale Rental Module manages rental order.
Sale Rental Progress Invoice
The Sale Rental Progress Invoice Module allows creating progress invoices for
rental orders.
Stock Shipment Customs
The Stock Shipment Customs Module enables the generation of commercial
invoices for both customer and supplier return shipments.
Stock Shipping Point
The Stock Shipping Point Module adds a shipping point to shipments.
Changes for the System Administrator
Server
We now made the server stream the JSON and gzip response to reduce the memory consumption.
Now the trytond-console gains an option to execute a script from a file.
We now replaced the [cron] clean_days configuration by [cron] log_size. Now the storage of the logs of scheduled tasks only depends on its size and no longer on its frequency.
Now we made the login process send the URL for the host of the bus. This way the clients do not need to rely on the browser to manage the redirection. Which wasn’t working on recent browsers, anyway.
We now made the login sessions only valid for the IP address of the client that generates it. This enforces the security against session leak.
Now we let the server set a Message-Id header in all sent emails.
Product
We added a timestamp parameter to the URLs of product images. This allows to force a refresh of the old cached images.
Web Shop
Now we added routes to open products, variants, customers and orders using their Shopify-ID. This can be used to customize the admin UI to add a direct link to Tryton.
Changes for the Developer
Server
In this release we introduce notifications. Their messages are sent to the user as soon as they are created via the bus. They can be linked to a set of records or an action that will be opened when the user click on it.
We made it now possible to configure a ModelSQL based on a table_query to be materialized. The configuration defines the interval at which the data must be refreshed and a wizard lets the user force a refresh.
This is useful to optimize some queries for which the data does not need to be exactly fresh but that could benefit from some indexes.
Now we register the models, wizards and reports in the tryton.cfg module file. This reduces the memory consumption of the server. It does no longer need to import all the installed modules but only the activated modules.
This is also a first step to support typing with the Tryton modular design.
We now added the attribute multiple to the <button> on tree view. When set, the button is shown at the bottom of the view.
Now we implemented the declaration of read-only Wizards. Such wizards use a read-only transaction for the execution and because of this write access on the records is not needed.
We now store only immutable structures in the MemoryCache. This prevents the alteration of cached data.
Now we added a new method to the Database to clear the cached properties of the database. This is useful when writing tests that alter those properties.
We now use the SQL FILTER syntax for aggregate functions.
Now we use the SQL EXISTS operator for searching Many2One fields with the where domain operator.
We introduced now the trytond.model.sequence_reorder method to update the sequence field according to the current order of a record list.
Now we refactored the trytond.config to add cache. It is no more needed to retrieve the configuration as a global variable to avoid performance degradation.
We removed the has_window_functions function from the Database, because the feature is supported by all the supported databases.
Now we added to the trytond.tools pair and unpair methods which are equivalent implementation in Python of the sql_pairing.
Proteus
We now implemented the support of total ordering in Proteus Model.
Marketing
We now set the One-Click header on the marketing emails to let the receivers unsubscribe easily.
Sales
Now we renamed the advance payment conditions into lines for more coherence.
Web Shop
We now updated the Shopify module to use the GraphQL API because their REST-API is now deprecated.
2 posts - 1 participant
December 16, 2025 07:00 AM UTC
December 15, 2025
Peter Bengtsson
Comparison of speed between gpt-5, gpt-5-mini, and gpt-5-nano
gpt-5-mini is 3 times faster than gpt-5 and gpt-5-nano.
December 15, 2025 11:37 PM UTC
The Python Coding Stack
If You Love Queuing, Will You Also Love Priority Queuing? • [Club]
Exploring Python’s heapq
December 15, 2025 04:53 PM UTC
Real Python
Writing DataFrame-Agnostic Python Code With Narwhals
If you're a Python library developer looking to write DataFrame-agnostic code, this tutorial will show how the Narwhals library could give you a solution.
December 15, 2025 02:00 PM UTC
Quiz: Writing DataFrame-Agnostic Python Code With Narwhals
If you're a Python library developer wondering how to write DataFrame-agnostic code, the Narwhals library is the solution you're looking for.
December 15, 2025 12:00 PM UTC
Python Bytes
#462 LinkedIn Cringe
Topics include , docs, PyAtlas: interactive map of the top 10,000 Python packages on PyPI., and Buckaroo.
December 15, 2025 08:00 AM UTC
Python GUIs
Getting Started With Flet for GUI Development — Your First Steps With the Flet Library for Desktop and Web Python GUIs
Getting started with a new GUI framework can feel daunting. This guide walks you through the essentials of Flet, from installation and a first app to widgets, layouts, and event handling.
December 15, 2025 06:00 AM UTC
Zato Blog
Microsoft Dataverse with Python and Zato Services
Microsoft Dataverse with Python and Zato Services

Overview
Microsoft Dataverse is a cloud-based data storage and management platform, often used with PowerApps and Dynamics 365.
Integrating Dataverse with Python via Zato enables automation, API orchestration, and seamless CRUD (Create, Read, Update, Delete) operations on any Dataverse object.
Below, you'll find practical code examples for working with Dataverse from Python, including detailed comments and explanations. The focus is on the "accounts" entity, but the same approach applies to any object in Dataverse.
Connecting to Dataverse and retrieving accounts
The main service class configures the Dataverse client and retrieves all accounts. Both the handle and get_accounts methods are shown together for clarity.
# -*- coding: utf-8 -*-
# Zato
from zato.common.typing_ import any_
from zato.server.service import DataverseClient, Service
class MyService(Service):
def handle(self):
# Set up Dataverse credentials - in a real service,
# this would go to your configuration file.
tenant_id = '221de69a-602d-4a0b-a0a4-1ff2a3943e9f'
client_id = '17aaa657-557c-4b18-95c3-71d742fbc6a3'
client_secret = 'MjsrO1zc0.WEV5unJCS5vLa1'
org_url = 'https://org123456.api.crm4.dynamics.com'
# Build the Dataverse client using the credentials
client = DataverseClient(
tenant_id=tenant_id,
client_id=client_id,
client_secret=client_secret,
org_url=org_url
)
# Retrieve all accounts using a helper method
accounts = self.get_accounts(client)
# Process the accounts as needed (custom logic goes here)
pass
def get_accounts(self, client:'DataverseClient') -> 'any_':
# Specify the API path for the accounts entity
path = 'accounts'
# Call the Dataverse API to retrieve all accounts
response = client.get(path)
# Log the response for debugging/auditing
self.logger.info(f'Dataverse response (get accounts): {response}')
# Return the API response to the caller
return response
{'@odata.context': 'https://org1234567.crm4.dynamics.com/api/data/v9.0/$metadata#accounts',
'value': [{'@odata.etag': 'W/"11122233"', 'territorycode': 1,
'accountid': 'd92e6f18-36fb-4fa8-b7c2-ecc7cc28f50c', 'name': 'Zato Test Account 1',
'_owninguser_value': 'ea4dd84c-dee6-405d-b638-c37b57f00938'}]}
Let's check more examples - you'll note they all follow the same pattern as the first one.
Retrieving an Account by ID
def get_account_by_id(self, client:'DataverseClient', account_id:'str') -> 'any_':
# Construct the API path using the account's GUID
path = f'accounts({account_id})'
# Call the Dataverse API to fetch the account
response = client.get(path)
# Log the response for traceability
self.logger.info(f'Dataverse response (get account by ID): {response}')
# Return the fetched account
return response
Retrieving an account by name
def get_account_by_name(self, client:'DataverseClient', account_name:'str') -> 'any_':
# Construct the API path with a filter for the account name
path = f"accounts?$filter=name eq '{account_name}'"
# Call the Dataverse API with the filter
response = client.get(path)
# Log the response for auditing
self.logger.info(f'Dataverse response (get account by name): {response}')
# Return the filtered account(s)
return response
Creating a new account
def create_account(self, client:'DataverseClient') -> 'any_':
# Specify the API path for account creation
path = 'accounts'
# Prepare the data for the new account
account_data = {
'name': 'New Test Account',
'telephone1': '+1-555-123-4567',
'emailaddress1': 'hello@example.com',
'address1_city': 'Prague',
'address1_country': 'Czech Republic',
}
# Call the Dataverse API to create the account
response = client.post(path, account_data)
# Log the response for traceability
self.logger.info(f'Dataverse response (create account): {response}')
# Return the API response
return response
Updating an existing account
def update_account(self, client:'DataverseClient', account_id:'str') -> 'any_':
# Prepare the data to update
update_data = {
'name': 'Updated Account Name',
'telephone1': '+1-555-987-6543',
'emailaddress1': 'hello2@example.com',
}
# Call the Dataverse API to update the account by ID
response = client.patch(f'accounts({account_id})', update_data)
# Log the response for auditing
self.logger.info(f'Dataverse response (update account): {response}')
# Return the updated account response
return response
Deleting an Account
def delete_account(self, client:'DataverseClient', account_id:'str') -> 'any_':
# Call the Dataverse API to delete the account
response = client.delete(f'accounts({account_id})')
# Log the response for traceability
self.logger.info(f'Dataverse response (delete account): {response}')
# Return the API response
return response
API path vs. PowerApps UI table names

A detail to note when working with Dataverse APIs is that the names you see in the PowerApps or Dynamics UI are not always the same as the paths expected by the API. For example:
- In PowerApps, you may see a table called Account.
- In the API, you must use the path accounts (lowercase, plural) when making requests.
This pattern applies to all Dataverse objects: always check the API documentation or inspect the metadata to determine the correct entity path.
Working with other Dataverse objects
While the examples above focus on the "accounts" entity, the same approach applies to any object in Dataverse: contacts, leads, opportunities, custom tables, and more. Simply adjust the API path and payload as needed.
Full CRUD Support
With Zato and Python, you get full CRUD (Create, Read, Update, Delete) capability for any Dataverse entity. The methods shown above can be adapted for any object, allowing you to automate, integrate, and orchestrate data flows across your organization.
Summary
This article has shown how to connect to Microsoft Dataverse from Python using Zato, perform CRUD operations, and understand the mapping between UI and API paths. These techniques enable robust integration and automation scenarios with any Dataverse data.
More resources
➤ Microsoft 365 APIs and Python Tutorial
➤ Python API integration tutorials
➤ What is an integration platform?
➤ Python Integration platform as a Service (iPaaS)
➤ What is an Enterprise Service Bus (ESB)? What is SOA?
➤ Open-source iPaaS in Python
December 15, 2025 03:00 AM UTC
Python Anywhere
Changes on PythonAnywhere Free Accounts
tl;dr
Starting in January 2026, all free accounts will shift to community-powered support instead of direct support and will have some reduced features. If you want to upgrade, you can lock in the current $5/month (€5/month in the EU system) Hacker plan rate before January 8 (EU) or January 15 (US). After that, the base paid tier will be $10/month (€10/month in the EU system).
If you’re currently a paying customer, you can learn more about the new pricing tiers and guidance for current customers here.
December 15, 2025 12:00 AM UTC
New PythonAnywhere Plans: Updated Features and Pricing
tl;dr
We’re restructuring our pricing for the first time since 2013. We’re combining the Hacker ($5/month or €5/month in the EU system) and Web Developer ($12/month or €12/month in the EU system) tiers into a new Developer tier ($10/month €10/month in the EU system).
These changes will start January 8 (EU) and January 15 (US). Free users who upgrade before the change will lock in the current Hacker rate of $5/month (€5/month in the EU system). This lets us invest in platform upgrades, better security, and the features you’ve been requesting.
Read about the broader changes to PythonAnywhere and guidance for free tier users here.
December 15, 2025 12:00 AM UTC
December 14, 2025
EuroPython
Humans of EuroPython: Moisés Guimarães
EuroPython wouldn&apost exist without the dedicated volunteers who invest countless hours behind the scenes.
From coordinating speaker logistics and managing registration systems to designing the conference program, handling sponsorship relations, ensuring great quality of talk recordings, moderating sessions, organizing social events, and capturing key moments in photos—
December 14, 2025 10:56 PM UTC
EuroPython Society
List of EPS Board Candidates for 2025/2026
At this year’s EuroPython Society General Assembly (GA), planned for Wednesday, December 17th, 2025, 20:00 CET, we will vote in a new board of the EuroPython Society for the term 2025/2026
List of Board Candidates
The EPS bylaws require one chair, one vice chair and 2
December 14, 2025 12:07 PM UTC
Kushal Das
Johnnycanencrypt 0.17.0 released
A few weeks ago I released Johnnycanencrypt 0.17.0. It is a Python module written in Rust, which provides OpenPGP functionality including allows usage of Yubikey 4/5 as smartcards.
Added
- Adds
verify_userpinandverify_adminpinfunctions. #186
Fixed
- #176 updates kushal's public key and tests.
- #177 uses sequoia-openpgp
1.22.0 - #178 uses scriv for changelog
- #181 updates pyo3 to
0.27.1 - #42, we now have only acceptable
expectcalls and nounwrapcalls. - Removes
cargo clippywarnings.
The build system now moved back to maturin. I managed to clean up CI, and now testing properly in all 3 platforms (Linux, Mac, Windows). Till this release I had to manually test the smartcard functionalities by connecting a Yubikey in Linux/Mac systems, but that will change for the future releases. More details will come out soon :)
December 14, 2025 08:16 AM UTC
December 13, 2025
Ahmed Bouchefra
Let’s be honest. There’s a huge gap between writing code that works and writing code that’s actually good. It’s the number one thing that separates a junior developer from a senior, and it’s something a surprising number of us never really learn.
If you’re serious about your craft, you’ve probably felt this. You build something, it functions, but deep down you know it’s brittle. You’re afraid to touch it a year from now.
Today, we’re going to bridge that gap. I’m going to walk you through eight design principles that are the bedrock of professional, production-level code. This isn’t about fancy algorithms; it’s about a mindset. A way of thinking that prepares your code for the future.
And hey, if you want a cheat sheet with all these principles plus the code examples I’m referencing, you can get it for free. Just sign up for my newsletter from the link in the description, and I’ll send it right over.
Ready? Let’s dive in.
1. Cohesion & Single Responsibility
This sounds academic, but it’s simple: every piece of code should have one job, and one reason to change.
High cohesion means you group related things together. A function does one thing. A class has one core responsibility. A module contains related classes.
Think about a UserManager class. A junior dev might cram everything in there: validating user input, saving the user to the database, sending a welcome email, and logging the activity. At first glance, it looks fine. But what happens when you want to change your database? Or swap your email service? You have to rip apart this massive, god-like class. It’s a nightmare.
The senior approach? Break it up. You’d have:
- An
EmailValidatorclass. - A
UserRespositoryclass (just for database stuff). - An
EmailServiceclass. - A
UserActivityLoggerclass.
Then, your main UserService class delegates the work to these other, specialized classes. Yes, it’s more files. It looks like overkill for a small project. I get it. But this is systems-level thinking. You’re anticipating future changes and making them easy. You can now swap out the database logic or the email provider without touching the core user service. That’s powerful.
2. Encapsulation & Abstraction
This is all about hiding the messy details. You want to expose the behavior of your code, not the raw data.
Imagine a simple BankAccount class. The naive way is to just have public attributes like balance and transactions. What could go wrong? Well, another developer (or you, on a Monday morning) could accidentally set the balance to a negative number. Or set the transactions list to a string. Chaos.
The solution is to protect your internal state. In Python, we use a leading underscore (e.g., _balance) as a signal: “Hey, this is internal. Please don’t touch it directly.”
Instead of letting people mess with the data, you provide methods: deposit(), withdraw(), get_balance(). Inside these methods, you can add protective logic. The deposit() method can check for negative amounts. The withdraw() method can check for sufficient funds.
The user of your class doesn’t need to know how it all works inside. They just need to know they can call deposit(), and it will just work. You’ve hidden the complexity and provided a simple, safe interface.
3. Loose Coupling & Modularity
Coupling is how tightly connected your code components are. You want them to be as loosely coupled as possible. A change in one part shouldn’t send a ripple effect of breakages across the entire system.
Let’s go back to that email example. A tightly coupled OrderProcessor might create an instance of EmailSender directly inside itself. Now, that OrderProcessor is forever tied to that specific EmailSender class. What if you want to send an SMS instead? You have to change the OrderProcessor code.
The loosely coupled way is to rely on an “interface,” or what Python calls an Abstract Base Class (ABC). You define a generic Notifier class that says, “Anything that wants to be a notifier must have a send() method.”
Then, your OrderProcessor just asks for a Notifier object. It doesn’t care if it’s an EmailNotifier or an SmsNotifier or a CarrierPigeonNotifier. As long as the object you give it has a send() method, it will work. You’ve decoupled the OrderProcessor from the specific implementation of the notification. You can swap them in and out interchangeably.
A quick pause. I want to thank boot.dev for sponsoring this discussion. It’s an online platform for backend development that’s way more interactive than just watching videos. You learn Python and Go by building real projects, right in your browser. It’s gamified, so you level up and unlock content, which is surprisingly addictive. The core content is free, and with the code techwithtim, you get 25% off the annual plan. It’s a great way to put these principles into practice. Now, back to it. —
4. Reusability & Extensibility
This one’s a question you should always ask yourself: Can I add new functionality without editing existing code?
Think of a ReportGenerator function that has a giant if/elif/else block to handle different formats: if format == 'text', elif format == 'csv', elif format == 'html'. To add a JSON format, you have to go in and add another elif. This is not extensible.
The better way is, again, to use an abstract class. Create a ReportFormatter interface with a format() method. Then create separate classes: TextFormatter, CsvFormatter, HtmlFormatter, each with their own format() logic.
Your ReportGenerator now just takes any ReportFormatter object and calls its format() method. Want to add JSON support? You just create a new JsonFormatter class. You don’t have to touch the ReportGenerator at all. It’s extensible without being modified.
5. Portability
This is the one everyone forgets. Will your code work on a different machine? On Linux instead of Windows? Without some weird version of C++ installed?
The most common mistake I see is hardcoding file paths. If you write C:\Users\Ahmed\data\input.txt, that code is now guaranteed to fail on every other computer in the world.
The solution is to use libraries like Python’s os and pathlib to build paths dynamically. And for things like API keys, database URLs, and other environment-specific settings, use environment variables. Don’t hardcode them! Create a .env file and load them at runtime. This makes your code portable and secure.
6. Defensibility
Write your code as if an idiot is going to use it. Because someday, that idiot will be you.
This means validating all inputs. Sanitizing data. Setting safe default values. Ask yourself, “What’s the worst that could happen if someone provides bad input?” and then guard against it.
In a payment processor, don’t have debug_mode=True as the default. Don’t set the maximum retries to 100. Don’t forget a timeout. These are unsafe defaults.
And for the love of all that is holy, validate your inputs! Don’t just assume the amount is a number or that the account_number is valid. Check it. Raise clear errors if it’s wrong. Protect your system from bad data.
7. Maintainability & Testability
The most expensive part of software isn’t writing it; it’s maintaining it. And you can’t maintain what you can’t test.
Code that is easy to test is, by default, more maintainable.
Look at a complex calculate function that parses an expression, performs the math, handles errors, and writes to a log file all at once. How do you even begin to test that? There are a million edge cases.
The answer is to break it down. Have a separate OperationParser. Have simple add, subtract, multiply functions. Each of these small, pure components is incredibly easy to test. Your main calculate function then becomes a simple coordinator of these tested components.
8. Simplicity (KISS, DRY, YAGNI)
Finally, after all that, the highest goal is simplicity.
- KISS (Keep It Simple, Stupid): Simple code is harder to write than complex code, but it’s a million times easier to understand and maintain. Swallow your ego and write the simplest thing that works.
- DRY (Don’t Repeat Yourself): If you’re doing something more than once, wrap it in a reusable function or component.
- YAGNI (You Aren’t Gonna Need It): This is the counter-balance to all the principles above. Don’t over-engineer. Don’t add a flexible, extensible system if you’re just building a quick prototype to validate an idea. When I was coding my startup, I ignored a lot of these patterns at first because speed was more important. Always ask what the business need is before you start engineering a masterpiece.
Phew, that was a lot. But these patterns are what it takes to level up. It’s a shift from just getting things done to building things that last.
If you enjoyed this, let me know. I’d love to make more advanced videos like this one. See you in the next one.
December 13, 2025 05:52 PM UTC
Hugo van Kemenade
Steering Council results
The Python Steering Council 2026 election results are in and congratulations to the new Python Steering Council!
Welcome Savannah for the first time, and thank you to Greg Smith and Emily Morehouse for four and three years’ service each.
Three are starting their sixth terms, and four members have been or are release managers.
The chart above only covers the Steering Council years. Let’s also not forget Guido van Rossum’s BDFL years:
December 13, 2025 02:40 PM UTC
Talk Python to Me
#530: anywidget: Jupyter Widgets made easy
For years, building interactive widgets in Python notebooks meant wrestling with toolchains, platform quirks, and a mountain of JavaScript machinery. Most developers took one look and backed away slowly. Trevor Manz decided that barrier did not need to exist. His idea was simple: give Python users just enough JavaScript to unlock the web’s interactivity, without dragging along the rest of the web ecosystem. That idea became anywidget, and it is quickly becoming the quiet connective tissue of modern interactive computing. Today we dig into how it works, why it has taken off, and how it might change the way we explore data.
December 13, 2025 08:00 AM UTC
Ahmed Bouchefra
A Pythonista’s Guide to the 2026 Code Rush
Look, we know the truth. Python is the best language ever written. It reads like English, it runs the AI revolution, and it doesn’t force us to worry about memory pointers or semi-colons.
But even I have to admit: the industry in 2026 is getting crowded. The “job market is brutal” chatter isn’t wrong. While we sit comfortably at the top of the TIOBE index, the ground is moving. New tech is pushing for raw speed and type safety, and “just knowing Python” might not be the golden ticket it was five years ago.
So, how do we—the whitespace-loving, bracket-hating crowd—stay on top? We don’t abandon ship. We fortify.
Here is how the rest of the programming ecosystem looks through snake-tinted glasses, and what you should actually bother learning to keep your edge.
1. Python: Still the King, But Watch the Throne
Let’s get the validation out of the way first. Python is still the engine of the modern world. Stack Overflow’s 2025 survey has us at nearly 58% usage. We aren’t going anywhere.
- AI & ML: If you are touching AI, you are writing Python. Period. The heavy lifting happens in C++, but we hold the remote control (PyTorch, TensorFlow).
- Data: Pandas and NumPy are standard equipment.
- Backend: FastAPI and Django are still shipping products faster than anyone else.
The Elephant in the Room (The GIL): We have to talk about the Global Interpreter Lock. It’s that annoying guardrail that stops Python from using multiple CPU cores at once for a single process. It’s why the “speed freaks” make fun of us.
Does it matter? Mostly, no. For 90% of apps, developer speed beats execution speed. But in 2026, efficiency is starting to count again. If you are building high-scale systems, Python is strictly the glue code. You need a partner language for the heavy computing.
2. The “Friends” We Can Tolerate
If you have to step outside the Python ecosystem, you want languages that don’t make you miserable.
Rust: The Best Friend You’re Jealous Of
If you learn one other language this year, make it Rust.
Why? Because Rust is what Python wants to be when it grows up and hits the gym. It gives you memory safety (no segfaults!) and C++ speed, but the tooling is actually modern.
For us, Rust is the perfect backend companion. Tools like Ruff (the super-fast Python linter) and Polars (the pandas alternative) are written in Rust. Writing Python extensions in Rust using PyO3 is a superpower. You write the slow parts in Rust, wrap them up, and call them from Python. You look like a genius optimization engineer, but you still get to write .py files most of the day.
TypeScript: The Only Sane Way to Do Frontend
I know, I know. We hate JavaScript. It’s messy and weird.
But unless you are using HTMX or Streamlit for everything (which, respect), you eventually have to touch the browser. TypeScript is the answer. It brings sanity to the chaos. It has types (like Python’s Type Hints, but actually enforced), so the code doesn’t explode at runtime.
Think of TypeScript as the “Pythonic” way to write JavaScript. It catches your mistakes before you push to prod. If you are doing full-stack, this is non-negotiable.
3. The “Necessary Evils”
Go: The Boring Plumber
Go (Golang) is… fine. It’s Google’s language for cloud infrastructure. It’s very simple, very fast, and very boring.
I see Go as the “anti-Python” in philosophy. Python is about expression and “one obvious way to do it.” Go is about “copy-paste this error check three times.” But, if you work in DevOps, Docker, or Kubernetes, you have to read Go. It’s a great paycheck language, even if it lacks soul.
Java: The Corporate Suit
Java is still everywhere in big banks and legacy enterprise systems. It’s verbose and heavy. Unless you are specifically targeting a job at a Fortune 500 bank or building Android apps (and even then, use Kotlin), you can probably skip this. Let the enterprise devs handle the boilerplates.
4. The “Don’t Bother” List (For Us)
- C++: Respect to the grandfathers, but life is too short for manual memory management. Unless you are building a game engine or writing the next PyTorch core, leave C++ to the specialists. We have Rust now.
- Raw JavaScript: Just use TypeScript. Friends don’t let friends write vanilla JS in 2026.
The Strategy: The T-Shaped Pythonista
So, what’s the play? Do you drop Python?
Absolutely not. You double down on Python, but you stop being a “one-trick pony.”
- The Core: Be a master of Python. Know the internals. Use Type Hints. Understand
asynciodeeply. - The Edge: Pick Rust as your performance weapon. When Python is too slow, don’t complain—rewrite that specific function in Rust.
- The Reach: Learn TypeScript just enough to not break the frontend.
That is how you survive the shift. You don’t chase every trend. You keep your home base in Python, and you selectively raid the other villages for their best tools.







