skip to navigation
skip to content

Planet Python

Last update: February 14, 2025 01:43 AM UTC

February 13, 2025


Bojan Mihelac

Prefixed Parameters for Django querystring tag

An overview of Django 5.1's new querystring tag and how to add support for prefixed parameters.

February 13, 2025 09:37 PM UTC


Peter Bengtsson

get in JavaScript is the same as property in Python

Prefix a function, in an object or class, with `get` and then that acts as a function call without brackets. Just like Python's `property` decorator.

February 13, 2025 12:41 PM UTC


EuroPython

EuroPython February 2025 Newsletter

Hey ya 👋 

Hope you&aposre all having a fantastic February. We sure have been busy and got some exciting updates for you as we gear up for EuroPython 2025, which is taking place once again in the beautiful city of Prague. So let&aposs dive right in!

🗃️ Community Voting on Talks & Workshops

EuroPython 2025 is right around the corner and our programme team is hard at work putting together an amazing lineup. But we need your help to shape the conference! We received over 572 fantastic proposals, and now it’s time for Community Voting! 🎉 If you&aposve attended EuroPython before or submitted a proposal this year, you’re eligible to vote.

📢 More votes = a stronger, more diverse programme! Spread the word and get your EuroPython friends to cast their votes too.

🏃The deadline is Monday next week, so don’t miss your chance!

🗳️ Vote now: https://ep2025.europython.eu/programme/voting

🧐Call for Reviewers

Want to play a key role in building an incredible conference? Join our review team and help select the best talks for EuroPython 2025! Whether you&aposre a Python expert or an enthusiastic community member, your insights matter.

We’d like to also thank the over 100 people who have already signed up to review! For those who haven’t done so yet, please remember to accept your Pretalx link and get your reviews in by Monday 17th February.

You can already start reviewing proposals, and each review takes as little as 5 minutes. We encourage reviewers to go through at least 20-30 proposals, but if you can do more, even better! With almost 600 submissions to pick from, your help ensures we curate a diverse and engaging programme.

If you&aposre passionate about Python and want to contribute, we’d love to have you. Sign up here: forms.gle/4GTJjwZ1nHBGetM18.

🏃The deadline is Monday next week, so don’t delay!

Got questions? Reach out to us at programme@europython.eu

📣 Community Outreach

EuroPython isn’t just present at other Python events—we actively support them too! As a community sponsor, we love helping local PyCons grow and thrive. We love giving back to the community and strengthening Python events across Europe! 🐍💙

PyCon + Web in Berlin
The EuroPython team had a fantastic time at PyCon + Web in Berlin, meeting fellow Pythonistas, exchanging ideas, and spreading the word about EuroPython 2025. It was great to connect with speakers, organizers, and attendees. 

Ever wondered how long it takes to walk from Berlin to Prague? A huge thank you to our co-organizers, Cheuk, Artur, and Cristián, for answering that in their fantastic lightning talk about EuroPython!

alt

FOSDEM 2025
We had some members of the EuroPython team at FOSDEM 2025, connecting with the open-source community and spreading the Python love! 🎉 We enjoyed meeting fellow enthusiasts, sharing insights about the EuroPython Society, and giving away the first EuroPython 2025 stickers. If you stopped by—thank you and we hope to see you in Prague this July.

alt

🦒 Speaker Mentorship Programme

The signups for The Speaker Mentorship Programme closed on 22nd January 2025. We’re excited to have matched 43 mentees with 24 mentors from our community. We had an increase in the number of mentees who signed up and that’s amazing! We’re glad to be contributing to the journey of new speakers in the Python community. A massive thank you to our mentors for supporting the mentees and to our mentees; we’re proud of you for taking this step in your journey as a speaker. 

26 mentees submitted at least 1 proposal. Out of this number, 13 mentees submitted 1 proposal, 9 mentees submitted 2 proposals, 2 mentees submitted 3 proposals, 1 mentee submitted 4 proposals and lastly, 1 mentee submitted 5 proposals. We wish our mentees the best of luck. We look forward to the acceptance of their proposals.

In a few weeks, we will host an online panel session with 2–3 experienced community members who will share their advice with first-time speakers. At the end of the panel, there will be a Q&A session to answer all the participants’ questions.

You can watch the recording of the previous year’s workshop here:

💰Sponsorship

EuroPython is one of the largest Python conferences in Europe, and it wouldn’t be possible without our sponsors. We are so grateful for the companies who have already expressed interest. If you’re interested in sponsoring EuroPython 2025 as well, please reach out to us at sponsoring@europython.eu.

🎤 EuroPython Speakers Share Their Experiences

We asked our past speakers to share their experiences speaking at EuroPython. These videos have been published on YouTube as shorts, and we&aposve compiled them into brief clips for you to watch.

A big thanks goes to Sebastian Witowski, Jan Smitka, Yuliia Barabash, Jodie Burchell, Max Kahan, and Cheuk Ting Ho for sharing their experiences.

Why You Should Submit a Proposal for EuroPython? Part 2

Why You Should Submit a Proposal for EuroPython? Part 3

📊 EuroPython Society Board Report 

The EuroPython conference wouldn’t be what it is without the incredible volunteers who make it all happen. 💞 Behind the scenes, there’s also the EuroPython Society—a volunteer-led non-profit that manages the fiscal and legal aspects of running the conference, oversees its organization, and works on a few smaller projects like the grants programme. To keep everyone in the loop and promote transparency, the Board is sharing regular updates on what we’re working on.

The January board report is ready: https://europython-society.org/board-report-for-january-2025/

🐍 Upcoming Events in the Python Community

That&aposs all for now! Keep an eye on your inbox and our website for more news and announcements. We&aposre counting down the days until we can come together in Prague to celebrate our shared love for Python. 🐍❤️

Cheers,
The EuroPython Team

February 13, 2025 08:36 AM UTC

February 12, 2025


Kay Hayen

Nuitka Release 2.6

This is to inform you about the new stable release of Nuitka. It is the extremely compatible Python compiler, “download now”.

This release has all-around improvements, with a lot effort spent on bug fixes in the memory leak domain, and preparatory actions for scalability improvements.

Bug Fixes

Package Support

New Features

Optimization

Anti-Bloat

Organizational

Tests

Cleanups

Summary

This a major release that it consolidates Nuitka big time.

The scalability work has progressed, even if no immediately visible effects are there yet, the next releases will have them, as this is the main area of improvement these days.

The memory leaks found are very important and very old, this is the first time that asyncio should be working perfect with Nuitka, it was usable before, but compatibility is now much higher.

Also, this release puts out a much nicer help output and handling of plugins help, which no longer needs tricks to see a plugin option that is not enabled (yet), during --help. The user interface is hopefully more clean due to it.

February 12, 2025 11:00 PM UTC


Giampaolo Rodola

psutil: drop Python 2.7 support

About dropping Python 2.7 support, 3 years ago I stated:

Not a chance, for many years to come. [Python 2.7] currently represents 7-10% of total downloads, meaning around 70k / 100k downloads per day.

Only 3 years later, and to my surprise, downloads for Python 2.7 dropped to 0.36%! As such, as of psutil 7.0.0, I finally decided to drop support for Python 2.7!

The numbers

These are downloads per month:

$ pypinfo --percent psutil pyversion
Served from cache: False
Data processed: 4.65 GiB
Data billed: 4.65 GiB
Estimated cost: $0.03

| python_version | percent | download_count |
| -------------- | ------- | -------------- |
| 3.10           |  23.84% |     26,354,506 |
| 3.8            |  18.87% |     20,862,015 |
| 3.7            |  17.38% |     19,217,960 |
| 3.9            |  17.00% |     18,798,843 |
| 3.11           |  13.63% |     15,066,706 |
| 3.12           |   7.01% |      7,754,751 |
| 3.13           |   1.15% |      1,267,008 |
| 3.6            |   0.73% |        803,189 |
| 2.7            |   0.36% |        402,111 |
| 3.5            |   0.03% |         28,656 |
| Total          |         |    110,555,745 |

According to pypistats.org Python 2.7 downloads are 0.28% of the total, around 15.000 downloads per day.

The pain

Maintaining 2.7 support in psutil had become increasingly difficult, but still possible. E.g. I could still run tests by using old PYPI backports. GitHub Actions could still be tweaked to run tests and produce wheels on Linux and macOS. Not on Windows though, for which we have to use a separate service (Appveyor). Still, the amount of hacks in psutil source code necessary to support Python 2.7 piled up over the years, and became quite big. Some disadvantages that come to mind:

psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl
psutil-6.1.1-cp27-none-win32.whl
psutil-6.1.1-cp27-none-win_amd64.whl
psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl
psutil-6.1.1-cp27-cp27m-manylinux2010_x86_64.whl
psutil-6.1.1-cp27-cp27mu-manylinux2010_i686.whl
psutil-6.1.1-cp27-cp27mu-manylinux2010_x86_64.whl

The removal

The removal was done in PR-2841, which removed around 1500 lines of code (nice!). It felt liberating. In doing so, in the doc I still made the promise that the 6.1.* serie will keep supporting Python 2.7 and will receive critical bug-fixes only (no new features). It will be maintained in a specific python2 branch. I explicitly kept the setup.py script compatible with Python 2.7 in terms of syntax, so that it can emit an informative error message on pip install. The user trying to install psutil on Python 2.7 will see:

$ pip2 install psutil
As of version 7.0.0 psutil no longer supports Python 2.7.
Latest version supporting Python 2.7 is psutil 6.1.X.
Install it with: "pip2 install psutil==6.1.*".

Related tickets

February 12, 2025 11:00 PM UTC


EuroPython Society

Board Report for January 2025

The top priority for the board in January was finishing the hiring of our event manager. We’re super excited to introduce Anežka Müller! Anežka is a freelance event manager and a longtime member of the Czech Python community. She’s a member of the Pyvec board, co-organizes PyLadies courses, PyCon CZ, Brno Pyvo, and Brno Python Pizza. She’ll be working closely with the board and OPS team, mainly managing communication with service providers. Welcome onboard!

Our second priority was onboarding teams. We’re happy that we already have the Programme team in place—they started early and launched the Call for Proposals at the beginning of January. We’ve onboarded a few more teams and are in the process of bringing in the rest.

Our third priority was improving our grant programme in order to support more events with our limited budget and to make it more clear and transparent. We went through past data, came up with a new proposal, discussed it, voted on it, and have already published it on our blog.

Individual reports:

Artur

Mia

Cyril

Aris

Ege

Shekhar

Anders

February 12, 2025 03:08 PM UTC


Python Morsels

Avoid over-commenting in Python

When do you need a comment in Python and when should you consider an alternative to commenting?

Table of contents

  1. Documenting instead of commenting
  2. Non-obvious variables and values
  3. Unnamed code blocks
  4. Missing variables due to embedded operations
  5. Indexes instead of variables
  6. Comment the "why" more than the "what"
  7. Consider whether there's a better alternative to your comments

Documenting instead of commenting

Here is a comment I would not write in my code:

def first_or_none(iterable):
    # Return the first item in given iterable (or None if empty).
    for item in iterable:
        return item
    return None

That comment seems to describe what this code does... so why would I not write it?

I do like that comment, but I would prefer to write it as a docstring instead:

def first_or_none(iterable):
    """Return the first item in given iterable (or None if empty)."""
    for item in iterable:
        return item
    return None

Documentation strings are for conveying the purpose of function, class, or module, typically at a high level. Unlike comments, they can be read by Python's built-in help function:

>>> help(first_or_none)
Help on function first_or_none in module __main__:

first_or_none(iterable)
    Return the first item in given iterable (or None if empty).

Docstrings are also read by other documentation-oriented tools, like Sphinx.

Non-obvious variables and values

Here's a potentially helpful comment:

Read the full article: https://www.pythonmorsels.com/avoid-comments/

February 12, 2025 03:05 PM UTC


Real Python

Python Keywords: An Introduction

Python keywords are reserved words with specific functions and restrictions in the language. Currently, Python has thirty-five keywords and four soft keywords. These keywords are always available in Python, which means you don’t need to import them. Understanding how to use them correctly is fundamental for building Python programs.

By the end of this tutorial, you’ll understand that:

  • There are 35 keywords and four soft keywords in Python.
  • You can get a list of all keywords using keyword.kwlist from the keyword module.
  • Soft keywords in Python act as keywords only in specific contexts.
  • print and exec are keywords that have been deprecated and turned into functions in Python 3.

In this article, you’ll find a basic introduction to all Python keywords and soft keywords along with other resources that will be helpful for learning more about each keyword.

Get Your Cheat Sheet: Click here to download your free cheat sheet that summarizes the main keywords in Python.

Take the Quiz: Test your knowledge with our interactive “Python Keywords: An Introduction” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Python Keywords: An Introduction

In this quiz, you'll test your understanding of Python keywords and soft keywords. These reserved words have specific functions and restrictions in Python, and understanding how to use them correctly is fundamental for building Python programs.

Python Keywords

Python keywords are special reserved words that have specific meanings and purposes and can’t be used for anything but those specific purposes. These keywords are always available—you’ll never have to import them into your code.

Python keywords are different from Python’s built-in functions and types. The built-in functions and types are also always available, but they aren’t as restrictive as the keywords in their usage.

An example of something you can’t do with Python keywords is assign something to them. If you try, then you’ll get a SyntaxError. You won’t get a SyntaxError if you try to assign something to a built-in function or type, but it still isn’t a good idea. For a more in-depth explanation of ways keywords can be misused, check out Invalid Syntax in Python: Common Reasons for SyntaxError.

There are thirty-five keywords in Python. Here’s a list of them, each linked to its relevant section in this tutorial:

Two keywords have additional uses beyond their initial use cases. The else keyword is also used with loops and with try and except in addition to in conditional statements. The as keyword is most commonly used in import statements, but also used with the with keyword.

The list of Python keywords and soft keywords has changed over time. For example, the await and async keywords weren’t added until Python 3.7. Also, both print and exec were keywords in Python 2.7 but were turned into built-in functions in Python 3 and no longer appear in the keywords list.

Python Soft Keywords

As mentioned above, you’ll get an error if you try to assign something to a Python keyword. Soft keywords, on the other hand, aren’t that strict. They syntactically act as keywords only in certain conditions.

This new capability was made possible thanks to the introduction of the PEG parser in Python 3.9, which changed how the interpreter reads the source code.

Leveraging the PEG parser allowed for the introduction of structural pattern matching in Python. In order to use intuitive syntax, the authors picked match, case, and _ for the pattern matching statements. Notably, match and case are widely used for this purpose in many other programming languages.

To prevent conflicts with existing Python code that already used match, case, and _ as variable or function names, Python developers decided to introduce the concept of soft keywords.

Currently, there are four soft keywords in Python:

You can use the links above to jump to the soft keywords you’d like to read about, or you can continue reading for a guided tour.

Value Keywords: True, False, None

There are three Python keywords that are used as values. These values are singleton values that can be used over and over again and always reference the exact same object. You’ll most likely see and use these values a lot.

There are a few terms used in the sections below that may be new to you. They’re defined here, and you should be aware of their meaning before proceeding:

Read the full article at https://realpython.com/python-keywords/ »


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

February 12, 2025 02:00 PM UTC


EuroPython Society

Changes in the Grants Programme for 2025

TL;DR:

Background:

The EPS introduced a Grant Programme in 2017. Since then, we have granted almost EUR 350k through the programme, partly via EuroPython Finaid and by directly supporting other Python events and projects across Europe. In the last two years, the Grant Programme has grown to EUR 100k per year, with even more requests coming in.

With this growth come new challenges in how to distribute funds fairly so that more events can benefit. Looking at data from the past two years, we’ve often been close to or over our budget. The guidelines haven’t been updated in a while. As grant requests become more complex, we’d like to simplify and clarify the process, and better explain it on our website.

We would also like to acknowledge that EuroPython, when traveling around Europe, has an additional impact on the host country, and we’d like to set aside part of the budget for the local community.

The Grant Programme is also a primary funding source for EuroPython Finaid. To that end, we aim to allocate 30% of the total Grant Programme budget to Finaid, an increase from the previous 25%.

Changes:

Using 2024 data, and the budget available for Community Grants (60% of total), we’ve simulated different budget caps and found a sweet spot at 6000EUR, where we are able to support all the requests with most of the grants being below that limit. For 2025 we expect to receive a similar or bigger number of requests.


2024

6k

5k

4k

3.5

3

Grant #1

€ 4,000.00

€ 4,000.00

€ 4,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #2

€ 8,000.00

€ 6,000.00

€ 5,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #3

€ 4,000.00

€ 4,000.00

€ 4,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #4

€ 5,000.00

€ 5,000.00

€ 5,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #5

€ 10,000.00

€ 6,000.00

€ 5,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #6

€ 4,000.00

€ 4,000.00

€ 4,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #7

€ 1,000.00

€ 1,000.00

€ 1,000.00

€ 1,000.00

€ 1,000.00

€ 1,000.00

Grant #8

€ 5,000.00

€ 5,000.00

€ 5,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #9

€ 6,000.00

€ 6,000.00

€ 5,000.00

€ 4,000.00

€ 3,500.00

€ 3,000.00

Grant #10

€ 2,900.00

€ 2,900.00

€ 2,900.00

€ 2,900.00

€ 2,900.00

€ 2,900.00

Grant #11

€ 2,000.00

€ 2,000.00

€ 2,000.00

€ 2,000.00

€ 2,000.00

€ 2,000.00

Grant #12

€ 3,000.00

€ 3,000.00

€ 3,000.00

€ 3,000.00

€ 3,000.00

€ 3,000.00

Grant #13

€ 450.00

€ 450.00

€ 450.00

€ 450.00

€ 450.00

€ 450.00

Grant #14

€ 3,000.00

€ 3,000.00

€ 3,000.00

€ 3,000.00

€ 3,000.00

€ 3,000.00

Grant #15

€ 1,000.00

€ 1,000.00

€ 1,000.00

€ 1,000.00

€ 1,000.00

€ 1,000.00

Grant #16

€ 2,000.00

€ 2,000.00

€ 2,000.00

€ 2,000.00

€ 2,000.00

€ 2,000.00

Grant #17

€ 3,500.00

€ 3,500.00

€ 3,500.00

€ 3,500.00

€ 3,500.00

€ 3,000.00

Grant #18

€ 1,500.00

€ 1,500.00

€ 1,500.00

€ 1,500.00

€ 1,500.00

€ 1,500.00

SUM

€ 66,350.00

€ 60,350.00

€ 57,350.00

€ 52,350.00

€ 48,350.00

€ 43,850.00


alt

We are introducing a special 10% pool of money to be used on projects in the host country (in 2025 that’s again Czech Republic). This pool is set aside at the beginning of the year, with one caveat that we would like to deploy it in the first half of the year. Whatever is left unused goes back to the Community Pool to be used in second half of the year.

Expected outcome:

February 12, 2025 01:16 PM UTC


Real Python

Quiz: Python Keywords: An Introduction

In this quiz, you’ll test your understanding of Python Keywords.

Python keywords are reserved words with specific functions and restrictions in the language. These keywords are always available in Python, which means you don’t need to import them. Understanding how to use them correctly is fundamental for building Python programs.


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

February 12, 2025 12:00 PM UTC


Zato Blog

Modern REST API Tutorial in Python

Modern REST API Tutorial in Python

Great APIs don't win theoretical arguments - they just prefer to work reliably and to make developers' lives easier.

Here's a tutorial on what building production APIs is really about: creating interfaces that are practical in usage, while keeping your systems maintainable for years to come.

Sound intriguing? Read the modern REST API tutorial in Python here.

Modern REST API tutorial in Python

More resources

➤ Python API integration tutorials
What is a Network Packet Broker? How to automate networks in Python?
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

February 12, 2025 08:00 AM UTC


Kushal Das

pass using stateless OpenPGP command line interface

Yesterday I wrote about how I am using a different tool for git signing and verification. Next, I replaced my pass usage. I have a small patch to use stateless OpenPGP command line interface (SOP). It is an implementation agonostic standard for handling OpenPGP messages. You can read the whole SPEC here.

Installation

cargo install rsop rsop-oct

And copied the bash script from my repository to the path somewhere.

The rsoct binary from rsop-oct follows the same SOP standard but uses the card to signing/decryption. I stored my public key in ~/.password-store/.gpg-key file, which is in turn used for encryption.

Usage

Here nothing changed related my daily pass usage, except the number of time I am typing my PIN :)

February 12, 2025 05:26 AM UTC

February 11, 2025


PyCoder’s Weekly

Issue #668: NumPy, Compiling Python 1.0, BytesIO, and More (Feb. 11, 2025)

#668 – FEBRUARY 11, 2025
View in Browser »

The PyCoder’s Weekly Logo


NumPy Techniques and Practical Examples

In this video course, you’ll learn how to use NumPy by exploring several interesting examples. You’ll read data from a file into an array and analyze structured arrays to perform a reconciliation. You’ll also learn how to quickly chart an analysis & turn a custom function into a vectorized function.
REAL PYTHON course

Let’s Compile Python 1.0

As part of the celebration of 31 years of Python, Bite Code compiles the original Python 1.0 and plays around with it.
BITE CODE!

Postman AI Agent Builder Is Here: The Quickest Way to Build AI Agents. Start Building

alt

Postman AI Agent Builder is a suite of solutions that accelerates agent development. With centralized access to the latest LLMs and APIs from over 18,000 companies, plus no-code workflows, you can quickly connect critical tools and build multi-step agents — all without writing a single line of code →
POSTMAN sponsor

Save Memory With BytesIO

If you want to save memory when reading from a BytesIO object, getvalue() is surprisingly a good choice.
ITAMAR TURNER-TRAURING

Beautifulsoup 4.13 Released

LAUNCHPAD.NET

PEP 759: External Wheel Hosting (Withdrawn)

PYTHON.ORG

PEP 2026: Calendar Versioning for Python (Rejected)

PYTHON.ORG

PEP 739: Static Description File for Build Details (Accepted)

PYTHON.ORG

Python 3.13.2 and 3.12.9 Released

CPYTHON DEV BLOG

Quiz: How to Split a String in Python

REAL PYTHON

Discussions

Add a New ApiMisuseException

PYTHON.ORG

PEP 764: Inlined Typed Dictionaries

PYTHON.ORG

Python Jobs

Backend Software Engineer (Anywhere)

Brilliant.org

More Python Jobs >>>

Articles & Tutorials

How to Split a String in Python

This tutorial will help you master Python string splitting. You’ll learn to use .split(), .splitlines(), and re.split() to effectively handle whitespace, custom delimiters, and multiline text, which will level up your data parsing skills.
REAL PYTHON

The Mutable Trap: Avoiding Unintended Side Effects in Python

“Ever had a Python function behave strangely, remembering values between calls when it shouldn’t? You’re not alone! This is one of Python’s sneakiest pitfalls—mutable default parameters.”
CRAIG RICHARDS • Shared by Bob

Posit Package Manager: Secure Python Library Management

Python developers use Posit Package Manager to mirror public & internally developed repos within their firewalls. Get reporting on known vulnerabilities to proactively address potential threats. High-security environments can even run air-gapped.
POSIT sponsor

Decorator JITs: Python as a DSL

There are several Just In Time compilation tools out there that allow you to decorate a function to indicate you want it compiled. This article shows you how that works.
ELI BENDERSKY

Better Unit-Tests for Email With Django 5.2

Django 5.2 contains a new helper on the email class to make it easier to write unit-tests validating that your email contains the content you expect it to contain.
MEDIUM.COM/AMBIENT-INNOVATION • Shared by Ronny Vedrilla

Rendering Form Fields as Group in Django

Django 5.0 added the concept of field groups which make it easier to customize the layout of Django forms. This article covers what groups are and how to use them.
VALENTINO GAGLIARDI

Developer Philosophy

The author was recently invited with other senior devs to give a lightning talk on their personal development philosophy. This post captures those thoughts.
QNTM

Interrupting Scripts Without Tracebacks

This Things-I’ve-Learned post talks about how you can suppress the KeyboardInterrupt expression so your program doesn’t exit with a traceback.
RODRIGO GIRÃO SERRÃO

PEP 772: Packaging Governance Process

This PEP proposes a Python Packaging Council with broad authority over packaging standards, tools, and implementations.
PYTHON.ORG

Python Terminology: An Unofficial Glossary

“Definitions for colloquial Python terminology (effectively an unofficial version of the Python glossary).”
TREY HUNNER

Projects & Code

shellingham: Tool to Detect Surrounding Shell

GITHUB.COM/SARUGAKU

mrab-regex: Backwards Compatible, Improved RegEx Engine

GITHUB.COM/MRABARNETT

python-sortedcontainers: Python Sorted Container Types

GITHUB.COM/GRANTJENKS

soupsieve: CSS Selector Implementation for BeautifulSoup

GITHUB.COM/FACELESSUSER

platformdirs: Get Platform-Specific Dirs, e.g. “User Data Dir”

GITHUB.COM/TOX-DEV

Events

Weekly Real Python Office Hours Q&A (Virtual)

February 12, 2025
REALPYTHON.COM

Python Atlanta

February 14, 2025
MEETUP.COM

Python Barcamp Karlsruhe 2025

February 15 to February 17, 2025
BARCAMPS.EU

Inland Empire Python Users Group Monthly Meeting

February 19, 2025
MEETUP.COM

PyData Bristol Meetup

February 20, 2025
MEETUP.COM

DjangoCongress JP 2025

February 22 to February 23, 2025
DJANGOCONGRESS.JP

PyConf Hyderabad 2025

February 22 to February 24, 2025
PYCONFHYD.ORG


Happy Pythoning!
This was PyCoder’s Weekly Issue #668.
View in Browser »

alt

[ Subscribe to 🐍 PyCoder’s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]

February 11, 2025 07:30 PM UTC


Python Insider

Python 3.14.0 alpha 5 is out

Here comes the antepenultimate alpha.

https://www.python.org/downloads/release/python-3140a5/

This is an early developer preview of Python 3.14

Major new features of the 3.14 series, compared to 3.13

Python 3.14 is still in development. This release, 3.14.0a5, is the fifth of seven planned alpha releases.

Alpha releases are intended to make it easier to test the current state of new features and bug fixes and to test the release process.

During the alpha phase, features may be added up until the start of the beta phase (2025-05-06) and, if necessary, may be modified or deleted up until the release candidate phase (2025-07-22). Please keep in mind that this is a preview release and its use is not recommended for production environments.

Many new features for Python 3.14 are still being planned and written. Among the new major new features and changes so far:

The next pre-release of Python 3.14 will be the penultimate alpha, 3.14.0a6, currently scheduled for 2025-03-14.

More resources

And now for something completely different

2025-01-29 marked the start of a new lunar year, the Year of the Snake 🐍 (and the Year of Python?).

For centuries, π was often approximated as 3 in China. Some time between the years 1 and 5 CE, astronomer, librarian, mathematician and politician Liu Xin (劉歆) calculated π as 3.154.

Around 130 CE, mathematician, astronomer, and geographer Zhang Heng (張衡, 78–139) compared the celestial circle with the diameter of the earth as 736:232 to get 3.1724. He also came up with a formula for the ratio between a cube and inscribed sphere as 8:5, implying the ratio of a square’s area to an inscribed circle is √8:√5. From this, he calculated π as √10 (~3.162).

Third century mathematician Liu Hui (刘徽) came up with an algorithm for calculating π iteratively: calculate the area of a polygon inscribed in a circle, then as the number of sides of the polygon is increased, the area becomes closer to that of the circle, from which you can approximate π.

This algorithm is similar to the method used by Archimedes in the 3rd century BCE and Ludolph van Ceulen in the 16th century CE (see 3.14.0a2 release notes), but Archimedes only went up to a 96-sided polygon (96-gon). Liu Hui went up to a 192-gon to approximate π as 157/50 (3.14) and later a 3072-gon for 3.14159.

Liu Hu wrote a commentary on the book The Nine Chapters on the Mathematical Art which included his π approximations.

In the fifth century, astronomer, inventor, mathematician, politician, and writer Zu Chongzhi (祖沖之, 429–500) used Liu Hui’s algorithm to inscribe a 12,288-gon to compute π between 3.1415926 and 3.1415927, correct to seven decimal places. This was more accurate than Hellenistic calculations and wouldn’t be improved upon for 900 years.

Happy Year of the Snake!

Enjoy the new release

Thanks to all of the many volunteers who help make Python Development and these releases possible! Please consider supporting our efforts by volunteering yourself or through organisation contributions to the Python Software Foundation.

Regards from a remarkably snowless Helsinki,

Your release team,
Hugo van Kemenade
Ned Deily
Steve Dower
Łukasz Langa

February 11, 2025 04:25 PM UTC


Real Python

Building a Python Command-Line To-Do App With Typer

Building an application to manage your to-do list can be an interesting project when you’re learning a new programming language or trying to take your skills to the next level. In this video course, you’ll build a functional to-do application for the command line using Python and Typer, which is a relatively young library for creating powerful command-line interface (CLI) applications in almost no time.

With a project like this, you’ll apply a wide set of core programming skills while building a real-world application with real features and requirements.

In this video course, you’ll learn how to:


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

February 11, 2025 02:00 PM UTC


Kushal Das

Using openpgp-card-tool-git with git

One of the power of Unix systems comes from the various small tools and how they work together. One such new tool I am using for some time is for git signing & verification using OpenPGP and my Yubikey for the actual signing operation via openpgp-card-tool-git. I replaced the standard gpg for this usecase with the oct-git command from this project.

Installation & configuration

cargo install openpgp-card-tool-git

Then you will have to configuration your (in my case the global configuration) git configuration.

git config --global gpg.program <path to oct-git>

I am assuming that you already had it configured before for signing, otherwise you have to run the following two commands too.

git config --global commit.gpgsign true
git config --global tag.gpgsign true

Usage

Before you start using it, you want to save the pin in your system keyring.

Use the following command.

oct-git --store-card-pin

That is it, now your git commit will sign the commits using oct-git tool.

In the next blog post I will show how to use the other tools from the author for various different OpenPGP oeprations.

February 11, 2025 11:12 AM UTC


Django Weblog

DSF member of the month - Lily Foote

For February 2025, we welcome Lily Foote (@lilyf) as our DSF member of the month! ⭐

Lily Foote is a contributor to Django core for many years, especially on the ORM. She is currently a member of the Django 6.x Steering Council and she has been a DSF member since March 2021.
You can learn more about Lily by visiting her GitHub profile.

Let’s spend some time getting to know Lily better!

Can you tell us a little about yourself (hobbies, education, etc)

My name is Lily Foote and I’ve been contributing to Django for most of my career. I’ve also recently got into Rust and I’m excited about using Rust in Python projects. When I’m not programming, I love hiking, climbing and dancing (Ceilidh)! I also really enjoying playing board games and role playing games (e.g. Dungeons and Dragons).

How did you start using Django?

I’d taught myself Python in my final year at university by doing Project Euler problems and then decided I wanted to learn how to make a website. Django was the first Python web framework I looked at and it worked really well for me.

What other framework do you know and if there is anything you would like to have in Django if you had magical powers?

I’ve done a small amount with Flask and FastAPI. More than any new features, I think the thing that I’d most like to see is more long-term contributors to spread the work of keeping Django awesome.

What projects are you working on now?

The side project I’m most excited about is Django Rusty Templates, which is a re-implementation of Django’s templating language in Rust.

Which Django libraries are your favorite (core or 3rd party)?

The ORM of course!

What are the top three things in Django that you like?

Django Conferences, the mentorship programme Djangonaut Space and the whole community!

You have been a mentor multiple times with GSoC and Djangonaut Space program, what is required according to you to be a good mentor?

I think being willing to invest time is really important. Checking in with your mentees frequently and being an early reviewer of their work. I think this helps keep their motivation up and allows for small corrections early on.

Any advice for future contributors?

Start small and as you get more familiar with Django and the process of contributing you can take on bigger issues. Also be patient with reviewers – Django has high standards, but is mostly maintained by volunteers with limited time.

Yes! It’s a huge honour! Since January, we’ve been meeting weekly and it feels like we’ve hardly scratched the surface of what we want to achieve. The biggest thing we’re trying to tackle is how to improve the contribution experience – especially evaluating new feature ideas – without draining everyone’s time and energy.

You have a lot of knowledge in the Django ORM, how did you start to contribute to this part?

I added the Greatest and Least expressions in Django 1.9, with the support of one of the core team at the time. After that, I kept showing up (especially at conference sprints) and finding a new thing to tackle.

Is there anything else you’d like to say?

Thanks for having me on!


Thank you for doing the interview, Lily!

February 11, 2025 04:51 AM UTC


Seth Michael Larson

Building software for connection (#2: Consensus)

This is the second article in a series about “software for connection”.

In the previous article we concluded that a persistent always-on internet connection isn't required for software to elicit feelings of connection between humans.

Building on this conclusion: let's explore how Animal Crossing software was able to intercommunicate without requiring a centralized server and infrastructure and the trade-offs for these design decisions.


Image of Tom Nook from an Animal Crossing online contest (Nookipedia)

Distributing digital goods without the internet

Animal Crossing has over 1,000 unique items that need to be collected for a complete catalog, including furniture, wallpapers, clothing, parasols, and carpets. Many of these items are quite rare or were only programmed to be accessible through an official Nintendo-affiliated distribution such as a magazine or online contest.

Beyond official distributions, it's clear Animal Crossings' designer, Katsuya Eguchi, wanted players to cooperate to complete their catalogs. The game incentivized trading items between towns by assigning one “native fruit” (Apple, Orange, Cherry, Peach, or Pear) and randomly making a subset of items harder to find than others depending on a hidden “item group” variable (either A, B, or C).

Items could be exchanged between players when one player visits another town, but this required physically bringing your memory card to another players' GameCube. The GameCube might have come with a handle, but the 'cube wasn't exactly a portable console. Sharing a physical space isn't something you can do with everyone or on a regular basis.

So what did Katsuya Eguchi design for Animal Crossing? To allow for item distributions from magazines and contests and to make player-to-player item sharing easier Animal Crossing included a feature called “secret codes”.

This feature worked by allowing players to exchange 28-character codes with Tom Nook for items. Players could also generate codes for their friends to “send” an item from their own game to a different town. Codes could be shared by writing them on a paper note, instant message, or text message.


Huntr R. explaining how “secret codes” are implemented. A surprising amount of cryptography!

The forgotten durability of offline software

This Reddit comment thread from the GameCube subreddit was the initial inspiration for this entire series. The post is about someone's niece who just started playing Animal Crossing for the first time. The Redditor asked folks to send items to their nieces' town using the secret code system.

This ended up surprising many folks that this system still worked in a game that was over 23 years old! For reference, Nintendo Wi-Fi Connection and Nintendo Network were only available for 8 and 13 years respectively. Below are a handful of the comments from the thread:

  • “That's still online???”
  • “It was online???!”
  • “For real does this still work lol?”
  • “...Was it ever online?”


secret code for my favorite Animal Crossing NES game Wario's Woods:

Xvl5HeG&C9prXu
IWhuzBinlVlqOg

It's hard not to take these comments as indicators that something is very wrong with internet-connected software today. What had to go wrong for a system continuing to work to be met with surprise? Many consumers' experience with software products today is that they become useless e-waste after some far-away service is discontinued a few years after purchase.

My intuition from this is that software that requires centralized servers and infrastructure to function will have shorter lifetimes than software which is offline or only opportunistically uses online functionality.

I don't think this is particularly insightful, more dependencies always means less resilience. But if we're building software for human connection then the software should optimally only be limited by the availability of humans to connect.

What is centralization good for?

CODETYPE
CODETYPE
HIT%
HIT%
S?
S?
NPC CODE
NPC CODE
PLAYER NAME
PLAYER NAME
ITEM NUMBER
ITEM NUMBER
TOWN NAME
TOWN NAME
CHKSM
CHKSM
1 byte
1 byte
20 bytes total
20 bytes t...Text is not SVG - cannot display
Data layout of secret codes before being encrypted (Animal Crossing decompilation project)

Animal Crossings' secret code system is far from perfect. The system is easily abusable, as the same secret codes can be reused over-and-over by the same user to duplicate items without ever expiring. The only limit was that 3 codes could be used per day.

Secret codes are tied to a specific town and recipient name, but even this stopgap can be defeated by setting your name and town name to specific values to share codes across many different players.

Not long after Animal Crossing's release the secret code algorithm was reverse-engineered so secret codes for any item could be created for any town and recipient name as if they came from an official Nintendo distribution. This was possible because the secret code system relied on "security through obscurity".

Could centralization be the answer to preventing these abuses?

The most interesting property that a centralized authority approach provides is global consensus: forcing everyone to play by the same rules. By storing the “single source-of-truth” a central authority is able to prevent abuses like the ones mentioned above.

For example, a centralized “secret code issuing server” could generate new unique codes per-use and check each code's validity against a database to prevent users from generating their own illegitimate codes or codes being re-used multiple times.

The problem with centralized consensus is it tends to be viral to cover the entire software state. A centralized server can generate codes perfectly, but how can that same server know that the items you're exchanging for codes were obtained legitimately? To know this the server would also need to track item legitimacy, leading to software which requires an internet connection to operate.

This is optimal from a correctness perspective, but as was noted earlier, I suspect that if such a server was a mandatory part of the secret code system in Animal Crossing that the system would likely not be usable today.

This seems like a trade-off, which future would you rather have?

Redesigning Animal Crossing secret codes

If I were designing Animal Crossings' secret code system with modern hardware, what would it look like? How can we keep the offline fall-back while providing consensus and being less abusable, especially for official distributions.

I would likely use a public-key cryptographic system for official distributions, embedding a certificate that could be used to “verify” that specific secret codes originated from the expected centralized entity. Codes that are accepted would be recorded to prevent reusing the same code multiple times in the same town. Using public-key cryptography prevents the system from being reverse-engineered to distribute arbitrary items until the certificate private key was cracked.

For sharing items between players I would implement a system where each town generated a public and private key and the public key was shared to other towns whenever the software was able to, such as when a player visited the other town. Players would only be able to send items to players that they have visited (which for Animal Crossing required physical presence, more on this later!)

Each sender could store a nonce value for each potential recipient. Embedding that nonce into the secret code would allow the recipients' software to verify that the specific code hadn't been used yet. The nonce wouldn't have to be long to avoid simple reusing of codes.

Both above systems would require much more data to be embedded into each “secret code” compared to the 28-character codes from the GameCube. For this I would use QR codes to embed over 2KB of data into a single QR code. Funnily enough, Animal Crossing New Leaf and onwards use QR code technology for players to share design patterns.

This design is still abusable if users can modify their software or hardware but doesn't suffer from the trivial-to-exploit flaws of Animal Crossing's secret code system.

Decentralized global consensus?

What if we could have the best of both worlds: we want consensus that is both global and decentralized. At least today, we are out of luck.

Decentralized global consensus is technologically feasible, but the existing solutions (mostly blockchains) are expensive (both in energy and capital) and can't handle throughput on any sort of meaningful scale.

Efficient
Efficient
Decentralized
Decentralized
Global
Global
Impossible?
Impossible?Text is not SVG - cannot display
Pick two: Decentralized, Global, and Efficient

There are many other decentralized consensus systems that are able to form “pockets” of useful peer-to-peer consensus using a fraction of the resources, such as email, BitTorrent, ActivityPub, and Nostr. These systems are only possible by adding some centralization or by only guaranteeing local consensus.

When is global consensus needed?

Obviously global consensus is important for certain classes of software like financial, civics, and infrastructure, but I wonder how the necessity of consensus in software changes for software with different risk profiles.

For software which has fewer risks associated with misuse is there as much need for global consensus? How can software for connection be designed to reduce risk and require less consensus to be effective? If global consensus and centralized servers become unnecessary, can we expect software for connection to be usable on much longer timescales, essentially for as long as there are users?

February 11, 2025 12:00 AM UTC


Quansight Labs Blog

PEP 517 build system popularity

Analysis of PEP 517 build backends used in 8000 top PyPI packages

February 11, 2025 12:00 AM UTC

February 10, 2025


Python Morsels

Newlines and escape sequences in Python

Python allows us to represent newlines in strings using the \n "escape sequence" and Python uses line ending normalization when reading and writing with files.

Table of contents

  1. Newline characters
  2. Escape sequences in Python
  3. Using escape sequences to avoid syntax errors
  4. Whitespace characters in Python
  5. Carriage returns
  6. The history of line endings
  7. Newline normalization in Python
  8. Writing files with custom line endings
  9. Python uses \n to represent newline characters

Newline characters

This string contains a newline character:

>>> text = "Hello\nworld"
>>> text
'Hello\nworld'

That's what \n represents: a newline character.

If we print this string, we'll see that \n becomes an actual newline:

>>> print(text)
Hello
world

Why does Python represent a newline as \n?

Escape sequences in Python

Every character in a Python …

Read the full article: https://www.pythonmorsels.com/newlines-and-escape-sequences/

February 10, 2025 03:17 PM UTC


Real Python

How to Join Strings in Python

Python’s built-in string method .join() lets you combine string elements from an iterable into a single string, using a separator that you specify. You call .join() on the separator, passing the iterable of strings to join.

By the end of this tutorial, you’ll understand that:

  • You use .join() in Python to combine string elements with a specified separator.
  • A separator is the piece of text you want inserted between each substring.
  • To join list elements, you call .join() on a separator string, passing the list as the argument.
  • .join() inserts the separator between each list element to form a single string.
  • The .join() method returns a new string that is the concatenation of the elements in the iterable, separated by the specified string.
  • For smaller string concatenation tasks, you can use the concatenation operator (+) or f-strings instead of .join().

Python’s built-in str.join() method gives you a quick and reliable way to combine multiple strings into a single string. Whether you need to format output or assemble data for storage, .join() provides a clean and efficient approach for joining strings from an iterable.

In the upcoming sections, you’ll learn the basic usage of .join() to concatenate strings effectively. You’ll then apply that knowledge to real-world scenarios, from building CSV files to constructing custom log outputs. You’ll also discover some surprising pitfalls and learn how to avoid them.

Get Your Code: Click here to download the free sample code that shows you how to join strings in Python.

Take the Quiz: Test your knowledge with our interactive “How to Join Strings in Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

How to Join Strings in Python

Test your understanding of Python's .join() string method for combining strings, handling edge cases, and optimizing performance.

How to Join Strings in Python Using .join()

To use the string method .join(), you call .join() on a separator string and pass an iterable of other strings as the argument. The method returns a single string, where it has inserted the separator string between each element of the iterable:

Python
>>> words = ["Time", "flies", "like", "an", "arrow!"]
>>> " ".join(words)
'Time flies like an arrow!'
Copied!

In this example, you joined a list of words into one sentence, separated by spaces.

At first glance, this usage might look a little backward. In many other string operations, you call the method on the main string that you want to manipulate. However, with .join(), you call the method on the separator string, then pass the iterable of strings that you want to combine:

Python
>>> separator = " "
>>> separator.join(words)
'Time flies like an arrow!'
Copied!

This example achieves the same result as the earlier one but splits the process into two steps. Defining separator separately makes the code more readable and avoids the potentially odd-looking syntax of calling .join() directly on a short string literal.

Note: Remember that .join() is a string method, which means that you’ll need to call it on a single string object. Keeping that in mind may help you remember why you need to call it on the separator string.

You rarely see code that’s written in multiple steps where you assign the separator string to a variable, like you did in the example above.

In typical usage, you call .join() directly on the separator string, all in one line. This approach is more concise and highlights that any valid string can be your separator, whether it’s whitespace, a dash, or a multicharacter substring.

Join With an Empty String

What if you don’t want any separator at all, but just want to concatenate the items? One valid approach is to use an empty string ("") as the separator:

Python
>>> letters = ["A", "B", "C", "D"]
>>> "".join(letters)
'ABCD'
Copied!

This code snippet concatenates the letters in the list, forming a single string "ABCD". Using an empty string as the separator is a great way to assemble strings without a delimiter between them.

Combine Strings of Characters

Since .join() can take any iterable of strings—not just lists—you can even pass a string as an argument. Because strings are iterable, Python iterates over each character in that string, considering each character as a separate element:

Python
>>> characters = "ABCD"
>>> ",".join(characters)
'A,B,C,D'
Copied!

By calling .join() on "," and passing the string characters, you effectively place a comma between every single character in "ABCD". This might not always be what you intend, but it’s a neat trick to keep in mind if you ever need to treat each character as a separate element.

Read the full article at https://realpython.com/python-join-string/ »


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

February 10, 2025 02:00 PM UTC


Quansight Labs Blog

Two years of contributions to conda-forge: work done during our CZI EOSS 5 grant

In 2022 we were awarded a CZI EOSS grant for conda-forge. The proposal, co-submitted by Quansight Labs and QuantStack, targeted three areas: maintaining and improving conda-forge's infrastructure, creating a new maintainer's dashboard, and implementing OCI-based mirroring for the packages. This work has now concluded and we would like to publish a summary of what we achieved!

February 10, 2025 12:00 AM UTC


meejah.ca

Terminal Visualizer

Showing numbers is fun and hard

February 10, 2025 12:00 AM UTC

February 09, 2025


Anarcat

A slow blogging year

Well, 2024 will be remembered, won't it? I guess 2025 already wants to make its mark too, but let's not worry about that right now, and instead let's talk about me.

A little over a year ago, I was gloating over how I had such a great blogging year in 2022, and was considering 2023 to be average, then went on to gather more stats and traffic analysis... Then I said, and I quote:

I hope to write more next year. I've been thinking about a few posts I could write for work, about how things work behind the scenes at Tor, that could be informative for many people. We run a rather old setup, but things hold up pretty well for what we throw at it, and it's worth sharing that with the world...

What a load of bollocks.

A bad year for this blog

2024 was the second worst year ever in my blogging history, tied with 2009 at a measly 6 posts for the year:

anarcat@angela:anarc.at$ curl -sSL https://anarc.at/blog/ | grep 'href="\./' | grep -o 20[0-9][0-9] | sort | uniq -c | sort -nr | grep -v 2025 | tail -3
      6 2024
      6 2009
      3 2014

I did write about my work though, detailing the migration from Gitolite to GitLab we completed that year. But after August, total radio silence until now.

Loads of drafts

It's not that I have nothing to say: I have no less than five drafts in my working tree here, not counting three actual drafts recorded in the Git repository here:

anarcat@angela:anarc.at$ git s blog
## main...origin/main
?? blog/bell-bot.md
?? blog/fish.md
?? blog/kensington.md
?? blog/nixos.md
?? blog/tmux.md
anarcat@angela:anarc.at$ git grep -l '\!tag draft'
blog/mobile-massive-gallery.md
blog/on-dying.mdwn
blog/secrets-recovery.md

I just don't have time to wrap those things up. I think part of me is disgusted by seeing my work stolen by large corporations to build proprietary large language models while my idols have been pushed to suicide for trying to share science with the world.

Another part of me wants to make those things just right. The "tagged drafts" above are nothing more than a huge pile of chaotic links, far from being useful for anyone else than me, and even then.

The on-dying article, in particular, is becoming my nemesis. I've been wanting to write that article for over 6 years now, I think. It's just too hard.

Writing elsewhere

There's also the fact that I write for work already. A lot. Here are the top-10 contributors to our team's wiki:

anarcat@angela:help.torproject.org$ git shortlog --numbered --summary --group="format:%al" | head -10
  4272  anarcat
   423  jerome
   117  zen
   116  lelutin
   104  peter
    58  kez
    45  irl
    43  hiro
    18  gaba
    17  groente

... but that's a bit unfair, since I've been there half a decade. Here's the last year:

anarcat@angela:help.torproject.org$ git shortlog --since=2024-01-01 --numbered --summary --group="format:%al" | head -10
   827  anarcat
   117  zen
   116  lelutin
    91  jerome
    17  groente
    10  gaba
     8  micah
     7  kez
     5  jnewsome
     4  stephen.swift

So I still write the most commits! But to truly get a sense of the amount I wrote in there, we should count actual changes. Here it is by number of lines (from commandlinefu.com):

anarcat@angela:help.torproject.org$ git ls-files | xargs -n1 git blame --line-porcelain | sed -n 's/^author //p' | sort -f | uniq -ic | sort -nr | head -10
  99046 Antoine Beaupré
   6900 Zen Fu
   4784 Jérôme Charaoui
   1446 Gabriel Filion
   1146 Jerome Charaoui
    837 groente
    705 kez
    569 Gaba
    381 Matt Traudt
    237 Stephen Swift

That, of course, is the entire history of the git repo, again. We should take only the last year into account, and probably ignore the tails directory, as sneaky Zen Fu imported the entire docs from another wiki there...

anarcat@angela:help.torproject.org$ find [d-s]* -type f -mtime -365 | xargs -n1 git blame --line-porcelain 2>/dev/null | sed -n 's/^author //p' | sort -f | uniq -ic | sort -nr | head -10
  75037 Antoine Beaupré
   2932 Jérôme Charaoui
   1442 Gabriel Filion
   1400 Zen Fu
    929 Jerome Charaoui
    837 groente
    702 kez
    569 Gaba
    381 Matt Traudt
    237 Stephen Swift

Pretty good! 75k lines. But those are the files that were modified in the last year. If we go a little more nuts, we find that:

anarcat@angela:help.torproject.org$ $ git-count-words-range.py  | sort -k6 -nr | head -10
parsing commits for words changes from command: git log '--since=1 year ago' '--format=%H %al'
anarcat 126116 - 36932 = 89184
zen 31774 - 5749 = 26025
groente 9732 - 607 = 9125
lelutin 10768 - 2578 = 8190
jerome 6236 - 2586 = 3650
gaba 3164 - 491 = 2673
stephen.swift 2443 - 673 = 1770
kez 1034 - 74 = 960
micah 772 - 250 = 522
weasel 410 - 0 = 410

I wrote 126,116 words in that wiki, only in the last year. I also deleted 37k words, so the final total is more like 89k words, but still: that's about forty (40!) articles of the average size (~2k) I wrote in 2022.

(And yes, I did go nuts and write a new log parser, essentially from scratch, to figure out those word diffs. I did get the courage only after asking GPT-4o for an example first, I must admit.)

Let's celebrate that again: I wrote 90 thousand words in that wiki in 2024. According to Wikipedia, a "novella" is 17,500 to 40,000 words, which would mean I wrote about a novella and a novel, in the past year.

But interestingly, if I look at the repository analytics. I certainly didn't write that much more in the past year. So that alone cannot explain the lull in my production here.

Arguments

Another part of me is just tired of the bickering and arguing on the internet. I have at least two articles in there that I suspect is going to get me a lot of push-back (NixOS and Fish). I know how to deal with this: you need to write well, consider the controversy, spell it out, and defuse things before they happen. But that's hard work and, frankly, I don't really care that much about what people think anymore.

I'm not writing here to convince people. I have stop evangelizing a long time ago. Now, I'm more into documenting, and teaching. And, while teaching, there's a two-way interaction: when you give out a speech or workshop, people can ask questions, or respond, and you all learn something. When you document, you quickly get told "where is this? I couldn't find it" or "I don't understand this" or "I tried that and it didn't work" or "wait, really? shouldn't we do X instead", and you learn.

Here, it's static. It's my little soapbox where I scream in the void. The only thing people can do is scream back.

Collaboration

So.

Let's see if we can work together here.

If you don't like something I say, disagree, or find something wrong or to be improved, instead of screaming on social media or ignoring me, try contributing back. This site here is backed by a git repository and I promise to read everything you send there, whether it is an issue or a merge request.

I will, of course, still read comments sent by email or IRC or social media, but please, be kind.

You can also, of course, follow the latest changes on the TPA wiki. If you want to catch up with the last year, some of the "novellas" I wrote include:

(Well, no, you can't actually follow changes on a GitLab wiki. But we have a wiki-replica git repository where you can see the latest commits, and subscribe to the RSS feed.)

See you there!

February 09, 2025 04:19 PM UTC


Talk Python to Me

#493: Quarto: Open-source technical publishing

In this episode, I'm joined by JJ Allaire, founder and executive chairman at Posit, and Carlos Scheidegger, a software engineer at Posit, to explore Quarto, an open-source tool revolutionizing technical publishing. We discuss how Quarto empowers users to seamlessly transform Jupyter notebooks into polished reports, dashboards, e-books, websites, and more. JJ shares his journey from creating RStudio to developing Quarto as a versatile, multi-language tool, while Carlos delves into its roots in reproducibility and the challenges of academic publishing. Don't miss this deep dive into a tool that's shaping the future of data-driven storytelling!<br/> <br/> <strong>Episode sponsors</strong><br/> <br/> <a href='https://talkpython.fm/training'>Talk Python Courses</a><br> <a href='https://talkpython.fm/digitalocean-partner'>DigitalOcean</a><br/> <br/> <h2>Links from the show</h2> <div><strong>JJ Allaire</strong><br/> <strong>JJ on LinkedIn</strong>: <a href="https://www.linkedin.com/in/jjallaire/?featured_on=talkpython" target="_blank" >linkedin.com</a><br/> <strong>JJ on GitHub</strong>: <a href="https://github.com/jjallaire?featured_on=talkpython" target="_blank" >github.com</a><br/> <br/> <strong>Carlos Scheidegger</strong><br/> <strong>Personal site</strong>: <a href="https://cscheid.net?featured_on=talkpython" target="_blank" >cscheid.net</a><br/> <strong>Mastodon</strong>: <a href="https://mastodon.social/@scheidegger?featured_on=talkpython" target="_blank" >@scheidegger</a><br/> <br/> <strong>Fast AI</strong>: <a href="https://fast.ai?featured_on=talkpython" target="_blank" >fast.ai</a><br/> <strong>nbdev</strong>: <a href="https://nbdev.fast.ai/?featured_on=talkpython" target="_blank" >nbdev.fast.ai</a><br/> <strong>nbsanity - Share Notebooks as Polished Web Pages in Seconds</strong>: <a href="https://www.answer.ai/posts/2024-12-13-nbsanity.html?featured_on=talkpython" target="_blank" >answer.ai</a><br/> <strong>Pandoc</strong>: <a href="https://pandoc.org/?featured_on=talkpython" target="_blank" >pandoc.org</a><br/> <strong>Observable</strong>: <a href="https://github.com/observablehq/framework?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Quarto Pub</strong>: <a href="https://quartopub.com/?featured_on=talkpython" target="_blank" >quartopub.com</a><br/> <strong>Deno</strong>: <a href="https://deno.com/?featured_on=talkpython" target="_blank" >deno.com</a><br/> <strong>Real World Data Science site</strong>: <a href="https://realworlddatascience.net/?featured_on=talkpython" target="_blank" >realworlddatascience.net</a><br/> <strong>Typst</strong>: <a href="https://typst.app/?featured_on=talkpython" target="_blank" >typst.app</a><br/> <strong>Github Actions for Quarto</strong>: <a href="https://github.com/quarto-dev/quarto-actions?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Watch this episode on YouTube</strong>: <a href="https://www.youtube.com/watch?v=AXkwy8dzCrA" target="_blank" >youtube.com</a><br/> <strong>Episode transcripts</strong>: <a href="https://talkpython.fm/episodes/transcript/493/quarto-open-source-technical-publishing" target="_blank" >talkpython.fm</a><br/> <br/> <strong>--- Stay in touch with us ---</strong><br/> <strong>Subscribe to Talk Python on YouTube</strong>: <a href="https://talkpython.fm/youtube" target="_blank" >youtube.com</a><br/> <strong>Talk Python on Bluesky</strong>: <a href="https://bsky.app/profile/talkpython.fm" target="_blank" >@talkpython.fm at bsky.app</a><br/> <strong>Talk Python on Mastodon</strong>: <a href="https://fosstodon.org/web/@talkpython" target="_blank" ><i class="fa-brands fa-mastodon"></i>talkpython</a><br/> <strong>Michael on Bluesky</strong>: <a href="https://bsky.app/profile/mkennedy.codes?featured_on=talkpython" target="_blank" >@mkennedy.codes at bsky.app</a><br/> <strong>Michael on Mastodon</strong>: <a href="https://fosstodon.org/web/@mkennedy" target="_blank" ><i class="fa-brands fa-mastodon"></i>mkennedy</a><br/></div>

February 09, 2025 08:00 AM UTC