Planet Python
Last update: July 08, 2026 07:50 AM UTC
July 08, 2026
Python GUIs
Why Widgets Appear as Separate Windows — Understanding widget parenting in Qt and how to fix widgets that float outside your main window
Sometimes when I dynamically add widgets to tabs in my PyQt6 application, they pop out as windows instead. What's going on?
If you're dynamically adding widgets to your PyQt6 application and finding that they pop out as separate floating windows instead of appearing neatly inside your application, you're running into one of Qt's gotchas: widget parenting.
This problem usually shows up when widgets are added from a callback, event listener or signal handler. But there are a million different ways to screw this up. Let's look at why this happens and how to fix it.
How Qt decides what's a window
In Qt, every widget can optionally have a parent widget. The parent determines where a widget lives visually — a widget with a parent is drawn inside that parent. A widget without a parent becomes a top-level window, floating independently on your desktop.
This is the root cause of widgets appearing outside your main window. When you create a widget and it doesn't have a parent — either because you didn't set one, or because the parent was lost somehow — Qt treats it as a standalone window.
Three ways to Get a Parent-less Widget
Here are the most common reasons widgets end up floating:
Creating widgets without a parent
# This widget has no parent — it will be a floating window
tabs = QTabWidget()
# This widget has a parent — it will appear inside parent_widget
tabs = QTabWidget(parent_widget)
When you add a widget to a layout, the layout assigns the parent automatically. But if something goes wrong between creation and layout insertion (like an exception, or the widget being shown prematurely), the widget stays parentless.
The safest approach is to pass a parent when creating widgets:
def create_new_tab(self):
wdg = QWidget()
layout = QGridLayout(wdg)
tabs = QTabWidget(wdg) # Explicitly set parent
tab1 = QWidget(tabs) # Explicitly set parent
tab2 = QWidget(tabs) # Explicitly set parent
tabs.addTab(tab1, "Start")
tabs.addTab(tab2, "Profile")
layout.addWidget(tabs)
return wdg
...although, honestly, I don't usually bother. If I know I'll be adding a widget to a layout immediately, I'll omit the parent assignment.
In an window __init__ the safety question is less relevant because, if there is an unhandled exception that blocks the adding your sub-widget to a layout, it will also block the creation of the parent window.
Accidentally recreating a widget
If you have a tab widget stored as self.w and somewhere in your code you do:
self.w = QTabWidget()
...the original tab widget is replaced. If the old widget gets garbage collected, all the tabs that had it as their parent suddenly become orphans — parentless widgets that float as independent windows.
Be careful not to reassign widget attributes unintentionally, especially in callbacks that might run multiple times.
Losing the parent reference
If you explicitly set a widget's parent to None, it becomes a top-level window:
widget.setParent(None) # This widget is now a floating window
This sometimes happens indirectly. For example, removing a widget from a layout in certain ways can clear its parent.
A clean approach to dynamic tabs
Here's a complete, working example that dynamically adds tabs without any floating-window issues. It demonstrates the correct way to set up a QTabWidget with a "+" button that adds new tabs:
import sys
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QTabWidget,
QWidget, QVBoxLayout, QLabel
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Dynamic Tabs")
self.setFixedSize(600, 400)
self.tabs = QTabWidget(self)
self.tabs.currentChanged.connect(self.on_tab_changed)
# Add an initial tab
self.add_content_tab("Tab 1")
# Add the "+" tab for creating new tabs
self.tabs.addTab(QWidget(self.tabs), "+")
self.setCentralWidget(self.tabs)
def on_tab_changed(self, index):
# Check if the "+" tab was clicked
if self.tabs.tabText(index) == "+":
self.add_new_tab()
def add_new_tab(self):
# Count existing content tabs (exclude the "+" tab)
tab_count = self.tabs.count() # includes "+"
new_title = f"Tab {tab_count}"
# Insert the new tab before the "+" tab
new_tab = self.create_tab_content(new_title)
insert_index = self.tabs.count() - 1
self.tabs.insertTab(insert_index, new_tab, new_title)
# Switch to the newly created tab (avoid retriggering)
self.tabs.blockSignals(True)
self.tabs.setCurrentIndex(insert_index)
self.tabs.blockSignals(False)
def add_content_tab(self, title):
"""Add a content tab before the + tab."""
tab = self.create_tab_content(title)
# Insert before the last tab if "+" exists, otherwise just add
plus_index = None
for i in range(self.tabs.count()):
if self.tabs.tabText(i) == "+":
plus_index = i
break
if plus_index is not None:
self.tabs.insertTab(plus_index, tab, title)
else:
self.tabs.addTab(tab, title)
def create_tab_content(self, title):
"""Create the widget content for a tab."""
widget = QWidget(self.tabs) # Parent is the tab widget
layout = QVBoxLayout(widget)
label = QLabel(f"Content for {title}", widget)
layout.addWidget(label)
return widget
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
A few things to notice in this example:
- The main window inherits from
QMainWindow, andQApplicationis created separately. - Every widget is created with an explicit parent:
QWidget(self.tabs),QLabel(text, widget), etc. blockSignals(True)is used when programmatically changing the current tab to prevent thecurrentChangedsignal from firing recursively.- New tabs are inserted before the "+" tab using
insertTab, so the "+" always stays at the end.
Summary
Widget parenting is one of those things in Qt that works invisibly when everything is correct — and causes confusing visual glitches the moment something is slightly off. The good news is that once you understand the pattern, the fix is almost always the same: make sure every widget has a parent.
If you're new to PyQt6, our guide to creating your first window covers the basics of setting up a QMainWindow, while the widgets tutorial walks through the most common widgets and how to use them correctly.
For an in-depth guide to building Python GUIs with PyQt6 see my book, Create GUI Applications with Python & Qt6.
July 07, 2026
Brett Cannon
How to publish to PyPI using GitHub Actions securely
There have been several security incidents lately that involved compromising GitHub Actions workflows. This has led some to say "GitHub Actions is the weakest link" in publishing and to GitHub publishing a GitHub Actions security roadmap update. But saying it&aposs an issue and acknowledging the fact is one thing, but you still need to do the mitigation work today so you are not going to be the next headline. So this post is going to outline 3 things to do so you can publish to PyPI securely when using GitHub Actions.
But before I go any farther, I want to make 2 things very clear. One is this post is in no way meant to shame anyone into using GitHub Actions. For instance, I have heard people trying to shame maintainers into using GitHub Actions to use Trusted Publishing, and I think that&aposs wrong. Now, if you choose to use a platform that supports Trusted Publishing, then you should definitely use it. But Trusted Publishing is not a reason to change your publishing workflow if the one you have is already secure. In other words, use whatever works best for you to publish securely to PyPI, and if that&aposs GitHub Actions then this blog post is for you.
Two, the title of this post explicitly says "publishing" and not "building and publishing". Doing builds securely is a separate concern that I am not covering. The one piece of advice I will give, though, is one the Python security developer in residence gave me: you should have building and publishing be separate workflows.
With that out of the way, here are 3 steps to securing GitHub Actions for publishing to PyPI that should be relatively painless.
Use zizmor
The zizmor tool examines your GitHub Actions workflows to find things that at dubious when it comes to security. They pretty much all stem from GitHub Actions having insecure defaults in the name of convenience. There are 2 parts to using zizmor:
- Make it happy
- Set it up in CI
You can do those two things in whatever order you want but you need to do both to make sure you fix any current issues you have and prevent any new issues from slipping in. Luckily both things are easy to do.
Make zizmor happy
To run zizmor you can do uvx zizmor --quiet --fix .github/ , pipx zizmor --quiet --fix .github/ , or however you choose to run it. That will run zizmor and fix anything that it can in a clean way. Chances are, though, there will be three things to fix by hand.
No permissions by default
By default, the token GitHub Actions gives to your workflow via GITHUB_TOKEN is way too broad, so zizmor flags it. Easiest way to fix this issue is to turn off all permissions at the global level for a workflow and then turn any permissions you need on at the job level. So put the following at the global level of your workflow file (I personally put it just before jobs:):
permissions: {}If you happen to need some specific permission, you can then specify it per-job so you scope it as tightly as possible. Or if you really need something for everything, you can still set it globally, but you at least you will be explicit about exactly what you want.
The reason you do this is you don&apost want some action to get a hold of your token that can do something as if you&aposre you and do something bad.
No persisted credentials after checkout
When you use the checkout action, GitHub Actions is running Git on your behalf, complete with credentials so the git checkout command works. The problem is those credentials persist passed the checkout action unless you specifically say to not keep them around. So add the following with: clause to your checkout action:
with:
persist-credentials: falseYou do this so your credentials don&apost leak out to some action that will do something bad with them.
Pin your actions
When you specify an action to use in a workflow, you were probably told to use some Git tag like uses: actions/checkout@v7 which specifies using the v7 tag from the https://github.com/actions/checkout repo. The problem with that is if that action gets compromised, an attacker can just update that tag to point to malicious code and so now you&aposre compromised.
You work around this by pinning your actions to commit hashes. This might sound like a massive hassle, but there are tools that can pin all your actions for you.
- gha-update
zizmor --fix --gh-tokenwith a (permissionless) token- Pinact
Those go from simplest to fanciest, but they all get the job done. I personally use gha-update as it&aposs quick and updates my versions along the way. But if you want to keep your current versions as-is then zizmor will do it for you, but you need to give it a token to do the updates (the token is required to avoid being throttled by GitHub). The best thing to do is to use a permissionless token, but if you&aposre being lazy and trust zizmor (and any tool you might be using to run it, e.g. uvx), you can get a token from gh auth token (the following example is for the Fish shell; adjust the syntax for calling gh accordingly for your shell and how you prefer to call zizmor):
zizmor --quiet --fix --gh-token (gh auth token) .githubIf you need fancier than any of that, use Pinact.
You also want to require pinning not only for your workflows but any actions that use actions themselves so you&aposre pinned top to bottom. The easiest way to make that a requirement is to run the following command:
gh api "/repos/{owner}/{repo}/actions/permissions" --method PUT --field enabled=true --field sha_pinning_required=trueThere&aposs also a way to do it via the UI:
Screenshot of turning on required SHA pinning in a repo under Settings - Actions - GeneralBonus: Dependabot to keep actions up-to-date
Dependabot will recognize your use of pins, so you can still use it to keep your actions up-to-date (if you so choose; it&aposs okay if you don&apost want to use Dependabot). The one thing I suggest is using a cooldown so you don&apost accidentally pick to a malicious update by adding a cooldown of a week to your dependabot.yml:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
cooldown:
default-days: 7Add zizmor to CI
Conveniently, zizmor has an action you can set up in your repo. Using it will cause any issues found to be reported as a code scanning result under the "Security and quality" tab (which can be turned off).
Screenshot showing the "Code scanning" view under the "Security and quality" tab on GitHubThis means the results are private and thus you don&apost have to worry about exposing anything publicly. You can also use the results as a TODO list if you would find that more motivating to have something to check off instead of getting everything working upfront. As well, if you want to do it gradually this will give you a checklist of things to fix.
You can also run zizmor manually if you want in CI, but I personally just use the zizmor action in a dedicated workflow since the zizmor docs provide such a workflow configuration.
Use Trusted Publishing
If you&aposre going to use GitHub Actions to publish to PyPI, I don&apost see any reason not to use Trusted Publishing. It means you don&apost have to manage any API tokens and you can get attestations. Basically it means you get to outsource your security concerns for how you communicate with PyPI for publishing to GitHub&aposs security team.
The one thing you should make sure to do when setting up Trusted Publishing is set up a GitHub environment. The Trusted Publishing docs strongly encourage it and so do I. You can even have the environment do nothing, but doing it now at least gives you an easy option to use it for something later. But I do suggest you use environments to ...
Require approval to publish
The one specific thing I suggest you do with your GitHub environment is require reviewers to run your publishing workflow. The required reviewer can be yourself! But the key point is to require someone to approve the workflow to run.
You might be wondering what&aposs the point if you trigger the release yourself? It&aposs to add a gate to protect against accidental running of your publishing workflow. The accident could be from you or it could be from a malicious actor who has managed to trigger the workflow. By requiring your approval, neither scenario can happen without you clicking that approval button while logged into your GitHub account. And that means someone would need to hack your GitHub account to work around it (and as mentioned above, that means you get to lean on the GitHub security team from preventing that from happening).
Out of everything I have listed, this is probably the most arduous as it&aposs a cost every time you want to do a release. But it&aposs one approval and you&aposre probably already going to be doing something to trigger the release, so you&aposre already online.
And that&aposs it! Those 3 steps get you a long way towards publishing securely from GitHub Actions to PyPI.
Acknowledgments
Thanks to Seth Larson for providing feedback on a draft of this post and giving advice on Mastodon when I posted about these steps. Thanks to William Woodruff for creating zizmor and also giving advice on Mastodon. And thanks to everyone who participated constructively in the discussion on Mastodon.
PyCoder’s Weekly
Issue #742: Wagtail as Admin, Random Values, Code Quality, and More (2026-07-07)
#742 – JULY 7, 2026
View in Browser »
Wagtail as Django Admin on Steroids
Wagtail can do pretty much everything the Django Admin can do, but includes a much more modern UI and more features. This article shows you how to use Wagtail as an Admin alternative.
TIM KAMANIN
Selecting Random Values in Python
Python’s random module provides utilities for generating pseudorandom numbers. For cryptographically-secure randomness, use the secrets module instead.
TREY HUNNER
Let AI Agents Into Your B2B App. Securely.
More of your users are asking to connect AI agents to your product, and you want to say yes. PropelAuth lets you give each agent scoped, revocable access, so you stay in control of what it can do. Learn more →
PROPELAUTH sponsor
Managing and Measuring Python Code Quality
Master Python code quality tools like linters, formatters, type checkers, and profilers to measure, manage, and improve the code you write.
REAL PYTHON course
Discussions
Articles & Tutorials
Thinking About Running for the PSF Board? Let’s Talk!
The Python Software Foundation Board has announced two office-hour sessions dedicated to giving information on running for the PSF Board. If you’re thinking of running in the upcoming election, these sessions can help you understand the ins and outs.
PYTHON SOFTWARE FOUNDATION
Celery on AWS ECS: Complete Guide
Running Celery on AWS ECS without losing tasks and making sure that all the work is done is not as straightforward as it may seem. Learn how to configure Celery and structure your tasks for reliable processing.
JAN GIACOMELLI • Shared by Špela Giacomelli
Learn the Agentic Coding Workflow That Actually Works on Real Projects
65% of Python developers are stuck using AI for small tasks that fall apart on anything real. This 2-day live course (July 11-12 via Zoom) walks you through building a complete Python app with OpenAI’s Codex, from an empty directory to a shipped project on GitHub. See the Full Curriculum →
REAL PYTHON sponsor
Free-Threaded Python: Past, Present, and Future
This post summarizes a talk by core developer Thomas Wouters at PyCon US 2026 on Free-threaded Python: the attempt to remove the GIL. It describes why it is being done and what future work looks like.
JAKE EDGE
In Search of a New Contribution Model
This opinion piece from Carlton Gibson, a core Django contributor, talks about the state of contributions to OSS, how AI has made them more complicated, and how some key things are still broken.
CARLTON GIBSON
How to Get TIFF MetaData With Python
The Pillow image library gives you lots of tools for dealing with images. This article teaches you how to extract metadata from TIFF files in a few lines of Python.
MIKE DRISCOLL
Profile First: A 10x Faster Django Test Suite
Bob’s Django test suite took 30 seconds. cProfile showed 83% of it was one function: password hashing. Here’s how he found the bottleneck and the five-line fix.
BOB BELDERBOS
Python 3.15 Preview: Upgraded JIT Compiler
Learn how the upgraded Python 3.15 JIT compiler speeds up your code with a new tracing frontend, register allocation, and in-place numeric operations.
REAL PYTHON
Store Extra Data for Objects in a WeakKeyDictionary
In several programs Adam has wanted to solve the problem of associating extra data with an object. This article outlines his latest approach.
ADAM JOHNSON
How to Get Started With the GitHub Copilot CLI
Learn how to install, authenticate, and use the GitHub Copilot CLI to plan, write, and review Python code from your terminal with AI agents.
REAL PYTHON
Projects & Code
pytest-tia: Run Only the Tests Your Git Diff Actually Affects
GITHUB.COM/BREADMSA • Shared by BreadWasEaten
purejq: A Pure-Python Implementation of jq
GITHUB.COM/ADAM2GO • Shared by adam2go
Events
Python Atlanta
July 9 to July 10, 2026
MEETUP.COM
PyDelhi User Group Meetup
July 11, 2026
MEETUP.COM
DFW Pythoneers 2nd Saturday Teaching Meeting
July 11, 2026
MEETUP.COM
EuroPython 2026
July 13 to July 20, 2026
EUROPYTHON.EU
SciPy 2026
July 13 to July 20, 2026
SCIPY.ORG
EuroSciPy 2026
July 18 to July 24, 2026
EUROSCIPY.ORG
Happy Pythoning!
This was PyCoder’s Weekly Issue #742.
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 ]
PyCharm
Best Object Detection Models for Machine Learning in 2026
Object detection powers transformative applications, from autonomous vehicles navigating city streets and security systems identifying threats in real time to retail analytics tracking inventory and medical imaging detecting tumors. But choosing the right model for your computer vision project can be challenging, especially with dozens of architectures claiming superiority across different metrics.
In this guide, we’ll examine the top object detection models available in 2026, comparing their architectures, performance characteristics, and ideal use cases to help you determine which models are best suited to your applications.
Whether you’re building real-time video analytics, high-precision inspection systems, or resource-constrained edge applications, you’ll find clear guidance on which model best fits your requirements.
What is object detection?
Object detection aims to identify and localize multiple objects within images or video frames. Unlike image classification, which only classifies the broad identity of an image, object detection identifies the objects in an image/video frame and their exact positions within it.
In a nutshell, object detection solves two interdependent problems:
- Localizing (detecting) the objects on the image, by drawing the bounding boxes for the objects on the image (it is possible that there are zero objects!). A bounding box is usually defined as a tuple (x, y, h, w), where x and y are the top-left coordinates of the bounding box rectangle, and h and w are the height and width of the bounding box, respectively.
- Classifying the identities of these images (like a person, car, or dog).
This dual capability makes object detection more complex than classification alone, requiring models that can handle multiple objects of different sizes appearing anywhere in an image.
As with classification tasks, a simple accuracy metric is not sufficient to assess model performance. We need metrics of two types. Firstly, performance metrics that gauge the trade-off between incorrectly detecting objects (false positives) and not detecting objects at all in the image when they were present (false negatives). Secondly, we also need metrics to assess how long it will take our model to perform the task in question: We will call these compute efficiency metrics. Usually, the new architectures for object detection are benchmarked on the validation partition of the COCO dataset and run on T4 NVIDIA GPU hardware.
Here are the standard metrics used in the object detection community:
- Basic building block of performance metric: Intersection over union (IoU) is the foundational geometric measure used to decide whether a predicted bounding box is correct. It is calculated as the area of overlap between the predicted box and the ground-truth box, divided by the area of their union – producing a score between 0 (no overlap) and 1 (perfect match). A detection is counted as a true positive only if its IoU with the nearest ground-truth box exceeds a chosen threshold (e.g. 0.5). A low IoU threshold is lenient about box placement; a high one demands tight localization.
- Performance metric: Mean average precision (mAP), which evaluates detection accuracy by measuring how well predicted boxes overlap with ground truth annotations across different confidence thresholds. The most commonly cited variant, mAP@[50:95] (also written AP50:95), averages precision over IoU thresholds from 0.50 to 0.95 in steps of 0.05, which is a stringent measure that penalizes imprecise localization as much as missed detections.
- mAP50 vs. mAP50:95: mAP50 measures detection at IoU ≥ 0.5 and scores appear higher, favoring faster models. mAP50:95 averages across IoU thresholds 0.5–0.95 – the stricter, preferred metric. For precision-critical applications (robotics, medical), it is common to optimize for mAP50:95.
- Compute efficiency metric: Frames per second (FPS), which measures inference speed, determining whether a model can process video in real-time. For standard videos, real-time is defined as >= 30 FPS (Google or original YOLO paper) or latency <= 33.3ms (1/FPS * 1000). Naturally, for such applications as self-driving cars or robotics, there are higher requirements on the FPS rate, going up as high as 60–100+ FPS.
- Compute efficiency metric: Parameter count is a quality of the model that influences its performance. There is a trade-off between the model’s accuracy and its parameter count. That’s why models are provided in different sizes of the same architecture (S, M, L, XL, etc.) to cater to various scenarios of this trade-off. This is similar to the concept of parameter count in LLMs.
There are a few popular choices of datasets to evaluate the performance of object detection models. As mentioned above, the standard choice for benchmarking object detection is the COCO dataset, containing 80 object categories across 330,000 images. Naturally, there are a lot of other datasets, specialized for certain domains, such as self-driving cars, or certain scenarios, such as the detection of objects in cluttered environments. What is important to remember is that the values of object detection metrics, IoU, and mAP depend on the dataset they were evaluated on, so mAP@[50:95]=60.1 on the COCO dataset may not be directly transferable to your custom dataset. These metrics should always be re-evaluated on your dataset to define the baseline performance of the models on it.
Object detection algorithms and architecture families
Object detection models fall into two different processing flows and two different architectural families.
Architectures
CNN-based
Examples: Faster R-CNN, Mask R-CNN, Cascade R-CNN, YOLO
CNN-based detectors rely on convolutional layers to extract local features hierarchically across the image, traditionally using predefined anchor boxes as spatial priors for localizing objects. Spatial priors are predefined assumptions about where and what size objects are likely to appear in an image, giving the model a starting point for detection rather than searching randomly.
Transformer-based
Examples: RF-DETR, RT-DETR, D-FINE
Transformer-based detectors, inspired by advances in natural language processing, instead apply global self-attention mechanisms that allow the model to reason about relationships across the entire image simultaneously.
Specifically, transformer-based detectors use learned object queries and global self-attention, where each query is trained to correspond to at most one object, unlike CNN-based detectors, which build spatial understanding locally through convolutional layers with limited receptive fields.
However, in modern architectures, there exists a fusion of the two architectures: a CNN network can use self-attention modules in its architecture, such as YOLOv12 or YOLOv13, leading to cross-architectural designs.
Processing flows
Two-stage detectors
Examples: Faster R-CNN, Mask R-CNN, Cascade R-CNN
The network makes two sequential passes, each with a distinct job:
- Stage 1: Region Proposal:
- Scans the image and proposes ~1000–2000 candidate regions (RoIs) that might contain objects.
- Doesn’t care about class yet, just the fact that “something interesting is here”.
- This is the region proposal network (RPN) in classic detectors
- Stage 2: RoI classification and refinement:
- Takes only the proposed regions from Stage 1.
- Crops/pools features for each region.
- Predicts the exact class and refined box coordinates for each proposal.
Single-stage detectors
Examples: YOLO series, SSD, RetinaNet, DETR
The network directly predicts class labels and bounding boxes from feature maps. It does everything in one forward pass. Usually, the following happens:
- A dense grid of anchor boxes (or points) is placed over the image.
- For each anchor, the network simultaneously predicts:
- Whether there is an object there (objectness/class score).
- How the box should be adjusted (box regression offsets).
- In older versions of single-stage detectors, one needed to filter overlapping bounding boxes at the end; it was done using the non-maximum suppression (NMS) algorithm. From YOLOv10 on, using NMS is a redundant step.
Furthermore, modern single-stage detectors have moved away from anchor-based designs entirely, predicting box coordinates directly from grid points and pixels, eliminating the need for dataset-specific anchor tuning altogether.
Historically, two-stage detectors offered better accuracy at the cost of speed, but modern single-stage detectors have largely closed this gap, achieving comparable or superior results while remaining significantly faster. Thus, we will focus on single-stage detectors only when evaluating the state-of-the-art models for practical applications.
Top object detection models in 2026
Two-stage pipelines (Faster R-CNN, Mask R-CNN) are no longer competitive. The current frontier is defined by single-stage NMS-free transformer architectures and models of the YOLO family. Each model below excels in a specific deployment scenario.
RF-DETR (by Roboflow) – Highest Accuracy
Real-Time Detection Transformer · ICLR 2026
| Metric | Value |
|---|---|
| mAP50:95 (N) | 48.4 |
| mAP50:95 (M) | 54.7 |
| Latency (N) | 2.3 ms |
| Latency (M) | 4.4 ms |
| mAP50:95 (2XL) | 60.1 (COCO record) |
| Latency (2XL) | 21.8 ms |
The strongest real-time model available. RF-DETR uses DINOv2 to extract deeply rich, globally-aware feature representations of the input image, then uses deformable cross-attention in the detection head to efficiently query those features and predict bounding boxes without needing anchor boxes or NMS post-processing. The result is a model that’s simultaneously more accurate on complex scenes and faster at inference than the naive combination of those components would suggest. RF-DETR is the first real-time detector to break 60 mAP on MS COCO. Designed from the ground up for fine-tuning, DINOv2 pre-training on internet-scale data gives it unmatched domain adaptability across aerial imagery, medical scans, industrial inspection, and more. It comes in four sizes: Nano, Small, Medium, Large (plus XL/2XL under a PML license).
Strengths:
- Highest mAP of any real-time model.
- Exceptional domain transfer (fine-tunes fast).
- Best on occluded and complex scenes.
- Supports detection + segmentation in a single API.
- Apache 2.0, fully commercial-friendly.
Limitations:
- Heavier than YOLO on edge/mobile.
- XL/2XL models require a PML license.
- Higher GPU memory vs. YOLO variants.
License: Apache 2.0 (N/S/M/L) · PML 1.0 (XL/2XL)
Repository: https://github.com/roboflow/rf-detr
YOLO12 (Tsinghua University) – Research / Benchmark
Attention-centric YOLO · NeurIPS 2025
| Metric | Value |
|---|---|
| mAP50:95 (N) | 40.4 |
| mAP50:95 (M) | 52.5 |
| Latency (N) | 1.60 ms |
| Latency (M) | 4.27 ms |
| License | AGPL-3.0 |
YOLO12 is the first YOLO model to place attention mechanisms at the core rather than CNNs, matching CNN-based inference speeds while gaining the global context benefits of self-attention. Key innovations: Area attention (A²) divides feature maps into regions to reduce the quadratic cost of full self-attention; Residual ELAN (R-ELAN) stabilizes training of large attention blocks; FlashAttention reduces memory bottlenecks. It is deployable on NVIDIA Jetson, NVIDIA GPUs, and macOS.
A note on implementations. YOLO12 exists in two separate codebases, and the distinction matters in practice. The original authors (Tsinghua/University at Buffalo) actively maintain their own repository at sunsmarterjie/yolov12. In June 2025, they explicitly warned against using Ultralytics’ integration, stating it “is inefficient, requires more memory, and has unstable training” – issues they have fixed in their own repo. The training instability and memory criticisms often cited against YOLO12 are therefore criticisms of the Ultralytics port, not the model itself. Ultralytics’ recommendation to prefer YOLO26 over YOLO12 should be read with this context in mind: The comparison is partly against their own suboptimal implementation.
If you use YOLO12, install from the original repository rather than via pip install ultralytics.
Strengths:
- Strong accuracy at the nano scale (beats YOLO11-N by 0.9% mAP).
- Long-range context via attention mechanisms: It can take into account the entire image when detecting an object, rather than a local pixel neighborhood, as in pure CNN architectures.
- Jetson-, Android-, and macOS-deployable.
- Original repo fixes memory and training stability issues present in the Ultralytics port.
- Actively maintained by original authors with ongoing updates (turbo variant, segmentation, and classification).
Limitations:
- If using Ultralytics implementation:
- AGPL-3.0 commercial use requires an enterprise license.
- Training instability and high memory on large models.
- If using an open-source implementation:
- AGPL-3.0 commercial use requires an enterprise license.
- Claims to have stable training and inference in comparison with Ulitralytics implementation.
- Requires installing from the original repo to avoid Ultralytics port issues, resulting in slightly more setup friction.
- Smaller ecosystem and community support than Ultralytics-native models.
License: AGPL-3.0 (open-source) · Enterprise license via Ultralytics for commercial use
Open-source repository: https://github.com/sunsmarterjie/yolov12
Ultralytics repository: https://github.com/ultralytics/ultralytics
YOLO26 (Ultralytics) – Best for edge / production
Edge-first unified YOLO · September 2025
| Metric | Value |
|---|---|
| mAP50:95 range | 40.9–57.5 |
| Latency range | 1.7–11.8 ms |
| CPU gain vs. YOLO11 (nano) | +43% |
| Unified tasks | 5 |
Ultralytics’ flagship for 2025–2026. YOLO26 shifts focus from accuracy maximization toward deployment-oriented simplification: It removes NMS and distribution focal loss (DFL) for end-to-end inference, introduces the MuSGD optimizer for stable convergence, and adds progressive loss balancing (ProgLoss), which makes sure that the model doesn’t over-optimize one objective at the expense of others, and small-target-aware label assignment (STAL), which ensures extra attention to small objects. Five tasks are solved by this one YOLO26: detection, segmentation, pose estimation, oriented bounding boxes detection, and open-vocabulary detection and segmentation. It is explicitly designed for NVIDIA Jetson Orin/Xavier, Qualcomm Snapdragon AI, and ARM CPUs. Supports INT8 and FP16 quantization, plus ONNX, TensorRT, CoreML, and TFLite export.
Strengths:
- Best edge and mobile performance (Jetson Orin and Snapdragon).
- NMS-free leads to lower latency.
- 43% faster CPU inference than YOLO11(N) at comparable accuracy, ideal for devices without a GPU.
- Five tasks in one architecture.
- Stable INT8/FP16 quantization.
Limitations:
- AGPL-3.0: commercial use requires an enterprise license.
- Lower peak accuracy than RF-DETR XL.
License: AGPL-3.0 (open-source) · Enterprise license via Ultralytics for commercial/industrial use.
Repository: https://github.com/ultralytics/ultralytics
Benchmark comparison
To give a comparison between the models, here are the exact benchmark values. All scores on MS COCO val2017. Latency was measured on an NVIDIA T4 GPU.
| Model | mAP50 | mAP50:95 | Latency | Params | Edge-ready | License |
|---|---|---|---|---|---|---|
| RF-DETR-N | 67.6 | 48.4 | 2.3 ms | 30.5 M | Server GPU | Apache 2.0 |
| RF-DETR-M | 73.6 | 54.7 | 4.4 ms | 33.7 M | Server GPU | Apache 2.0 |
| RF-DETR-2XL | 78.5 | 60.1 | 17.2 ms | 126.9 M | Server GPU | PML 1.0 |
| YOLO12-N | 56.7 | 40.4 | 1.6 ms | 2.5 M | ARM / Mobile / Jetson | AGPL-3.0 |
| YOLO12-L | 70.7 | 53.8 | 5.83 ms | 26.5 M | Jetson / TensorRT | AGPL-3.0 |
| YOLO26-N | — | 40.1 | 1.7 ms | 2.4 M | ARM / Mobile / Jetson | AGPL-3.0 |
| YOLO26-X | — | 56.9 | 11.8 ms | 55.7 M | Jetson / TensorRT | AGPL-3.0 |
Here is a visualization of the above results alongside additional modern object detection models for a more holistic comparison:

Use-case guidance
Occluded objects: RF-DETR (M/L) is the clear choice. Its DINOv2 backbone models global context across the full image, making it significantly better than CNN-based models at finding partially hidden objects.
Small objects: RF-DETR uses multi-scale feature extraction. YOLO26 also includes STAL (small-target-aware label assignment), making it competitive for small objects on edge hardware.
Edge / mobile / Jetson: YOLO26-N or YOLO12-N. YOLO26 is the Ultralytics recommendation for Jetson Orin/Xavier, Snapdragon AI, and ARM CPUs. It has 43% faster CPU inference than YOLO11n at comparable accuracy.
Custom domain / fine-tuning: RF-DETR by a significant margin. DINOv2 pre-training means it adapts to new domains (medical, aerial, and industrial) faster and with less data than any other model here.
Licensing Summary
| Model | License | Commercial use |
|---|---|---|
| RF-DETR (base) | Apache 2.0 | Free for all uses, including commercial products |
| RF-DETR XL/2XL | PML 1.0 | Contact Roboflow for commercial licensing |
| YOLO12 | AGPL-3.0 | Free for open source / personal use; commercial applications require an Ultralytics Enterprise license |
| YOLO26 | AGPL-3.0 | Free for open source / personal use; commercial applications require an Ultralytics Enterprise license |
Quick-start code
RF-DETR
# Install
pip install rfdetr
# Inference
from rfdetr import RFDETRBase
model = RFDETRBase()
detections = model.predict("image.jpg")
# Fine-tune on your dataset
model.train(dataset_dir="./my_dataset", epochs=50, batch_size=4)
YOLO26 / YOLO12 (via Ultralytics)
# Install
pip install ultralytics
# Inference — YOLO26
from ultralytics import YOLO
model = YOLO("yolo26n.pt") # or yolo26s/m/l/x
results = model.predict("image.jpg")
# Inference — YOLO12
model = YOLO("yolo12n.pt")
results = model.predict("image.jpg")
# Export for edge (TensorRT / CoreML / ONNX)
model.export(format="engine") # TensorRT for Jetson
model.export(format="coreml") # Apple Silicon / iOS
model.export(format="tflite") # Android / ARM
YOLO12 (use original open-source repo – not the Ultralytics integration)
# Install from the original authors' repo
conda create -n yolov12 python=3.11
conda activate yolov12
git clone https://github.com/sunsmarterjie/yolov12 && cd yolov12
pip install -r requirements.txt
pip install -e .
# Inference
from ultralytics import YOLO
model = YOLO("yolov12n.pt") # or s/m/l/x
results = model("path/to/image.jpg")
results[0].show()
# Export for edge
model.export(format="engine", half=True) # TensorRT FP16
model.export(format="onnx") # ONNX for broad compatibility
Transfer learning and fine-tuning
RF-DETR – recommended for domain shift. Thanks to a DINOv2 backbone that is pre-trained on internet-scale data, fine-tuning requires less labeled data and converges faster. Use the rfdetr package with a COCO pre-trained checkpoint. Roboflow also offers a hosted fine-tuning UI.
YOLO26 / YOLO12 – easiest pipeline. Ultralytics’ training API is the most mature fine-tuning ecosystem. It supports YOLO-format and COCO-format datasets and has good documentation and an active community.
# Fine-tuning YOLO26 on a custom dataset (YOLO format)
from ultralytics import YOLO
model = YOLO("yolo26m.pt") # start from pretrained weights
model.train(
data="custom_dataset.yaml", # path to your dataset config
epochs=100,
imgsz=640,
batch=16,
device=0, # GPU index; "cpu" for CPU
)
metrics = model.val() # evaluate on validation set
Summary: Choosing the right model for your project
Selecting an object detection model requires matching your specific requirements against each model’s strengths. The decision framework below maps common scenarios to optimal model choices.
| Your goal | Best choice | Runner-up |
|---|---|---|
| Highest accuracy, cloud deployment | RF-DETR M/XL | YOLO26-X |
| Edge / Jetson / mobile | YOLO26-N/S | YOLO12-N |
| Fine-tuning on a custom domain | RF-DETR | YOLO26 |
| Occluded / complex scenes | RF-DETR | YOLO26 |
| Research / benchmarking | YOLO12 | RF-DETR |
| Apache 2.0 + commercial use | RF-DETR (base) | YOLO26 |
| Multi-task (detect + segment + pose) | YOLO26 | RF-DETR (det+seg) |
Get started with PyCharm today
Selecting an object detection architecture in 2026 is a strategic decision dictated by the specific requirements of the application and the available computational budget. Whether prioritizing the record-breaking accuracy of RF-DETR for complex scenes or the unmatched efficiency of the YOLO family for edge deployment, the choice must balance mAP requirements against real-time latency constraints.
The landscape of computer vision is rapidly shifting toward zero-shot detection frameworks that recognize novel objects without task-specific supervision. As foundation models increasingly integrate sophisticated image embedders like CLIP or DINOv2 into detection pipelines, the boundaries of high-precision detection on resource-constrained hardware will continue to expand. While transformer-based architectures are developing quickly, the YOLO family’s established ecosystem ensures it remains a cornerstone for real-time production environments.
To achieve the best results for your specific use case, we encourage you to experiment with the models and code samples provided in this guide. To that end, PyCharm provides the perfect ecosystem for experimentation with various open-source models via Code -> Insert HF Model interface. If you’d like to try this yourself, PyCharm Pro comes with a 30-day trial.
For a hands-on starting point, this tutorial shows how to build a live object detection app using TensorFlow and PyCharm Jupyter notebooks, then deploy it on a robot – covering everything from single-frame inference to a live web dashboard with annotated detections. Moreover, stay tuned for the next tutorial post, where we will discuss all three object detection models in action.
Ari Lamstein
This Thursday: Building Data Apps with Streamlit and Copilot
On July 9 (9am–1pm Pacific) — this Thursday — I’ll be teaching a 4-hour live workshop for O’Reilly: Building Data Apps with Streamlit and Copilot.
This is the second time I’ve run this workshop, and I’ve made several improvements based on what I learned the first time.
If you work in Python and want to turn your analyses into interactive, shareable tools, this workshop is designed for you. We’ll start from a Jupyter notebook and build a complete Streamlit app that lets users explore a dataset through interactive controls, charts, and maps. Along the way, you’ll also learn to use Copilot as a companion while developing software — everything from learning the library faster to improving the quality of the code you write.
What we’ll cover
- Structuring a Streamlit app
- Working with user input (select boxes, filters, etc.)
- Creating interactive graphics with Plotly
- Organizing the UI with columns and tabs
- Deploying your app to Streamlit Cloud
The workshop is hands-on: you’ll build the app step-by-step, and by the end you’ll have a working project you can adapt to your own data.
What You’ll Build
Here’s a screenshot from the app we’ll build together:
The app lets users choose a state and demographic statistic, explore how it changes over time, and view the data as a chart, map, or table.
And while the example uses demographic data, the skills you’ll learn — structuring an app, building interactive controls, and creating dynamic visualizations — apply to any Streamlit project you want to build.
Who is this for?
- Data scientists and analysts who want to make their work more interactive
- Python users who want to build dashboards without learning web development
- Anyone curious about Streamlit or Copilot
How to Register
The workshop is hosted on O’Reilly, which is a membership platform. If you’re not already a member, O’Reilly offers a free 10-day trial — plenty of time to register and attend this week.
Also worth knowing: the workshop is recorded. So if July 9 doesn’t work for you, it’s still worth registering — you’ll have access to the recording.
I’d love to see you there.
Django Weblog
Django security releases issued: 6.0.7 and 5.2.16
In accordance with our security release policy, the Django team is issuing releases for Django 6.0.7 and Django 5.2.16. These releases address the security issues detailed below. We encourage all users of Django to upgrade as soon as possible.
CVE-2026-48588: Potential exposure of private data via cached Set-Cookie response
django.middleware.cache.UpdateCacheMiddleware and django.views.decorators.cache.cache_page avoided caching responses that set a cookie while varying on Cookie only when the incoming request contained no cookies at all. When the request already carried an unrelated cookie (such as a language or theme preference cookie), the protection did not apply, allowing a response that sets a session or other sensitive cookie to be stored in Django's shared cache.
This issue has severity "low" according to the Django security policy.
Thanks to Chris Whyland for the report.
CVE-2026-53877: Heap buffer over-read in GDALRaster
When django.contrib.gis.gdal.GDALRaster was instantiated with a bytes object representing a raster file, the vsi_buffer property could over-read the allocated buffer by approximately 32 bytes. This could result in information disclosure of adjacent heap memory or, in rare cases, a segmentation fault. Only rasters stored in GDAL's virtual filesystem were affected.
This issue has severity "low" according to the Django security policy.
Thanks to Bence Nagy for the report.
CVE-2026-53878: Header injection possibility since DomainNameValidator accepted newlines in input
django.core.validators.DomainNameValidator accepted newlines in domain names. If such values were included in HTTP responses, header injection attacks were possible. Django itself wasn't vulnerable because HttpResponse prohibits newlines in HTTP headers.
The vulnerability only affected uses of DomainNameValidator outside Django form fields, as CharField strips newlines by default.
This issue has severity "low" according to the Django security policy.
Thanks to Bence Nagy for the report.
Affected supported versions
- Django main
- Django 6.1 (currently at beta status)
- Django 6.0
- Django 5.2
Resolution
Patches to resolve the issue have been applied to Django's main, 6.1 (currently at beta status), 6.0, and 5.2 branches. The patches may be obtained from the following changesets.
CVE-2026-48588: Potential exposure of private data via cached Set-Cookie response
- On the main branch
- On the 6.1 branch
- On the 6.0 branch
- On the 5.2 branch
CVE-2026-53877: Heap buffer over-read in GDALRaster
- On the main branch
- On the 6.1 branch
- On the 6.0 branch
- On the 5.2 branch
CVE-2026-53878: Header injection possibility since DomainNameValidator accepted newlines in input
- On the main branch
- On the 6.1 branch
- On the 6.0 branch
- On the 5.2 branch
The following releases have been issued
The PGP key ID used for this release is Jacob Walls: 131403F4D16D8DC7
General notes regarding security reporting
As always, we ask that potential security issues be reported via private email
to security@djangoproject.com, and not via Django's Trac instance, nor via
the Django Forum. Please see
our security policies for further
information.
PyCon
Welcome, Kattni!

Everyone has different reasons for attending PyCon US; the event has a wide range of things to offer. For me, it's about the people -- seeing old friends and making new ones is by far my favorite part of the conference. I want to focus on helping the PyCon US community grow, to further cultivate the myriad perspectives, and increase the opportunities for everyone involved to have their own amazing and memorable experiences. When it comes down to it, PyCon US happens at all because of those who participate. It wouldn't exist without, at a very minimum, those who: organise it, run it, volunteer both before and during, submit to the CFP, speak, teach tutorials, present posters, sponsor, and attend.
I hope you'll join us next year in Long Beach, as I begin my journey helping make PyCon US a wonderful event. I'm incredibly excited to be working alongside Jon, and the rest of the staff, organisers, and volunteers, through the next four years. I'm looking forward to seeing everyone next year!
We'll have more news to share about PyCon US 2027 in the coming months. Stay tuned to the blog and newsletter for updates. We look forward to welcoming you back to Long Beach next May 12 - 18.
Python Bytes
#487 Minimum requirements
<strong>Topics covered in this episode:</strong><br> <ul> <li><strong><a href="https://github.com/bootandy/dust?featured_on=pythonbytes">dust</a> - a better du</strong></li> <li><strong><a href="https://hermes-agent.org/?featured_on=pythonbytes">Hermes Agent</a>: The AI agent that grows with you</strong></li> <li><strong><a href="https://github.com/simonw/llm-coding-agent/releases/tag/0.1a0?featured_on=pythonbytes">llm-coding-agent 0.1a0</a></strong></li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=KubyT7Ttggg' style='font-weight: bold;'data-umami-event="Livestream-Past" data-umami-event-episode="487">Watch on YouTube</a><br> <p><strong>About the show</strong></p> <p>Sponsored by us! Support our work through:</p> <ul> <li>Our <a href="https://training.talkpython.fm/?featured_on=pythonbytes"><strong>courses at Talk Python</strong></a></li> <li>Consulting from <a href="https://sixfeetup.com/?featured_on=pythonbytes"><strong>Six Feet Up</strong></a></li> </ul> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy">Mastodon</a> / <a href="https://bsky.app/profile/mkennedy.codes?featured_on=pythonbytes">BlueSky</a> / <a href="https://x.com/mkennedy?featured_on=pythonbytes">X</a> / <a href="https://www.linkedin.com/in/mkennedy/?featured_on=pythonbytes">LinkedIn</a></li> <li>Calvin: <a href="https://sixfeetup.social/@calvin?featured_on=pythonbytes">Mastodon</a> / <a href="https://bsky.app/profile/calvinhp.com?featured_on=pythonbytes">BlueSky</a> / <a href="https://x.com/calvinhp?featured_on=pythonbytes">X</a> / <a href="https://www.linkedin.com/in/calvinhp/?featured_on=pythonbytes">LinkedIn</a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes">Mastodon</a> / <a href="https://bsky.app/profile/pythonbytes.fm">BlueSky</a> / <a href="https://x.com/PythonBytes?featured_on=pythonbytes">X</a></li> </ul> <p>Join us on YouTube at <a href="https://pythonbytes.fm/stream/live"><strong>pythonbytes.fm/live</strong></a> to be part of the audience. Usually <strong>Tuesday at 7am PT</strong>. Older video versions available there too.</p> <p>Finally, if you want an artisanal, hand-crafted digest of every week of the show notes in email form? Add your name and email to <a href="https://pythonbytes.fm/friends-of-the-show">our friends of the show list</a>, we'll never share it.</p> <p><strong>Michael #1: <a href="https://github.com/bootandy/dust?featured_on=pythonbytes">dust</a> -</strong> a better du</p> <ul> <li><code>du</code> + Rust = <code>dust</code> - a fast, visual, intuitive disk-usage CLI</li> <li>Run <code>dust</code> and immediately see the biggest directories and files without piping through <code>sort</code>, <code>head</code>, or <code>awk</code></li> <li>Smart recursive output focuses on what matters instead of dumping every folder</li> <li>Colored bars show relative size and parent/child hierarchy, making “where did the space go?” obvious</li> <li>Perfect for Python projects bloated by <code>.venv</code>, caches, Docker volumes, downloaded datasets, and local AI models</li> <li>Install via <code>brew</code>, <code>cargo install du-dust</code>, <code>conda-forge</code>, Scoop, Snap, deb-get, or GitHub releases</li> </ul> <p><strong>Calvin #2</strong>: <a href="https://github.com/astral-sh/war/blob/main/SPEC.md?featured_on=pythonbytes">A Way better ARchive format for Python packaging</a></p> <ul> <li><strong>war</strong> - new archive format spec from Astral (same team as uv/ruff), v0.0.2, still no binary encoding defined yet</li> <li>Header-Index-Store layout: header IDs the file, index maps names to store offsets, store holds compressed data</li> <li>Index uses a finite-state transducer (FST) to dedupe common path prefixes across entry names</li> <li>Supports three entry types (file, directory, link) and three compression modes (store/DEFLATE/zstd), plus an "executable" metadata flag</li> <li>Unpacking is atomic - writes to a temp dir, then renames into place, so a failed extract never leaves a half-unpacked directory</li> <li>Strict name-segment rules (no NUL/control chars, no leading/trailing whitespace, blocks Windows-reserved names like CON/PRN) to avoid path traversal and cross-platform footguns</li> </ul> <p><strong>Michael #3:</strong> <a href="https://hermes-agent.org/?featured_on=pythonbytes">Hermes Agent</a>: The AI agent that grows with you</p> <ul> <li>Hermes Agent is an open-source, Python-built AI agent framework from Nous Research - think ChatGPT-style assistant, but connected to your tools, files, shell, browser, calendar, memory, and messaging apps</li> <li>I’m using it in Discord as a long-running agent conversation, not just a one-off chatbot session</li> <li>Hermes can connect through a gateway to platforms like Discord, Telegram, Slack, WhatsApp, email, webhooks, and more - so the same assistant can follow you across surfaces</li> <li>In my setup, I can send Hermes voice/text from Discord, keep project context across turns as threads, and ask it to actually do things: read GitHub repos, run commands, edit files, schedule calendar events, generate drafts, and verify results</li> <li>A fun workflow: I can trigger one-shot actions from an Apple Watch shortcut - dictate a request, send it to Hermes, and have the agent execute it asynchronously</li> <li>Hermes has persistent memory, so it can remember durable preferences and facts - for example, how I like my research formatted</li> <li>It also has “skills,” which are reusable procedures the agent can load later, so Hermes can self-improve over time instead of rediscovering the same workflow repeatedly</li> <li>It supports scheduled jobs / cron-style automations, so it can proactively watch for releases, send summaries, run checks, or remind you about things</li> <li>It’s provider-agnostic: OpenRouter, Anthropic, Google, xAI, local models, Nous Portal, and others</li> <li>The big idea: Hermes turns an LLM from “a chat box I visit” into “an agent I can reach from anywhere that knows my workflows and can take real actions and learns over time.”</li> </ul> <p><strong>Calvin #4: <a href="https://github.com/simonw/llm-coding-agent/releases/tag/0.1a0?featured_on=pythonbytes">llm-coding-agent 0.1a0</a></strong></p> <ul> <li>Simon Willison built a Claude/Codex-style coding agent on top of his <code>llm</code> library, using an alpha of the <code>llm</code> package plus his python-lib-template-repo</li> <li>Built almost entirely via prompted TDD - asked an agent to write a <a href="http://spec.md?featured_on=pythonbytes">spec.md</a>, then commit + implement with red/green tests, occasionally hitting a real OpenAI key to sanity-check</li> <li>Shipped to PyPI as an alpha: <code>uvx --prerelease=allow --with llm-coding-agent llm code</code></li> <li>Tool set mirrors familiar coding-agent primitives: read_file, edit_file (exact string replace + diff), write_file, list_files, search_files, execute_command</li> <li>Also exposes a Python API - <code>CodingAgent(model="gpt-5.5", root=..., approve=True).run(...)</code> - which Simon didn't ask for but got anyway</li> <li>Demo: <code>llm code --yolo</code> told GPT-5.5 to build a SwiftUI CLI clock; model correctly noted SwiftUI isn't really CLI-friendly and still produced an ASCII-art time display</li> </ul> <p><strong>Extras</strong></p> <p>Calvin:</p> <ul> <li>Slides, but for developers <a href="https://sli.dev/?featured_on=pythonbytes">https://sli.dev/</a></li> <li>Wanna reduce your token usage…. only issue is that its lossy <a href="https://github.com/teamchong/pxpipe?featured_on=pythonbytes">https://github.com/teamchong/pxpipe</a></li> <li><strong>PEP 772 - Python Packaging Council inaugural election dates set, nominations open July 28, voting September 1-15</strong></li> </ul> <p>Michael:</p> <ul> <li><a href="https://mkennedy.codes/posts/what-the-pls/?featured_on=pythonbytes">What the pls?</a> revisited!</li> </ul> <p><strong>Joke:</strong> <a href="https://x.com/AlfinCodes/status/2057443209127903456?featured_on=pythonbytes">Min requirements for Linux</a></p>
The No Title® Tech Blog
Optimize Images X now supports WEBP and drag and drop
Optimize Images X, the multi-platform desktop application that helps you reduce the file size of your images on macOS, Windows and Linux, has just been updated to version 2.1.0. This new version adds drag-and-drop, conversion to more output formats, including WebP and AVIF, a new image info window with a readable EXIF report, and a PyInstaller spec file for building standalone executables.
July 06, 2026
Rodrigo Girão Serrão
Write a coding agent from first principles: better tools
Improve the capabilities of your agent by providing it with better tools.
Introduction
This tutorial builds on the coding agent you implemented in the tutorial “Write a coding agent from first principles”. In this tutorial, you'll take your agent and improve its capabilities by implementing the text edit and bash command tools that Anthropic provides.
Why use Anthropic's tools?
In the previous tutorial you implemented a coding agent that has a few tools that it can use to read, write, and execute, code.
The tool "bash" can be used to execute arbitrary commands and the tools "read", "write", "replace", and "insert", can be used to edit files.
As it turns out, these tools are so universally useful that Anthropic trained its models on specific schema definitions for these tools. The tools still run on the client side, so you'll still get tool use blocks in the API responses, but you don't have to define the schema for the tool. You just specify the tools by their Anthropic types and names, and the LLMs will happily request tool uses.
Anthropic trains their models on a number of useful tools but you'll focus your attention on two tools that emulate the functionality you already have:
- Text editor tool: this tool replaces the four tools you defined to read, write, replace, and insert, text in text files
- Bash tool: this tool provides a persistent bash session that can run bash commands
By replacing your tools with Anthropic's, the agent will be able to make better tool calls consistently, since Anthropic trains their models on their specific tool schemas.
The native text editor tool
To define support for Anthropic's text editor tool you need to add it to your list of tools.
The name of the tool is "str_replace_based_edit_tool" and its type is "text_editor_20250728".
(The type carries a versioning suffix that may influence the tool's behaviour, so make sure you use the right date suffix.)
Since you'll be using Anthropic's text editor tool, you can delete the functions read, write, replace, and insert, and the corresponding dictionaries that go in the list TOOLS.
Instead, add the dictionary that specifies the Anthropic tool:
# ...
TOOLS = [
{
"type": "text_editor_20250728",
"name": "str_replace_based_edit_tool",
}
]
# Bash tool defined and added later.
For organisation purposes, you'll define the text editor tool and the bash tool in their own submodules, so create the folder tools and then create the file tools/str_replace_based_edit_tool.py under src/agent.
In there, you'll define the code to handle the tool call.
The text editor tool is a 4-in-1 tool that allows you to view, replace, create, and insert, text. To disambiguate the action you want to do, the tool use request includes a command:
# Example tool use dictionary:
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "str_replace_based_edit_tool",
"input": {
"command": "view", # <--
# ...
}
}
You'll use the key "command" from the...
Seth Michael Larson
Mario Kart World and “seamless” media
Mario Kart World for the Nintendo Switch 2 adds a new unique game mechanic for the series where courses that neighbor each other in the sprawling world map can physically and thematically morph over a short transitionary “lap”.
These course-connecting laps are commonly called Routes or “Intermission Tracks”. There are 202 routes connecting the 30 courses in Mario Kart World, some of which change depending on your direction between courses.
This new mechanic enables what I consider the highlight of the game: a new mode named “Knockout Tour” that is reminiscent of arcade racing games with time-based checkpoints.
Diagram showing which courses can transition to one another. Created by u/SnooHamsters6067
Previously in Mario Kart, beginning a new course meant selecting the course or Grand Prix by name in a menu. Lakitu would provide a sweeping camera fly-over of the course, highlighting the title and the challenges to come. The racers would be presented and a countdown from three would begin, with varying times to hold the throttle to boost off the starting line. After a winner had passed the finish line the placements would be tallied and the cycle would begin anew.
Mario Kart World has done away with all of this in Grand Prix, Knockout Tour, and other rally racing formats. There is no ceremony for each course, there is only beginning and ending play. The courses all blur together at the edges. This seamless approach reminded me of the many new forms of passive media participation including short-form infinite video streams, “Auto Play”, or algorithmically curated DJs and playlists.
The effects of this “seamlessness” are similar for Mario Kart World courses and other digital mediums. Despite playing Mario Kart World for many hours, I can't remember many courses by name (besides Rainbow Road). Compare that to previous Mario Kart titles, where the courses are iconic and much easier to recall from a single screenshot. I feel the same is true for media that is algorithmically curated for me versus media that I've actively chosen to engage with.
To be clear: the design and game mechanics of Mario Kart World are not even close to being as negative as some of the dark patterns in media platforms today. In Mario Kart World the mechanic is used to enable new types of play where in digital media removing seams is used to separate you from the artists and your peers, placing the platform as a necessary intermediary.
When you're a passive participant, there are no ceremonies. Ceremonies are reduced because ceremonies are inflection points that might disturb you just enough to question whether you're enjoying what you're experiencing.
You don't choose an artist based on the mood you're in. You don't need to curate a playlist ahead of time for a longer listening session. You don't select a video based on mutual interest with others. You won't be able to recall what media you've experienced, who the author was, or how to follow for future works. Is it possible to be a “fan” of media you're experiencing without the ceremony?
Thanks for reading ♥ I would love to hear your thoughts! Contact me via Mastodon, Bluesky, or email. Browse the blog archive. Check out my blogroll.
July 05, 2026
Christian Ledermann
Migrate From mypy To ty And pyrefly
I wanted to migrate one of my Python packages from mypy to ty and pyrefly. I handed this task over to Claude, and at the end I asked it to write out some guidance on how to perform it most efficiently. So what follows is AI-generated 'slop'.
This guide is not about using fastkml. It documents how fastkml itself was migrated from mypy to Astral's ty and Meta's pyrefly, so the same playbook can be replayed on other codebases with less trial and error. Keep it here because the next migration (human- or agent-driven) should start from findings, not from zero.
Running two checkers instead of one is deliberate, not incidental. ty and pyrefly disagree with each other and with mypy often enough that running only one gives a false sense of completeness. Budget for both, and expect them to catch different subsets of the same bugs.
TL;DR
- Get a raw error-count baseline for both tools before touching any source. Categorize by error kind, not by file.
- Look for one systemic root cause before fixing anything file-by-file. In most codebases with an optional C-extension backend (lxml, pydantic-core, orjson, etc.) there is a single architectural fix that collapses 60-90% of the noise.
- Fix genuine bugs the tools surface (there will be some — both tools are pickier than mypy about
Optional/union narrowing, positional-only stubs, and unpacking). - Bulk-suppress test-file "constructed-then-accessed-without-narrowing" noise scoped to
tests/**, not case by case, and not by widening the rule to error kinds that could hide real bugs (invalid-argument-typeis notunresolved-attribute). - Turn on strict presets, then promote specific rules the preset doesn't cover, and explicitly cut the ones that create disproportionate mechanical churn (ask a human before doing a 80-site
@overridesweep). - Verify: both tools clean, full test suite green (with and without optional runtime deps installed), linter clean, and the TOML re-parses.
Phase 0 — Inventory the mypy config honestly
Before deleting [tool.mypy], read what each flag actually bought you, because that's the strictness bar ty/pyrefly need to match or exceed:
| mypy setting | Rough ty/pyrefly equivalent |
|---|---|
disallow_any_generics |
ty missing-type-argument = "error"
|
warn_redundant_casts |
ty redundant-cast; pyrefly redundant-cast (both exist, check current default level) |
warn_unused_ignores |
ty unused-ignore-comment / unused-type-ignore-comment; pyrefly unused-ignore (on by default under strict) |
warn_unreachable |
pyrefly's strict preset covers this; ty has no exact analog — don't assume parity, spot-check |
disallow_untyped_defs |
Neither tool has a literal flag for this — ty infers types through unannotated bodies by default (different philosophy from mypy). Don't expect a 1:1 mapping; re-derive intent instead of hunting for the same flag name. |
Per-module disable_error_code overrides |
pyrefly [[tool.pyrefly.sub-config]] + matches glob; ty [[tool.ty.overrides]] + include glob |
Also inventory stale per-module overrides — a mypy config that's been edited over years accumulates dead entries (a module path that was renamed or deleted, but the override survived). Grep for the referenced paths; don't carry dead config forward.
Phase 1 — Install both tools and get a baseline
uv pip install ty pyrefly
ty check <src> <tests>
pyrefly check <src> <tests>
Immediately categorize, don't read line by line yet:
ty check <src> <tests> 2>&1 | grep -oE '^error\[[a-zA-Z-]+\]' | sort | uniq -c | sort -rn
ty check <src> <tests> 2>&1 | grep -E '^\s+-->' | sed -E 's/^\s+--> //; s/:[0-9]+:[0-9]+$//' | sort | uniq -c | sort -rn
The second command (errors by file) usually reveals the systemic cause immediately: a handful of files concentrate a disproportionate share of the errors, and they're usually the ones touching an optional/duck-typed dependency.
If a partial migration already exists in the repo (CI switched over but pyproject.toml grew broad ignore-missing-imports = ["*"] / blanket missing-attribute = "ignore" sub-configs), treat that as a red flag, not a starting point. Broad suppressions accumulated during an in-progress migration usually mean someone hit friction and silenced it rather than fixed it. Re-run with those suppressions removed to see the real baseline before deciding what's worth keeping.
Phase 2 — Find the systemic root cause first
The highest-leverage move in this kind of migration is almost never "fix errors file by file." It's finding the one architectural mismatch that both checkers are tripping over identically across dozens of call sites.
The recurring pattern: a project supports an optional, richer backend (lxml over xml.etree.ElementTree, orjson over json, ujson, a C-accelerated regex engine, etc.) via a runtime try/except import, and defines either a Protocol or just relies on structural duck-typing to abstract over both. mypy tolerated this for years via ignore_missing_imports = true, which silently treats the untyped backend as Any everywhere. Neither ty nor pyrefly degrade that gracefully by default — they'll either partially resolve the untyped backend's real (but incomplete) info, or fall back to typing it against whichever branch of the try/except does have full stubs (usually the stdlib fallback), and then report every method/kwarg the richer backend uniquely offers as invalid.
The fix, applied at the import site (not scattered across every call site):
from typing import TYPE_CHECKING
if TYPE_CHECKING:
# Type-checkers see the richer backend's own stubs; the preferred
# backend's API is treated as a superset of the fallback's.
from lxml import etree
else:
try:
from lxml import etree
except ImportError:
import xml.etree.ElementTree as etree
If the project also defines its own Protocol to abstract over both backends (e.g. types.py: class Element(Protocol): ...), consider going one step further and making that Protocol literally alias the richer backend's real type under TYPE_CHECKING, falling back to the structural Protocol only for runtime/non-typechecking purposes:
if TYPE_CHECKING:
from lxml.etree import _Element as Element
else:
class Element(Protocol):
... # the original structural protocol, unchanged
This one change collapsed roughly 150 of ~240 diagnostics in the fastkml migration, because it fixed both the "backend-specific kwarg doesn't exist" class of errors and the "structural Protocol isn't assignable to a concrete stdlib parameter type" class in one shot (see pitfall below).
If a stub package exists for the richer backend (lxml-stubs, types-ujson, etc.), add it to your typing dev-dependencies — but read the pitfalls section before assuming it's a strict improvement for both checkers.
Pitfalls (the part worth re-reading before your second migration)
1. # type: ignore[code] is not portable
Neither ty nor pyrefly parses mypy's bracketed error-code suppression the way mypy does.
-
tyhonors a bare# type: ignore(no brackets) as a blanket suppression for that line, but a bracketed# type: ignore[some-code]is not recognized as a ty-ignore at all — it's inert noise as far as ty is concerned, and the underlying error still fires. -
pyreflydoes honor# type: ignore[...]by default (its--enabled-ignoresdefaults totype,pyrefly), so bracketed mypy comments mostly still work for pyrefly specifically. - Both tools have their own dedicated syntax:
# ty: ignore[rule-name]and# pyrefly: ignore/# pyrefly: ignore[error-kind].
Verify empirically before trusting any of this — behavior can change between tool versions:
def f() -> int:
return "y" # type: ignore[return-value]
Run ty check and pyrefly check against a two-line repro before deciding on a suppression strategy for the whole codebase.
Practical rule that worked well: keep the original # type: ignore[code] comment (documents intent, keeps pyrefly happy) and append # ty: ignore[rule-name] on the same physical line for ty. Don't strip the mypy-era comments outright; they're free documentation of why a line is exceptional.
2. pyrefly's TOML keys are snake_case even though its CLI flags are kebab-case
This is the single most time-consuming mistake to make. pyrefly check --replace-imports-with-any 'lxml.*' works from the CLI. Writing the "obvious" TOML equivalent:
[tool.pyrefly]
replace-imports-with-any = ["lxml.*"] # WRONG — silently different key
...does not raise an error from pyrefly check in some code paths, but it does hard-fail with pyrefly dump-config (unknown variant 'replace-imports-with-any'... Fatal configuration error), and depending on invocation order this can also break pyrefly check itself later. The correct TOML key uses underscores:
[tool.pyrefly]
replace_imports_with_any = ["lxml.*"]
Meanwhile, error-kind names (used as dict keys under [tool.pyrefly.errors] or inside a rules = {...} table) do use hyphens (missing-override-decorator, redundant-cast, etc.) — matching the CLI's --error/--ignore rule-name spelling, not the config-field spelling. There is no single consistent casing convention across the whole config surface; check pyrefly dump-config after every config change, not just pyrefly check, because check can look clean while a nearby key is silently ignored.
After any pyrefly config edit, run both pyrefly dump-config (schema/parse validation) and pyrefly check (behavioral validation) — one catches structural mistakes the other doesn't surface.
3. pyrefly's [[tool.pyrefly.sub-config]] array-of-tables is fragile against interleaving
Pyrefly's per-path overrides use TOML's array-of-tables syntax:
[[tool.pyrefly.sub-config]]
matches = "tests/**/*"
[tool.pyrefly.sub-config.errors]
missing-attribute = "ignore"
TOML allows other, unrelated top-level tables to appear between [[tool.pyrefly.sub-config]] and its paired [tool.pyrefly.sub-config.errors] — the nested table still binds to the most-recently-opened array element regardless of what's interleaved. That means a pyproject.toml that grew organically (auto-migration tooling appending blocks near whatever happened to be at the end of the file) can end up with three sub-config blocks scattered across 100+ lines of unrelated project/tool config, and it will still parse. It becomes a landmine the moment someone (or an agent) deletes one [[tool.pyrefly.sub-config]] header without also deleting its now-orphaned [tool.pyrefly.sub-config.errors] block — the orphaned errors table then either binds to the wrong array element or breaks parsing entirely.
Fix: keep every pyrefly (and ty) config block contiguous in one place in the file, even if that means moving it away from wherever an automated tool first inserted it. Re-parse after every edit:
python3 -c "import tomllib; tomllib.load(open('pyproject.toml','rb')); print('OK')"
4. A Protocol is not assignable to a concrete class parameter
If your duck-typing abstraction is a Protocol (say, types.Element) and internal code passes Element-typed values into functions that are typed against the concrete stdlib/third-party class (xml.etree.ElementTree.SubElement(parent: Element[Any], ...)), both ty and pyrefly will reject it — even though the Protocol is structurally compatible at every call site. Protocol → concrete-class assignability doesn't work the way concrete → Protocol does, and a mutable/invariant attribute (text: str on the Protocol vs. text: str | None on the real class) makes it worse.
This resolves itself for free once you apply the Phase 2 fix (alias the Protocol to the real backend's type under TYPE_CHECKING) for internal, backend-facing code. Keep the original Protocol only for the codebase's genuinely-public, backend-agnostic API surface.
5. Registry/callback-style dispatch can't be narrowed at the signature level
A common pattern: a generic dispatch table stores classes: tuple[type[object], ...] and a matching Protocol requires every registered callback to accept exactly that (necessarily wide) signature, even though any individual callback only ever receives one concrete class at runtime. You cannot narrow an individual callback's parameter type to the concrete class it actually expects (tuple[type[SpecificClass], ...]) — that breaks structural assignability against the wider Protocol the dispatcher requires (parameter types are contravariant; a callback that only accepts a narrower type can't stand in for one the dispatcher will call with the wider type).
Fix at the call site inside the function body instead of the signature: cast("tuple[type[SpecificClass], ...]", classes), or cls = cast("type[SpecificClass]", classes[0]). Keep the public signature honestly wide.
6. **heterogeneous_dict splats can't be validated against multi-parameter constructors
fields = {"type_": DataType.int_, "name": "Integer"}
SimpleField(**fields)
Both checkers infer fields: dict[str, DataType | str] and then check every keyword argument against that whole union, rather than against each parameter's own specific type — because plain dict[str, ...] splatting isn't a TypedDict, so there's no per-key type information available. This isn't a narrowing bug or an inherent-bad-input case; it's a structural limitation of **dict splatting itself. Either convert the dict to a TypedDict (real fix, more invasive) or accept a targeted ignore comment — don't spend time trying to "fix" it any other way.
7. Community stub packages can behave differently per checker
If you add a community-maintained stub package (lxml-stubs, etc.) rather than relying on inline py.typed types, expect it to have its own bugs, and expect those bugs to manifest differently per checker. In this migration, lxml-stubs declares several attributes using the legacy stub syntax tag = ... # type: str (pre-PEP 526). ty tolerates this by falling back to Unknown for that attribute (safe, just loses precision). pyrefly mis-parses it as the attribute's type being the literal value Ellipsis, then reports every subsequent use (.strip(), .split(), slicing) as an error on a nonexistent EllipsisType method — a wave of dozens of false positives that has nothing to do with your code.
There is no fix on your side other than working around the stub bug per-checker:
[tool.pyrefly]
# Force pyrefly to treat the whole (broken-for-pyrefly) stub package as Any,
# while `ty` still gets full value from the same installed stub package.
replace_imports_with_any = ["lxml.*"]
Don't assume "we added the stub package" is the end of the story — verify both tools independently after adding any third-party stub dependency, because "more type information" is not always strictly better across tools.
8. Fixing the root cause makes old workaround cast()s redundant — clean them up
Once you fix the systemic issue, re-run both tools and look specifically for redundant-cast warnings. Every cast("Element", root) that existed purely to placate mypy's ignore_missing_imports fallback becomes genuinely unnecessary once the real type flows through correctly, and leaving it in is now dead weight (and a ty/pyrefly warning) rather than a workaround. This is a good automatic signal that the root-cause fix actually landed.
9. Some "strict" rules are mechanical churn, not bug-catching — decide explicitly, don't default to on
Pyrefly's strict preset (and to a lesser extent ty's optional rules) includes checks like missing-override-decorator (PEP 698 @override), which can flag dozens to low-hundreds of ordinary __repr__/__init__/method overrides in any codebase with meaningful inheritance. This is a legitimate check with real value in large team codebases (catches silently-broken overrides after a base-class rename), but adding @override everywhere is a large, purely mechanical diff disconnected from the actual "fix type errors" task.
Don't silently turn this on or off. Surface it explicitly (to a human reviewer, or via an explicit question if you're an agent) before deciding: disable it with a documented reason, or actually do the sweep. Either is defensible; picking silently isn't.
10. Test-file "narrowing noise" is real but should not become a license to blanket-suppress everything
The overwhelming majority of test-file errors in a mature test suite will be the same shape: construct an object, then immediately access/assign a field typed X | None or A | B | None, without a narrowing assert x is not None — because the test knows the value is set (it just set it three lines up) but the checker doesn't. This is legitimately safe to bulk-suppress scoped to the test tree:
[[tool.pyrefly.sub-config]]
matches = "tests/**/*"
[tool.pyrefly.sub-config.errors]
missing-attribute = "ignore"
[[tool.ty.overrides]]
include = ["tests/**"]
rules = { unresolved-attribute = "ignore", invalid-assignment = "ignore" }
But scope the suppressed rule names narrowly (unresolved-attribute/missing-attribute/the specific invalid-assignment shape), not broad categories like invalid-argument-type/bad-argument-type — those catch genuinely wrong types passed into calls, which do happen in test code (typos, copy-paste of the wrong fixture) and are worth keeping visible. In the fastkml migration, roughly 90% of test-file errors were narrowing noise safely bulk-suppressed, and the remaining 10% surfaced one genuine test bug (a string literal assigned where an enum member was expected) plus a batch of deliberately-invalid-input tests that just needed their ignore-comment syntax migrated (see pitfall 1).
Phase 3 — Work file by file for what's left
After the systemic fix and the bulk test-suppression, what remains is usually a short, tractable list (tens, not hundreds, of diagnostics). For each:
-
Read the surrounding code before deciding how to fix it. The same
unresolved-attributeshape can be a real narrowing gap (addassert x is not None, matching the style already used elsewhere in the file), a genuine latent bug (an off-by-one/empty-tuple case an unpacking*argscall didn't guard against), or a structural dispatch limitation (pitfall 5). -
Prefer a real fix over a cast or ignore wherever one exists cheaply: correcting a wrong return-type annotation (
Optional[X]that never actually returnsNone), addingisinstancenarrowing instead of a loose dict-dispatch, genericizing afind/find_all-style utility with@overload+ aTypeVarinstead of returningobject. -
Use
cast()with a one-line comment explaining *why* when the limitation is structural (pitfalls 4-6), not because you're in a hurry. - Re-run the checker on just that file after each fix (
ty check path/to/file.py) — faster feedback loop than re-running the whole tree, and it confirms the fix didn't introduce a new diagnostic in the same file.
Phase 4 — Tighten to "maximum quality"
Once both tools report zero errors on the fixed baseline, raise the bar deliberately rather than assuming the default preset is already strict:
[tool.pyrefly]
preset = "strict" # not "legacy" / "default" — legacy exists specifically to ease mypy migrations, it's a floor, not a target
[tool.ty.rules]
# Promote rules ty ships at warn/ignore by default; discover the full list with `ty explain rule`.
possibly-missing-attribute = "error"
possibly-missing-import = "error"
possibly-unresolved-reference = "error"
missing-type-argument = "error"
unused-ignore-comment = "error"
unused-type-ignore-comment = "error"
redundant-cast = "error"
Discover what's available rather than guessing:
ty explain rule # every rule, default level, rationale, examples
pyrefly check --help # --preset options, --error/--warn/--ignore, --replace-imports-with-any, etc.
pyrefly dump-config # what's actually active for this project right now
Phase 5 — Verify
Don't call it done on "the type checker is quiet." Run the full loop:
ty check <src> <tests>
pyrefly check <src> <tests>
ruff check --no-fix <src> <tests> # type-fix edits (casts, isinstance, overloads) can introduce lint issues
ruff format --check <src> <tests>
python -m pytest # with optional runtime deps installed
uv pip uninstall <optional-dep> # e.g. lxml
python -m pytest -m "not <slow-marker>" # confirm the fallback code path still works
uv pip install -e ".[typing,<optional-dep>]"
python3 -c "import tomllib; tomllib.load(open('pyproject.toml','rb'))"
The "uninstall the optional dependency and re-run tests" step matters specifically because Phase 2's fix changes how the optional backend is typed, not just how it's imported — if the runtime fallback logic was touched at all while chasing type errors, this is the step that catches a broken fallback path before it ships.
Case study numbers (fastkml)
For calibration on what "a lot of noise, mostly one root cause" looks like in practice:
- Baseline:
tyreported 237 diagnostics across source + tests;pyreflyreported 40 errors (with an already-too-permissive config suppressing 38 more). - One import-site fix (Phase 2, aliasing the duck-typed
ElementProtocol andetreemodule tolxml's real stubs underTYPE_CHECKING) collapsedty's source-only count from 47 to 13 in a single step. - Total genuine bugs found and fixed in library source: 5 (a missed
.getroot()call, an unguarded empty-tuple unpack in azip_longestloop, a too-loosetype[object]dispatch signature, aSelf-in-a-list invariance issue, and one example script passingbyteswherestrwas expected). - Total genuine bugs found and fixed in tests: 1 (a string literal compared against an enum field).
- Final state: zero errors on
ty checkandpyrefly checkfor the CI-covered scope, with all pre-existing tests still passing, both with and without the optionallxmlbackend installed.
If you made it this far, you might be interested in the cost:
- Total cost: $40.44
- Total duration (API): 58m 29s
- Total duration (wall): 1h 48m 15s
- Total code changes: 635 lines added, 224 lines removed
Usage by model:
- claude-haiku-4-5: 1.1k input, 39 output, 0 cache read, 0 cache write ($0.0013)
- claude-sonnet-5: 23.1k input, 227.0k output, 106.7m cache read, 993.5k cache write ($40.44)
Sebastian Pölsterl
scikit-survival 0.28.0 released
I am pleased to announce the release of scikit-survival 0.28.0.
A highlight of this release is the support for Polars DataFrames alongside pandas DataFrames via the Narwhals dataframe abstraction layer. In addition, this release adds support for scikit-learn 1.9.
Support for Polars
Polars is a data frame library similar to pandas, but with its core written in Rust instead of Python, which often gives Polars an advantage in terms of performance.
All datasets shipped with scikit-survival can now be loaded as a Polars DataFrame by specifying the
output_type argument.
from sksurv.datasets import load_gbsg2
# return X as a polars DataFrame
X, y = load_gbsg2(output_type="polars")
X.head()
shape: (5, 8)
┌──────┬────────┬───────┬──────────┬────────┬─────────┬────────┬───────┐
│ age ┆ estrec ┆ horTh ┆ menostat ┆ pnodes ┆ progrec ┆ tgrade ┆ tsize │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ enum ┆ enum ┆ f64 ┆ f64 ┆ enum ┆ f64 │
╞══════╪════════╪═══════╪══════════╪════════╪═════════╪════════╪═══════╡
│ 70.0 ┆ 66.0 ┆ no ┆ Post ┆ 3.0 ┆ 48.0 ┆ II ┆ 21.0 │
│ 56.0 ┆ 77.0 ┆ yes ┆ Post ┆ 7.0 ┆ 61.0 ┆ II ┆ 12.0 │
│ 58.0 ┆ 271.0 ┆ yes ┆ Post ┆ 9.0 ┆ 52.0 ┆ II ┆ 35.0 │
│ 59.0 ┆ 29.0 ┆ yes ┆ Post ┆ 4.0 ┆ 60.0 ┆ II ┆ 17.0 │
│ 73.0 ┆ 65.0 ┆ no ┆ Post ┆ 1.0 ┆ 26.0 ┆ II ┆ 35.0 │
└──────┴────────┴───────┴──────────┴────────┴─────────┴────────┴───────┘
Transforming DataFrames
scikit-learn enables transformers to return Polars data frames via the set_output API.
from sklearn import set_config
from sklearn.preprocessing import StandardScaler
set_config(transform_output="polars")
# standarize the columns of a polars DataFrame
X_standarized = StandardScaler().fit_transform(
X.select("age", "tsize")
)
scikit-surival has two transformers that now accept polars and pandas data frames as input: ClinicalKernelTransform, and OneHotEncoder.
ClinicalKernelTransform
ClinicalKernelTransform computes a kernel matrix, so the output will be a numpy array, as before.
With scikit-survival 0.28.0, it is aware of the
polars colum types
String, Categorical, Enum and Object.
However, polars does not have a concept similar to ordered categories in pandas: pd.Categorical([…], ordered=True).
When computing the clinical kernel for a polars data frame, you can specify the order
of categories with the ordinal_categories argument, otherwise all non-numeric columns
will be treated as nominal columns, where values have no specific order
(e.g. the column horTh with values “yes” and “no” in the example below).
from sksurv.kernels import ClinicalKernelTransform
K = ClinicalKernelTransform(
ordinal_categories={"tgrade": ["I", "II", "III"]},
).fit_transform(
X.select("age", "horTh", "tgrade")
)
OneHotEncoder
OneHotEncoder encodes the string-type columns to numeric columns.
It automatically returns a data frame of the same type as the input:
from sksurv.preprocessing import OneHotEncoder
X_onehot = OneHotEncoder().fit_transform(X.select("horTh", "tgrade"))
X_onehot.head()
shape: (5, 3)
┌───────────┬───────────┬────────────┐
│ horTh=yes ┆ tgrade=II ┆ tgrade=III │
│ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f64 │
╞═══════════╪═══════════╪════════════╡
│ 0.0 ┆ 1.0 ┆ 0.0 │
│ 1.0 ┆ 1.0 ┆ 0.0 │
│ 1.0 ┆ 1.0 ┆ 0.0 │
│ 1.0 ┆ 1.0 ┆ 0.0 │
│ 0.0 ┆ 1.0 ┆ 0.0 │
└───────────┴───────────┴────────────┘
Fitting survival models
Finally, you can pass a polars data frame to the fit and predict functions of any estimator.
from sklearn.pipeline import make_pipeline
from sksurv.linear_model import CoxPHSurvivalAnalysis
pipe = make_pipeline(
OneHotEncoder(), StandardScaler(), CoxPHSurvivalAnalysis()
)
pipe.fit(X[:500], y[:500])
risk_scores = pipe.predict(X[500:])
Note that this only works for eager data frames, lazy data frames will give an error.
X_lazy = X.lazy()
pipe.fit(X_lazy[:500], y[:500])
Traceback (most recent call last):
File "example.py", line 21, in <module>
pipe.fit(X_lazy[:500], y[:500])
File "…/site-packages/sklearn/base.py", line 1403, in wrapper
return fit_method(estimator, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
…
File "…/site-packages/sksurv/_dataframe/_input.py", line 96, in ensure_eager_dataframe
_reject_polars_lazyframe(obj)
File "…/site-packages/sksurv/_dataframe/_input.py", line 84, in _reject_polars_lazyframe
raise TypeError(_LAZYFRAME_NOT_SUPPORTED_MSG)
TypeError: polars.LazyFrame is not supported; call .collect() before passing to scikit-survival.
New Contributors
A big shoutout to our two new contributors:
- cakedev0 for adding support for scikit-learn 1.9
- 55Kamiryo for adding support for Polars data frames.
Updated Dependencies
With this release, the minimum supported version are:
| Package | Minimum Version |
|---|---|
| narwhals | 2.0.1 |
| scikit-learn | 1.9.0 |
Install
scikit-survival is available for Linux, macOS, and Windows and can be installed either
via pip:
pip install scikit-survival
or via conda
conda install -c conda-forge scikit-survival
Core Dispatch
Core Dispatch #7
Welcome back to Core Dispatch! This edition covers June 19 through July 5, 2026. Python 3.15.0 beta 3 landed on June 23, and beta 4 is up next on July 18, with 3.13.15 and 3.14.7 following on August 4.
Last edition, we covered the Steering Council's request that the experimental JIT chart its future through a Standards Track PEP. A new PEP has now been drafted and discussion has started: PEP 836, "JIT Go Brrr: The Path to a Supported JIT Compiler for CPython," sketches what moving the JIT from experiment to supported feature could involve, from performance expectations to interop and tooling compatibility.
PyCon US 2026 talk recordings have also started landing, and we've pulled a few from the core team into Core Team Musings below: Pablo Galindo Salgado and László Kiss Kollár on Python 3.15's new Tachyon sampling profiler, Thomas Wouters on the past, present, and future of free-threaded Python, and Emma Smith on Rust for CPython. Not everything is up yet, so expect more picks as the rest are published.
On the packaging side, the inaugural Packaging Council election dates are out. And looking ahead, EuroPython 2026 kicks off July 13, so expect a wave of talks and Language Summit coverage to pull from in a future edition.
As always, if you maintain a package or just like living on the edge, give the latest 3.15 beta a spin and file any issues you find.
Upcoming Releases
- Python 3.15.0 beta 4 — Jul 18
- Python 3.13.15 — Aug 04
- Python 3.14.7 — Aug 04
Official News
- Packaging Council Inaugural Election Dates — By Pradyun Gedam
- Mitigated API authentication bypass for python.org download metadata — By Seth Larson
- Python 3.15.0 beta 3 is here! — By Hugo van Kemenade
- Thinking about running for the PSF Board? Let’s talk! — By Marie Nordin
PEP Updates
- PEP 752: Implicit namespaces for package repositories
- PEP 810: Explicit lazy imports
- PEP 799: A dedicated
profilingpackage for organizing Python profiling tools - PEP 836: JIT Go Brrr: The Path to a Supported JIT Compiler for CPython
Merged PRs
- Add
remove()andrepack()toZipFile - Normalize all line endings (CR, CRLF, and LF) in
configparser - Fix abrupt closing of empty comment in
HTMLParser - Limit trailer lines and interim responses read by
http.client - Fix symlink escape via
tarfilehardlink-extraction fallback - Implement
BytesIO.peek() - Fix
SyntaxErrormessage forfrom x lazy import y - Improve
SyntaxErrormessage for&&and||operators
Discussion
- PEP 835: Shorthand Syntax for Annotated Type Metadata — 🔥 122 new replies · 3.1k views
- PEP 832: Virtual Environment Discovery — 🔥 21 new replies · 8.3k views
- PEP 836: JIT Go Brrr: The Path to a Supported JIT Compiler for CPython — 🆕 🔥 15 new replies · 975 views
- PEP 822: Dedented Multiline String (d-string) — 2 new replies · 6.9k views
Core Dev Musings
- Python 3.15’s Ultra-Low Overhead Interpreter Profiling Mode — By Ken Jin
- PEP 814: Add frozendict built-in type — By Victor Stinner
- Tachyon: Python 3.15's sampling profiler — By Pablo Galindo Salgado & László Kiss Kollár
- Free-threaded Python: past, present and future — By Thomas Wouters
- Rust for CPython: Making Python Safer and More Robust for Everyone — By Emma Smith
Upcoming CFPs & Conferences
- EuroPython 2026 — Jul 13
- SciPy 2026 — Jul 13
- EuroSciPy 2026 — Jul 18
- PyData PyCon Armenia 2026 — Jul 24
- PyOhio 2026 — Jul 25
- 📋 PyCon France 2026 Deadline — Aug 01
- 📋 PyCon Ireland 2026 Deadline — Aug 01
- 📋 PyBay 2026 Deadline — Aug 01
- 📋 Plone Conference 2026 Deadline — Aug 01
- 📋 PyData Global 2026 Workshop Deadline — Aug 04
Credits
July 04, 2026
PyCon Ireland
Update on PyCon Ireland 2026: Venue Change and Extended CFP Deadline
Our booked venue, Trinity College Dublin, has informed us that it can no longer host the conference. This decision was made by the venue, and it means we can no longer confirm 17 October 2026 as our event date.
We know this is disappointing, especially for everyone who was planning to submit a talk, book travel, or simply mark the date in their calendar. We’re disappointed too, but we want to be transparent with the community as soon as we have news, rather than staying quiet while we sort things out.
What This Means Right Now
- The Call for Proposals deadline has been extended to 30 August 2026. We don’t want the venue search to hold back speakers who want to share their Python knowledge, so submissions remain open, you don’t need to wait for the new venue and date to submit. Submit your proposal on our Sessionize page.
- The conference date of 17 October 2026 no longer stands. We will announce a new date once we have one.
- We are actively looking for a new venue in the Dublin area that can accommodate the conference. As soon as a venue and date are confirmed, we will update the website and announce it through our usual channels.
- If you already submitted a talk, don’t worry, your proposal is safe. We will let speakers know the new date and venue as soon as they’re confirmed, so you can make your own arrangements in good time.
What Stays the Same
- PyCon Ireland 2026 is still happening. We are treating this as a reschedule, not a cancellation.
- Our commitment to the community remains unchanged, we are as enthusiastic as ever to showcase your talks, from first-time speakers to seasoned practitioners.
- The team has already begun the process of securing a new venue. We will share updates as soon as there is something concrete to report.
- We still welcome proposals from first-time and experienced speakers alike, across the full range of Python topics, in both full-talk (30 minutes) and lightning-talk (5 minutes) formats. See the full CFP page for submission guidelines.
How to Stay Updated
The fastest way to hear about the new venue and date is to follow us on Mastodon, X, or LinkedIn. We will also post updates here on the blog.
Thank you for your patience and continued support of Python Ireland. We look forward to sharing better news with you soon, and we can’t wait to read your proposals.
Bob Belderbos
One Core, Two Interfaces, No Rewrites
When building applications, I always build the core first, then the interfaces. It was no different with Ask the Canon: a uv run main.py ask "..." CLI for quick iteration and validation, then the web app for MVP. Search, ranking, citations, all using the same engine.
Ask the Canon's core is a handful of pure functions in one module. Both interfaces are thin wrappers. This is the second post in a series on how it's built. The first one was about the retrieval engine. This one is about the wider architecture.
Functional core, two interfaces
I just have one module with pure functions, clear contracts, and no hidden state:
def embed(texts: list[str]) -> np.ndarray: ...
def load_library(book_ids=None) -> tuple[list[Passage], np.ndarray]: ...
def search_passages(query, passages, vectors, ...) -> list[tuple[int, float]]: ...
def reflow(text: str) -> str: ...
load_library reads the cached .npy files off disk and hands back a list of Passage tuples plus the stacked matrix.
search_passages takes those two and a query and returns ranked (index, score) pairs.
The web layer consumes the core functions, no re-implementation:
from main import (
embed,
Passage,
humanize_author,
load_library,
reflow,
search_passages,
)
The CLI's ask() and the web app's /api/ask share the same spine: load the library, call search_passages, walk the ranked (index, score) pairs. From there each does its own thing. The CLI prints rich panels and offers an interactive deep-read; the web app serializes to Match JSON and logs a bit of analytics on the way out.
The ranking decision, what comes back and in what order, is shared. Everything downstream is presentation, which is exactly where a CLI and a web app should differ.
We do the same in our agentic AI program: one core engine, three interfaces (CLI, Telegram, API / web dashboard).
I needed caching
load_library is not cheap. It walks books/, reads a JSON file and an .npy file per book, and stacks 80k vectors into one matrix with np.vstack. You don't want to pay that overhead on every HTTP request!
In the CLI that's a non-issue: the process loads once and exits. On the web side, it's one decorator away:
from functools import cache
@cache
def library() -> tuple[list[Passage], np.ndarray]:
return load_library()
@cache turns the first call into the real load and every call after into a dictionary lookup, much faster.
@app.get("/api/ask")
def ask(q: str, k: int = 5, per_book: int = 2, floor: float = 0.6) -> list[Match]:
passages, vectors = library() # cached
...Pre-warm on startup, not on the first visitor
There's a subtlety @cache doesn't solve on its own. If the first request is what triggers library() ("wakes up PyTorch"), then the first real visitor pays that tax. App restarts are rare, but making the first visitor wait still isn't acceptable.
FastAPI's lifespan offers a nice fix for this: do it as soon as the app starts, before the first request:
@asynccontextmanager
async def lifespan(app: FastAPI):
init_db()
logger.info("Pre-warming vector library and loading models into RAM...")
_ = library() # fills the @cache with the stacked matrix
_ = embed(["warmup"]) # forces PyTorch to wake up and allocate
logger.info("Ready for traffic.")
yield
app = FastAPI(title="classics", lifespan=lifespan)
- I left a log line to watch the startup time. I also added some comments for possible collaborators and my future self.
- I use
_as a throwaway variable to make it clear the return value is ignored. - You can put shutdown logic after
yield, similar to how pytest fixtures work. Clean.
By the time the first request lands, both are warm.
Lazy loading
I am a proponent of imports at the top, but lazy loading is a serious performance consideration. It's coming in 3.15:
Lazy imports defer the loading and execution of a module until the first time the imported name is used, in contrast to ‘normal’ imports, which eagerly load and execute a module at the point of the import statement. - PEP 810 – Explicit lazy imports
That's the automatic version, landing in 3.15. Here I do it by hand: defer the model import into the function that needs it:
@cache
def _model():
import sentence_transformers as st # lazy, so the offline env vars take effect first
return st.SentenceTransformer(EMBED_MODEL)
So the model loads once, and only if something actually calls _model(). @cache hands back the same instance every time after.
The "offline env vars" part refers to the second reason I need the import here. At the top of the module I have:
os.environ.setdefault("HF_HUB_OFFLINE", "1")
os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
os.environ.setdefault("TQDM_DISABLE", "1")
Hugging Face reads HF_HUB_OFFLINE at import time. Import sentence-transformers before those are set and it will try to reach out to the internet, which is not what I want because I have the data and model cached locally. Set them first and the model stays fully offline, no surprise network calls.
Functions vs classes
None of this needs a class. The core is functions over plain data (Passage and Chunk are NamedTuples), the only state is a memoized function, and the two interfaces are thin adapters that share common behavior.
That's the payoff. When I want a third interface tomorrow (e.g. a scheduled job or a different API), it imports the same functions and gets the same behavior for free.
Next up in part 3: the small post-processing tricks that make the results actually good, no bigger model required.
Armin Ronacher
Better Models: Worse Tools
A very strange Pi issue
sent me down a rabbit hole over the last two days. The short version is that
newer Claude models sometimes call Pi’s edit tool with extra, invented fields in
the nested edits[] array. And not Haiku or some small model: Opus 4.8. The
edit itself is usually correct but the arguments do not match the schema as
the model invents made-up keys and Pi thus rejects the tool call and asks to
try again.
That alone is not too surprising as models emit malformed tool calls sometimes. Particularly small ones. What surprised me is that this is getting worse with newer Anthropic models as both Opus 4.8 and Sonnet 5 show it but none of the older models. In other words, the SOTA models of the family are worse at this specific tool schema than their older siblings.
In case you are curious about Fable: I intentionally did not test it because I was not sure if the classifiers they are running might downgrade me to Opus silently.
Tool Calls Are Text
If you have not spent too much time looking at LLM tool calling internals, the important thing to understand is that tool calls are not magic and use some rather crude in-band signalling. The model receives a transcript, a system prompt and a list of available tools. The server munches that into a large prompt with special marker tokens. Because the model was trained and reinforced on examples of that format, at some point during generation it emits something that the API or client interprets as “call this tool with these arguments”.
For a file edit tool, the intended invocation payload might say something like this:
{
"path": "some/file.py",
"edits": [
{
"oldText": "text to replace",
"newText": "replacement text"
}
]
}
A harness then validates the arguments, performs the edit, and feeds the result back into the model. If validation fails, the model sees an error and usually tries again.
How exactly that formatting happens is not known for the Anthropic models, but some people have gotten out “ANTML” markers and they at times do leak also into public communications. To the best of my knowledge, the call above would come out serialized like this from the model:
<antml:function_calls>
<antml:invoke name="edit">
<antml:parameter name="path">some/file.py</antml:parameter>
<antml:parameter name="edits">
[
{
"oldText": "text to replace",
"newText": "replacement text"
}
]
</antml:parameter>
</antml:invoke>
</antml:function_calls>
An important thing to note here is that this thing, while looking like XML, is not really XML. It’s just a thing they found convenient to tokenize and train on. The other thing to note is that a basic top-level string parameter appears in-line whereas an array of objects is implemented via JSON serialization. While I’m not entirely sure that this is how it works, there are some indications that this is not too far off. This will become relevant later.
There are two very different ways to make the model produce a structure like this:
- You can ask the model to produce valid JSON matching a schema and then validate it afterwards.
- You can constrain the sampler so that invalid JSON, or even invalid schema shapes, cannot be sampled in the first place.
The second approach is what people usually refer to as grammar-aware or
constrained decoding. The sampler masks out tokens that would violate the
grammar. If the model is currently inside a JSON object and the schema says
only oldText and newText are allowed, the sampler can prevent it from
emitting "in_file" or "type". Grammar-aware decoding can be used both to
constrain something to be syntactically valid JSON and also to enforce specific
enum values or keys.
Without any form of constraints the model is merely following a learned convention.
The Failure
Pi’s edit tool supports multiple exact string replacements in one call. That is
why the arguments contain an edits array. In the failing cases the model
produces entries like this:
{
"oldText": "...",
"newText": "...",
"requireUnique": true
}
or this:
{
"oldText": "...",
"newText": "...",
"oldText2": "",
"newText2": ""
}
Across repeated trials I saw a whole zoo of invented trailing keys: type,
id, kind, unique, requireUnique, matchCase, in_file,
forceMatchCount, children, notes, cost, oldText2, newText2,
oldText_2, newText_2, and even an event.0.additionalProperties key inside
the edit object itself.
The most annoying part is that the actual oldText and newText payloads were
byte-correct in the invalid calls I inspected. The model had in fact produced
the right invocation but then added nonsense at the end of the object.
The failure is also heavily context-dependent. A fresh single-turn prompt like “edit this file” did not reproduce it at all for me. An agentic history where the model had read files, diagnosed a problem and then composed a multi-line edit could reproduce it. And more annoyingly, not all transcripts will show that behavior. In fact, I needed Petr Baudis‘s transcripts to reproduce this for me at all! In that user’s session continuing the session caused Opus 4.8 to fail around 20% of the time. Stripping thinking blocks from history reduced the failure rate by half. Turning on strict tool invocation eliminated it in my runs.
Why It’s Getting Worse
My strongest hypothesis is that this is not random deterioration but a training artifact.
When older Anthropic models were trained, they were trained on some tools (some of which were documented). But that training did not yet have a user-shipped harness like Claude Code as the obvious target. Modern Anthropic models are most likely different because their post-training includes Claude Code or a harness that looks very similar. The model learns what a successful tool call looks like in that environment. It also learns what mistakes are tolerated by that environment.
Claude Code’s own tools are comparatively flat. The ordinary edit tool is not
Pi’s nested edits[] shape; it is closer to file_path, old_string,
new_string, and an optional flag (replace_all). Looking at Claude Code’s
client is very instructive: it contains retry paths for malformed tool use,
parameter aliases, type coercions, Unicode repairs and filtering of unknown
keys. In other words, Anthropic’s own client appears to expect and accept a
fair amount of slop and repairs it, mostly silently.
If reinforcement learning happens in a harness like that, or a simulation of one, then slightly malformed tool calls can still complete the task and receive reward. The harness fully absorbs the error and there is little gradient against inventing an alias, adding a stray field or using a nearby parameter name.
Worse, the model may become very strongly adapted to the canonical Claude Code edit tool shape. A different harness can present a tool with the same semantic intent but a different schema. Such a tool can increasingly be off-distribution. The better-trained model might actually fight you harder because its prior is stronger.
This is not too surprising, but it is a change from how this was a few months ago. When Opus 4.5 launched, it adapted to other edit tools exceptionally well. In fact, I was pretty convinced that we’re on a good path where the models are more likely to adapt to any sort of tool shape that comes around for as long as the instructions are good.
Now I’m somewhat worried about the track we’re on here. Alternative tool schemas might not just be unfamiliar. They might be implicitly punished by post-training that optimizes for one particular, forgiving tool ecology. And that ecology is not documented. While there is a text editor tool that is documented, you will see that this format is in fact not followed by Claude Code. What Claude Code does internally (which is a closed-source harness) is hidden from you.
The Slop Harness
Claude Code is obviously closed-source but we can look at the minified code and get some idea of what it does. And honestly, it’s very forgiving of incoming data.
For a start, Claude Code checks the model’s visible text for leaked <invoke
markup. It also emits some telemetry when that happens and then it has its
own state machine to retry such bad calls by pushing back to the model.
It has explicit Unicode escape repair which fixes broken \uXXXX sequences and
lone surrogates in string values. It also has per-tool aliases for parameters.
For instance, Edit accepts old_str (presumably from the times when the models
were trained on the officially documented text editor tool), the newer old_string
from the schema, new_str/new_string, path as an alias for file_path, and some more.
It also silently filters out unexpected keys and it does not use strict mode
either. The issue with strict mode is that Anthropic applies complexity
limits to the tool definitions that cause API requests to fail, so presumably
that’s why Claude Code does not attempt to use it.
Strictness
Will this problem be with us in other harnesses too? One huge issue with Anthropic is that the models are completely closed, and so is the harness. Codex models are also closed, but at least the harness is not. We also have gpt-oss which is at least a bit interesting. The models are explicitly trained to use OpenAI’s harmony response format and there is a lot of documentation that at least tells us how OpenAI people think about this.
Harmony makes channels and tool-call content types part of the prompt format. A function call can look like this:
<|start|>assistant<|channel|>commentary to=functions.get_weather
<|constrain|>json<|message|>{"location":"San Francisco"}<|call|>
The important bit is <|constrain|>json. The model can express in-band that
this message body is JSON, and an inference stack can use that boundary to
switch into JSON-constrained sampling for the body of the tool call. Presumably
a bit of this also happens in Anthropic’s models, at least in strict mode
I would imagine.
The marker in harmony helps the sampler to detect when it needs to sample with a specific grammar, and because it is part of the transcript, it makes that rather easy to do. For hosted GPT models, there is also an option to provide a LARK grammar for custom tools that need to adhere to something like this.
Anthropic appears different from that, though maybe not entirely. If an array
of objects is represented as JSON, as it appears to be, then the model has to
write JSON inside the tool parameter. There is probably basic
grammar-constrained sampling going on, and that may partly explain the extra
keys. For a nested array parameter, that JSON includes escaped multi-line file
content inside string literals, inside one tag. The unexpected,
made-up keys appear exactly at the highest-entropy point of that task: after
closing a several-hundred-token escaped newText string, where the model must
decide } vs , "...".
Opus 4.8 and Sonnet 5 seem to have much stronger priors about what an edit tool
call should look like and that prior appears to be Claude Code’s edit schema: a
flat old/new string pair, plus the optional replace_all flag. My guess is
that Opus has learned that an edit operation may have one extra optional field,
but under Pi’s nested oldText/newText shape it has no trained name for that
field. So it samples a plausible name fresh each time, which is why the
failures produce dozens of random keys rather than one stable alias.
As strict mode in Anthropic appears to fix this, I presume that on the server
side they are refusing to sample a key that is not permitted by the JSON schema
structure. That would also explain why they have limits to the complexity of
the tool definitions when strict mode is enabled.
So far, the Codex models I tested did not show this type of regression. I tested all available ones except 5.6, which I do not have access to yet.
What This Means For Harnesses
The uncomfortable lesson is that tool schemas are not neutral, at least not on Anthropic models. We like to pretend that a schema is an abstract contract and the model is a general reasoner that will follow it, but that might no longer be the case for some of the tools.
Tool schemas are somewhere in the distribution and some shapes are close to what the model saw during post-training and some are far away. Some are easy for the provider’s hidden encoding (e.g. top-level attributes in ANTML), whereas some require the model to write large escaped JSON objects inside nested arrays after long multiline strings. The model may be smart enough to understand the schema and still be bad at sampling the exact shape under pressure.
If this type of model behavior continues, I wonder what the implications for
harnesses are. Obviously one could turn on strict sampling in
Anthropic and the problem should go away. On the other hand, that the model
has this behavior shows the impact that reinforcement learning has on them.
Fighting that prior is probably futile if you want to get the best model performance.
Right now the reality is that Claude Code is not open source and we cannot really know what they are doing in their RL environments either. We cannot assume Claude-Code-trained behavior will transfer cleanly to your tools unless they are a close match. The more post-training happens inside one dominant harness, the more every other harness will have to inherit its quirks.
I used to be more skeptical of strict grammar-constrained tool invocation because constrained decoding can have quality tradeoffs. I still think that can be true in general, but this bug moved my priors significantly. If the newest models get better at solving the task while getting worse at faithfully emitting an alternative tool schema, then the harness needs stronger guarantees somewhere.
If you want to find out more, or you want to discuss this, consider reading the issue on the Pi tracker.
July 03, 2026
Mycli
Release v2.0.0
mycli is a command line interface for MySQL which includes
auto-completion and syntax highlighting.
Read the install instructions to find out how to get the latest version.
Mycli v2.0.0 has breaking changes!
Major features added in recent months include
Tryton News
Security Release for issue #5160 and #14869
The user titou has discovered that the administrator group can execute Python code on the server which is hidden inside an uploaded report template.
And Dan Shallom has discovered that the same can also be accomplished by the marketing group when uploading marketing email templates.
Impact
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: Low
- User Interaction: None
- Scope: Unchanged
- Confidentiality: High
- Integrity: None
- Availability: None
Workaround
There is no workaround.
Resolution
All affected users should upgrade trytond to the latest version.
Affected versions per series:
trytond:- 8.0: <= 8.0.5
- 7.8: <= 7.8.11
- 7.0: <= 7.0.52
Not affected versions per series:
trytond:- 8.0: >= 8.0.6
- 7.8: >= 7.8.12
- 7.0: >= 7.0.53
Some custom reports may fail after the upgrade because they are using dynamic or private attributes. Such reports must be updated to use only the allowed statements.
Reference
- Report customisation (#5160) · Issues · Tryton / Tryton · GitLab
- Remote Code Execution via Template Injection (#14869) · Issues · Tryton / Tryton · GitLab
Concerns?
Any security concerns should be reported on the bug-tracker at https://bugs.tryton.org/ with the confidential checkbox checked.
3 posts - 2 participants
Security Release for issue 14907
Cédric Krier has discovered that access is not enforced when browsing record instances in template.
Impact
- Attack Vector: Network
- Attack Complexity: Low
- Privileges Required: Low
- User Interaction: None
- Scope: Unchanged
- Confidentiality: High
- Integrity: None
- Availability: None
Workaround
There is no workaround.
Resolution
All affected users should upgrade trytond, marketing_automation and marketing_email to the latest version.
Affected versions per series:
trytond:- 8.0: <= 8.0.5
- 7.8: <= 7.8.11
- 7.0: <= 7.0.52
markting_automation:- 8.0: <= 8.0.0
- 7.8: <= 7.8.2
- 7.0: <= 7.0.3
marketing_email:- 8.0: <= 8.0.0
- 7.8: <= 7.8.2
- 7.0: <= 7.0.2
Not affected versions per series:
trytond:- 8.0: >= 8.0.6
- 7.8: >= 7.8.12
- 7.0: >= 7.0.53
markting_automation:- 8.0: >= 8.0.1
- 7.8: >= 7.8.3
- 7.0: >= 7.0.4
marketing_email:- 8.0: >= 8.0.1
- 7.8: >= 7.8.3
- 7.0: >= 7.0.3
Reference
Concerns?
Any security concerns should be reported on the bug-tracker at https://bugs.tryton.org/ with the confidential checkbox checked.
1 post - 1 participant
July 02, 2026
Anwesha Das
Dreams are real, so chase them
A Mortgage lawyer had dream to work full time in technology and policy,
The journey started long ago with a new mum reading "Intellectual Property and Open Source: A Practical Guide to Protecting Code" by Van Lindberg, to her keep entertained during breastfeeding sessions.Then from meaning of library changing from being a room full of books to the screen full of gibberish code, from rebooting PyLadies Pune in Red Hat Pune office to working in Red Hat for the last 3 years. All of these seemed to pretty unreal at times.
And now I am starting my journey at the Red Hat OSAIPO team as the "Security Community and Compliance Architect". This will give me opportunity to to work with the intersection of law, technology, policy and community. The people I look up to, I admire, I will get a chance to work as my teammates. I so looking forward to the opportunity.
My journey till date has been a maneuver to hold on to the dream. The quote "Dreams are real, so chase them" sums up my journey to it&aposs core.

EuroPython
EuroPython 2026 Job Opportunities from Our Sponsors
As EuroPython 2026 approaches and we prepare for another exciting edition, we’d like to thank our community and sponsors for their continued support.
We’re also pleased to share some fantastic job opportunities from our sponsors. Take a look and perhaps you’ll discover your next role.
ActiveCampaign
Software Engineer, Agent Development
About ActiveCampaign
ActiveCampaign is the autonomous marketing platform for people at the heart of the action. It empowers teams to automate their campaigns with AI agents that imagine, activate, and validate–freeing them from step-by-step workflows and unlocking limitless ways to orchestrate their marketing.
With AI, goal-based automation, and 1,000+ app integrations, agencies, marketers, and owners can build cross-channel campaigns in minutes–fine-tuned with billions of data points to drive real results for their unique business.
ActiveCampaign is the trusted choice to help businesses unlock a new world of boundless opportunities–where ideas become impact and potential turns into real results.
As a global multicultural company, we are proud of our inclusive culture which embraces diverse voices, backgrounds, and perspectives. We don’t just celebrate our differences, we believe our diversity is what empowers our innovation and success. You can find out more about our DEI initiatives here.
About the job
ActiveCampaign is seeking a Software Engineer to join our Kraków Hub and build production-ready AI agents for autonomous marketing. Our platform empowers businesses to automate their customer engagement — and we are now extending that with intelligent, agentic workflows that can reason, act, and adapt on behalf of our customers. Our first wave of agents is already in production. This role will drive the expansion, building the next generation of autonomous experiences across the full breadth of the platform.
You &aposll work at the intersection of solid software engineering and applied AI — designing, building, evaluating, and operating agents that run at scale in a production marketing automation platform. This means engineering rigor comes first: agents need to be reliable, observable, performant, and safe before they are clever.Our Kraków Hub houses multiple engineering teams — Forms, Integrations, Ecommerce, Agency, CRM, Mobile, CampaignsUI, and Content — working across different product pillars with a shared focus on building agentic workflows into all core functionalities across the platform. You will collaborate closely with these teams, understanding their domains and building agents that integrate seamlessly into the broader system,This is not a pure research or ML role. We need engineers who can build — end to end — from prompt design through backend orchestration to frontend integration, and who can ship, measure, and iterate fast. Our environment moves quickly — we value high ownership, tight feedback loops, and close cross-functional collaboration. If you thrive in a hands-on, high-pace setting where you build, ship, and iterate rapidly, this role is for you.
What Your Day Could Consist Of
- Designing and building production-grade AI agents and autonomous marketing workflows across the ActiveCampaign platform
- Implementing multi-step agentic flows involving LLM orchestration, tool use, and integration with platform services
- Working with LLM APIs (prompt engineering, response handling, evaluation) and building reliable, testable agent pipelines
- Designing and running agent evaluations — building eval frameworks, defining quality metrics, and continuously measuring agent accuracy, reliability, and performance in production at scale
- Monitoring and optimizing agent performance — latency, cost, token usage, error rates — and iterating rapidly based on production dataIterating on agent behavior based on user feedback, observability data, and quality metrics
- Leveraging AI-assisted development tools and practices in your daily workflow — you don&apost just build AI features, you build with AI
- Writing production-quality code in Python for agent services and backend orchestration
- Contributing to PHP backend and frontend (React/Ember) integration points where agents interact with the core platform
- Building and maintaining APIs, data flows, and service interfaces that agents depend on
- Working iteratively — shipping code fast, learning from production, and improving continuously
- Ensuring code quality through testing, code review, and adherence to engineering standards
- Participating in on-call rotation for incidents related to agent services
- Working closely with Product, Design, and domain engineering teams to define agent capabilities and user experiences
- Engaging in pairing and code reviews across teams
- Contributing to documentation of agent architectures, patterns, and best practices
- Maintaining regular overlap with US-based teams (Chicago timezone) to ensure alignment and tight collaboration across the organization
What We&aposre Looking For
- 2-4 years of experience in a software engineering role
- Hands-on experience building and running AI agents or agentic workflows in a production environment — not just prototypes or side projects
- Experience with agent evaluation and performance optimization — you know how to measure whether an agent is working well and how to make it better
- Solid backend development skills in Python; working familiarity with PHP is a plus
- Understanding of how to build reliable, testable, and observable systems
- Active practitioner of AI-driven development — you use AI coding assistants and agentic development tools as part of how you work, not just what you build
- Ability to work iteratively and ship code fast — you&aposre comfortable with rapid release cycles and learning from production
- Familiarity with DB technologies: MySQL, DynamoDB, Redis
- Strong problem-solving skills and ability to work across multiple codebases and technologies
- Fluent in English (B2 minimum)
- Willingness to work flexible hours with regular afternoon availability to overlap with US Central timezone
- Experience with agentic AI frameworks or multi-agent orchestration patterns
- Familiarity with Model Context Protocol (MCP) or tool-use patterns for LLM agents
- Experience building eval pipelines or quality measurement systems for LLM-based features
- Frontend experience with React or EmberFamiliarity with AWS, CI/CD practices, and observability tooling
- Understanding of prompt engineering, evaluation methodologies, or LLM fine-tuningExperience in SaaS, marketing automation, or CRM domain
What We Offer
- A front-row seat in ActiveCampaign&aposs AI-first transformation — you&aposll build the autonomous marketing agents customers interact with
- Collaboration with experienced engineers across the Kraków Hub, including senior and staff-level technical leadership
- An environment that values ownership, fast iteration, and learning by doing
- Active investment in AI-first engineering practices and tooling
- Hybrid work model from our Kraków office with flexibility to adjust working hours for collaboration with US-based teams (Chicago timezone overlap)
This is an exciting time to join ActiveCampaign as we build out our new office in Poland. You will be a large part of developing our office culture in this new Krakow hub location.
Perks And Benefits
At ActiveCampaign, we prioritize employees’ well-being and professional growth by cultivating a culture centered on collaboration and innovation. When you join our team, you’ll not only have the opportunity to make a significant impact, but also enjoy a range of benefits tailored to support your personal and career development.
Here Are Some Of The Benefits We Offer
- Comprehensive Health & Wellness: Top-tier benefits package that includes medical and dental benefits paid 100% by ActiveCampaign for you and 50% for your dependents, and reimbursements on vision expenses. In addition, employees receive complimentary access to telehealth services, and a free subscription to Calm.
- Growth & Development: Access to LinkedIn Learning, professional development programs, and career growth opportunities in a fast-growing organization.
- Generous Paid Time Off: Recharge and take the time you need to maintain work-life balance.
- Total Rewards: Pension scheme with matching up to 1.5% of your contribution, MultiSport Plus card to support your active lifestyle, home office stipend to cover your commuter or work from home expenses, and a four-week paid sabbatical with bonus after five years.
- Collaborative Culture: Work alongside brilliant, passionate colleagues in an environment that values innovation, teamwork, and mutual support.
ActiveCampaign is an equal opportunity employer. We recruit, hire, pay, grow, and promote no matter of gender, race, ethnicity, sexual orientation, marital status, political opinion, national origin, social origin, parentage, workers union membership, economic status, religion, age, health condition, disability or any other grounds protected by law.
Our Employee Resource Groups (ERGs) strive to foster a diverse inclusive environment by supporting each other, building a strong sense of belonging, and creating opportunities for mentorship and professional growth for their members.
We may use artificial intelligence (AI) tools to support parts of the hiring process, such as reviewing applications, analyzing resumes, or assessing responses and identifying potential inconsistencies or verification signals in application materials based on available information. These tools assist our recruitment team but do not replace human judgment. Final hiring decisions are ultimately made by humans. If you would like more information about how your data is processed, please contact us.
Compensation details listed in this posting reflect the base rate only and do not include bonus, equity, sales incentives or other role specific compensation that the role may be eligible for. ActiveCampaign believes in and is committed to equitable compensation practices. The salary range provided above is a good faith estimate of the pay range determined by the location associated with the job posting. The actual salary depends on a candidate’s skills, experience, and work location.
Apply here: https://jobs.lever.co/activecampaign/3e093aec-8540-49c7-93f1-719727c1191d/apply
Apify
Software Engineer for Fraud Prevention (TypeScript)
About Apify:
Apify is the largest marketplace of tools for AI. 40,000 Actors helping people and agents get real-time web data, track competitors, generate leads, or integrate their apps. Actors are built by a global creator community that now earns more than $1M every month.
Software Engineer for Fraud Prevention (TypeScript)
Job description:
As our Fraud Engineer, you will design and build systems to stop bad actors in real time. You&aposll investigate new attack patterns, deploy quick fixes, and create internal tools to handle incidents. It&aposs a hands-on role where you will work with security and product teams to keep our AI platform safe without slowing down legitimate users.
How to apply: https://jobs.ashbyhq.com/apify/7c93886a-9f1d-4ed9-ba71-d753c8f34c82?utm_source=career_page_apify
BCG X
Who We Are
Boston Consulting Group partners with leaders in business and society to tackle their most important challenges and capture their greatest opportunities. BCG was the pioneer in business strategy when it was founded in 1963. Today, we help clients with total transformation-inspiring complex change, enabling organizations to grow, building competitive advantage, and driving bottom-line impact.
To succeed, organizations must blend digital and human capabilities. Our diverse, global teams bring deep industry and functional expertise and a range of perspectives to spark change. BCG delivers solutions through leading-edge management consulting along with technology and design, corporate and digital ventures—and business purpose. We work in a uniquely collaborative model across the firm and throughout all levels of the client organization, generating results that allow our clients to thrive.
We Are BCG X
We&aposre a diverse team of more than 3,000 tech experts united by a drive to make a difference. Working across industries and disciplines, we combine our experience and expertise to tackle the biggest challenges faced by society today. We go beyond what was once thought possible, creating new and innovative solutions to the world&aposs most complex problems. Leveraging BCG&aposs global network and partnerships with leading organizations, BCG X provides a stable ecosystem for talent to build game-changing businesses, products, and services from the ground up, all while growing their career. Together, we strive to create solutions that will positively impact the lives of millions.
What You&aposll Do
Our BCG X teams own the full analytics value-chain end to end: framing new business challenges, designing innovative algorithms, implementing, and deploying scalable solutions, and enabling colleagues and clients to fully embrace AI. Our product offerings span from fully custom-builds to industry specific leading edge AI software solutions.
Our Forward Deployed AI Engineer and Senior Forward Deployed AI Engineer are part of our rapidly growing engineering team and help to build the next generation of AI solutions. You&aposll have the chance to partner with clients in a variety of BCG regions and industries, and on key topics like climate change, enabling them to design, build, and deploy new and innovative solutions. Additional responsibilities will include developing and delivering thought leadership in scientific communities and papers as well as leading conferences on behalf of BCG X.
We are looking for dedicated individuals with a passion for software development, large-scale data analytics and redefining organizations into AI led innovative companies. Successful candidates possess the following:
- Apply software development practices and standards to develop robust and maintainable software
- Actively involved in every part of the software development process
- Experienced at guiding non-technical teams and consultants in best practices for robust software development
- Optimize and enhance computational efficiency of algorithms and software design
- Motivated by a fast-paced, service-oriented environment and interacting directly with clients on new features for future product releases
- Enjoy collaborating in teams to share software design and solution ideas
- A natural problem-solver and intellectually curious across a breadth of industries and topics
- Fluency in both Italian and English
What You&aposll Bring
Requirements:
- Master&aposs or PhD degree program in Computer Science, Data Science, Statistics, Operations Research, or related field
Technologies
- Programming: Python, Java/Scala
- Frameworks/Libraries: TensorFlow, PyTorch, Scikit-learn, Keras
- Data Processing: NumPy, Pandas, Apache Spark, Dask
- Cloud Platforms: AWS (SageMaker, EC2), GCP (AI Platform), Azure (ML Studio)
- DevOps: Docker, Kubernetes, Terraform, Jenkins
- Data Visualization: Matplotlib, Seaborn, Plotly/Dash, Tableau
- Tools: Jupyter Notebooks, Google Colab, Git (GitHub/GitLab), MLflow, TensorBoard
- Deployment: Flask, FastAPI, TensorFlow Serving, Streamlit
Apply: https://careers.bcg.com/global/en/job/55983/Forward-Deployed-AI-Engineer-Italy-BCG-X
Bloomberg
Senior Software Engineer - AI App Enablement & Observability
📍Location: Dublin
Description & Requirements
Platform Engineering builds the core platforms, tooling, and paved roads that Bloomberg engineers rely on to ship reliable, secure, and high-performing systems at scale.The AI App Enablement & Observability team accelerates how AI products are built across Bloomberg Industry Group. Our mission is to make AI systems reliable, performant, cost-efficient, and continuously improving through platform tooling, deep observability, and automated feedback loops.We build developer-facing platforms and workflows that enable teams to experiment, deploy, and operate AI and agent-based systems with confidence. This includes LLM gateways, agent platforms, benchmarking systems, telemetry pipelines, and self-improving infrastructure that closes the loop between observability and action. We emphasise strong developer experience, intuitive APIs/SDKs, and end-to-end ownership.
What’s in it for you?
You will help define how Bloomberg Industry Group builds and operates AI systems at scale by working on platforms that:
- Accelerate AI product development through reusable tooling and paved roads
- Provide end-to-end observability across AI systems (models, agents, pipelines, applications)
- Enable self-improving systems through telemetry-driven feedback loops
- Optimise cost, performance, and reliability of AI workloads
- Support both production AI systems and internal engineering agents
You’ll collaborate across AI product, infrastructure, and platform teams to deliver foundational systems.
We’ll trust you to:Platform & Enablement
- Build and evolve AI platform tooling (e.g., developer workflows, benchmarking systems)
- Design developer-friendly APIs, SDKs, and interfaces
- Contribute to systems across the Model Development Lifecycle (experimentation, deployment, evaluation)
Observability & Telemetry
- Build and operate observability platforms and telemetry pipelines (logs, metrics, traces, events)
- Provide visibility into latency, token usage, cost, quality, drift, and reliability
- Define instrumentation standards, schemas, and conventions
- Implement distributed tracing using modern approaches (e.g., OpenTelemetry)
AI System Insights & Debugging
- Enable end-to-end debugging of AI and agent workflows (model calls, tool usage, retrieval, orchestration)
- Build benchmarking, regression detection, and performance analysis capabilities
- Support observability for both production systems and internal engineering agents
Closed-loop Optimization & Automation
- Develop systems that turn telemetry into action (automated experimentation, regression detection, alerting)
- Build feedback loops that continuously improve model quality and system behavior
- Enable self-healing and self-optimising workflows
Cost, Performance & Reliability
- Build tooling for cost visibility, forecasting, and optimization
- Define SLOs, alerting, and performance tuning practices
- Improve reliability and scalability of AI infrastructure
Ownership & Collaboration
- Own projects end-to-end (RFCs, architecture, implementation, rollout, production support)
- Partner with AI teams to drive adoption of platform tooling and standards
- Produce high-quality documentation and improve developer experience
You’ll need to have:
- Demonstrated experience building production software or platform systems
- Strong engineering fundamentals with distributed systems or backend platforms
- Experience or strong interest in observability and debugging complex systems
- Experience or strong interest in AI/ML systems, LLMs, or agent-based architectures
- Strong ownership mindset and ability to drive ambiguous problems to production
- Hands-on experience with modern agentic coding tools and multi-model workflows
- Working knowledge of agent architecture internals (context engineering, tool loops, sub-agent orchestration)
We’d love to see:
- Experience with OpenTelemetry and modern observability ecosystems, including instrumentation, collectors, exporters, and tools like Prometheus, Grafana, and tracing/log systems
- Experience designing and operating telemetry pipelines, including sampling, retention, cardinality, and cost tradeoffs, as well as integrating observability into CI/CD and developer workflows
- Familiarity with AI/agent frameworks, including instrumentation of LLM calls, tool usage, workflows, and evaluation signals (quality metrics, benchmarking, regression detection)
- Experience building cost monitoring, forecasting, and optimization systems for AI workloads
- Familiarity with cloud and infrastructure tooling (e.g., AWS, Azure, Kubernetes, Terraform)
- Experience with agentic infrastructure concepts such as MCP servers, hooks, skills, subagents, sandboxing, and persistent memory patterns
- Active engagement with the agentic engineering frontier, including emerging patterns (e.g., harness vs. model, review debt, feedback loops)
- Demonstrated agent-native development practices (iterating with agents using testing, verification, and feedback loops)
- Strong security awareness for autonomous systems, including sandboxing, prompt injection risks, credential exposure, and guardrails
If indicated, please note that years of experience are a guide; we will consider applications from all candidates who can demonstrate the skills necessary for the role.Discover what makes Bloomberg unique - watch our podcast series for an inside look at our culture, values, and the people behind our success.
Apply here: https://bloomberg.avature.net/careers/Public?jobId=18854
Hudson River Trading (HRT)
Quantitative Latency Engineer
📍Austin, TX, United States; Chicago, Illinois, United States; London, United Kingdom; New York, NY, United States; Singapore
Hudson River Trading (HRT) is seeking curious, thoughtful engineers who enjoy working with data and solving real-world technical problems to join our growing Market Structure Analysis team. In this role as a Quantitative Latency Engineer, you’ll apply data-driven methodologies to understand and optimize trading technology and real-time interactions with financial markets across the globe, spanning traditional and crypto exchanges. No prior finance experience is needed!
Responsibilities
- Analyze time series network and exchange protocol captures
- Become familiar with the details of specific markets, attend presentations and liaise with exchange counterparts
- Research exchange features, capabilities, and architecture
- Automate collection and visualization of metrics that quantify efficacy of exchange communication
- Formulate and conduct controlled experiments that measure impact of calculated changes to HRT’s trading infrastructure
- Communicate ideas, requirements, and results across disparate teams
- Improve fill rate of our hardware-based trading strategy
- Reduce incidence of cancel-reject responses
- Investigate and report details of various latency-sensitive exchanges
Profile
- You possess a degree in Data Analytics or a related field
- You can collect and interpret network and/or financial market data
- You have professional experience in latency reduction, preferably in finance
- You have a basic understanding of proprietary trading and exchange technologies
Skills
- Proficiency in data analytics including statistics, data visualization, and working with large data sets
- Basic understanding of TCP and UDP network protocols
- Extensive experience with Python and relevant data libraries (Pandas, Numpy/Scipy)
- Some familiarity with the details of modern computer systems and networks
- Experience with real time exchange market data and order entry a plus
The estimated base salary range for this position is 200,000 to 300,000 USD per year (or local equivalent). The base pay offered may vary depending on multiple individualized factors, including location, job-related knowledge, skills, and experience. This role will also be eligible for discretionary performance-based bonuses and a competitive benefits package.
Culture
Hudson River Trading (HRT) brings a scientific approach to trading financial products. We have built one of the world&aposs most sophisticated computing environments for research and development. Our researchers are at the forefront of innovation in the world of algorithmic trading.
At HRT we welcome a variety of expertise: mathematics and computer science, physics and engineering, media and tech. We’re a community of self-starters who are motivated by the excitement of being at the cutting edge of automation in every part of our organization—from trading, to business operations, to recruiting and beyond. We value openness and transparency, and celebrate great ideas from HRT veterans and new hires alike. At HRT we’re friends and colleagues – whether we are sharing a meal, playing the latest board game, or writing elegant code. We embrace a culture of togetherness that extends far beyond the walls of our office.
Feel like you belong at HRT? Our goal is to find the best people and bring them together to do great work in a place where everyone is valued. HRT is proud of our diverse staff; we have offices all over the globe and benefit from our varied and unique perspectives. HRT is an equal opportunity employer; so whoever you are we’d love to get to know you.
Please be advised: Use of AI tools during interviews or assessments is strictly prohibited, unless otherwise instructed or agreed upon. We employ various methods to evaluate the authenticity of candidate responses. If we determine that AI assistance was used during any stage of the hiring process, we reserve the right to immediately disqualify your candidacy or rescind any job offers extended.
Apply here: https://www.hudsonrivertrading.com/hrt-job/quantitative-latency-engineer/
Modal
The production cloud for AI. Run inference, training, batch processing and sandboxes with sub-second cold starts, instant autoscaling across thousands of GPUs and a developer experience that feels local.
Member of Technical Staff - Python SDK
AI needs a new infrastructure layer. We&aposre building it at Modal.
Every era of computing brought new workloads that previous infrastructure couldn&apost support: mainframes, databases, and the cloud. Each time, the company that rebuilt the layer underneath defined the decade. AI is no different, except it touches everything instead of one slice, and the window to build the layer underneath it is open right now.
Our customers include category-defining companies like Lovable, Ramp, Cognition, DoorDash, and Suno. They rely on Modal for instant GPU access, sub-second container starts, and native storage, so it&aposs simple to serve low-latency inference, fine-tune models, and access production-ready sandboxes at scale.
We recently raised a $355M Series C at a $4.65B valuation, led by General Catalyst and Redpoint Ventures. We&aposve crossed $300M+ ARR and grown fivefold since September.
Our team includes creators of popular open-source projects (e.g.,Seaborn, Luigi), academic researchers, international olympiad medalists, and experienced engineering and product leaders with decades of experience.
The Role:
We’re looking for strong engineers with experience building developer tools that users love to work with. Our ideal candidate is someone with a demonstrated drive to build beautiful interfaces that enhance developer productivity.
Requirements:
- 5+ years of experience developing high-quality Python libraries with broad user-bases, ideally including some experience maintaining open-source software.
- Knowledge of advanced Python features, especially async programming.
- A strong product sense that manifests as a focus on developer ergonomics and productivity.
- A high level of customer empathy, good communication skills, and an openness to working directly with our users to help solve their problems.
- Ability to participate in on-call rotation and respond to production incidents.
- Ability to work in-person in our NYC or Stockholm office.
- Any of the following would be a plus:
- Familiarity with modern data / ML / AI tools and workflows
- Experience with Typescript, Go, or Rust
How to apply:
https://jobs.ashbyhq.com/modal/265d6127-dd34-433b-819a-1f935572c7d8
Numberly
SRE - Site Reliability Engineer
📍Hybrid - Paris
Numberly is recognized as one of the world&aposs leading data marketing specialists with nearly 500 employees and 11 offices worldwide serving more than 300 clients (L&aposOréal, Campari, Colgate, Nestlé, HSBC...). By putting technology to work for brands and consumers, Numberly is at the heart of business growth and everyone&aposs desire for more sustainable and relevant marketing. Numberly leverages the latest advances in data processing, analysis and activation, incorporating artificial intelligence technologies. This approach is part of a virtuous circle in which business competitiveness goes hand in hand with greater respect for privacy and data protection.
To achieve this, Numberly has always mastered, developed, and operated its own on-premise technical platforms thanks to the expertise of its in-house teams, without overlooking the Cloud when relevant. From development to datacenter hosting and network connectivity, everything is designed, built, maintained, and secured by our teams and we are proud of it.
Numberly is looking for a Site Reliability Engineer to design, operate, and improve the reliability of its infrastructure in order to always better serve its clients.
SRE Responsibilities
- Design, build, and operate highly available, robust, and scalable system architectures.
- Ensure the availability, performance, and reliability of production services.
- Know how to leverage existing tools (open-source or internal) and develop new solutions when relevant.
- Collaborate with other technical teams to proactively anticipate and resolve scaling challenges.
- Participate in the continuous improvement of operations practices (automation, monitoring, observability, incident management).
Qualifications
- Master’s degree (Bac +4/+5) from a university, engineering school, or equivalent.
- 3 to 5 years of experience in operating production systems.
- Strong troubleshooting and problem-solving skills.
- Good communicator, capable of popularizing their work, defending their ideas, and listening to others.
- Willingness to grow and help others grow, both technically (meetups, internal training) and humanly.
- Strong understanding of Linux internals.
- Fluent in French and proficient in English.
Technical Environment
- Physical infrastructure: 3 datacenters located in the Paris region
- Servers: +500
- Automation: Ansible, Terraform
- CI/CD: GitLab
- Virtualization: Proxmox
- Containers: Kubernetes (on-premises Kubespray and Talos, AWS EKS, Azure AKS)
- Load-balancing: HAProxy, OpenResty (nginx), Envoy
- Monitoring: Prometheus, Thanos, Kafka, Elasticsearch, Graylog
- Tracing: Sentry
- Languages: Python, Go, Rust
- Server OS: Ubuntu / Debian
- User OS: Windows / MacOS / Linux
- APIs: REST
- Cloud: AWS, Azure
- Databases: PostgreSQL, Hadoop, MSSQL, ScyllaDB, MongoDB
Security Tools & Frameworks
- MDM: Intune, Iru (Kandji)
- Logs: Kafka, Graylog, Vector, CrowdSec
- IDS/IPS: Falco
- EDR: HarfangLab, Microsoft Defender for Endpoint
- Scanning: Ivre, Burp Suite
- SAST: GitLab SAST, Semgrep, etc.
- KMS/PKI: HashiCorp Vault
- Containers: Kyverno, Harbor
Additional information
- At Numberly, we share a passion for transmission: weekly internal talks, meetings with expert professionals in their field, continuous learning.
- Fast and powerful onboarding, in particular thanks to: the mentor assigned to each newcomer; to Live my life in different teams; Happy Meetings: monthly internal meetings to meet up with all our teams around the world and share group news.
- We cultivate freedom of speech which allows everyone to participate in the development of the group.
- We act positively on our ecosystem through 1000mercis impacts and via our activities which create value in the Open Internet and contribute to the enrichment of Open Source.
- Numberly is an actor of diversity with a gender equity score of 97/100.
- Numberly is ISO/IEC 27001:2023 certified; this certification recognizes compliance with the highest standards in information security.
- Numberly is an international environment with more than 30 nationalities in our teams.
- Offices in the image of each of the teams, a generous library, a large fully equipped music studio, two cats, vermicomposting, the possibility of bringing your pet and space for bicycles! In each kitchen: coffee, tea, unlimited infusions and also mystery lunches, Wellpass (ex-Gymlib) partnership, sports classes and parties (often in disguise).
- Possible remote working days.
- Swile card (meal vouchers).
- Possible mobility in our various offices abroad.
- Numberly welcomes people with disabilities.
Revolut
Mid/Senior Software Engineer (Python)
📍Remote: Cyprus · Czech Republic · Poland · Porto · Portugal · Romania · Serbia · Spain · Sweden · UAE · UK
About Revolut
People deserve more from their money. More visibility, more control, and more freedom. Since 2015, Revolut has been on a mission to deliver just that. Our powerhouse of products — including spending, saving, investing, exchanging, travelling, and more — help our 75+ million customers get more from their money every day.
As we continue our lightning-fast growth, 2 things are essential to our success: our people and our culture. In recognition of our outstanding employee experience, we&aposve been certified as a Great Place to Work™. So far, we have 13,000+ people working around the world, from our offices and remotely, to help us achieve our mission. And we&aposre looking for more brilliant people. People who love building great products, redefining success, and turning the complexity of a chaotic world into the simplicity of a beautiful solution.
About the role
Our Technology team builds the systems and experiences that keep Revolut moving. From the infrastructure behind our innovative app to the features used by millions of people around the world, they bring sharp thinking, speed, and a focus on meaningful impact to everything they do.
We’re looking for a Python Engineer who can write high-quality code and build innovative solutions for heavily regulated financial systems.
Whether designing our own chatbot or creating automated financial crime quality controls in just a few weeks, our engineering projects are varied. You’ll collaborate on a product team with Data Scientists, Analysts, Engineers, Product Owners, and Operations Managers to deliver the most value to our customers.
Up to shape what&aposs next in finance? Let&aposs get in touch.
What you’ll be doing
- Building APIs and jobs and data pipelines, making sure they&aposre properly designed and scaled according to business needs
- Writing event consumers to build data models for new flows and processes
What you&aposll need
- 5+ years of experience as a Software Engineer
- 3+ years of experience engineering with Python as your primary language
- An academic background in STEM
- Fluency in Python, SQL, and other OOPLs
- Experience with API development and integration
- A practical understanding of distributed systems
- The ability to write concurrent code in IO/CPU bound situations
- Experience with Docker, K8s, Ansible, Teamcity, monitoring, and alerting
Nice to have
- Experience with prototyping and sketching
- Multiple side projects or open source contributions
- Exposure to GCP
How to apply: https://revolut.la/EuroPython2026_MidSeniorSoftwareEngineerPython
Python Software Foundation
Everything Security at PyCon US 2026
Phew, PyCon US 2026 is a wrap! Now it's time to share about everything security that happened in case you weren't able to attend (or you just want to reminisce). Subscribe to the PyCon US channel on YouTube so you're notified as soon as recordings for each talk are published. This blog post will also be updated with links once all talks are available.
![]() |
| Hala Ali on generating SBOMs directly from the Python runtime |
Juanita Gomez and Seth Larson were the chairs of the first talk track dedicated to security at PyCon US: Trailblazing Python Security! We're excited to share the recordings for each talk featured in the track:
- Anatomy of a Phishing Campaign by Mike Fiedler
- Zero Trust in 200ms: Implementing Identity-Per-Transaction with Python and Serverless by Tristian McKinnon
- Rust for CPython by Emma Smith
- Asleep at the Wheel: SBOMit for Python builds by Sanchit Sahay and Abhishek Reddypalle
- Post-Incident Runtime SBOM Generation from Python memory by Hala Ali
- GitHub Actions Security in Python Packages by Andrew Nesbitt
- Breaking Bad (Packages): Why Traditional Vulnerability Tracking Fails Supply Chain Attacks by Shelby Cunningham and Madison Ficorilli
Thanks so much to the speakers and volunteers who helped make this inaugural track a success. For several of the talks above the room was standing-room only! The support and interest in security topics from the Python community was incredible to see and we're hoping to see you all again next year to continue learning and sharing ideas.
| "Security isn't free!" |
Following Amanda Casari's amazing keynote, Mike Fiedler and Seth Larson took the stage to give a brief update of the past year of security work at the Python Software Foundation (PSF).
Overall 2026 was the year of more, both good and not-so-good. More packages than ever and being published to the Python Package Index (PyPI), but also more malware and specifically watering-hole attacks targeting PyPI users. The double-edged sword of being a popular and widely-used programming language also makes Python and its users a more interesting target for attackers.
The slides for this presentation are available for download via speakerdeck.
For the fourth year in a row Seth Larson hosted a security-themed Open Space at PyCon US. This year the open space was titled "Security for Open Source project maintainers" with the goal of "gather with fellow open source project maintainers to discuss current challenges with open source security".
A handful of Open Source maintainers were present to discuss security issues. The format was open-ended discussion with a few prompts to start the discussion off including vulnerability handling and CI/CD security.
Following the many watering-hole attacks on established Open Source projects involving CI/CD pipelines, hardening project CI/CD pipeline definitions was the first discussion topic. The overwhelming recommendation was to use Zizmor
with its --fix mode and a GH_TOKEN. Other tools came up such as gha-update,
pinact, Dependabot, Renovate, and using lock files like pip-compile to lock
dependencies in your CI/CD workflows. Dependency Cooldowns were also a popular concept for dependencies involved in builds and publishing.
The most recent resource published for all-in-one repository security was a blog post by William Woodruff on open source security at Astral that details CI/CD security and how to configure repositories.
The bulk of the discussion was about vulnerabilities and challenges around handling the volume of reports from reporters using LLMs. The prevailing theme is that the volume of reports has increased substantially, with anec-data being that vulnerability handling "previously was ~20% of time spent on a project" and is now "almost all" the time spent. Many reports are duplicates, verbose, extremely low quality due to the use of LLMs but the number of valid or almost-security issues has increased, too.
This "almost all" number is particularly frightening, many Open Source contributors didn't get into this line of volunteering because they wanted to work on security-related tasks.
There was some side discussion about how to judge whether handling a vulnerability in private was still a useful thing to do if the vulnerability is trivially discoverable using a publicly available LLM. The conversation referenced the Linux kernel's discussion of the same topic.
Talking about ways to mitigate the negative effects of LLMs and agents on security work lead to a discussion of security policies and threat models. Few projects, especially smaller ones, have tried this approach of documenting their threat model to see if this has a meaningful impact on the quality or quantity of reports received.
Python, Django, Node, and curl were given as good examples of threat models to copy and learn from for your own projects.
There was an issue of discoverability, some documentation is
in CONTRIBUTING.md, or on a website, but not checked into
source control for the actual project, or used an organization-wide
.github/SECURITY.md. Some projects didn't use an AGENTS.md (and
didn't want to, for fear of inviting even more LLM-driven contributions), and it was difficult to tell whether any particular documentation was
having an effect. There's also the difficulty of models changing or
becoming more capable over time. More testing is necessary here!
A separate meta-conversation through the previous topics was about having a way to signal that a particular contributor or security researcher had a high "contributor quality". The value of such a signal would tell maintainers where to focus their limited time, such as reports from someone more likely to engage with the process and follow instructions. "Talking with an LLM, indirectly" was mentioned multiple times as a negative but unfortunately common experience of maintainers interacting with first-time contributors.
gh-profiler from Eric Matthes was referenced during the discussion, and a few maintainers tested this on their own profiles and profiles of low-quality contributions they'd received recently. There was an interest in finding metrics or signals that are tougher to automate or fake. The group identified that as soon as such a signal was widely used that agents would simply "route around" the barrier.
Alpha-Omega × Python Software Foundation
Thanks to Alpha-Omega for sponsoring security at the PSF. Their support funds two roles: the Security Developer-in-Residence, held by Seth Larson, and the PyPI Safety & Security Engineer, held by Mike Fiedler. Seth and Mike delivered a joint update on their work at PyCon US 2026.
The over-arching theme of the update was the impact of higher volumes of reports, vulnerabilities, malware, and supply-chain attacks are having on the Python ecosystem along with work done to mitigate some of the hockey-stick graphs we're seeing.
Seth detailed the Python Security Response Team (PSRT) governance and process changes detailed in PEP 811. These changes aim to improve the capacity of the PSRT ahead of an increasing workload triaging and remediating security vulnerabilities reported to Python and pip.
Mike detailed work for mitigating malware and supply-chain attacks to PyPI, especially novel attacks such as the Shai-Hulud worm that targets and exploits insecure CI/CD pipelines and developer API tokens to propagate malware.
If you are interested the full set of slides is available for download via speakerdeck.
Thinking about running for the PSF Board? Let’s talk!
PSF Board elections are a chance for the community to choose representatives to help the PSF create a vision for and build the future of the Python community. This year, there are 4 seats open on the PSF Board. Check out who is currently on the PSF Board on our website. (Cheuk Ting Ho, Christopher Neugebauer, Denny Perez, and Georgi Ker are at the end of their current terms.)
Office Hours Information
This year, the PSF Board is dedicating a few of their regular Office Hour sessions on the PSF Discord to the topic of the election. This is your chance to connect with current board members to ask questions and learn more about what being a part of the Board entails.
The two upcoming Office Hour sessions will be dedicated to the topic of the election:
We welcome you to join the PSF Discord to participate in Office Hours. The server is moderated by PSF Staff and locked between office hours sessions. If you’re new to Discord, check out some Discord Basics to help you get started.
Who runs for the Board?
Who runs for the board? People who care about the Python community, who want to see it flourish and grow, and also have a few hours a month to attend regular meetings, serve on committees, participate in conversations, and promote the Python community. We're looking for candidates with a diverse range of skills and backgrounds, including leadership experience, fundraising knowledge, non-profit familiarity, and event organizing. Technical expertise, a record of collaboration, and experience speaking or teaching in the Python community are also all qualities we hope to see in Board members.
Want to learn more about being on the PSF Board? Check out the following resources to learn more about the PSF, as well as what being a part of the PSF Board entails:
- Life as Python Software Foundation Director video on YouTube
- FAQs About the PSF Board video on YouTube
- Our past few Annual Impact Reports:
You can nominate yourself or someone else. If you're nominating someone else, we'd encourage you to reach out to them first to make sure they're excited about the opportunity and give them a heads up that they'll need to submit their own nomination statement too. Nominations open on Tuesday, July 28th, 2:00 pm UTC, so you have time to talk with potential nominees, research the role, and craft a nomination statement for yourself or others. Take a look at last year’s nomination statements for reference.
Nomination information
You can nominate yourself or someone else. If you're nominating someone else, we'd encourage you to reach out to them first to make sure they're excited about the opportunity and give them a heads up that they'll need to submit their own nomination statement too. Nominations open on Tuesday, July 28th, 2:00 pm UTC, so you have time to talk with potential nominees, research the role, and craft a nomination statement for yourself or others. The nomination period ends on Tuesday, August 11th, 2:00 pm UTC. There will be a ‘call for nominations’ blog post with more information and resources about nominations coming soon.
July 01, 2026
Tryton News
Tryton News July 2026
Once again this month the community put most of its energy into fixing bugs, refining existing behaviour, and improving performance on top of our last LTS release 8.0. In addition, we are happy to present a selection of new features and documentation updates in this newsletter.
For an in depth overview of the Tryton issues please take a look at our issue tracker or see the issues and merge requests filtered by label.
Changes for the User
Sales, Purchases and Projects
We now move the warehouse and the shipping date of the sale to a different page, which keeps the sale form a little more compact.
Accounting, Invoicing and Payments
The entry for invoice payment methods is now moved under the invoice payments menu, so the menus for invoices and invoice payments are no longer mixed.
Accounts with the setting party required are now grouping their account move lines per party in the general ledger.
The lines of the general ledger account are now ordered consistently with the cumulative balance.
We update the version of Stripe used by the payment gateway to the latest one.
Stock, Production and Shipments
On the stock move form, the cost fields are now displayed more cleanly: the commission price is only shown when it is set.
User Interface
The binary and image widgets now accept a custom filters attribute, so administrators can restrict the file types users see when picking or saving a file.
When downloading a product image, the file is now suffixed with the proper .jpg extension, which makes the file easier to open from a folder.
For tall screens, the maximal height of tree views and list forms is now relative to the viewport instead of a fixed pixel value, so the available vertical space is no longer wasted.
In the domain parser, selection values are now completed using a “contains” matcher instead of “starts with”, which makes it easier to write a filter when several options share a common prefix.
Searching by record name on a contact mechanism now also matches the contact mechanism’s own name field, so users can find a phone number or e-mail by typing a label like “office” or “personal”.
New Documentation
The help text of the tax rule fields on the party is now explicit about what happens when the field is left empty.
The usage of the active_test context key in ModelSQL.search_domain is now documented in the reference manual.
The module tutorial has been updated to match the layout of the project skeleton generated by cookiecutter, so newcomers can follow it without surprises.
New Releases
We released bug fixes for the currently maintained long term support series
8.0 and 7.0, and for the penultimate series 7.8.
Changes for Implementers and Developers
The WSGI dispatcher now handles exceptions raised from within the with_pool decorator itself: unexpected exceptions are logged at exception level and their traceback is written to the WSGI wsgi.errors stream, while exceptions used as HTTP responses have their description converted to a plain string.
This text is produced by utilising minimax-m3.
2 posts - 1 participant


