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!
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.
🦒 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
- GeoPython
Basel, February 24-26, 2025 https://2025.geopython.net/ - PyCon Austria
Eisenstadt, April 6-7, 2025 https://pycon.pyug.at/en/ - PyCon Lithuania
Vilnius, April 23-25, 2025 https://pycon.lt/ - DjangoCon Europe
Dublin, April 23-27, 2025 https://2025.djangocon.eu/ - PyCon DE & PyData
Darmstadt, April 23-25, 2025 https://2025.pycon.de/ - Pycon Italia
Bologna, May 28-31, 2025 https://2025.pycon.it/en - PyCamp CZ 25 beta
Třeštice, September 12-14, 2025 https://pycamp.cz/ - Pycon UK
Manchester, September 19-22, 2025 https://2025.pyconuk.org/ - PyCon Estonia
Tallinn, October 2-3, 2025 https://pycon.ee/
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
MSYS2: Path normalization to native Windows format was required in more places for the
MinGW
variant of MSYS2.The
os.path.normpath
function doesn’t normalize to native Win32 paths with MSYS2, instead using forward slashes. This required manual normalization in additional areas. (Fixed in 2.5.1)UI: Fix, give a proper error when extension modules asked to include failed to be located. instead of a proper error message. (Fixed in 2.5.1)
Fix, files with illegal module names (containing
.
) in their basename were incorrectly considered as potential sub-modules for--include-package
. These are now skipped. (Fixed in 2.5.1)Stubgen: Improved stability by preventing crashes when stubgen encounters code it cannot handle. Exceptions from it are now ignored. (Fixed in 2.5.1)
Stubgen: Addressed a crash that occurred when encountering assignments to non-variables. (Fixed in 2.5.1)
Python 3: Fixed a regression introduced in 2.5 release that could lead to segmentation faults in exception handling for generators. (Fixed in 2.5.2)
Python 3.11+: Corrected an issue where dictionary copies of large split directories could become corrupted. This primarily affected instance dictionaries, which are created as copies until updated, potentially causing problems when adding new keys. (Fixed in 2.5.2)
Python 3.11+: Removed the assumption that module dictionaries always contain only strings as keys. Some modules, like
Foundation
on macOS, use non-string keys. (Fixed in 2.5.2)Deployment: Ensured that the
--deployment
option correctly affects the C compilation process. Previously, only individual disables were applied. (Fixed in 2.5.2)Compatibility: Fixed a crash that could occur during compilation when unary operations were used within binary operations. (Fixed in 2.5.3)
Onefile: Corrected the handling of
__compiled__.original_argv0
, which could lead to crashes. (Fixed in 2.5.4)Compatibility: Resolved a segmentation fault occurring at runtime when calling
tensorflow.function
with only keyword arguments. (Fixed in 2.5.5)macOS: Harmless warnings generated for x64 DLLs on arm64 with newer macOS versions are now ignored. (Fixed in 2.5.5)
Python 3.13: Addressed a crash in Nuitka’s dictionary code that occurred when copying dictionaries due to internal changes in Python 3.13. (Fixed in 2.5.6)
macOS: Improved onefile mode signing by applying
--macos-signed-app-name
to the signature of binaries, not just app bundles. (Fixed in 2.5.6)Standalone: Corrected an issue where too many paths were added as extra directories from the Nuitka package configuration. This primarily affected the
win32com
package, which currently relies on thepackage-dirs
import hack. (Fixed in 2.5.6)Python 2: Prevented crashes on macOS when creating onefile bundles with Python 2 by handling negative CRC32 values. This issue may have affected other versions as well. (Fixed in 2.5.6)
Plugins: Restored the functionality of code provided in
pre-import-code
, which was no longer being applied due to a regression. (Fixed in 2.5.6)macOS: Suppressed the app bundle mode recommendation when it is already in use. (Fixed in 2.5.6)
macOS: Corrected path normalization when the output directory argument includes “~”.
macOS: GitHub Actions Python is now correctly identified as a Homebrew Python to ensure proper DLL resolution. (Fixed in 2.5.7)
Compatibility: Fixed a reference leak that could occur with values sent to generator objects. Asyncgen and coroutines were not affected. (Fixed in 2.5.7)
Standalone: The
--include-package
scan now correctly handles cases where both a package init file and competing Python files exist, preventing compile-time conflicts. (Fixed in 2.5.7)Modules: Resolved an issue where handling string constants in modules created for Python 3.12 could trigger assertions, and modules created with 3.12.7 or newer failed to load on older Python 3.12 versions when compiled with Nuitka 2.5.5-2.5.6. (Fixed in 2.5.7)
Python 3.10+: Corrected the tuple code used when calling certain method descriptors. This issue primarily affected a Python 2 assertion, which was not impacted in practice. (Fixed in 2.5.7)
Python 3.13: Updated resource readers to accept multiple arguments for
importlib.resources.read_text
, and correctly handleencoding
anderrors
as keyword-only arguments.Scons: The platform encoding is no longer used to decode
ccache
logs. Instead,latin1
is used, as it is sufficient for matching filenames across log lines and avoids potential encoding errors. (Fixed in 2.5.7)Python 3.12+: Requests to statically link libraries for
hacl
are now ignored, as these libraries do not exist. (Fixed in 2.5.7)Compatibility: Fixed a memory leak affecting the results of functions called via specs. This primarily impacted overloaded hard import operations. (Fixed in 2.5.7)
Standalone: When multiple distributions for a package are found, the one with the most accurate file matching is now selected. This improves handling of cases where an older version of a package (e.g.,
python-opencv
) is overwritten with a different variant (e.g.,python-opencv-headless
), ensuring the correct version is used for Nuitka package configuration and reporting. (Fixed in 2.5.8)Python 2: Prevented a potential crash during onefile initialization on Python 2 by passing the directory name directly from the onefile bootstrap, avoiding the use of
os.dirname
which may not be fully loaded at that point. (Fixed in 2.5.8)Anaconda: Preserved necessary
PATH
environment variables on Windows for packages that require loading DLLs from those locations. OnlyPATH
entries not pointing inside the installation prefix are removed. (Fixed in 2.5.8)Anaconda: Corrected the
is_conda_package
check to function properly when distribution names and package names differ. (Fixed in 2.5.8)Anaconda: Improved package name resolution for Anaconda distributions by checking conda metadata when file metadata is unavailable through the usual methods. (Fixed in 2.5.8)
MSYS2: Normalized the downloaded gcc path to use native Windows slashes, preventing potential compilation failures. (Fixed in 2.5.9)
Python 3.13: Restored static libpython functionality on Linux by adapting to a signature change in an unexposed API. (Fixed in 2.5.9)
Python 3.6+: Prevented
asyncgen
from being resurrected when a finalizer is attached, resolving memory leaks that could occur withasyncio
in the presence of exceptions. (Fixed in 2.5.10)UI: Suppressed the gcc download prompt that could appear during
--version
output on Windows systems without MSVC or with an improperly installed gcc.Ensured compatibility with monkey patched
os.lstat
oros.stat
functions, which are used in some testing scenarios.Data Composer: Improved the determinism of the JSON statistics output by sorting keys, enabling reliable build comparisons.
Python 3.6+: Fixed a memory leak in
asyncgen
with finalizers, which could lead to significant memory consumption when usingasyncio
and encountering exceptions.Scons: Optimized empty generators (an optimization result) to avoid generating unused context code, eliminating C compilation warnings.
Python 3.6+: Fixed a reference leak affecting the
asend
value inasyncgen
. While typicallyNone
, this could lead to observable reference leaks in certain cases.Python 3.5+: Improved handling of
coroutine
andasyncgen
resurrection, preventing memory leaks withasyncio
andasyncgen
, and ensuring correct execution offinally
code in coroutines.Python 3: Corrected the handling of
generator
objects resurrecting during deallocation. While not explicitly demonstrated, this addresses potential issues similar to those encountered with coroutines, particularly for old-style coroutines created with thetypes.coroutine
decorator.PGO: Fixed a potential crash during runtime trace collection by ensuring timely initialization of the output mechanism.
Package Support
Standalone: Added inclusion of metadata for
jupyter_client
to support its own usage of metadata. (Added in 2.5.1)Standalone: Added support for the
llama_cpp
package. (Added in 2.5.1)Standalone: Added support for the
litellm
package. (Added in 2.5.2)Standalone: Added support for the
lab_lamma
package. (Added in 2.5.2)Standalone: Added support for
docling
metadata. (Added in 2.5.5)Standalone: Added support for
pypdfium
on Linux. (Added in 2.5.5)Standalone: Added support for using the
debian
package. (Added in 2.5.5)Standalone: Added support for the
pdfminer
package. (Added in 2.5.5)Standalone: Included missing dependencies for the
torch._dynamo.polyfills
package. (Added in 2.5.6)Standalone: Added support for
rtree
on Linux. The previous static configuration only worked on Windows and macOS; this update detects it from the module code. (Added in 2.5.6)Standalone: Added missing
pywebview
JavaScript data files. (Added in 2.5.7)Standalone: Added support for newer versions of the
sklearn
package. (Added in 2.5.7)Standalone: Added support for newer versions of the
dask
package. (Added in 2.5.7)Standalone: Added support for newer versions of the
transformers
package. (Added in 2.5.7)Windows: Placed
numpy
DLLs at the top level for improved support in the Nuitka VM. (Added in 2.5.7)Standalone: Allowed excluding browsers when including
playwright
. (Added in 2.5.7)Standalone: Added support for newer versions of the
sqlfluff
package. (Added in 2.5.8)Standalone: Added support for the
opencv
conda package, disabling unnecessary workarounds for its dependencies. (Added in 2.5.8)Standalone: Added support for newer versions of the
soundfile
package.Standalone: Added support for newer versions of the
coincurve
package.Standalone: Added support for newer versions of the
apscheduler
package.macOS: Removed the error and workaround forcing that required bundle mode for PyQt5 on macOS, as standalone mode now appears to function correctly.
Standalone: Added support for
seleniumbase
package downloads.
New Features
Module: Implemented 2-phase loading for all modules in Python 3.5 and higher. This improves loading modules as sub-packages in Python 3.12+, where the loading context is no longer accessible.
UI: Introduced the
app
value for the--mode
parameter. This creates an app bundle on macOS and a onefile binary on other platforms, replacing the--macos-create-app-bundle
option. (Added in 2.5.5)UI: Added a
package
mode, similar tomodule
, which automatically includes all sub-modules of a package without requiring manual specification with--include-package
.Module: Added an option to completely disable the use of
stubgen
. (Added in 2.5.1)Homebrew: Added support for
tcl9
with thetk-inter
plugin.Package Resolution: Improved handling of multiple distributions installed for the same package name. Nuitka now attempts to identify the most recently installed distribution, enabling proper recognition of different versions in scenarios like
python-opencv
andpython-opencv-headless
.Python 3.13.1 Compatibility: Addressed an issue where a workaround introduced for Python 3.10.0 broke standalone mode in Python 3.13.1. (Added in 2.5.6)
Plugins: Introduced a new feature for absolute source paths (typically derived from variables or relative to constants). This offers greater flexibility compared to the
by_code
DLL feature, which may be removed in the future. (Added in 2.5.6)Plugins: Added support for
when
conditions invariable
sections within Nuitka Package configuration.macOS: App bundles now automatically switch to the containing directory when not launched from the command line. This prevents the current directory from defaulting to
/
, which is rarely correct and can be unexpected for users. (Added in 2.5.6)Compatibility: Relaxed the restriction on setting the compiled frame
f_trace
. Instead of outright rejection, the deployment flag--no-deployment-flag=frame-useless-set-trace
can be used to allow it, although it will be ignored.Windows: Added the ability to detect extension module entry points using an inline copy of
pefile
. This enables--list-package-dlls
to verify extension module validity on the platform. It also opens possibilities for automatic extension module detection on major operating systems.Watch: Added support for using
conda
packages instead of PyPI packages.UI: Introduced
--list-package-exe
to complement--list-package-dlls
for package analysis when creating Nuitka Package Configuration.Windows ARM: Removed workarounds that are no longer necessary for compilation. While the lack of dependency analysis might require correction in a hotfix, this configuration should now be supported.
Optimization
Scalability: Implemented experimental code for more compact code object usage, leading to more scalable C code and constants usage. This is expected to speed up C compilation and code generation in the future once fully validated.
Scons: Added support for C23 embedding of the constants blob. This will be utilized with Clang 19+ and GCC 15+, except on Windows and macOS where other methods are currently employed.
Compilation: Improved performance by avoiding redundant path checks in cases of duplicated package directories. This significantly speeds up certain scenarios where file system access is slow.
Scons: Enhanced detection of static libpython, including for self-compiled, uninstalled Python installations.
Anti-Bloat
Improved
no_docstrings
support for thexgboost
package. (Added in 2.5.7)Avoided unnecessary usage of
numpy
for thePIL
package.Avoided unnecessary usage of
yaml
for thenumpy
package.Excluded
tcltest
TCL code when usingtk-inter
, as these TCL files are unused.Avoided using
IPython
from thecomm
package.Avoided using
pytest
from thepdbp
package.
Organizational
UI: Added categories for plugins in the
--help
output. Non-package support plugin options are now shown by default. Introduced a dedicated--help-plugins
option and highlighted it in the general--help
output. This allows viewing all plugin options without needing to enable a specific plugin.UI: Improved warnings for onefile and OS-specific options. These warnings are now displayed unless the command originates from a Nuitka-Action context, where users typically build for different modes with a single configuration set.
Nuitka-Action: The default
mode
is nowapp
, building an application bundle on macOS and a onefile binary on other platforms.UI: The executable path in
--version
output now uses the report path. This avoids exposing the user’s home directory, encouraging more complete output sharing.UI: The Python flavor name is now included in the startup compilation message.
UI: Improved handling of missing Windows version information. If only partial version information (e.g., product or file version) is provided, an explicit error is given instead of an assertion error during post-processing.
UI: Corrected an issue where the container argument for
run-inside-nuitka-container
could not be a non-template file. (Fixed in 2.5.2)Release: The PyPI upload
sdist
creation now uses a virtual environment. This ensures consistent project name casing, as it is determined by the setuptools version. While currently using the deprecated filename format, this change prepares for the new format.Release: The
osc
binary is now used from the virtual environment to avoid potential issues with a broken system installation, as currently observed on Ubuntu.Debugging: Added an experimental option to disable the automatic conversion to short paths on Windows.
UI: Improved handling of external data files that overwrite the original file. Nuitka now prompts the user to provide an output directory to prevent unintended overwrites. (Added in 2.5.6)
UI: Introduced the alias
--include-data-files-external
for the external data files option. This clarifies that the feature is not specific to onefile mode and encourages its wider use.UI: Allowed
none
as a valid value for the macOS icon option. This disables the warning about a missing icon when intentionally not providing one.UI: Added an error check for icon filenames without suffixes, preventing cases where the file type cannot be inferred.
UI: Corrected the examples for
--include-package-data
with file patterns, which used incorrect delimiters.Scons: Added a warning about using gcc with LTO when
make
is unavailable, as this combination will not work. This provides a clearer message than the standard gcc warnings, which can be difficult for Python users to interpret.Debugging: Added an option to preserve printing during reference count tests. This can be helpful for debugging by providing additional trace information.
Debugging: Added a small code snippet for module reference leak testing to the Developer Manual.
Tests
Temporarily disabled tests that expose regressions in Python 3.13.1 that mean not to follow.
Improved test organization by using more common code for package tests. The scanning for test cases and main files now utilizes shared code.
Added support for testing variations of a test with different extra flags. This is achieved by exposing a
NUITKA_TEST_VARIANT
environment variable.Improved detection of commercial-only test cases by identifying them through their names rather than hardcoding them in the runner. These tests are now removed from the standard distribution to reduce clutter.
Utilized
--mode
options in tests for better control and clarity. Standalone mode tests now explicitly check for the application of the mode and error out if it’s missing. Mode options are added to the project options of each test case instead of requiring global configuration.Added a test case to ensure comprehensive coverage of external data file usage in onefile mode. This helps detect regressions that may have gone unnoticed previously.
Increased test coverage for coroutines and async generators, including checks for
inspect.isawaitable
and testing both function and context objects.
Cleanups
Unified the code used for generating source archives for PyPI uploads, ensuring consistency between production and standard archives.
Harmonized the usage of
include <...>
vsinclude "..."
based on the origin of the included files, improving code style consistency.Removed code duplication in the exception handler generator code by utilizing the
DROP_GENERATOR_EXCEPTION
functions.Updated Python version checks to reflect current compatibility. Checks for
>=3.4
were changed to>=3
, and outdated references to Python 3.3 in comments were updated to simply “Python 3”.Scons: Simplified and streamlined the code for the command options. An
OrderedDict
is now used to ensure more stable build outputs and prevent unnecessary differences in recorded output.Improved the
executeToolChecked
function by adding an argument to indicate whether decoding of returnedbytes
output tounicode
is desired. This eliminates redundant decoding in many places.
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:
- Having to maintain a Python compatibility layers like psutil/_compat.py. This translated in extra extra code and extra imports.
- The C compatibility layer to differentiate between Python 2 and 3 (
#if PY_MAJOR_VERSION <= 3
, etc.). - Dealing with the string vs. unicode differences, both in Python and in C.
- Inability to use modern language features, especially f-strings.
- Inability to freely use
enum
s, which created a difference on how CONSTANTS were exposed in terms of API. - Having to install a specific version of
pip
and other (outdated) deps. - Relying on the third-party Appveyor CI service to run tests and produce 2.7 wheels.
- Running 4 extra CI jobs on every commit (Linux, macOS, Windows 32-bit, Windows 64-bit) making the CI slower and more subject to failures (we have quite a bit of flaky tests).
- The distribution of 7 wheels specific for Python 2.7. E.g. in the previous release I had to upload:
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
- Updating onboarding/offboarding checklists for Volunteers and Board Members
- Started development of https://github.com/EuroPython/internal-bot
- Event Manager onboarding
- Various infrastructure updates including new website deployment and self-hosted previews for Pull Requests to the website.
- Setting up EPS AWS account.
- Working out the Grant Guidelines update for 2025
- Attending PyConWeb and FOSDEM
- Reviewing updates to the Sponsors setup and packages for 2025
- More documentation, sharing know-how and reviewing new proposals.
Mia
- Brand strategy: Analysis of social media posts from previous years and web analytics. Call with a European open-source maintainer and a call with a local events organizer about EP content.
- Comms & design: Call for proposal announcements, EP 2024 video promotions, speaker mentorship, and newsletter. Video production - gathering videos from speakers, video post-production, and scheduling them on YouTube shorts, and social media.
- Event management coordination: Calls with the event manager and discussions about previous events.
- Grants: Work on new grant guidelines and related comms.
- Team onboarding: Calls with potential comms team members and coordination.
- PR: Delivering a lightning talk at FOSDEM.
Cyril
- Offboarding the old board
- Permission cleanup
- Team selection
- Onboarding new team members
- Administrative work on Grants
Aris
- Worked on the Grants proposal
- Teams selection
- Follow-up with team members
- Board meetings
- Financial updates
- Community outreach: FOSDEM
Ege
- Working on various infrastructure updates, mostly related to the website.
- Reviewing Pull Requests for the website and the internal bot
- Working on the infrastructure team proposal.
Shekhar
- Timeline: Discussion with the Programme Team, and planning to do the same with the other teams.
- Visa Request letter: Setup and Test Visa Request Automation for the current year
- Team selection discussion with past volunteers
- Board Meetings
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
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 thekeyword
module. - Soft keywords in Python act as keywords only in specific contexts.
print
andexec
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 IntroductionIn 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:
False |
await |
else |
import |
pass |
None |
break |
except |
in |
raise |
True |
class |
finally |
is |
return |
and |
continue |
for |
lambda |
try |
as |
def |
from |
nonlocal |
while |
assert |
del |
global |
not |
with |
async |
elif |
if |
or |
yield |
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:
- We are making small changes to the Grant Programme
- We are increasing transparency and reducing ambiguity in the guidelines.
- We would like to support more events with our limited budget
- We’ve introduced caps for events in order to make sure all grants are fairly given and we can support more communities.
- We’ve set aside 10% of our budget for the local community.
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:
- We’ve updated the text on our website, and split it into multiple sub-pages to make it easier to navigate. The website now includes a checklist of what we would like to see in a grant application, and a checklist for the Grants Workgroup – so that when you apply for the Grant you already know the steps that it will go through later and when you can expect an answer from us.
- We looked at the data from previous years, and size and timing of the grant requests. With the growing number and size of the grants, to make it more accessible to smaller conferences and conferences happening later in the year, we decided to introduce max caps per grant and split the budget equally between the first and second half of the year. We would also explicitly split the total budget into three categories – 30% goes to the EuroPython finaid, 10% is reserved for projects in the host country. The remaining 60% of the budget goes to fund other Python Conferences. This is similar to the split in previous years, but more explicit and transparent.
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 |

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:
- Fairer Funding: By spreading our grants out during the year, conferences that happen later won’t miss out.
- Easy to Follow: Clear rules and deadlines cut down on confusion about how much you can get and what it’s for.
- Better Accountability: We ask for simple post-event reports so we can see where the money went and what impact it made.
- Stronger Community: Funding more events grows our Python network across Europe, helping everyone learn, connect, and collaborate.
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.
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 »
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
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
Discussions
Python Jobs
Backend Software Engineer (Anywhere)
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
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 »
[ 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:
- PEP 649: deferred evaluation of annotations
- PEP 741: Python configuration C API
- PEP 761: Python 3.14 and onwards no longer provides PGP signatures for release artifacts. Instead, Sigstore is recommended for verifiers.
- Improved error messages
- A new type of interpreter. For certain newer compilers, this interpreter provides significantly better performance. Opt-in for now, requires building from source.
- Python removals and deprecations
- C API removals and deprecations
- (Hey, fellow core developer, if a feature you find important is missing from this list, let Hugo know.)
The next pre-release of Python 3.14 will be the penultimate alpha, 3.14.0a6, currently scheduled for 2025-03-14.
More resources
- Online documentation
- PEP 745, 3.14 Release Schedule
- Report bugs at github.com/python/cpython/issues
- Help fund Python and its community
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:
- Build a functional to-do application with a Typer CLI in Python
- Use Typer to add commands, arguments, and options to your to-do app
- Test your Python to-do application with Typer’s
CliRunner
and pytest
[ 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.
You are now part of the Steering Council, congratulations again! Do you have any words to share related to that?
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?
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.
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
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 PythonTest 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:
>>> words = ["Time", "flies", "like", "an", "arrow!"]
>>> " ".join(words)
'Time flies like an arrow!'
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:
>>> separator = " "
>>> separator.join(words)
'Time flies like an arrow!'
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:
>>> letters = ["A", "B", "C", "D"]
>>> "".join(letters)
'ABCD'
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:
>>> characters = "ABCD"
>>> ",".join(characters)
'A,B,C,D'
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:
- TPA-RFC-33: Monitoring: Nagios to Prometheus conversion, see also the extensive Prometheus documentation we wrote
- TPA-RFC-45: email architecture: draft of long-term email
services at
torproject.org
- TPA-RFC-62: TPA password manager: switch to password-store
- TPA-RFC-63: storage server budget: buy a new backup storage server (5k$ + 100$/mth)
- TPA-RFC-65: PostgreSQL backups: switching from legacy to pgBackRest for database backups
- TPA-RFC-68: Idle canary servers: provision test servers that sit idle to monitor infrastructure and stage deployments
- TPA-RFC-71: Emergency email deployments, phase B: deploy a new sender-rewriting mail forwarder, migrate mailing lists off the legacy server to a new machine, migrate the remaining Schleuder list to the Tails server, upgrade eugeni.
- TPA-RFC-76: Puppet merge request workflow: how to mirror our private Puppet repo to GitLab safely
- TPA-RFC-79: General merge request workflows: how to use merge requests, assignees, reviewers, draft and threads on GitLab projects
(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>