07 Aug 2025
Planet Python
Python Engineering at Microsoft: Python in Visual Studio Code – August 2025 Release
We're excited to announce the August 2025 release of the Python, Pylance and Jupyter extensions for Visual Studio Code!
This release includes the following announcements:
- Python Environments extension rollout update
- Python shell integration support for Python 3.13 and above
- Enhanced terminal suggestions with documentation support
- Jupyter support for uv virtual environments
If you're interested, you can check the full list of improvements in our changelogs for the Python, Jupyter and Pylance extensions.
Python Environments extension rollout update
The Python Environments extension continued to receive bug fixes and improvements as part of the controlled roll-out currently available to 20% of Stable users. To use the Python Environments extension during the roll-out, make sure the extension is installed and add the following to your VS Code settings.json
file: "python.useEnvironmentsExtension": true
. If you are experiencing issues with the extension, please report issues in our repo, and you can disable the extension by setting "python.useEnvironmentsExtension": false
in your settings.json
file.
As the Python environment manager ecosystem is evolving with new environment managers, new capabilities to existing environment managers, we want to make it easier for the community to get these newer features. This extension provides an API surface for extensions to integrate with the existing features of the Python Extension, this was asked for by the community that wanted to build newer features for their favorite environment managers.
This also helps us in a few ways:
- Extensions can be developed independently of the core python extension. This means they can ship at their own cadence. Issues and Features can be added to those extensions without waiting for a new release of the core extension.
- Helps us to concentrate on the core functionality of the Python Extension, while allowing the community to innovate and extend the functionality through their own extensions.
With that in mind, we ask each community to leverage our API interface to build tailored support for their tool of choice. Our team is committed to help enable and integrate these extensions, but we recognize we are not experts in each tool and cannot provide the best support for each. There are a number of published or work-in-progress extensions:
Env Manage | Link | Status |
pixi |
https://github.com/renan-r-santos/pixi-code | Published |
uv |
https://github.com/InSyncWithFoo/uv-for-vscode | In-development |
hatch |
https://github.com/flying-sheep/vscode-hatch | In-development |
Python shell integration support for Python 3.13 and above
We now support shell integration for Python when using version 3.13 or later. This enhancement brings rich terminal features to Python REPLs, including better command detection, output parsing, and integration with VS Code's terminal capabilities. When shell integration is enabled, PyREPL is automatically disabled to ensure compatibility and provide the best experience.
You can control this behavior through the Python shell integration setting, python.terminal.shellIntegration.enabled
, in your VS Code settings.
Enhanced terminal suggestions with documentation support
Terminal suggestions powered by language servers now include inline documentation, similar to what you see in the editor. Starting with the Python REPL, you'll get helpful descriptions and usage details for commands as you type, making it easier to discover and use Python functions and methods directly in the terminal.
To enable this feature, you'll need these settings:
"python.terminal.shellIntegration.enabled": true
"python.analysis.supportAllPythonDocuments": true
We plan to enable these settings by default in future releases as we continue to refine the experience.
Jupyter support for uv virtual environments
We now support installing required dependencies when you run Jupyter Notebooks against a virtual environment created using uv. This enhancement makes it seamless to work with uv-managed environments in your Jupyter workflows, automatically handling package installation when needed.
Other Changes and Enhancements
We have also added small enhancements and fixed issues requested by users that should improve your experience working with Python and Jupyter Notebooks in Visual Studio Code. Some notable changes include:
- Enhanced chat and notebook integration with support for agent tools in notebook inline chat, improving the development experience when working with Jupyter notebooks.
We would also like to extend special thanks to this month's contributors
- @Dhanika-Botejue Fixed grammar in macOS Python note in Quick Start in vscode-python#25235
- @Copilot Fixed skip-issue-check to support short issue references in vscode-python#25260
- @igorgaming Added
__file__
variable to globals inexec()
in vscode-python#25225 - @tgrue-openai Allow Activated Virtual Envs to Be Detected w/ a Workspace File in vscode-python#25141
Try out these new improvements by downloading the Python extension and the Jupyter extension from the Marketplace, or install them directly from the extensions view in Visual Studio Code (Ctrl + Shift + X or ⌘ + ⇧ + X). You can learn more about Python support in Visual Studio Code in the documentation. If you run into any problems or have suggestions, please file an issue on the Python VS Code GitHub page.
The post Python in Visual Studio Code - August 2025 Release appeared first on Microsoft for Python Developers Blog.
07 Aug 2025 8:58pm GMT
Mike Driscoll: Python 101: Reading TOML with Python
The TOML (Tom's Obvious Minimal Language) format came out in 2013, so it's been around for more than a decade. Python added support for TOML in Python 3.11 with its tomllib
module in the standard library. However, unlike some of Python's other standard libraries, such as json
or its XML-related libraries, the tomllib
library is only for reading, not writing. To create TOML documents in Python, you will need a third-party TOML package, such as tomlkit or toml.
Many Python developers use TOML as their configuration format of choice. In fact, you will find that most popular Python packages use a file called pyproject.toml for configuration. You can even use that file as a replacement for requirements.txt. Mypy, Flake8, and other tools can also be configured using pyproject.toml. You can learn more about that file and how it is formatted in the Python packaging guide.
In this tutorial, you will focus only on what Python itself provides for TOML support.
Let's get started!
Getting Started
Python's tomllib
is based on the tomli package. You can read all about the implementation details and why TOML support was added to Python in PEP 680.
The nice thing about having TOML support built-in to Python is that you do not need to install anything other than Python itself. However, if you need to be able to create or edit a TOML document, then you will need a third-party package as tomllib
is read-only.
Reading TOML with Python
To really understand what happens when you use the tomllib
module, you need a TOML file. You can pick your favorite Python package and grab a TOML file from it. For the purposes of this tutorial, you can use the Squall package's TOML file. Squall is a TUI for viewing and editing SQLite databases.
Here's what the TOML looks like:
[project] name = "squall_sql" dynamic = [ "version", ] description = "Squall - SQLite Editor" readme = "README.md" requires-python = ">=3.11" authors = [ { name = "Mike Driscoll", email = "mike@nlah.org" }, ] maintainers = [ { name = "Mike Driscoll", email = "mike@nlah.org" }, ] classifiers = [ "License :: OSI Approved :: MIT License", "Environment :: Console", "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "Intended Audience :: Other Audience", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows :: Windows 10", "Operating System :: Microsoft :: Windows :: Windows 11", "Operating System :: POSIX :: Linux", "Topic :: Software Development :: Libraries :: Python Modules", "Typing :: Typed", ] keywords = [ "tui", "sql", "sqlite", "terminal", ] dependencies = [ "rich>=13.9.4", "SQLAlchemy>=2.0.38", "textual>=2.1.1", ] packages = [ "src/squall", ] [project.license] file = "LICENSE" [project.urls] Homepage = "https://github.com/driscollis/squall" Documentation = "https://github.com/driscollis/squall/blob/main/README.md" Repository = "https://github.com/driscollis/squall" Issues = "https://github.com/driscollis/squall/issues" Discussions = "https://github.com/driscollis/squall/discussions" Wiki = "https://github.com/driscollis/squall/wiki" [project.scripts] squall = "squall.squall:main" [build-system] requires = [ "hatchling", "wheel", ] build-backend = "hatchling.build" [dependency-groups] dev = [ "build>=1.2.1", "ruff>=0.9.3", "pyinstrument>=5.0.1", "textual-dev>=1.7.0", ] [tool.hatch.version] path = "src/squall/__init__.py" [tool.hatch.build.targets.wheel] packages = [ "src/squall", ] include = [ "py.typed", "**/*.py", "**/*.html", "**/*.gif", "**/*.jpg", "**/*.png", "**/*.md", "**/*.tcss", ] [tool.hatch.build.targets.sdist] include = [ "src/squall", "LICENSE", "README.md", "pyproject.toml", ] exclude = [ "*.pyc", "__pycache__", "*.so", "*.dylib", ] [tool.pytest.ini_options] pythonpath = [ "src" ]
The next step is to write some Python code to attempt to read in the TOML file above. Open up your favorite Python IDE and create a file. You can call it something like pyproject_parser.py
if you want.
Then enter the following code into it:
import tomllib from pathlib import Path from pprint import pprint pyproject = Path("pyproject.toml") with pyproject.open("rb") as config: data = tomllib.load(config) pprint(data)
Here you open the TOML file from Squall and load it using the tomllib
module. You use Python's pprint
module to print it out. The nice thing about the tomllib
module is that it returns a dictionary.
Of course, the dictionary doesn't print nicely without using pretty print, which is why you use the pprint
module here.
The following is the output you will get if you run this code:
{'build-system': {'build-backend': 'hatchling.build', 'requires': ['hatchling', 'wheel']}, 'dependency-groups': {'dev': ['build>=1.2.1', 'ruff>=0.9.3', 'pyinstrument>=5.0.1', 'textual-dev>=1.7.0']}, 'project': {'authors': [{'email': 'mike@pythonlibrary.org', 'name': 'Mike Driscoll'}], 'classifiers': ['License :: OSI Approved :: MIT License', 'Environment :: Console', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Other Audience', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Operating System :: MacOS', 'Operating System :: Microsoft :: Windows :: ' 'Windows 10', 'Operating System :: Microsoft :: Windows :: ' 'Windows 11', 'Operating System :: POSIX :: Linux', 'Topic :: Software Development :: Libraries :: ' 'Python Modules', 'Typing :: Typed'], 'dependencies': ['rich>=13.9.4', 'SQLAlchemy>=2.0.38', 'textual>=2.1.1'], 'description': 'Squall - SQLite Editor', 'dynamic': ['version'], 'keywords': ['tui', 'sql', 'sqlite', 'terminal'], 'license': {'file': 'LICENSE'}, 'maintainers': [{'email': 'mike@pythonlibrary.org', 'name': 'Mike Driscoll'}], 'name': 'squall_sql', 'packages': ['src/squall'], 'readme': 'README.md', 'requires-python': '>=3.11', 'scripts': {'squall': 'squall.squall:main'}, 'urls': {'Discussions': 'https://github.com/driscollis/squall/discussions', 'Documentation': 'https://github.com/driscollis/squall/blob/main/README.md', 'Homepage': 'https://github.com/driscollis/squall', 'Issues': 'https://github.com/driscollis/squall/issues', 'Repository': 'https://github.com/driscollis/squall', 'Wiki': 'https://github.com/driscollis/squall/wiki'}}, 'tool': {'hatch': {'build': {'targets': {'sdist': {'exclude': ['*.pyc', '__pycache__', '*.so', '*.dylib'], 'include': ['src/squall', 'LICENSE', 'README.md', 'pyproject.toml']}, 'wheel': {'include': ['py.typed', '**/*.py', '**/*.html', '**/*.gif', '**/*.jpg', '**/*.png', '**/*.md', '**/*.tcss'], 'packages': ['src/squall']}}}, 'version': {'path': 'src/squall/__init__.py'}}, 'pytest': {'ini_options': {'pythonpath': ['src']}}}}
Awesome! You can now read a TOML file with Python and you get a nicely formatted dictionary!
Wrapping Up
The TOML format is great and well established, especially in the Python world. If you ever plan to create a package of your own, you will probably need to create a TOML file. If you work in dev/ops or as a system administrator, you may need to configure tools for CI/CD in the pyproject.toml file for Mypy, Flake8, Ruff, or some other tool.
Knowing how to read, write and edit a TOML file is a good tool to have in your kit. Check out Python's tomllib
module for reading or if you need more power, check out tomlkit or toml.
Related Reading
- Python and TOML: New Best Friends - Real Python
- PEP 680 - tomllib: Support for Parsing TOML in the Standard Library
The post Python 101: Reading TOML with Python appeared first on Mouse Vs Python.
07 Aug 2025 12:44pm GMT
06 Aug 2025
Planet Python
Python Insider: Python 3.13.6 is now available
The latest version of Python 3.13 is now available!
Python 3.13.6
https://www.python.org/downloads/release/python-3136/
This is the sixth maintenance release of Python 3.13
Python 3.13 is the newest major release of the Python programming language, and it contains many new features and optimizations compared to Python 3.12. 3.13.6 is the sixth maintenance release of 3.13, containing around 200 bugfixes, build improvements and documentation changes since 3.13.5.
More resources
- Online Documentation
- PEP 719, 3.13 Release Schedule
- Report bugs at https://github.com/python/cpython/issues.
- Help fund Python directly (or via GitHub Sponsors), and support the Python community.
Enjoy the new releases
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 organization contributions to the Python Software Foundation, especially now.
Regards from your package managers,
Thomas WoutersNed Deily
Steve Dower
Łukasz Langa
06 Aug 2025 5:35pm GMT
Mirek Długosz: Experience report: Implementing High Volume Automated Testing system
I first heard about High Volume Automated Testing in 2017, and I've wanted to try it ever since. The opportunity came in 2022, when I was working on revamping a UI testing framework for software called Quipucords. This article focuses on the decision points I encountered, the alternatives I considered, and the choices I made. At the end, I share the lessons I learned from this experience.
Keep in mind that this is not intended to apply to all possible High Volume Automated Testing systems, nor is it meant to be an exhaustive list of such system design decisions. I can't talk about alternatives I didn't think of, I have not actually tried all the alternatives, and I was working in the context of the UI layer of a specific product.
I share links to my implementation near the end of the article. Looking at the code might help to understand some of the more abstract parts for the article.
But before we begin, let's briefly talk about High Volume Automated Testing.
What is High Volume Automated Testing
High Volume Automated Testing (HiVAT) takes the idea that automation changes the very concept of how tests can be created, run and evaluated, and extends it to its logical conclusion. When learning about test automation, people tend to think about programming a computer to do the same things that a human would do when interacting with a software, but faster and unaffected by boredom or fatigue. HiVAT is born from the question: why stop there? If you program a computer to interact with a software, you can program it to do things that would be too costly for a human to do, or which a human could never do.
I first heard about it in 2017, from the article published by Cem Kaner in 2013, but the idea is much older than that. Kaner gave a couple of talks on the topic in 2003 and 2004. He claims to have experienced systems like that first-hand in 1980s, and in various places he mentions prior work dating back to 1970s and even 1960s. However, the phrase "High Volume Automated Testing" and the acronym "HiVAT" are relatively obscure. They don't seem to be discussed in the community often, and I reckon most testers have never heard of them.
These days, the ideas behind HiVAT see mainstream recognition in property-based testing and fuzzing.
I highly recommend reading the Kaner article for a better explanation and full background before moving on.
Designing a HiVAT system
Kaner lists twelve techniques that he considers to be examples of HiVAT. So the very first decision to make is: do you want to implement one of already known techniques? If so, which one? If not, how is it different from established solutions?
I wanted a robot that is able to automatically and autonomously wander through the application web interface. It should click random links, fill the forms, edit and delete items where possible etc. The idea was that it should work without supervision for some time, say 15 to 30 minutes. During that time it would most likely cover all the same scenarios we explicitly covered in the standard test suite, but would do so in a random order, while canceling some operations, while repeating certain actions etc. If it encountered any problems, they would likely be caused by the specific data that was introduced or the order of actions that was taken. Ideally, it should just finish the session without any troubles.
This is one decision that I haven't considered too comprehensively. It was kind of obvious for me that in the context I was working in, this is how HiVAT would look like.
In Python-inspired pseudo-code, the entire system may be approximated as a relatively simple loop. That loop doubles as a map of the main decisions that we have to make.
global browser_driver = create_browser_driver()
while should_still_run():
methods = get_methods_available_at_this_place()
chosen_method = random_choice(methods)
method_arguments = get_arguments_required_for_method(chosen_method)
try:
chosen_method(method_arguments)
except Exception:
# could be a bug!
handle_exception()
What are the system stopping criteria?
An autonomous testing session can last a long time, but it can't run forever. So when should it stop? How can a system decide if it should keep going?
The obvious solution is to just let it run, basically outsourcing the decision outside of the system. It will stop when the process is killed. That can be done using kill
command, by shutting down the machine, forcing a CI environment to stop execution etc. That approach has two fundamental problems: it's not particularly user-friendly and it might bring additional complexity if you need to clean up. Signal might be received in the middle of the loop, and you don't control when exactly it is sent.
Other approach is to let it run until the first exception, signaling a real issue. This builds on the assumption that issue might leave a system in erratic state that invalidates any later actions. The main drawback is that the faulty state might not apply to the entire system, and you lose the opportunity to observe how it operates for a longer period or under larger data volumes. I imagine that for most systems, invalid data causes them to enter a partially broken but still functional state.
You can stop the execution after a number of loop iterations. The main problem is that actions are essentially random, and they exercise the system unevenly - a simple navigation between pages has different impact than creating a report that needs to be computed in background task. If you are unlucky, even a relatively large number of iterations might end up executing mostly vain actions.
You can stop the execution after a certain time. This has similar problem to counting loop iterations, but this time it's longer actions that take undue part of the assigned budget. It might happen that a long session did relatively few actions, because all of them happened to take a lot of time.
Finally, you can try to introduce some system-specific stopping criteria. One way would be keeping separate track of low-impact and high-impact actions, and stopping when both are exceeded. Another would be keeping track of the number of objects created.
I decided to go with loop iterations counter. That was very easy to implement and at this stage I intended to run the tool exclusively on a local machine. I assumed that after a few sessions I will be in a better position to decide if things should stay that way, or change. I wasn't too worried about placing uneven demands on the system during execution, as these should smooth out across many sessions.
Which actions should be available to the system?
At every step, HiVAT should do "a random thing". But what does that mean? Are there any actions that should never be selected? How can a system know what actions are available?
In user interface, a single action may be broken down into multiple steps or interactions. Think about editing an existing object, like a user address. Usually user would have to click "Edit" button, which opens a form with address fields. After modifying the form user needs to click "Save" for data to be persisted in the system. Clicking "Cancel" discards changes.
There's no general consensus how that should be modeled by the test framework. Some people opt to provide methods for modifying a form, and a separate method for submitting changes. But that makes test code more verbose and ties tests to a specific implementation (what if form changes are saved automatically in the new version?). So other people think there should be a single method that both modifies a form and submits changes. But then the framework loses the ability to test a scenario where form is modified and user decides to cancel changes. This trade-off is explained in depth in "Going Deeper into the Page Object Model" article by Blake Norrish. On a side note, highly recommended read.
The main problem is that the solution you might prefer from the point of view of a generic testing framework might not be the same solution that you would pick for autonomous software traversing program.
In my case, I decided to do both. Page objects provided separate methods for granular actions, like changing the form and clicking "Submit". At the same time, they also provided more abstract methods representing user intent, like "modify the address". Usually this more abstract method would just call a sequence of methods for more specific actions. While the traditional tests used abstract methods exclusively, the system I was developing was operating only on concrete methods.
The specifics of how the main loop may obtain available actions depend on the programming language and this is something that might be worth considering before writing a single line of code. You probably want a language with rich runtime introspection capabilities, so you can take a random object and just see what methods it has available. Dynamic dispatch seems to also be a must. Language that offers interpreted runtime with dynamic typing is going to have an edge over the language that requires compilation and static typing.
The problem was relatively easy to solve in Python with type hints, which was what I used. I'm not sure if I would be able to solve it in Rust, but that might say more about my Rust expertise than the language itself.
How can a system call a method that requires arguments?
Conceptually, you can consider this an implementation detail of a previous section. Methods and functions usually require arguments. How can a system know what arguments are required? How can it differentiate valid values from invalid, and handle them appropriately?
Answer to the first question really depends on a programming language and features it offers. Similar to the previous section, languages with rich introspection capabilities will generally make it much easier than ones without. Here, however, a very strict typing will prove easier to use than a system with dynamic typing. If you need to pass "something" to a function for it to work, it helps to know the exact definition of "something". Practically speaking, you might need to create rich library of data transfer object classes that will model arguments taken by various methods across the system. It's even better if library can quickly generate DTOs filled with data. In case of Python, using type hints is almost required.
The second question refers not only to type validation, but also a business validation. If you have a method to delete a user, the argument of that method is not as much a string, but a string that represents the name of a user that exists in the system. Proper autonomous system needs a way of knowing if given input will pass system validation or not, and how to handle the second case.
For simplicity, I decided to stick to valid data. That changes a problem a little, as now my tool needs a way of obtaining such data, and possibly creating it on demand. Usually that can be done by manipulating the system database directly, or using APIs to manage the data. In my case it was not that much of a problem, as testing framework already had API tests and ability to obtain and create data. I only needed to wire these modules to the code I was writing.
One obvious alternative is for HiVAT to keep track of data it created (or deleted), and use that whenever existing data is needed to perform a step. That quickly gets complicated if your data has dependencies - testing software might want to perform a step that depends on specific data, but it would not be possible if it didn't happen to create that data first. This can be overcome with larger number of iterations in a single session.
This is one of the decision points that were mostly theoretical for me. I was extending an existing testing framework written in Python, and changing programming language was not up for debate. So I didn't need to weigh the options carefully as much as I had to figure out how to solve this specific problem in Python. As I mentioned, I decided to use type hints liberally and used introspection capabilities built into the language.
How to handle actions history?
Should the history of actions be tracked? How? Where should it be stored? Should it be displayed? How should it look like, then? How can you make the history machine-readable, if that's desired?
This is one of the few decision points where you don't really have to choose, but instead you can have your cake and eat it too. You can both save all the actions in more human-readable form in a log file and keep machine-readable format in memory during a session. You can serialize data from memory at the end of the session, or only if there was an issue during execution. Contrary to most of other decision points, there's no real reason why one choice here should prevent you from also doing another.
One of the most consequential choices is deciding where to store a list of actions. You can keep it in system memory, or you can persist it on a disk. The trade-offs are well known. If you decide to keep it in the memory, then you can lose everything if the entire system crashes. You also use the computer memory, making it unavailable to other processes, but that's probably negligible unless you run billions of actions. If you save to disk, you might need to think about ways of reading it back.
If you keep the data in system memory, you should probably model it as some kind of structured information. Depending on the programming language, you might also have an option to keep references to existing classes, instances or functions. This would make it much easier to use them if needed. You can always easily turn that into human-readable form. The alternative - keeping human-readable strings in memory - appears to be all downside and I struggle to take it seriously.
It's important to decide when the data is going to be displayed. I can think of four options: never, in real time (streaming, display action as soon as it is taken), at the end of the session and only in the case of failure. Displaying in real time and displaying in case of failure seem like main options to consider. The risk of displaying in case of failure is that a bug in HiVAT might prevent information from being displayed at all, which is especially problematic in case of critical failure of software under test. If you decide to display information in a streaming fashion, you might end up with a lot of information at the end of session where generally nothing noteworthy happened.
Finally, some actions might involve sensitive data, like private information or passwords. They probably should be protected, so they should be hidden or filtered out before being displayed. At the same time this is an information loss that would prevent fully reproducible session replays. If this is a concern, you should probably have different ways of storing and accessing human-readable and machine-readable data.
I decided to keep the history information in memory. Actions were modeled by a special class. A test runner would print action history only in the case of failure. When printed, actions were somewhere between human-readable and machine-readable. I did not go out of my way to make them easy for humans to read, but eventually I did not need to read them programmatically either.
Since there was no sensitive data that would require additional carefulness in handling, I did not implement any kind of filtering or hiding mechanism. However, this is something I did consider briefly.
Should the system allow for a control of certain sequences?
The HiVAT system I envisioned relied heavily on randomness, but it's not hard to think about cases where it could be beneficial to limit it. Should every session be independent from all the previous ones and purely random? Or should there be a way to re-run the previous session? Should it be possible to constrain randomness of certain parts of the session? Which ones?
Some of these questions might make sense only in the context of an answer to a question posed in the previous section, and some of answers to these questions are self-evident from the answer given in the previous section. If you want the ability to replay the entire session, or any subset of it, then you need a way to store the history of all actions in machine-readable format on a persistent storage.
The ideal outcome is that if you have a session that finds an issue, you can re-run it on a brand new instance of software with empty database. That would be a significant step towards making all the issues reproducible.
And imagine you could have a system which takes a session that finds an issue and automatically reduces it to a minimal reproducible path. One session might last 15 minutes, but it doesn't mean that everything that happened during that time is equally important. Probably most of things could be safely skipped, as there is relatively short subpath that guarantees the occurrence of the issue.
These two scenarios were main things I was thinking about when making that decision. It's really a binary choice - should you design a system that allows to do these capabilities, or should you just give up on the entire thing? I haven't spent too much time considering randomness of specific choices, like always picking up the same item from the list. I feel if I went down this path, there would be a lot of decisions to be made around communicating to the system which parts should be more reproducible.
While I have not implemented a replay system, and didn't spend any time actually thinking how a critical path finder could be built, I decided that my system should be designed in a way that makes such changes in the future possible. In practice it meant identifying possible extension points and leaving around few hooks that might help later.
How should unexpected errors be handled?
It's almost inevitable that system encounters an issue during a sufficiently long run, either due to a problem in a software under test, or due to a bug in the testing system itself. What should happen then?
This is something you might have covered when deciding about stopping criteria. One of the options back then was that the system should stop on the first error it can't handle.
But if you decided to stop based on a different condition, now is the time to revisit that choice. Should your system also stop on an error? Or should it press forward, unreliability of later information be damned? Or should you design more elaborate error handling code?
First two choices were already discussed in an earlier section, so I'm going to focus only on the last one. Maybe your testing system can communicate with the software under test and revert it back to a known working state? Or maybe there are some actions that you know how to cancel or revert? Or maybe you can make decisions based on the nature of the issue and context of the action - e.g. transient network errors probably can be ignored or retried.
In my case, I decided to ignore some errors that I knew can be safely ignored, but make all the others fatal. That decision goes back to an earlier choice to only print actions history in case of error. If I decided to ignore errors and continue running, then I would have to find a way to still print history at the end of the run, or change that other decision and always print the entire history. But then it would make it harder to decide if any given session encountered issues or not.
Sample implementation
The HiVAT system I implemented for Quipucords project is available at GitHub. It's only 25 lines of code!
However, that brevity was possible thanks to a number of helpers and design decisions made along the way. The UI framework that made HiVAT possible is introduced in Camayoc PR #365.
Lessons learned
The main lesson I learned was that my HiVAT was not a good fit for the project. In Quipucords, the most interesting part happens in the backend, when software interacts with the external world. Software that randomly walks the user interface and has limited ability to trigger interactions with the external world is not particularly helpful. I think this kind of tool would be much more applicable for more self-contained systems, like ecommerce, banking or social media.
Another important lesson is that some programming languages and design approaches can make non-deterministic testing much easier to implement. It definitely helps if you can make choices of technology and framework design while thinking about that particular use-case - it might be hard to bolt onto existing code.
The kind of HiVAT I created seems to be worth considering if your system meets two requirements:
-
It is stateful. This kind of testing requires application to live longer than a single request, ideally longer than any individual session. Most of backend code is like that. Frontend - standing alone - not necessarily. To give you an idea, this would not be a right approach to test a tool like grep, which needs to initialize every time it is started.
-
Most of the data comes from the user. There are hardly any pure systems like that - everything is interconnected. Online shops are pretty close (the inventory usually needs to be loaded from outside, but after initial setup, it's mostly user actions). Banks are also pretty close - obviously there are bank-to-bank transfers, and most banks don't allow to do everything online, but a large part of everyday operations can be modeled this way. Things like Evernote, JIRA or Trello are largely user-generated, but they also offer various integrations with 3rd party services.
Of course if your system does not meet these two requirements, it might still be worth to consider some other kind of HiVAT.
Thanks to James Thomas for his feedback and review of an earlier draft of this blog post.
06 Aug 2025 4:23pm GMT
James Bennett: Litestar is worth a look
A few years ago at work, I had a project which offered an opportunity to look at the new generation of async-first, type-hint-driven Python web frameworks. For reasons which aren't particularly relevant today, on that project I ended up choosing Litestar, which is the one that doesn't have a ravenous all-consuming hype machine surrounding it. And I'm very glad I did, because today I'm more convinced than ever it was the right choice, and for the last 18 months or so every new project I've started at my day job has been built with Litestar.
But even if you're someone who does Python web apps for a living, and even if you're someone who builds asynchronous type-hint-driven web apps, you might not be familiar with this absolute gem of the Python web ecosystem, and today I want to remedy that.
A taste
Here's the traditional single-file-app demo:
from litestar import Litestar, get
@get("/greet")
async def greet(name: str) -> str:
return f"Hi, {name}!"
app = Litestar([greet])
You save this as app.py
, run with litestar run
or hand it directly to the ASGI server of your choice, and it launches a web application. You go to /greet?name=Bob
and it replies "Hi, Bob!". Leave out the name
parameter and it responds with an HTTP 400 telling you the name
parameter is required.
So what. Big deal. The FastAPI Evangelism Strike Force will be along soon to bury you under rocket-ship emoji while explaining that FastAPI does the same thing but a million times better. And if you're a Java person used to Spring, or a .NET person used to ASP.NET MVC, well, there's nothing here that's new to you; you've had this style of annotation/signature-driven framework for years (and in fact one thing I like about Litestar is how often it reminds me of the good parts of those frameworks). And did anyone tell you FastAPI does this, too! 🚀🚀🚀🚀🚀🚀🚀🚀🚀
But there are a lot of things that make Litestar stand out to me in the Python world. I'm going to pick out three to talk about today, and one of them is hiding in plain sight in that simple example application.
What's in a name?
You might see older material referring to Litestar as "Starlite", which was its original name.
Starlette is a toolkit for doing async Python web development, which can be used standalone or as a component in a more complex library or framework. FastAPI still uses Starlette under the hood, for example. And Litestar originally was built on Starlette too, and was named "Starlite", presumably in recognition of that. Over time, though, it dropped the Starlette dependency in favor of its own implementations for that functionality, and people on social media complained that the "Starlite" name was confusing, especially since Starlette was no longer being used. So the project which had been "Starlite" was renamed to Litestar for version 2.0, released in 2023, and has had that name ever since.
Scaling (the other kind)
It's a bit unfortunate that the term "scaling" is almost always assumed to mean handling larger and larger quantities of traffic, because that's only one axis on which any given piece of of technology can "scale" (and, I'd argue, possibly the least important one). The type of scaling I want to talk about here is scaling of a codebase: how does something (in this case, a web framework) help or hinder you as you deal with different amounts of code?
Django, for example, has a reputation for not scaling "down" all that well. You can do it if you really want to, and every so often someone will come up with a new demo of doing a Django project in a single Python file, but it's just not something that comes naturally to Django. Quite the opposite: if you work through the official beginner Django tutorial and do things the "Django way", you'll have around a dozen files laid out in a specific structure of directories and subdirectories before you've written a single meaningful line of your own code.
But "micro" frameworks have often had the opposite problem: they're great at starting out with a tiny single-file application, and then get painful as your codebase grows and needs to spread out (single-file Django approaches have the same problem: you have to do a lot of work to get a "micro-Django" working, and then you have to undo all that work as soon as the code grows large enough to be worth splitting across multiple files).
Let's look at an example. Here's a FastAPI equivalent of the basic Litestar application I showed above:
from fastapi import FastAPI
app = FastAPI()
@app.get("/greet")
async def greet(name: str) -> str:
return f"Hello, {name}!"
Notice that the get()
decorator here is attached to the application object. This is a common pattern (Flask/Quart do the same thing, for example, and Starlette used to but has deprecated its entire decorator-based routing system), but it creates a problem once you have multiple files. You need to import the main application object into the other files in order to decorate the routes, but you need to import the other files into your "main" application file to make sure the route registrations are visible from there, and now you have a circular import, and that doesn't work.
The general solution these frameworks offer is some sort of alternative sub-application object which can act as a per-file route registry that's safe to import into the file where your application object is defined. FastAPI calls this object an "API router"; Flask/Quart call it a "blueprint". Either way, it's a necessary construct for those frameworks because their route decorators are always bound to some parent object, either the application object in a single-file app or an "API router"/"blueprint"/etc. object in a multi-file app.
That solves the circular-import problem, but creates a new issue: the whiz-bang quickstart demos of "micro" frameworks generally register all the example routes on the application object in a single file in order to keep everything as simple and flashy as possible, but now in order to build a real application (which will almost never stay in a single file) you'll need to use a different mechanism, or start out following the demo and then switch later on. You also have to know about that different mechanism; in one framework's documentation that I looked at, you can (at the time I'm writing this post) apparently get 40 pages into the user guide before encountering the section on how to register routes in a multi-file app 😖😖😖.
Litestar avoids this entire mess by having the route decorators be standalone functions, not bound to a parent application or application-like object. This may seem like a small thing to focus on, but if you've spent time with popular Python microframeworks you've probably had to deal with the transition from single- to multi-file applications.
More importantly, this small change in approach frees up Litestar's documentation to introduce route-grouping constructs early on and to present them as part of a coherent layered architecture/configuration concept rather than as an escape hatch for avoiding circular imports. Which is great, because Litestar's layered architecture is one of its best features: its grouping constructs, and their ability to share configuration, offer an elegant way to compose functionality. For example, a common pattern I use when writing a set of CRUD endpoints looks like this:
from litestar import Router
from litestar.di import Provide
# Imagine some CRUD routes for widgets defined here...
_write_widget_router = Router(
guards=[some_auth_function],
route_handlers=[
create_widget,
delete_widget,
update_widget,
]
)
widget_router = Router(
dependencies={"widget_dependency": Provide(some_widget_dependency)},
path="/widgets",
route_handlers=[
get_widget,
get_widget_list,
_write_widget_router,
]
)
This provides a single "public" Router
instance with all the widget routes, all of which have access to the same core dependencies, but with the data-modifying routes also having auth applied. That composability is extremely powerful, and is less obvious if the "router" has to be introduced initially as a way to solve circular-import problems.
Litestar's approach also means it's easy to do things like register a single route multiple times, each with different configuration. Which enables use cases like:
- Different authentication/authorization schemes for each registration. For example, a data-editing route might be written once, and registered once under a router which applies API key auth for machine-to-machine requests, then registered again under a router which uses session auth for interaction by a human user.
- Different sets of dependencies for each registration. For example, a route which queries and returns a list of widgets might just declare that it accepts an argument of type
WidgetRepository
, and leave it up to the router configuration to decide whether to dependency-inject one that sees all widgets, or perhaps only a subset, or only those which are active, etc.
If you know what you're doing, you can emulate some of this in the FastAPI/Flask style of bound route registration, but the techniques you'll end up using for that feel to me like fighting against the framework, which is something I usually want to avoid.
Not to be too Pydantic
Pydantic is a popular package for defining schema objects which perform validation and serialization/deserialization, driven by Python type annotations, and one major use case for this is the input/output schemas of web applications. FastAPI appears to use Pydantic exclusively, which comes with both upsides and downsides. Pydantic is very useful and very powerful, of course, but it also means FastAPI is somewhat limited by what Pydantic can support: mostly, this is Pydantic's own classes, and the Python standard library's dataclasses
.
One crucial limitation is an inability to derive validation/serialization behavior directly from SQLAlchemy ORM classes, even though they both support a very similar type-annotation-based declaration format. Which means that to use SQLAlchemy with a Pydantic-only framework (and SQLAlchemy is basically the standard database toolkit and ORM for Python), you either have to write out the shape of your data multiple times-once for SQLAlchemy, and then at least one more time (possibly more than one time) for Pydantic-or turn to a third-party package to help bridge the gap. FastAPI's author worked around this by writing a new DB toolkit which combines SQLAlchemy and Pydantic, and pushing it in FastAPI's documentation.
Litestar, meanwhile, supports Pydantic, but is not tightly coupled to Pydantic, which gives a bit more flexibility. By default Litestar lets you define input/output schemas using Pydantic models, dataclasses
, or msgspec; ships with plugins to enable the use of attrs and of SQLAlchemy models; and provides a protocol for writing your own serialization plugins to extend support to other kinds of objects.
That's very convenient already, but the convenience is amplified by Litestar's system for automatically deriving data-transfer objects from data-access or domain objects. Suppose, for example, that we have the following SQLAlchemy model class:
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
Base = DeclarativeBase()
class Widget(Base):
__tablename__ = "widget"
id: Mapped[int] = mapped_column(primary_key=True)
internal_notes: Mapped[str]
sku: Mapped[str] = mapped_column(unique=True)
name: Mapped[str]
price_cents: Mapped[int]
In a Pydantic-only world, we'd need to write multiple Pydantic models representing different use cases:
- A "read" schema for use in HTTP responses, which would probably not include the
internal_notes
field and probably also notid
(sincesku
is more likely to be the public identifier) - A "write" schema for creating widgets, which would exclude
id
since that likely is auto-generated on insert - Another "write" schema for updating widgets, setting all fields optional to allow partial update
As well as possibly more schemas like an admin-view "read" schema that does include the internal fields, etc. Even if you get clever and use inheritance to share field definitions among all these Pydantic classes, you still will write out the full set of fields for widgets at least twice, and the second time it will be fragmented across multiple Pydantic classes, creating the risk of making a change to the ORM model and forgetting to update all the corresponding field definitions in the Pydantic models.
Litestar's approach is a significant improvement on this. For example, here's how to use Litestar's DTO helpers to define the "read" schema:
from litestar.dto import DTOConfig
from litestar.plugins.sqlalchemy import SQLAlchemyDTO
class ReadWidget(SQLAlchemyDTO[Widget]):
config = DTOConfig(exclude={"id", "internal_notes"})
This will give you a DTO class containing all the fields of the Widget
ORM model except the two explicitly excluded, and will derive that set of fields, and the correct data types, from introspecting Widget
. It will also automatically handle conversion to and from instances of Widget
when you specify it as the input or return DTO type of a route. Similarly, it's possible to declare a list of fields to include, or to re-map field names for public consumption, or to declare a DTO which makes fields optional for partial updates. This means there's only one canonical definition of the fields-on the original class, which might be a SQLAlchemy ORM model, might be a dataclass
, etc.-and it doesn't have to be repeated in the DTOs because the DTOs will always derive their field definitions directly from the source class you point them at.
Of course, there are going to be cases where your DTOs are sufficiently different from your DAOs and domain objects that this isn't a big help, but my own experience is that "the DTO is a subset of the DAO's fields" is extremely common in real-world applications, so Litestar's approach really pays off in both reduced boilerplate and reduced errors from manual "transcription" of fields between different class definitions.
Alchemical architecture
I wasn't exaggerating earlier when I said that SQLAlchemy is the database toolkit and ORM for Python. While there are others out there, the only one I'm aware of that sees anything close to SQLAlchemy's usage is the Django ORM, and only because it's built into and tightly integrated with Django. So if you're going to be writing a database-backed web application in Python, and you're not doing Django, you are almost certainly going to be using SQLAlchemy.
And Litestar makes that easy. While officially remaining agnostic as to whether you even have a persistence layer, it still includes good integrations for SQLAlchemy: the serialization plugin mentioned earlier allows the direct use of SQLAlchemy ORM classes as input and output schemas; the DTO helpers can derive subsets and remappings of fields from SQLAlchemy ORM classes; and Litestar also ships with a plugin that manages a SQLAlchemy engine and per-request ORM session for you, as well as a single SQLAlchemy mega-plugin combining all the SQLAlchemy plugins' functionality.
So it's already pretty convenient to use SQLAlchemy in Litestar applications. But there's more! The Litestar team also maintains the excellent Advanced Alchemy library which provides a bunch of useful features on top of SQLAlchemy. While Advanced Alchemy is framework-agnostic, Litestar's SQLAlchemy plugin makes use of it and re-exports much of its functionality, giving you access to it automatically, and it does include Litestar-specific helpers for registering certain utility classes with Litestar's dependency injection.
Advanced Alchemy provides a lot of quality-of-life improvements for SQLAlchemy, including a variety of base classes and mixins and data types doing useful things like database-agnostic big-integer primary keys, automatic create/update timestamps, UUID-keyed models, a proper UTC timestamp type, and a JSON type which chooses the best column type for your database. There are also command-line helpers for database management (including creating and working with Alembic migrations), database dumping and seeding to/from JSON, and a lot more.
But the place where Advanced Alchemy really shines is in providing a generic repository implementation (both sync and async flavors) on top of SQLAlchemy models, along with a service-layer abstraction and helpers to integrate them into Litestar's dependency injection system.
Here's a basic example using the Widget
class from above:
from litestar.plugins.sqlalchemy import repository
class WidgetRepository(repository.SQLAlchemyAsyncRepository[Widget]):
model_type = Widget
WidgetRepository
will have all the methods you'd expect-list()
, get_one()
, add()
, delete()
, etc.-automatically derived from the Widget
model. And let me just say that having repository implementations automatically derived from any SQLAlchemy model, with not just basic CRUD operations but also things like paginated fetches, is a massive productivity boost compared to just using vanilla SQLAlchemy. It's maybe not quite on the level of Django's generic views, but it's a big step in that direction, and you probably could produce something like Django's generic views with Litestar and Advanced Alchemy if you wanted to (perhaps one day, in my copious free time, I'll even make an attempt at it).
I know it may seem strange to hear me saying this, since a few years ago I went on record as being strongly against these sorts of abstractions-specifically service layers-in Django. And I still think you absolutely should not try to retrofit repository or service-layer abstractions onto Django! They're not the native patterns of Django's architecture, and instead I think you should stick to what I recommended back then, which is to leverage Django's own architecture, especially its "manager" abstraction, rather than trying to force abstractions onto it that don't fit.
I also still think there are a lot of bad use cases for repositories and service layers that people should avoid, but that's a digression which should probably become its own post, so I'll just say for now that I think it's fine to use repositories and service layers as an organizing principle when you're using a less-structured framework which doesn't express strong opinions about how you should lay out your code. And that's exactly what I do when working with Litestar.
A lightweight star of Python
There are plenty of other features and conveniences in Litestar, many of which I use daily. Its auth system, supporting both simple guard functions and middlewares for attaching identity and complex authn/authz logic to requests. Its "stores" framework, which makes caching and similar tasks convenient. Its logging integrations which support both the Python standard library's logging
module and popular third-party tools like structlog
. Its built-in support for transforming errors to standard "problem details" structures. Its built-in support for recording and exporting metrics in standard Prometheus or OpenTelemetry formats. Its htmx support.
You can do this stuff in other microframeworks, but it typically involves a lot of tracking down of third-party add-ons and/or writing your own glue code to integrate things. Litestar manages to keep the "microframework" feel when starting a new project while also having all these nice bits optionally available with the framework itself when and if you decide you want them, and that's nothing to sneeze at. That's what I was getting at earlier when I said it reminds me of the things I like in certain frameworks from other languages. Litestar doesn't feel, to me, like it's trying to be a replacement for any pre-existing Python web framework. It's not trying to be the next Django or the next Flask or whatever; instead, it feels to me like a Pythonic take on the good parts of something like Spring Boot (and the way I like to set it up, doing things like using svcs behind the scenes as a service locator to feed things to both Litestar's and pytest's dependency injection, makes it feel even more that way).
I could go on for a lot longer listing things I like about Litestar, and probably wind up way too far into my own subjective preferences, but hopefully I've given you enough of a realistic taste of what it offers that, next time you're about to build a Python web app, you might decide to reach for 💡⭐ to carry you to the moon 🚀🚀🚀.
06 Aug 2025 3:55pm GMT
Real Python: What Are Mixin Classes in Python?
Mixins offer a powerful way to reuse code across multiple Python classes without forcing them into a rigid inheritance hierarchy. Instead of building deep and brittle class trees, you can use mixins to share common behaviors in a modular and flexible way. Learn when and how to implement mixin classes effectively in your Python projects.
By the end of this tutorial, you'll understand that:
- Mixins allow you to add isolated, reusable functionalities to classes without enforcing a strict type hierarchy.
- Python has no dedicated syntax for declaring mixins. You create mixins by defining classes with specific behaviors that other classes can inherit without forming an is-a relationship.
- Mixins rely on multiple inheritance to combine features from different classes, enhancing flexibility and code reuse.
- Stateful mixins require careful design to manage instance attributes and avoid conflicts with other classes.
- Python's method resolution order (MRO) determines the order in which classes are inherited, affecting how mixins are applied.
To get the most out of this tutorial, you should have a good understanding of object-oriented programming (OOP), SOLID principles, inheritance, and Python classes.
So, what is a Python mixin, and when should you use one?
Get Your Code: Click here to download the free sample code that shows you how to use mixin classes in Python.
Take the Quiz: Test your knowledge with our interactive "What Are Mixin Classes in Python?" quiz. You'll receive a score upon completion to help you track your learning progress:
Interactive Quiz
What Are Mixin Classes in Python?Test your knowledge of Python mixins-specialized classes that let you reuse methods without traditional inheritance.
In Short: Mixins Encapsulate Reusable Behaviors
In object-oriented programming (OOP), a mixin is a tool that allows you to reuse a common piece of functionality across several, often unrelated data types while keeping them loosely coupled. In practical terms, mixins can help you achieve a cleaner design with more flexible code consisting of modular and composable pieces.
Many programming languages, including Ruby, Dart, and Scala, support this pattern explicitly through specialized syntax and keywords. Others let you simulate mixins with existing constructs, such as default method implementations in Java interfaces. Conversely, Python relies on multiple inheritance as the underlying mechanism to facilitate the same concept.
Python mixins are ordinary classes with a special meaning. You use them to give other classes new superpowers or to modify their current behavior without forming a rigid type hierarchy. Mixins can also act as a form of dependency injection, allowing you to customize code beyond your control, for example, by plugging into a third-party library or framework.
To give you an idea of when mixin classes are most useful, imagine that you wanted to represent arbitrary Python objects with different data formats. Implementing the same serialization logic in each class would lead to duplicate code, which is a well-known code smell that violates the DRY principle:

Notice the repeated .serialize()
method, which appears across all classes in the UML class diagram above. You can fix this issue by extracting the shared functionality into a common base class and creating the following inheritance hierarchy:

But now you end up with an awkward inheritance hierarchy where animals and vehicles share a common ancestor. You've grouped them under a shared interface for structural reasons, while they conceptually belong to entirely different categories.
Moreover, a design centered around inheritance can be fragile, making it hard to accommodate future use cases. Maybe you'll need to implement a mechanical bird equipped with a vehicle's engine and the ability to fly, but without needing to eat or having a VIN number. As it stands, there's no straightforward way to fit that kind of data type into your class hierarchy without making inelegant compromises.
Another issue that sometimes arises from using inheritance is a tendency for overgeneralization. When the base class becomes too broad in scope, subclasses start to inherit unnecessary features. For example, if a vehicle requires another serialization method, then all animals automatically inherit it, whether they need one or not. This problem was aptly described by Joe Armstrong, the creator of Erlang, in an interview for Coders at Work:
Because the problem with object-oriented languages is they've got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
- Joe Armstrong
A better approach would be to design your types based on what they do rather than what they are. This means favoring composition over inheritance and delegating responsibilities to smaller, more focused components, promoting separation of concerns:

Compared to the inheritance-based version, this diagram looks much more intimidating due to the greater number of individual components. On the other hand, such a fine-grained design allows you to compose behaviors in novel ways, which could've been difficult to anticipate during the initial requirements analysis.
Wouldn't it be ideal to combine the convenience of inheritance with the flexibility of composition? That's precisely where mixins shine! Now that you understand the broader context, it's time to dig a little deeper by answering more specific questions about mixins.
How Can You Recognize a Mixin Class in the Wild?
For starters, here's a straightforward example of a mixin class defined in Python:
Read the full article at https://realpython.com/python-mixin/ »
[ 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 ]
06 Aug 2025 2:00pm GMT
Brian Okken: pytest fixtures nuts and bolts - revisited
In 2013/2014 I wrote a 4 part series on pytest fixtures.
It started out fine-ish. And then by part 4 I crammed way too many concepts into one huge post.
Well, I've recently reviewed all of this, and
- split the posts into one concept each
- made sure all of the info was correct for modern versions of Python and pytest
- reran the examples to make sure the output is correct
Here's the new/old series:
06 Aug 2025 1:13pm GMT
Real Python: Quiz: What Are Mixin Classes in Python?
In this quiz, you'll test your understanding of What Are Mixin Classes in Python?
By working through this quiz, you'll revisit Python mixins, how they're used, and the benefits they offer.
[ 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 ]
06 Aug 2025 12:00pm GMT
Django Weblog: Django bugfix release issued: 5.2.5
Today we've issued the 5.2.5 bugfix release.
The release package and checksums are available from our downloads page, as well as from the Python Package Index.
The PGP key ID used for this release is : 3955B19851EA96EF
06 Aug 2025 7:53am GMT
Python Insider: Python 3.14.0 beta 3 is here!
It's 3.14 beta 3!
https://www.python.org/downloads/release/python-3140b3/
This is a beta preview of Python 3.14
Python 3.14 is still in development. This release, 3.14.0b3, is the third of four planned beta releases.
Beta release previews are intended to give the wider community the opportunity to test new features and bug fixes and to prepare their projects to support the new feature release.
We strongly encourage maintainers of third-party Python projects to test with 3.14 during the beta phase and report issues found to the Python bug tracker as soon as possible. While the release is planned to be feature-complete entering the beta phase, it is possible that features may be modified or, in rare cases, deleted up until the start of the release candidate phase (Tuesday 2025-07-22). Our goal is to have no ABI changes after beta 4 and as few code changes as possible after the first release candidate. To achieve that, it will be extremely important to get as much exposure for 3.14 as possible during the beta phase.
This includes creating pre-release wheels for 3.14, as it helps other projects to do their own testing. However, we recommend that your regular production releases wait until 3.14.0rc1, to avoid the risk of ABI breaks.
Please keep in mind that this is a preview release and its use is not recommended for production environments.
Major new features of the 3.14 series, compared to 3.13
Some of the major new features and changes in Python 3.14 are:
New features
Note that PEPs 734 and 779 are exceptionally new in beta 3!
- PEP 779: Free-threaded Python is officially supported
- PEP 649: The evaluation of type annotations is now deferred, improving the semantics of using annotations.
- PEP 750: Template string literals (t-strings) for custom string processing, using the familiar syntax of f-strings.
- PEP 734: Multiple interpreters in the stdlib.
- PEP 784: A new module
compression.zstd
providing support for the Zstandard compression algorithm. - PEP 758:
except
andexcept*
expressions may now omit the brackets. - Syntax highlighting in PyREPL, and support for color in unittest, argparse, json and calendar CLIs.
- PEP 768: A zero-overhead external debugger interface for CPython.
- UUID versions 6-8 are now supported by the
uuid
module, and generation of versions 3-5 and 8 are up to 40% faster. - PEP 765: Disallow
return
/break
/continue
that exit afinally
block. - PEP 741: An improved C API for configuring Python.
- A new type of interpreter. For certain newer compilers, this interpreter provides significantly better performance. Opt-in for now, requires building from source.
- Improved error messages.
- Builtin implementation of HMAC with formally verified code from the HACL* project.
- A new command-line interface to inspect running Python processes using asynchronous tasks.
- The pdb module now supports remote attaching to a running Python process.
(Hey, fellow core developer, if a feature you find important is missing from this list, let Hugo know.)
For more details on the changes to Python 3.14, see What's new in Python 3.14. The next pre-release of Python 3.14 will be the final beta, 3.14.0b4, scheduled for 2025-07-08.
Build changes
- PEP 761: Python 3.14 and onwards no longer provides PGP signatures for release artifacts. Instead, Sigstore is recommended for verifiers.
- Official macOS and Windows release binaries include an experimental JIT compiler.
Incompatible changes, removals and new deprecations
- Incompatible changes
- Python removals and deprecations
- C API removals and deprecations
- Overview of all pending deprecations
Python install manager
The installer we offer for Windows is being replaced by our new install manager, which can be installed from the Windows Store or our FTP page. See our documentation for more information. The JSON file available for download below contains the list of all the installable packages available as part of this release, including file URLs and hashes, but is not required to install the latest release. The traditional installer will remain available throughout the 3.14 and 3.15 releases.
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
If you're heading out to sea, remember the Maritime Approximation:
π mph = e knots
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 sunny Helsinki with 19 hours of daylight,
Your release team,
Hugo van Kemenade
Ned Deily
Steve Dower
Łukasz Langa
06 Aug 2025 3:48am GMT
05 Aug 2025
Planet Python
PyCoder’s Weekly: Issue #693: Nested Functions, Bitwise Operators, Virtual Threads, and More (Aug. 5, 2025)
#693 - AUGUST 5, 2025
View in Browser »
Nested Functions in Python
Functions in Python can be defined within another function, this post shows you how and just why you might do that.
TREY HUNNER
Bitwise Operators in Python
Learn how to use Python's bitwise operators to manipulate individual bits of data at the most granular level.
REAL PYTHON
Prevent Postgres Slowdowns on Python Apps With This Check List
Avoid performance regressions in your Python app by staying on top of Postgres maintenance. This monthly check list outlines what to monitor, how to catch slow queries early, and ways to ensure indexes, autovacuum, and I/O are performing as expected →
PGANALYZE sponsor
From Async/Await to Virtual Threads
In this opinion piece, Armin describes how he wishes async in Python would work.
ARMIN RONACHER
Discussions
Articles & Tutorials
PyPI Users Email Phishing Attack
PyPI users are being targeted by an email phishing attack attempting to trick them into logging into a fake PyPI site. This post from the Security Engineer at PyPI discusses what is happening and what you should do about it. There is also a follow-up post.
MIKE FIEDLER
Smuggling Arbitrary Data Through an Emoji
Unicode includes flexibility through the use of Variation Selectors. These include the ability to change characters through a consecutive series of coding points. But, when used with code points that don't need them, they're ignored, so you can hide data in them.
PAUL BUTLER
Introducing an Intuitive IDE for Code and Data
Positron offers an intuitive environment for exploring data, plots, apps, and connections within your IDE.
POSIT sponsor
Creating an XML Editor With Textual
Several years ago, Mike created an XML editor with the wxPython GUI toolkit called Boomslang. Recently he ported it to Textual and this post gives you step by step instructions on how to create your own.
MIKE DRISCOLL
Stop Using Django's squashmigrations
Squashing merges multiple database migrations into a single consolidated file to speed up database setup and tidy up history. Learn why it is sometimes problematic and what else you can do.
JOHNNY METZ
When to Use Classes in Python?
Having trouble figuring out when to use classes? This article looks at another heuristic for using classes in Python, with examples from real-world code, and some things to keep in mind.
ADRIAN
Faster Python: Unlocking the Python Global Interpreter Lock
Take a look at true multithreading in Python 3.13 with the no-GIL option. Learn why it matters and compare performance with and without the Global Interpreter Lock.
CHEUK TING HO
TIL: Exception.add_note()
A quick "Thing I Learned" article showing the .add_note()
methods on Exceptions and how you can provide more information when something goes wrong.
DANIEL ROY GREENFELD
metap: A Meta-Programming Layer for Python
metap
is a meta-programming layer for Python. It is useful for automating coding patterns or applying transformations to your code.
BAZIOTIS & MENDIS
Working With Python's Built-in Exceptions
Learn the most common built-in Python exceptions, when they occur, how to handle them, and how to raise them properly in your code.
REAL PYTHON course
Coverage.py regex pragmas
Coverage.py uses regexes to define pragma syntax. This is surprisingly powerful, allowing for custom exclusions and plugins.
NED BATCHELDER
Following Up on the Python JIT
A summary of the current state of the JIT compiler inside CPython based on a couple of talks from one of the creators.
JAKE EDGE
Don't Call Dunder Methods
It's best to avoid calling dunder methods. It's common to define dunder methods, but uncommon to call them directly.
TREY HUNNER
Projects & Code
Free-Threaded Map-Reduce
GITHUB.COM/JOSEBLANCA • Shared by Jose Blanca
transfunctions: Support Both Sync and Async
GITHUB.COM/POMPONCHIK • Shared by pomponchik (Evgeniy Blinov)
Events
Weekly Real Python Office Hours Q&A (Virtual)
August 6, 2025
REALPYTHON.COM
Canberra Python Meetup
August 7, 2025
MEETUP.COM
Sydney Python User Group (SyPy)
August 7, 2025
SYPY.ORG
Buea
August 8 to August 9, 2025
NOKIDBEHIND.ORG
PyDelhi User Group Meetup
August 9, 2025
MEETUP.COM
DFW Pythoneers 2nd Saturday Teaching Meeting
August 9, 2025
MEETUP.COM
DjangoCon Africa 2025
August 11 to August 16, 2025
DJANGOCON.AFRICA
PyCon Somalia 2025
August 13 to August 15, 2025
PYCON.ORG.SO
PyCon Korea 2025
August 15 to August 18, 2025
PYCON.KR
Happy Pythoning!
This was PyCoder's Weekly Issue #693.
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 ]
05 Aug 2025 7:30pm GMT
Real Python: Exploring Python T-Strings
Python 3.14's t-strings allow you to intercept and transform input values before assembling them into a final representation. Unlike f-strings, which produce a str
object, t-strings resolve to a Template
instance, allowing you to safely process and customize dynamic content.
One of the key benefits of t-strings is their ability to help prevent security vulnerabilities like SQL injection and XSS attacks. They're also valuable in other fields that rely on string templates, such as structured logging.
By the end of this video course, you'll understand that:
- Python t-strings are a generalization of f-strings, designed to safely handle and process input values.
- The main components of a t-string include static string parts and interpolations, which are accessible through the
Template
class. - You process t-strings by iterating over their components, using attributes such as
.strings
,.interpolations
, and.values
for safe and customized handling.
[ 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 ]
05 Aug 2025 2:00pm GMT
PyCharm
PyCharm 2025.2 introduces the AI Toolkit: everything AI engineers need - now natively supported in your IDE.
This release's other highlights include the last supported version of PyCharm Community, improvements to AI Assistant, Junie, and notebooks, as well as a persistent UI for updating .lock
files and other enhancements.
Download the latest version from our website or update through our free Toolbox App.
PyCharm Community is moving toward the unified PyCharm
In our efforts to make PyCharm one powerful unified product, 2025.2 will be the last supported version of PyCharm Community. With the 2025.3 release, we'll offer a seamless way to migrate to the unified PyCharm. You can upgrade to 2025.2 Community using the Toolbox App or via the in-product upgrade notification.
If you'd already like to start using the unified PyCharm with Jupyter notebooks for free today, without waiting for the migration, you can download it here. Learn more about the unified PyCharm in this blog post.
AI Toolkit [Pro]
The PyCharm AI Toolkit integrates AI development directly into PyCharm, bringing experimentation, debugging, evaluation, fine-tuning, and deployment into one familiar environment.
The AI Toolkit is just getting started, but it's already packed with powerful features like the AI Playground and AI Agents Debugger.
Want to influence what comes next? Help shape the future of AI development tooling in PyCharm by sharing your feedback. We're building this for you, and we prioritize the features that matter most to our community. Let us know what you'd love to see.
AI Playground [Pro]
PyCharm 2025.2 introduces the versatile new AI Playground feature, which lets AI engineers compare responses from multiple AI models side by side. You can configure system prompts and fine-tune individual model parameters, enabling precise control over Temperature, Top P, and Max length parameters.
Go to More Tool Windows | AI Playground, where you will be able to choose between:
- All models included in your paid AI subscription (these will use your AI Assistant tokens[Experimenta]l).
- Custom providers that you can add via API keys.
- Locally running models.
AI Agents Debugger [Pro]
PyCharm 2025.2 also introduces the powerful new AI Agents Debugger plugin, designed to provide transparency and debugging capabilities for AI agent development. It's built for developers working with agentic systems who need to understand, debug, and optimize their agent workflows.
AI Agents Debugger transforms the typically opaque "black box" nature of agentic systems by providing detailed insights into the thought processes, metadata, inputs, and outputs of each agent node.
Junie
Junie, the coding agent by JetBrains, autonomously plans, writes, refines, and tests code to make your development experience smooth, efficient, and enjoyable. It handles tedious tasks like restructuring code, creating tests, and implementing refinements, so you can focus on bigger challenges and innovation.
Junie has received several major updates, including a speed boost of up to 30%! It now features MCP support, enabling Junie to connect to external servers and deliver more accurate solutions. Additionally, Junie is now available for remote development (currently supported on macOS and Linux) and has launched an Early Access Program for its GitHub integration. This integration allows you to work with pull requests without opening the IDE.
Junie is available in PyCharm as part of the JetBrains AI subscription.
AI Assistant
- Smarter completion across all supported languages, now with support for SQL, YAML, JSON, Markdown, and more.
- Expanded offline flexibility - connect any OpenAI-compatible model server like llama.cpp or LiteLLM.
- JetBrains AI Assistant now supports image attachments in the chat when using Anthropic and OpenAI models.
All JetBrains AI features are available for free with PyCharm Pro subscription, with unlimited code completion, powerful local workflows, and limited cloud-based features.
Persistent UI for updating .lock
files
We've added persistent controls to .toml
configuration files for uv
, Hatch, and Poetry. These controls allow you to apply changes made to a .toml
file by clicking the corresponding buttons to sync, lock, or update the dependencies in your environment.
Context-aware code generation for Jupyter notebooks and SQL cells [Pro]
You can now generate code for Jupyter notebooks and SQL cells using AI Assistant, which considers the source cell's context.
To do so, click AI Edit Cell in the context menu or navigate to it from the AI icon in the right-hand toolbar.
Logical structure tool window for Django [Pro]
Logical structure offers a framework-centric view of your Django project, allowing you to navigate and understand your application architecture more intuitively. It represents your project based on how Django sees it - surfacing the meaningful relationships between models, views, serializers, forms, admin classes, and other components you work with every day. It also allows you to generate templates for missing relations between entities, helping you quickly scaffold your code.
With an active JetBrains AI subscription, you can unlock more advanced capabilities. Instead of basic templates, the AI generates complete code that's fully aware of entity relationships, delivering smarter and more context-rich results.
Advanced image processing
A new image viewer provides options for performing additional manipulations, including restoring the original image, inverting the channels of the original image, converting the image to grayscale, setting the binarization threshold, and more.
Looking for more?
- Visit our What's New page to learn about other 2025.2 features and the latest bug fixes.
- Read the release notes for the full breakdown of the changes.
- If you encounter any problems, please report them via our issue tracker so we can address them promptly.
We'd love to hear your feedback on PyCharm 2025.2 - leave your comments below or connect with us on X.
05 Aug 2025 1:40pm GMT
Adrarsh Divakaran: Vibe Coding with Junie - JetBrains AI Coding Agent
05 Aug 2025 12:50pm GMT
PyCharm: PyCharm 2025.2: AI Toolkit for AI Engineers and More!
05 Aug 2025 11:04am GMT
Seth Michael Larson: Extracting 20+ NES & Famicom ROMs from Animal Crossing
Animal Crossing for the GameCube was a game far ahead of its time and one of my personal favorites growing up. One of the most beloved features was the addition of playable NES games as rare collectible furniture items.
This feature was implemented by including NES and FDS emulators and the actual game ROMs on the Animal Crossing disk. The NES emulator included with Animal Crossing is frequently referenced as being one of the most accurate NES emulators available, but comes with the requirement of playing or emulating a GameCube to access the feature.
What if you wanted to play these ROMs on a different emulator without having to jump into your Animal Crossing town, is that possible?
What game ROMs are available?
There are 21 total NES and Famicom Disk System (FDS) ROMs across the first generation of Animal Crossing games. Different games are included depending on the edition of the game (Doubutsu no Mori, Doubutsu no Mori+, Animal Crossing, or Doubutsu no Mori e+) with Animal Crossing containing the most unique games at 19. Different ROMs are sometimes used depending on the game's region.
I've included a full list of games available along with two other data columns. The first being the "loose" game price from Pricecharting at the time of publication. Buying physical copies of all the games included in Animal Crossing costs just over $500 USD!
The second column is whether the game is available as a part of the "Nintendo Entertaminment System™ Nintendo Switch Online" subscription, here abbreviated as "NSO".
Game | Price | NSO? |
---|---|---|
Balloon Fight | $30 | Yes |
Baseball | $5 | Yes |
Clu Clu Land | $40 | Yes |
Clu Clu Land D (1) | $42 | No |
Donkey Kong | $47 | Yes |
Donkey Kong 3 | $18 | Yes |
Donkey Kong Jr. | $151 | Yes |
Excitebike | $11 | Yes |
Golf | $7 | Yes |
Gomoku Narabe Renju (2) | $3 | No |
Ice Climber | $24 | Yes |
The Legend of Zelda | $27 | Yes |
Mahjong (2) | $8 | No |
Pinball | $4 | Yes |
Punch-Out!! | $19 | Yes |
Super Mario Bros. | $15 | Yes |
Tennis | $9 | Yes |
Wario's Woods | $44 | Yes |
Total | $504 |
NOTE (1): You will need to convert the QD file (
.qd
) for "Clu Clu Land: Welcome to New Clu Clu Land" into an FDS file (.fds
) to play the ROM with an emulator. Here's a simple Python script that I found on GitHub that worked perfectly.
NOTE (2): If you happen to also own Animal Forest+ (but not Animal Forest e+) you can also obtain "Mahjong" and "Renju Gomoku Narabe" (ごもくならべ) Famicom games.
Extracting the ROMs
So if we've archived the Animal Crossing ROM from the physical disk using CleanRip or the FlippyDrive backup tool, what can we do to extract the NES and FDS ROM files?
When I went looking for answers, originally I tried tracking down the exact offsets within the Animal Crossing ROM using the Animal Crossing decompilation project as a reference. This turned out to be unnecessary, as I discovered that the ROM data was likely compressed.
Knowing that the ROM data was compressed, on a hunch I checked the ROM for all Yaz0-compressed blobs. Yaz0 is commonly used by Nintendo for compressing assets. Sure enough, the ROMs were all compressed using Yaz0 which uses a highly identifiable magic string of "Yaz0
". This meant we could extract all ROM files by decompressing all Yaz0 blobs and then look for NES and FDS ROM headers.
Below is the entire script I used to archive the NES ROMs from the Animal Crossing ISO, including an implementation of the Yaz0 compression scheme:
Click to show animal-crossing-nes-roms.py
source code
import sys
import mmap
import hashlib
import struct
# MD5 hashes from https://datomatic.no-intro.org
# Headerless, as header is changed from non-AC releases.
known_roms = {
"0033972cb952bbbc4f04217decdaf3a7": "Mahjong (Japan) (Rev 2) (Animal Crossing).nes",
"0dd95c3047bb0336823c39fefb7639c3": "Donkey Kong (World) (Rev 1) (Animal Crossing).nes",
"1ca706896a8d4f2a2b5480d941130a4a": "Donkey Kong Jr. Math (USA, Europe).nes",
"1de41e13a2e691a8cc13b757a46ae3b8": "Clu Clu Land (World) (Animal Crossing).nes",
"27b4479df4228d48986698ffb94e9f6b": "Punch-Out!! (USA).nes",
"28c4a5b81feb4033acee9d67852d8ffc": "Gomoku Narabe Renju (Japan) (Animal Crossing).nes",
"2bf3976d15ec25a756846465a16b064c": "Excitebike (Japan, USA) (En) (Animal Crossing).nes",
"44d401f92e1da528ca4a9d7083acc9d2": "Clu Clu Land (Japan) (En) (GameCube, Virtual Console).qd",
"5f37d85ba0f296bd471cd674d63cb640": "Legend of Zelda, The (USA) (Rev 1) (Animal Crossing).nes",
"8e3630186e35d477231bf8fd50e54cdd": "Super Mario Bros. (World).nes",
"70c309cb6b9ead20c06d554cf48b3993": "Balloon Fight (USA).nes",
"108fea367dc5ba9a691b3500fc1464b4": "Baseball (USA, Europe) (Animal Crossing).nes",
"6631ceac1aaef8efb063a34da86bacb1": "Donkey Kong Jr. (World) (Rev 1) (Animal Crossing).nes",
"a2b5bddb4c7a5a39c8fac13e64494c9a": "Donkey Kong 3 (World).nes",
"bec7fa447c1c8e13a87bd4a5685ce563": "Wario's Woods (USA).nes",
"bfab5f738adb919f1ba389f5c38deb67": "Pinball (Japan, USA) (En) (Animal Crossing).nes",
"c9c94df2ebb19bd6d717b2cfbf977574": "Ice Climber (USA, Europe, Asia) (En).nes",
"c432b613606c41bafa4a09470d75e75f": "Soccer (Japan, USA) (En).nes",
"cbb2c477a37b28517e330d1c562049f8": "Tennis (Japan, USA) (En) (Animal Crossing).nes",
"d67ee6a0a7af959c417ce894470a49cb": "Mario Bros. (World) (Animal Crossing).nes",
"f0d94f25db202c935cd8f1cdde10a0aa": "Golf (USA, Asia) (En).nes",
# Multiboot ROMs
"594b8e60e9406a570c9990e9bbc4340f": "Clu Clu Land (USA, Europe) (Animal Crossing).mb",
"aa6bdfc4fce58b19d1a8a9f2f11042d9": "Donkey Kong (USA, Europe) (Animal Crossing).mb",
"ee8a23328607687b50a6706c7fdfc2e1": "Donkey Kong Jr. (USA, Europe) (Animal Crossing).mb",
"d3d227a1ca88b0629ef333d544686c41": "Excitebike (USA, Europe) (Animal Crossing).mb",
"684e46cb0c672e06664ae742825ae89c": "Mario Bros. (USA, Europe) (Animal Crossing).mb",
"c1c6eb0d42591c46f2e4dc68145e4c81": "Pinball (USA, Europe) (Animal Crossing).mb",
"34217e69f45e52d1550a8b241ce27404": "Super Mario Bros. (USA, Europe) (Animal Crossing).mb",
"a81a5d9b9268da64ea8426bdc6a987ba": "Soccer (USA, Europe) (Animal Crossing).mb",
"146bdf7f70335a2ad67b59ef9e07bfaf": "Tennis (USA, Europe) (Animal Crossing).mb",
"62c26ddf7579b5179b2a67073bc7e4a4": "Balloon Fight (USA, Europe) (Animal Crossing).mb",
"b0c8f4dfe47c3649760748ad5c96a649": "Baseball (USA, Europe) (Animal Crossing).mb",
"a7b95f64a01e7cc18968b1c501741414": "Donkey Kong 3 (USA, Europe) (Animal Crossing).mb",
"f92aeb4bc274cb08c2eabe9dd3aadcb4": "Golf (USA, Europe) (Animal Crossing).mb",
"f7adee0901bb73b6f1c1fbeb36b4ab4c": "Ice Climber (USA, Europe) (Animal Crossing).mb",
"1c8dcf20e4ce979cb9962c835c39a5c9": "Donkey Kong Jr. Math (USA, Europe) (Animal Crossing).mb",
}
def main():
animal_crossing_iso = sys.argv[1]
with open(animal_crossing_iso, mode="r+b") as f, mmap.mmap(f.fileno(), 0) as fm:
offset = -1
# Find all 'Yaz0' headers.
while -1 != (offset := fm.find(b"Yaz0", offset + 1)):
try:
data = yaz0(fm[offset : offset + 0x80000])
except Exception:
continue
# NES ROM
if data.startswith(b"NES\x1a"):
# Calculate the MD5 without the NES header.
rom_md5 = hashlib.md5(data[16:]).hexdigest()
# Famicom Disk System ROM
elif data.startswith(b"\x01*NINTENDO-HVC*\x01"):
rom_md5 = hashlib.md5(data).hexdigest()
# GBA Joyboot ROM
elif data[0xAC:0xB3] == b"AGBJ01\x96":
rom_md5 = hashlib.md5(data).hexdigest()
else:
continue
if rom_md5 not in known_roms:
print(f"Unknown ROM MD5: {rom_md5}")
continue
filename = known_roms[rom_md5]
print(f"Extracted ROM: {filename}")
with open(filename, mode="wb") as rom_fp:
rom_fp.truncate()
rom_fp.write(data)
def yaz0(data: bytes) -> bytes:
# Implementation of Yaz0 following
# this reference: http://www.amnoid.de/gc/yaz0.txt
(buf_length,) = struct.unpack(">xxxxLxxxxxxxx", data[:16])
data = data[16:]
src = dst = 0
buf = bytearray(buf_length)
while dst < buf_length and src < len(data):
bit_header = data[src]
src += 1
for _ in range(8):
if not (dst < buf_length and src < len(data)):
break
if bit_header & 0x80:
buf[dst] = data[src]
dst += 1
src += 1
else:
byte1, byte2 = struct.unpack(">BB", data[src : src + 2])
assert byte1 >= 0 and byte2 >= 0
src += 2
copy_src = dst - ((byte1 & 0x0F) << 8 | byte2) - 1
num_bytes = byte1 >> 4
if num_bytes == 0:
num_bytes = data[src] + 0x12
src += 1
else:
num_bytes += 2
for i in range(num_bytes):
buf[dst + i] = buf[copy_src + i]
dst += num_bytes
bit_header <<= 1
return bytes(buf)
if __name__ == "__main__":
main()
The source code above is also available as a Gist, licensed MIT. After running this script with an Animal Crossing ISO you'll find all the NES and Famicom Disk System ROMs in your current directory:
# Running the script with Python!
$ python animal-crossing-nes-roms.py ./AnimalCrossing\ \(v1.00\).iso
# All of the ROMs are extracted in the CWD.
Extracted ROM: 'Clu Clu Land (World) (Animal Crossing).nes'
Extracted ROM: 'Balloon Fight (USA).nes'
...
Extracted ROM: 'Super Mario Bros. (World).nes'
Extracted ROM: 'Legend of Zelda, The (USA) (Rev 1) (Animal Crossing).nes'
This script can be improved by adding more MD5 checksums of ROMs from different regions like Japan and the EU. Currently I've only tested using the North American copy of Animal Crossing.
These ROMs can now be used just like any other ROM with NES emulators. If you're playing on desktop and want to try your ROMs out you can use Pinky, a WASM-based NES emulator. If you want to play on mobile I recommend using the Delta emulator.
It feels quite magical to be playing NES games from a childhood copy of Animal Crossing on a modern iPhone. Wario's Woods was always my favorite, so I look forward to playing the game more often and keeping that nostalgic connection alive.
If you're looking for more ways to find legal ROMs of games, WULFF DEN published a video recently linking to a list of other "ROM extraction" methods like this one.
Playing Wario's Woods on the Delta emulator
What if I only have a Game Boy Advance?
You're in luck! Animal Crossing also had a feature called "Advance Play" which meant most NES games could be saved to your GBA via a mode called "Multiboot" or "Joyboot". Animal Crossing used separate ROMs that were compiled for the GBA CPU instead of the NES CPU.
These ROMs are detected by looking for the string AGBJ01\x96
at offset 0xAC
, corresponding to the Game Boy Advance ROM header that was chosen for these ROMs.
NOTE: To run these on a GBA emulator or with a GBA flash cartridge you need to convert the
.mb
files into.gba
files. This script worked for converting the files to.gba
.
05 Aug 2025 12:00am GMT