skip to navigation
skip to content

Planet Python

Last update: January 14, 2026 04:46 PM UTC

January 14, 2026


Real Python

How to Create a Django Project

Learn how to create a Django project and app in clear, guided steps. Use it as a reference for any future Django project and tutorial you'll work on.

January 14, 2026 02:00 PM UTC

Quiz: How to Create a Django Project

Check your Django setup skills. Install safely and pin requirements, create a project and an app. Start building your first site.

January 14, 2026 12:00 PM UTC


Armin Ronacher

Porting MiniJinja to Go With an Agent

January 14, 2026 12:00 AM UTC

January 13, 2026


Gaël Varoquaux

Stepping up as probabl’s CSO to supercharge scikit-learn and its ecosystem

Note

Probabl’s get together, in falls 2025

I’m thrilled to announce that I’m stepping up as Probabl’s CSO (Chief Science Officer) to supercharge scikit-learn and its ecosystem, pursuing my dreams of tools that help go from data to impact.

Scikit-learn, a central tool

Scikit-learn is central …

January 13, 2026 11:00 PM UTC


PyCoder’s Weekly

Issue #717: Unit Testing Performance, Cursor, Recursive match, and More (Jan. 13, 2026)

January 13, 2026 07:30 PM UTC


Real Python

Intro to Object-Oriented Programming (OOP) in Python

Learn Python OOP fundamentals fast: master classes, objects, and constructors with hands-on lessons in this beginner-friendly video course.

January 13, 2026 02:00 PM UTC


Python Software Foundation

Anthropic invests $1.5 million in the Python Software Foundation and open source security

January 13, 2026 08:00 AM UTC


Talk Python to Me

#534: diskcache: Your secret Python perf weapon

Your cloud SSD is sitting there, bored, and it would like a job. Today we’re putting it to work with DiskCache, a simple, practical cache built on SQLite that can speed things up without spinning up Redis or extra services. Once you start to see what it can do, a universe of possibilities opens up. We're joined by Vincent Warmerdam to dive into DiskCache.

January 13, 2026 05:32 AM UTC

January 12, 2026


Real Python

Python's deque: Implement Efficient Queues and Stacks

Use a Python deque to efficiently append and pop elements from both ends of a sequence, build queues and stacks, and set maxlen for history buffers.

January 12, 2026 02:00 PM UTC


The Python Coding Stack

Need a Constant in Python? Enums Can Come in Useful

Python doesn’t have constants. But it has enums

January 12, 2026 01:33 PM UTC


Python Bytes

#465 Stack Overflow is Cooked

Topics include port-killer, How we made Python's packaging library 3x faster, and.

January 12, 2026 08:00 AM UTC


Python GUIs

What does @pyqtSlot() do? — Is the pyqtSlot decorator even necessary?

When working with Qt slots and signals in PyQt6 you will discover the @pyqtSlot decorator. This decorator is used to mark a Python function or method as a slot to which a Qt signal can be connected. However, as you can see in our signals and slots tutorials you don't have to use this. Any Python function or method can be used, normally, as a slot for a Qt signals. But elsewhere, in our threading tutorials we do use it.

January 12, 2026 06:00 AM UTC


Zato Blog

SSH API Service in Python

SSH API Service in Python

This is a quick guide on how to turn SSH commands into a REST API service. The use-case may be remote administration of devices or equipment that does not offer a REST interface or making sure that access to SSH commands is restricted to selected external REST-based API clients only.

Python

The first thing needed is code of the service that will connect to SSH servers. Below is a service doing just that - it receives name of the command to execute and host to run in on, translating stdout and stderr of SSH commands into response documents which Zato in turn serializes to JSON.

# -*- coding: utf-8 -*-

# stdlib
from traceback import format_exc

# Zato
from zato.server.service import Service

class SSHInvoker(Service):
    """ Accepts an SSH command to run on a remote host and returns its output to caller.
    """

    # A list of elements that we expect on input
    input = 'host', 'command'

    # A list of elements that our responses will contain
    output = 'is_ok', 'cid', '-stdout', '-stderr'

    def handle(self):

        # Local aliases
        host = self.request.input.host
        command = self.request.input.command

        # Correlation ID is always returned
        self.response.payload.cid = self.cid

        try:
            # Build the full command
            full_command = f'ssh {host} {command}'

            # Run the command and collect output
            output = self.commands.invoke(full_command)

            # Assign both stdout and stderr to response
            self.response.payload.stdout = output.stdout
            self.response.payload.stderr = output.stderr

        except Exception:
            # Catch any exception and log it
            self.logger.warn('Exception caught (%s), e:`%s', self.cid, format_exc())

            # Indicate an error
            self.response.payload.is_ok = False

        else:
            # Everything went fine
            self.response.payload.is_ok = True

Dashboard

In the Zato Dashboard, let's go ahead and create an HTTP Basic Auth definition that a remote API client will authenticate against:

Now, the SSH service can be mounted on a newly created REST channel - note the security definition used and that data format is set to JSON. We can skip all the other details such as caching or rate limiting, for illustration purposes, this is not needed.

Usage

At this point, everything is ready to use. We could make it accessible to external API clients but, for testing purposes, let's simply invoke our SSH API gateway service from the command line:

$ curl "api:password@localhost:11223/api/ssh" -d \
    '{"host":"localhost", "command":"uptime"}'
{
    "is_ok": true,
    "cid": "27406f29c66c2ab6296bc0c0",
    "stdout": " 09:45:42 up 37 min,  1 user,  load average: 0.14, 0.27, 0.18\n"}
$
Note that, at this stage, the service should be used in trusted environments only, e.g. it will run any command that it is given on input which means that in the next iteration it could be changed to only allow commands from an allow-list, rejecting anything that is not recognized.

And this completes it - the service is deployed and made accessible via a REST channel that can be invoked using JSON. Any command can be sent to any host and their output will be returned to API callers in JSON responses.

More resources

➤ Python API integration tutorials
What is an integration platform?
Python Integration platform as a Service (iPaaS)
What is an Enterprise Service Bus (ESB)? What is SOA?
Open-source iPaaS in Python

January 12, 2026 03:00 AM UTC


Wingware

Wing Python IDE Version 11.0.7 - January 12, 2026

Wing Python IDE version 11.0.7 has been released. It improves performance of Search in Files on some machines, fixes using stdout.writelines in unit tests run from the Testing tool, reduces CPU used by rescanning for package managers, and fixes analysis failures on incorrect # type: comments.

Wing 11 Screen Shot

Downloads

Be sure to Check for Updates in Wing's Help menu after downloading, to make sure that you have the latest hot fixes.

Wing Pro 11.0.7

Wing Personal 11.0.7

Wing 101 11.0.7

Wing 10 and earlier versions are not affected by installation of Wing 11 and may be installed and used independently. However, project files for Wing 10 and earlier are converted when opened by Wing 11 and should be saved under a new name, since Wing 11 projects cannot be opened by older versions of Wing.

New in Wing 11

Improved AI Assisted Development

Wing 11 improves the user interface for AI assisted development by introducing two separate tools AI Coder and AI Chat. AI Coder can be used to write, redesign, or extend code in the current editor. AI Chat can be used to ask about code or iterate in creating a design or new code without directly modifying the code in an editor.

Wing 11's AI assisted development features now support not just OpenAI but also Claude, Grok, Gemini, Perplexity, Mistral, Deepseek, and any other OpenAI completions API compatible AI provider.

This release also improves setting up AI request context, so that both automatically and manually selected and described context items may be paired with an AI request. AI request contexts can now be stored, optionally so they are shared by all projects, and may be used independently with different AI features.

AI requests can now also be stored in the current project or shared with all projects, and Wing comes preconfigured with a set of commonly used requests. In addition to changing code in the current editor, stored requests may create a new untitled file or run instead in AI Chat. Wing 11 also introduces options for changing code within an editor, including replacing code, commenting out code, or starting a diff/merge session to either accept or reject changes.

Wing 11 also supports using AI to generate commit messages based on the changes being committed to a revision control system.

You can now also configure multiple AI providers for easier access to different models.

For details see AI Assisted Development under Wing Manual in Wing 11's Help menu.

Package Management with uv

Wing Pro 11 adds support for the uv package manager in the New Project dialog and the Packages tool.

For details see Project Manager > Creating Projects > Creating Python Environments and Package Manager > Package Management with uv under Wing Manual in Wing 11's Help menu.

Improved Python Code Analysis

Wing 11 makes substantial improvements to Python code analysis, with better support for literals such as dicts and sets, parametrized type aliases, typing.Self, type of variables on the def or class line that declares them, generic classes with [...], __all__ in *.pyi files, subscripts in typing.Type and similar, type aliases, type hints in strings, type[...] and tuple[...], @functools.cached_property, base classes found also in .pyi files, and typing.Literal[...].

Updated Localizations

Wing 11 updates the German, French, and Russian localizations, and introduces a new experimental AI-generated Spanish localization. The Spanish localization and the new AI-generated strings in the French and Russian localizations may be accessed with the new User Interface > Include AI Translated Strings preference.

Improved diff/merge

Wing Pro 11 adds floating buttons directly between the editors to make navigating differences and merging easier, allows undoing previously merged changes, and does a better job managing scratch buffers, scroll locking, and sizing of merged ranges.

For details see Difference and Merge under Wing Manual in Wing 11's Help menu.

Other Minor Features and Improvements

Wing 11 also adds support for Python 3.14, improves the custom key binding assignment user interface, adds a Files > Auto-Save Files When Wing Loses Focus preference, warns immediately when opening a project with an invalid Python Executable configuration, allows clearing recent menus, expands the set of available special environment variables for project configuration, and makes a number of other bug fixes and usability improvements.

Changes and Incompatibilities

Since Wing 11 replaced the AI tool with AI Coder and AI Chat, and AI configuration is completely different than in Wing 10, you will need to reconfigure your AI integration manually in Wing 11. This is done with Manage AI Providers in the AI menu. After adding the first provider configuration, Wing will set that provider as the default. You can switch between providers with Switch to Provider in the AI menu.

If you have questions, please don't hesitate to contact us at support@wingware.com.

January 12, 2026 01:00 AM UTC

January 10, 2026


EuroPython

Humans of EuroPython: Jakub Červinka

EuroPython wouldn’t exist if it weren’t for all the volunteers who put in countless hours to organize it. Whether it’s contracting the venue, selecting and confirming talks & workshops or coordinating with speakers, hundreds of hours of loving work have been put into making

January 10, 2026 09:49 AM UTC

January 09, 2026


Mike Driscoll

How to Switch to ty from Mypy

Python has supported type hinting for quite a few versions now, starting way back in 3.5. However, Python itself does not enforce type checking. Instead, you need to use an external tool or IDE. The first and arguably most popular is mypy. Microsoft also has a Python type checker that you can use in VS Code […]

The post How to Switch to ty from Mypy appeared first on Mouse Vs Python.

January 09, 2026 03:16 PM UTC


The Python Coding Stack

Parkruns, Python’s enumerate and zip, and Why Python Loops Are Different from Other Languages • [Club]

Don’t forget about enumerate() and zip() when coding in Python • A short post

January 09, 2026 01:57 PM UTC


Real Python

The Real Python Podcast – Episode #279: Coding Python With Confidence: Beginners Live Course Participants

Are you looking for that solid foundation to begin your Python journey? Would the accountability of scheduled group classes help you get through the basics and start building something? This week, two members of the Python for Beginners live course discuss their experiences.

January 09, 2026 12:00 PM UTC

January 08, 2026


Rodrigo Girão Serrão

Recursive structural pattern matching

Learn how to use structural pattern matching (the match statement) to work recursively through tree-like structures.

In this short article you will learn to use structural pattern matching in recursive, tree-like data structures.

The examples from this article are taken from a couple of recent issues of my weekly newsletter.

A recursive data structure

Structural pattern matching excels at... matching the structure of your objects! For the two examples in this article, we'll be using a number of dataclasses that you can use to build abstract Boolean expressions:

from dataclasses import dataclass

class Expr:
    pass

@dataclass
class And(Expr):
    exprs: list[Expr]

@dataclass
class Or(Expr):
    exprs: list[Expr]

@dataclass
class Not(Expr):
    expr: Expr

@dataclass
class Var(Expr):
    name: str

For example, the code Not(And([Var("A"), Var("B")])) represents the Boolean expression not (A and B).

Evaluating a Boolean expression

Suppose you have a Boolean expression built out of the components shared above. How do you evaluate that formula if you are given the assignments that map the variables to their values?

For example, if you have the assignments {"A": True, "B": False} (for example, a dictionary that maps variable names to values), how can you determine that the expression Not(And([Var("A"), Var("B")])) is True?

This is where structural pattern matching can be applied recursively and it's where it really shines!

To solve this problem, you will write a function called evaluate(expression: Expr, assignments: dict[str, bool]) -> bool. Your function accepts an expression and the assignments in the form of a dictionary and it returns the final Boolean value the expression evaluates to.

Since you're accepting an expression, you're going to use the match statement on the full expression and then create a case branch for each of the possible expressions you might have:

  1. a variable;
  2. an And expression;
  3. an Or expression; or
  4. a Not expression.

The structure of the code looks like this:

def evaluate(expression: Expr, assignments: dict[str, bool]) -> bool:
    match expression:
        case Var(): pass
        case And(): pass
        case Or(): pass
        case Not(): pass

The trick here is realising that you're using Expr as the type of the argument but really, you always expect the argument to be an instance of one of the subclasses of Expr, and not a direct Expr instance.

However, to make sure you don't trip on a weird bug later on, and because this matching is supposed to be exhaustive – you're supposed to have one case for each subclass of Expr – you can defend yourself by including a catch-all pattern that raises an error.

When I'm being lazy, I just raise a RuntimeError:

def evaluate(expression: Expr, assignments: dict[str, bool]) -> bool:
    match expression:
        case Var(): pass
        case And(): pass
        case Or(): pass
        case Not(): pass
        case _:
            raise RuntimeError(
                f"Couldn't evaluate expression of type {type(expression)}."
            )

Now, it's just a matter of implementing the evaluation logic. In the case of a variable, all you have to do is fetch the variable value from the corresponding dictionary. However, to make it more convenient to...

January 08, 2026 03:22 PM UTC


Stéphane Wirtel

Automating TLS Certificate Monitoring with GitHub Actions, certificate_watcher, and Slack

Introduction

As a consultant constantly working with clients, I found myself in a familiar predicament: my head was always down, focused on delivering value to customers, but my own infrastructure monitoring was non-existent. I had no simple way to track SSL/TLS certificate expirations across the multiple domains I managed - personal sites, client projects, and community services.

I needed a solution, but I had several constraints:

  1. No time for complex setup: I couldn’t afford to spend days installing, configuring, and deploying yet another monitoring service
  2. Easy maintenance: Whatever I built had to be low-maintenance - I didn’t want another system to babysit
  3. Transparency and control: I wanted a simple text file in Git listing the hosts to monitor, so I could see exactly what was being checked and track changes over time
  4. Zero infrastructure: No servers to provision, patch, or pay for

Around this time, a friend named Julien shared his project called certificate_watcher, a lightweight Python tool for checking SSL certificate expiration. I contributed a few patches (if memory serves), and it clicked: what if I could combine this with GitHub Actions and Slack notifications?

January 08, 2026 12:00 AM UTC

January 07, 2026


Real Python

How to Build a Personal Python Learning Roadmap

Learn how to create a personalized Python learning roadmap. Set goals, choose resources, and build a plan to track your progress and stay motivated.

January 07, 2026 02:00 PM UTC


Stéphane Wirtel

dsmtpd 1.2.0: Test Your Emails Risk-Free

The Test Email That Never Should Have Been Sent

You know that feeling? You’re developing a new email feature, you run your test script, and boom — you realize 3 seconds too late that you used the production database. Your CEO just received an email with the subject “TEST - DO NOT READ - LOREM IPSUM”.

Or worse: you configured a cloud SMTP server for testing, forgot to disable actual sending, and now your Mailgun account is suspended for “suspicious activity” because you sent 847 emails to test@example.com in 5 minutes.

January 07, 2026 12:00 AM UTC


Python⇒Speed

Unit testing your code's performance, part 1: Big-O scaling

January 07, 2026 12:00 AM UTC

January 06, 2026


PyCoder’s Weekly

Issue #716: Performance Numbers, async Web Apps, uv Speed, and More (Jan. 6, 2026)

January 06, 2026 07:30 PM UTC


Django Weblog

Django bugfix releases issued: 5.2.10, 6.0.1

Today we've issued the 5.2.10 and 6.0.1 bugfix releases.

The release packages and checksums are available from our downloads page, as well as from the Python Package Index.

The PGP key ID used for these releases is Jacob Walls: 131403F4D16D8DC7

January 06, 2026 06:00 PM UTC