04 Feb 2025

feedPlanet Python

Łukasz Langa: Generating visual art every day in January

There's a small but dedicated community of crazy artists that gather every January to generate stunning visual art for no apparent reason other than Genuary being a clever pun on January. I joined them this year to get better at PyScript and WebGL. Here's how it went.

04 Feb 2025 10:20pm GMT

feedDjango community aggregator: Community blog posts

Built with Django Newsletter - 2025 Week 6

Hey, Happy Thursday!

Why are you getting this: *You signed up to receive this newsletter on Built with Django. I promised to send you the latest projects and jobs on the site as well as any other interesting Django content I encountered during the month. If you don't want to receive this newsletter, feel free to unsubscribe anytime.

News and Updates

Sponsors

This issue is sponsored by SEO Blog Bot. Well, sponsor is a strong word. It is just another project of mine that I wanted to share with you 🙈. Fully free!

If you have a side project and are struggling with finding ideas for blog posts, this will help!

If you want to become a real sponsor, just reply to this email or check out available options here.


Projects


Jobs


From the Community


Top 3 links from last Issue


Support

You can support this project by using one of the affiliate links below. These are always going to be projects I use and love! No "Bluehost" crap here!

04 Feb 2025 8:30pm GMT

feedPlanet Python

PyCoder’s Weekly: Issue #667: String Templates, Missing Data, Dynamic Forms, and More (Feb. 4, 2025)

#667 - FEBRUARY 4, 2025
View in Browser »

The PyCoder’s Weekly Logo


A Revamped Python String-Formatting Proposal

PEP 750 proposes template strings, and the PEP has been through a lot of modifications since it was originally introduced. This article covers the latest and why the changes have been made to the proposal.
JAKE EDGE

How to Deal With Missing Data in Polars

In this tutorial, you'll learn how to deal with missing data in Polars to ensure it doesn't interfere with your data analysis. You'll discover how to check for missing values, update them, and remove them.
REAL PYTHON

Build AI Agents in Just Hours not Months

alt

Design and deploy multi-agent systems at zero cost using our free plan, no credit card required. Skip complex configurations with pre-integrated setups for top LLMs. Speed up development with pre-built agent templates, and unlock advanced workflow functionality using our open-source Python package →
DYNAMIQ sponsor

Dynamic Forms With Flask

This post shows you how to create dynamic web forms, where fields are added on the fly, when coding within the Flask web framework.
MIGUEL GRINBERG

Quiz: How to Deal With Missing Data in Polars

REAL PYTHON

PyOhio 2025 July 26-27, 2025 Announced

PYOHIO.ORG

Python Jobs

Backend Software Engineer (Anywhere)

Brilliant.org

More Python Jobs >>>

Articles & Tutorials

Python Rgonomics: 2025 Update

"Switching languages is about switching mindsets - not just syntax. New developments in Python data science toolings, like polars and seaborn's object interface, can capture the 'feel' that converts from R/tidyverse love while opening the door to truly pythonic workflows. (Updated from 2025 for new tools)."
EMILY RIEDERER

Portable Python Bundles on Windows

Packaging and distributing a Python environment on MS Windows can be tricky. What if neither venvs nor tools like PyInstaller meet your needs? Here's one take on it: Portable Python Bundles on Windows. Bundles are like venvs - but are self-contained, path independent and "just work" on any Windows machine.
DEV.TO • Shared by Chris Korneck

Learn How to Build GenAI Projects

Join a FREE 6-week virtual bootcamp and get hands-on experience building GenAI projects. It's open to all skill levels with live, instructor-led classes guiding you every step of the way. Spots are filling fast-register today!
INTEL CORPORATION sponsor

3D Printing Giant Things With a Python Jigsaw Generator

This is a long, detailed article on 3D printing objects too large for the printer bed. The author has created dovetail joints to assemble pieces together. He wrote a Python program to automatically split up the larger model files into the jigsaw pieces needed to build a final result.
CAL BRYANT

Creating a Scalable Flask Web Application From Scratch

In this video course, you'll explore the process of creating a boilerplate for a Flask web project. It's a great starting point for any scalable Flask web app that you wish to develop in the future, from basic web pages to complex web applications.
REAL PYTHON course

My First Steps With Playwright

Playwright is a browser based automation tool that can be used for web scraping or testing. This intro article shows you how to use the Python interface to access a page including using cookies.
NICOLAS FRÄNKEL

Looking at Django Task Runners and Queues

There are a lot of different ways of running asynchronous tasks in Django. This article talks about two mechanisms used by the author, as well as the extra challenge of one-off jobs.
KEVIN RENSKERS

PyPI Now Supports Project Archival

The ability to mark a project as archived has been added to the Python Package Index. This article covers what archival can be used for as well as hinting at future improvements.
FACUNDO TUESCA

Re-Creating Async in 10 Lines Using Generators

This article outlines a simple re-implementation of the concepts available in the async library so you can better learn how it works under the covers.
LUCAS SEIKI OSHIRO

Building Cython (Or C) Extensions Using uv

Developing Python libraries with C extensions can be tricky. Learn how uv and setuptools can work together to build Cython-powered projects.
SIDDHANT GOEL

Projects & Code

jmespath.py: Query Language for JSON

GITHUB.COM/JMESPATH

pycparser: Complete C99 Parser in Pure Python

GITHUB.COM/ELIBEN

pyfi: Convert Floating Point to/From Fixed Point

GITHUB.COM/CESARPIRESSEVERO

sphinx-testify: Testified Documentation Plugin for Sphinx

GITHUB.COM/BASICWOLF

Nyxelf: Analyze Malicious Linux ELF Binaries

GITHUB.COM/M3RCURYLAKE

Events

Weekly Real Python Office Hours Q&A (Virtual)

February 5, 2025
REALPYTHON.COM

Fine-Grained Authorization in Python (Webinar)

February 6, 2025
OSO

Canberra Python Meetup

February 6, 2025
MEETUP.COM

Sydney Python User Group (SyPy)

February 6, 2025
SYPY.ORG

PyCascades 2025

February 8 to February 10, 2025
PYCASCADES.COM

PyDelhi User Group Meetup

February 8, 2025
MEETUP.COM

DFW Pythoneers 2nd Saturday Teaching Meeting

February 8, 2025
MEETUP.COM


Happy Pythoning!
This was PyCoder's Weekly Issue #667.
View in Browser »

alt


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

04 Feb 2025 7:30pm GMT

Hugo van Kemenade: How to delay a Python release

Prologue #

This was a Twitter thread from 15th January 2022 about my first CPython bug. Eight days from report to fix to merge, not bad!


Delay #

I helped delay the release of Python 3.11.0a4! But in a good way! 😇

Python 3.11 is due out in October, but they make early alpha, beta and release candidates available for people to help test Python itself and their own code before the big release.

So I tested Pillow

Release manager Pablo tweeting: Yeah, no kidding! 😵‍💫 3.11.0a4 had a ridiculous amount of release blockers 🧐 we needed to solve and while everything was on fire we discovered something that made us make an early 3.10.2 release 🤯(you should upgrade: read the release notes for more info⚠️)! What a week 😅

Tests #

The Pillow test suite passed with 3.11 ✅

Next I tried building the documentation with 3.11 ❌

The docs program, Sphinx, emitted a couple of warnings. Warnings are often missed because they don't error. But luckily we use the "-W" option to turn warnings into hard errors.

Sphinx output showing “WARNING: image file not readable” Sphinx output turning the warning into an error

Sphinx #

Maybe Sphinx isn't ready for Python 3.11?

Rather than submitting a report with the full Pillow documentation (lots of files) I made a new, "minimal" example with just enough stuff to reproduce it.

This makes it easier to investigate what's up.

Report 1 #

I reported this to Sphinx. The problem was that a page in a subdirectory could load an image from one directory, but not from another, further away directory.

It occurs for the Python 3.11.0a3 alpha, but not 3.7-3.10.

A screenshot of a GitHub bug report

CPython #

A few hours later the Sphinx maintainer Takeshi said it looks like a change to a part of Python itself - os.path.normpath() - since 3.11.0a3, and as it wasn't mentioned on the "What's New in Python 3.11" page it could be a bug in Python.

He asked me to report it to Python.

Screenshot of Takeshi’s reply

Report 2 #

I reported it to Python with Takeshi's even more minimal example.

Half an hour later Christian pointed out a change which may have caused this.

I tested and confirmed.

The next day Steve confirmed it was a bug and set it as a "release blocker".

Fix #

Steve also said it will needs tests, because this bug slipped out due to a gap in testing.

I didn't know how to fix the bug, but I could write some test cases!

neonene then took the tests and fixed the bug! In doing so they found even more bugs!

Merge #

These extra bugs also existed in earlier versions.

But it turns out path handling can get pretty complicated in places, so Steve decided to only fix my bug now to get it released and the others can be sorted later.

The fix was merged and I confirmed it also worked with Sphinx ✅

Conclusion #

And that's about it!

It's now fixed in 3.11.0a4; much better to find these before 3.11.0 final is released to the world in October. Along the way we found more issues to address.

Short version: test your code with 3.11 now, you may find issues in your code or in Python itself 🚀


Epilogue #

Back to 2025: Please test and delay Python 3.14 alpha - but in a good way! 😇

04 Feb 2025 6:55pm GMT

31 Jan 2025

feedDjango community aggregator: Community blog posts

Django News - 400 DSF Members - Jan 31st 2025

News

PyPI Now Supports Project Archival

Projects on PyPI can now be marked as archived.

pypi.org

Django Software Foundation

400 individual members of the Django Software Foundation

The Django Software Foundation reached 400 individual members.

thib.me

Updates to Django

Today 'Updates to Django' is presented by Abigail Afi Gbadago from Djangonaut Space!

Last week we had 21 pull requests merged into Django by 12 different contributors - including 2 first-time contributors! Congratulations to Gregory Mariani and Igor Scheller for having their first commits merged into Django - welcome on board!🎊

Highlights of the changes made:

Django Newsletter

Wagtail CMS

Our updated Accessibility Conformance Report

Wagtail has released a new Accessibility Conformance Report for version 6.3, showcasing improvements across WCAG 2.2 criteria, detailing compliance in the CMS, documentation, and websites, and inviting feedback for further accessibility enhancements.

wagtail.org

Sponsored Link 1

Hiring Jr. Web Services Engineer

This position is for someone who can bring their python software development experience to support Playdate, Game Publishing, and our Apps! You would be responsible for contributing to and maintaining our growing flock of Flask and Django web applications.

Additionally, Panic hires interns every summer via The Script (https://thescript.org/internship-program/), designed for college students with underrepresented backgrounds entering freshman through senior years.

panic.com

Articles

Database Indexing in Django

Explore the fundamentals of database indexing in Django-covering types, advantages, and practical examples-to help you significantly improve query performance and scalability in your projects.

testdriven.io

An Introduction to Django Views

An overview of Django views, covering function-based vs. class-based approaches, best practices, and practical PyCharm tips for streamlined web development.

jetbrains.com

Django 5.2 simple_block_tag with HTMX

Django 5.2 introduces the new simple_block_tag(), demonstrated here with HTMX to validate and test custom attributes for more robust and maintainable components.

better-simple.com

Avoiding Mocks: Testing LLM Applications with LangChain in Django

A practical method for testing Django-based LLM apps with LangChain uses a custom fake backend to avoid mocks, enabling flexible refactoring and thorough validation of model interactions.

lincolnloop.com

Add headings to HTML landmarks

Use visually hidden headings to make your website easier to navigate.

marijkeluttekes.dev

Rendering form fields as group in Django

A welcome addition in Django 5.0, the concept of field groups makes a lot easier to customize the layout of our Django forms.

valentinog.com

Looking at Django task runners and queues

A look at different ways to combine scheduled and one-off background jobs in Django-comparing django-apscheduler, django-tasks, django-q2, Celery, and cron-to handle tasks like sending email and long-running processes efficiently.

loopwerk.io

Events

PyOhio 2025

PyOhio is a free annual Python community conference based in Ohio this July 26-27, 2025.

pyohio.org

Tutorials

How to Use GraphQL in Django with Elasticsearch

A guide to combining Graphene-Django with Django Elasticsearch DSL to create high-performance GraphQL endpoints, complete with advanced search and filtering via Elasticsearch.

djangotricks.com

Sponsored Link 2

Django & Wagtail Hosting for just $9/month

New year, new site! Finally put those domain names to use and launch that side-project, blog, or personal site in 2025. Lock in this limited time pricing by Feb 28.

codered.cloud

Podcasts

Real Python #236: Simon Willison: Using LLMs for Python Development

Simon talks about his LLM research and explores ways of writing Python code with these rapidly evolving tools.

realpython.com

Django News Jobs

Jr. Web Services Engineer at Panic 🆕 (featured)

Senior Backend Engineer at BactoBio 🆕

Software Engineer I at Spark

Senior Software Engineer at Spark

Engineering Manager at Spark

Front-end developer at cassandra.app

Lead Django Developer at cassandra.app

Développeur(se) back-end en CDI at Brief Media

Django Newsletter

Projects

drf-forms/drf-schema-adapter

Making using Django with frontend libraries and frameworks DRYer.

github.com

nanuxbe/django-classy-doc

django-classy-doc brings Classy Class-Based Views-style docs to your own code.

github.com


This RSS feed is published on https://django-news.com/. You can also subscribe via email.

31 Jan 2025 5:00pm GMT

Finishing Simple Signup - Building SaaS #213

In this episode, I completed the simplified sign up process for my JourneyInbox app. I finished off the final features that add account verification and initial engagement features to make sign up and nice and functional experience.

31 Jan 2025 6:00am GMT

15 Jan 2025

feedPlanet Twisted

Glyph Lefkowitz: Small PINPal Update

Today on stream, I updated PINPal to fix the memorization algorithm.

If you haven't heard of PINPal before, it is a vault password memorization tool. For more detail on what that means, you can check it out the README, and why not give it a ⭐ while you're at it.


As I started writing up an update post I realized that I wanted to contextualize it a bit more, because it's a tool I really wish were more popular. It solves one of those small security problems that you can mostly ignore, right up until the point where it's a huge problem and it's too late to do anything about it.

In brief, PINPal helps you memorize new secure passcodes for things you actually have to remember and can't simply put into your password manager, like the password to your password manager, your PC user account login, your email account1, or the PIN code to your phone or debit card.

Too often, even if you're properly using a good password manager for your passwords, you'll be protecting it with a password optimized for memorability, which is to say, one that isn't random and thus isn't secure. But I have also seen folks veer too far in the other direction, trying to make a really secure password that they then forget right after switching to a password manager. Forgetting your vault password can also be a really big deal, making you do password resets across every app you've loaded into it so far, so having an opportunity to practice it periodically is important.

PINPal uses spaced repetition to ensure that you remember the codes it generates.

While periodic forced password resets are a bad idea, if (and only if!) you can actually remember the new password, it is a good idea to get rid of old passwords eventually - like, let's say, when you get a new computer or phone. Doing so reduces the risk that a password stored somewhere on a very old hard drive or darkweb data dump is still floating around out there, forever haunting your current security posture. If you do a reset every 2 years or so, you know you've never got more than 2 years of history to worry about.

PINPal is also particularly secure in the way it incrementally generates your password; the computer you install it on only ever stores the entire password in memory when you type it in. It stores even the partial fragments that you are in the process of memorizing using the secure keyring module, avoiding plain-text whenever possible.


I've been using PINPal to generate and memorize new codes for a while, just in case2, and the change I made today was because encountered a recurring problem. The problem was, I'd forget a token after it had been hidden, and there was never any going back. The moment that a token was hidden from the user, it was removed from storage, so you could never get a reminder. While I've successfully memorized about 10 different passwords with it so far, I've had to delete 3 or 4.

So, in the updated algorithm, the visual presentation now hides tokens in the prompt several memorizations before they're removed. Previously, if the password you were generating was 'hello world', you'd see hello world 5 times or so, times, then •••• world; if you ever got it wrong past that point, too bad, start over. Now, you'll see hello world, then °°°° world, then after you have gotten the prompt right without seeing the token a few times, you'll see •••• world after the backend has locked it in and it's properly erased from your computer.

If you get the prompt wrong, breaking your streak reveals the recently-hidden token until you get it right again. I also did a new release on that same livestream, so if this update sounds like it might make the memorization process more appealing, check it out via pip install pinpal today.

Right now this tool is still only extremely for a specific type of nerd - it's command-line only, and you probably need to hand-customize your shell prompt to invoke it periodically. But I'm working on making it more accessible to a broader audience. It's open source, of course, so you can feel free to contribute your own code!

Acknowledgments

Thank you to my patrons who are supporting my writing on this blog. If you like what you've read here and you'd like to read more things like it, or you'd like to support my various open-source endeavors, you can support my work as a sponsor!


  1. Your email account password can be stored in your password manager, of course, but given that email is the root-of-trust reset factor for so many things, being able to remember that password is very helpful in certain situations.

  2. Funny story: at one point, Apple had an outage which made it briefly appear as if a lot of people needed to reset their iCloud passwords, myself included. Because I'd been testing PINPal a bunch, I actually had several highly secure random passwords already memorized. It was a strange feeling to just respond to the scary password reset prompt with a new, highly secure password and just continue on with my day secure in the knowledge I wouldn't forget it.

15 Jan 2025 12:54am GMT

10 Jan 2025

feedPlanet Twisted

Glyph Lefkowitz: The “Active Enum” Pattern

Have you ever written some Python code that looks like this?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from enum import Enum, auto

class SomeNumber(Enum):
    one = auto()
    two = auto()
    three = auto()

def behavior(number: SomeNumber) -> int:
    match number:
        case SomeNumber.one:
            print("one!")
            return 1
        case SomeNumber.two:
            print("two!")
            return 2
        case SomeNumber.three:
            print("three!")
            return 3

That is to say, have you written code that:

  1. defined an enum with several members
  2. associated custom behavior, or custom values, with each member of that enum,
  3. needed one or more match / case statements (or, if you've been programming in Python for more than a few weeks, probably a big if/elif/elif/else tree) to do that association?

In this post, I'd like to submit that this is an antipattern; let's call it the "passive enum" antipattern.

For those of you having a generally positive experience organizing your discrete values with enums, it may seem odd to call this an "antipattern", so let me first make something clear: the path to a passive enum is going in the correct direction.

Typically - particularly in legacy code that predates Python 3.4 - one begins with a value that is a bare int constant, or maybe a str with some associated values sitting beside in a few global dicts.

Starting from there, collecting all of your values into an enum at all is a great first step. Having an explicit listing of all valid values and verifying against them is great.

But, it is a mistake to stop there. There are problems with passive enums, too:

  1. The behavior can be defined somewhere far away from the data, making it difficult to:
    1. maintain an inventory of everywhere it's used,
    2. update all the consumers of the data when the list of enum values changes, and
    3. learn about the different usages as a consumer of the API
  2. Logic may be defined procedurally (via if/elif or match) or declaratively (via e.g. a dict whose keys are your enum and whose values are the required associated value).
    1. If it's defined procedurally, it can be difficult to build tools to interrogate it, because you need to parse the AST of your Python program. So it can be difficult to build interactive tools that look at the associated data without just calling the relevant functions.
    2. If it's defined declaratively, it can be difficult for existing tools that do know how to interrogate ASTs (mypy, flake8, Pyright, ruff, et. al.) to make meaningful assertions about it. Does your linter know how to check that a dict whose keys should be every value of your enum is complete?

To refactor this, I would propose a further step towards organizing one's enum-oriented code: the active enum.

An active enum is one which contains all the logic associated with the first-party provider of the enum itself.

You may recognize this as a more generalized restatement of the object-oriented lens on the principle of "separation of concerns". The responsibilities of a class ought to be implemented as methods on that class, so that you can send messages to that class via method calls, and it's up to the class internally to implement things. Enums are no different.

More specifically, you might notice it as a riff on the Active Nothing pattern described in this excellent talk by Sandi Metz, and, yeah, it's the same thing.

The first refactoring that we can make is, thus, to mechanically move the method from an external function living anywhere, to a method on SomeNumber . At least like this, we present an API to consumers externally that shows that SomeNumber has a behavior method that can be invoked.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from enum import Enum, auto

class SomeNumber(Enum):
    one = auto()
    two = auto()
    three = auto()

    def behavior(self) -> int:
        match self:
            case SomeNumber.one:
                print("one!")
                return 1
            case SomeNumber.two:
                print("two!")
                return 2
            case SomeNumber.three:
                print("three!")
                return 3

However, this still leaves us with a match statement that repeats all the values that we just defined, with no particular guarantee of completeness. To continue the refactoring, what we can do is change the value of the enum itself into a simple dataclass to structurally, by definition, contain all the fields we need:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from dataclasses import dataclass
from enum import Enum
from typing import Callable

@dataclass(frozen=True)
class NumberValue:
    result: int
    effect: Callable[[], None]

class SomeNumber(Enum):
    one = NumberValue(1, lambda: print("one!"))
    two = NumberValue(2, lambda: print("two!"))
    three = NumberValue(3, lambda: print("three!"))

    def behavior(self) -> int:
        self.value.effect()
        return self.value.result

Here, we give SomeNumber members a value of NumberValue, a dataclass that requires a result: int and an effect: Callable to be constructed. Mypy will properly notice that if x is a SomeNumber, that x will have the type NumberValue and we will get proper type checking on its result (a static value) and effect (some associated behaviors)1.

Note that the implementation of behavior method - still conveniently discoverable for callers, and with its signature unchanged - is now vastly simpler.

But what about...

Lookups?

You may be noticing that I have hand-waved over something important to many enum users, which is to say, by-value lookup. enum.auto will have generated int values for one, two, and three already, and by transforming those into NumberValue instances, I can no longer do SomeNumber(1).

For the simple, string-enum case, one where you might do class MyEnum: value = "value" so that you can do name lookups via MyEnum("value"), there's a simple solution: use square brackets instead of round ones. In this case, with no matching strings in sight, SomeNumber["one"] still works.

But, if we want to do integer lookups with our dataclass version here, there's a simple one-liner that will get them back for you; and, moreover, will let you do lookups on whatever attribute you want:

1
by_result = {each.value.result: each for each in SomeNumber}

enum.Flag?

You can do this with Flag more or less unchanged, but in the same way that you can't expect all your list[T] behaviors to be defined on T, the lack of a 1-to-1 correspondence between Flag instances and their values makes it more complex and out of scope for this pattern specifically.

3rd-party usage?

Sometimes an enum is defined in library L and used in application A, where L provides the data and A provides the behavior. If this is the case, then some amount of version shear is unavoidable; this is a situation where the data and behavior have different vendors, and this means that other means of abstraction are required to keep them in sync. Object-oriented modeling methods are for consolidating the responsibility for maintenance within a single vendor's scope of responsibility. Once you're not responsible for the entire model, you can't do the modeling over all of it, and that is perfectly normal and to be expected.

The goal of the Active Enum pattern is to avoid creating the additional complexity of that shear when it does not serve a purpose, not to ban it entirely.

A Case Study

I was inspired to make this post by a recent refactoring I did from a more obscure and magical2 version of this pattern into the version that I am presenting here, but if I am going to call passive enums an "antipattern" I feel like it behooves me to point at an example outside of my own solo work.

So, for a more realistic example, let's consider a package that all Python developers will recognize from their day-to-day work, python-hearthstone, the Python library for parsing the data files associated with Blizzard's popular computerized collectible card game Hearthstone.

As I'm sure you already know, there are a lot of enums in this library, but for one small case study, let's look a few of the methods in hearthstone.enums.GameType.

GameType has already taken the "step 1" in the direction of an active enum, as I described above: as_bnet is an instancemethod on GameType itself, making it at least easy to see by looking at the class definition what operations it supports. However, in the implementation of that method (among many others) we can see the worst of both worlds:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class GameType(IntEnum):
    def as_bnet(self, format: FormatType = FormatType.FT_STANDARD):
        if self == GameType.GT_RANKED:
            if format == FormatType.FT_WILD:
                return BnetGameType.BGT_RANKED_WILD
            elif format == FormatType.FT_STANDARD:
                return BnetGameType.BGT_RANKED_STANDARD
            # ...
            else:
                raise ValueError()
        # ...
        return {
            GameType.GT_UNKNOWN: BnetGameType.BGT_UNKNOWN,
            # ...
            GameType.GT_BATTLEGROUNDS_DUO_FRIENDLY: BnetGameType.BGT_BATTLEGROUNDS_DUO_FRIENDLY,
        }[self]

We have procedural code mixed with a data lookup table; raise ValueError mixed together with value returns. Overall, it looks like this might be hard to maintain this going forward, or to see what's going on without a comprehensive understanding of the game being modeled. Of course for most python programmers that understanding can be assumed, but, still.

If GameType were refactored in the manner above3, you'd be able to look at the member definition for GT_RANKED and see a mapping of FormatType to BnetGameType, or GT_BATTLEGROUNDS_DUO_FRIENDLY to see an unconditional value of BGT_BATTLEGROUNDS_DUO_FRIENDLY. Given that this enum has 40 elements, with several renamed or removed, it seems reasonable to expect that more will be added and removed as the game is developed.

Conclusion

If you have large enums that change over time, consider placing the responsibility for the behavior of the values alongside the values directly, and any logic for processing the values as methods of the enum. This will allow you to quickly validate that you have full coverage of any data that is required among all the different members of the enum, and it will allow API clients a convenient surface to discover the capabilities associated with that enum.

Acknowledgments

Thank you to my patrons who are supporting my writing on this blog. If you like what you've read here and you'd like to read more of it, or you'd like to support my various open-source endeavors, you can support my work as a sponsor!


  1. You can get even fancier than this, defining a typing.Protocol as your enum's value, but it's best to keep things simple and use a very simple dataclass container if you can.

  2. derogatory

  3. I did not submit such a refactoring as a PR before writing this post because I don't have full context for this library and I do not want to harass the maintainers or burden them with extra changes just to make a rhetorical point. If you do want to try that yourself, please file a bug first and clearly explain how you think it would benefit their project's maintainability, and make sure that such a PR would be welcome.

10 Jan 2025 10:37pm GMT

16 Dec 2024

feedPlanet Twisted

Glyph Lefkowitz: DANGIT

Over the last decade, it has become a common experience to be using a social media app, and to perceive that app as saying something specific to you. This manifests in statements like "Twitter thinks Rudy Giuliani has lost his mind", "Facebook is up in arms about DEI", "Instagram is going crazy for this new water bottle", "BlueSky loves this bigoted substack", or "Mastodon can't stop talking about Linux". Sometimes this will even be expressed with "the Internet" as a metonym for the speaker's preferred social media: "the Internet thinks that Kate Middleton is missing".

However, even the smallest of these networks comprises literal millions of human beings, speaking dozens of different languages, many of whom never interact with each other at all. The hot takes that you see from a certain excitable sub-community, on your particular timeline or "for you" page, are not necessarily representative of "the Internet" - at this point, a group that represents a significant majority of the entire human population.

If I may coin a phrase, I will refer to these as "Diffuse, Amorphous, Nebulous, Generalized Internet Takes", or DANGITs, which handily evokes the frustrating feeling of arguing against them.

A DANGIT is not really a new "internet" phenomenon: it is a specific expression of the availability heuristic.

If we look at our device and see a bunch of comments in our inbox, particularly if those comments have high salience via being recent, emotive, and repeated, we will naturally think that this is what The Internet thinks. However, just because we will naturally think this does not mean that we will accurately think it.

It is worth keeping this concept in mind when participating in public discourse because it leads to a specific type of communication breakdown. If you are arguing with a DANGIT, you will feel like you are arguing with someone with incredibly inconsistent, hypocritical, and sometimes even totally self-contradictory views. But to be self-contradictory, one needs to have a self. And if you are arguing with 9 different people from 3 different ideological factions, all making completely different points and not even taking time to agree on the facts beforehand, of course it's going to sound like cacophonous nonsense. You're arguing with the cacophony, it's just presented to you in a way that deceives you into thinking that it's one group.

There are subtle variations on this breakdown; for example, it can also make people's taste seem incoherent. If it seems like one week the Interior Designer internet loves stark Scandinavian minimalism, and the next week baroque Rococo styles are making a comeback, it might seem like The Internet has no coherent sense of taste, and these things don't go together. That's because it doesn't! Why would you expect it to?

Most likely, you are simply seeing some posts from minimalists, and then, separately, some posts from Rococo aficionados. Any particular person's feed may be dedicated to a specific, internally coherent viewpoint, aesthetic, or ideology, but if you dump them all into a blender to separate them from their context, of course they will look jumbled together.

This is what social media does. It is context collapse as a service. Even if you eliminate engagement-maximizing algorithms and view everything perfectly chronologically, even if you have the world's best trust & safety team making sure that there is nothing harmful and no disinformation, social media - like email - inherently remains that context-collapsing blender. There's no way for it not to be; if two people you follow, who do not follow and are not aware of each other, are both posting unrelated things at the same time, you're going to see them at around the same time.

Do not argue with a DANGIT. Discussions are the internet are famously Pyrrhic battles to begin with, but if you argue with a DANGIT it's not that you will achieve a Pyrrhic victory, you cannot possibly achieve any victory, because you are shadowboxing an imagined consensus where none exits.

You can't win against something that isn't there.

Acknowledgments

Thank you to my patrons who are supporting my writing on this blog. If you like what you've read here and you'd like to read more things like it, or you'd like to support my various open-source endeavors, you can support my work as a sponsor!

16 Dec 2024 10:58pm GMT

29 Nov 2024

feedPlanet Plone - Where Developers And Integrators Write

Maurits van Rees: Lightning talks Friday

Bonnie Tyler Sprint

On 12 August 2026 there is a total solar eclipse that can be seen from Valencia, Spain. So we organise a sprint there.

This conference

We had 291 participants, 234 in person and 57 online. 13 Brazilian states (that is all of them), 14 countries.

24.5 percent women, was 13% in 2013, so that has gone up, but we are not there yet. Thank you to PyLadies and Django Girls for making this happen.

We had more than 80 presenters, about 30 lightning talks, lots of talk in the hall ways.

Thanks also to the team!

Ramiro Luz: Yoga time

Yoga exercise.

Rikupekka: University case student portal

We have a student portal at the university. But mostly:

Welcome to Jyväskylä university in Finald for Plone conference 2025, October 13-19!

Jakob: Beethovensprint

26-30 mei 2025 in Bonn, Duitsland.

Afterwards, on May 30 and June 1 there will be FedCon in Bonn, a SciFi convention.

Piero/Victor: BYOUI

Add-ons first development with @plone/registry. See https://plone-registry.readthedocs.io/

It allows for development that is framework agnostic, so it is not only for Plone. It is around configuration that can be extended and injected, which is tricky in most javascript frameworks.

Imagine it.

Ana Dulce: 3D printing

For a difficult model I had trust the process, it took a week, but it worked.

Renan & Iza: Python Brasil

We organised the Python Brasil conference from 16 to 23 October this year in Rio de Janeiro.

Next year 21-27 October in São Paulo.

Erico: Python Cerrado

31 July to 2 August 2025 is the next Python Cerrado conference.

29 Nov 2024 10:25pm GMT

Maurits van Rees: Paul Roeland: The value of longevity

Link to talk information on Plone conference website.

I work for the Clean Clothes Campaign: https://cleanclothes.org/

After three large disasters in factories in 2012 and 2013 with over 1000 deaths, it took three years to get an agreement with clothes manufacturers to get 30 million dollar compensation. It does not bring lives back, but it helps the survivors.

See Open Supply Hub for open data that we collected, for checking which brands are produced in which factories.

Documenting history matters. Stories must be told.

The global closing industry is worth around 1.8 trillion dollars, in a country that would put them on the 12th place in the world. 75 million workers.

Our strongest weapon: backlinks. We have links from OECD, UN, wikipedia, school curriculum, books. Especially those last two don't change ever, so you should never change urls.

Plone: enable the sitemap, please, why not by default? Create a good robots.txt. I weekly check Google Search console, looking for broken links. Tag early, tag often, great tool, even if you have an AI do it.

Our website: started 1998 written in Notepad, 2004 Dreamweaver, 2006 Bluefish, 2010 Joomla, 2013 Plone 4, 2020 Castle CMS (opinionated distribution of Plone, but does not really exist anymore) 2024 Plone 6 with Volto Light Theme (work in progress). Thank you kitconcept for all the help, especially Jonas.

Migrations are painful. Along the years we used wget to csv to SQL to csv, Python script, "Franken-mogrifier", collective.exportimport.

Lessons learned: stable urls are awesome, migrations are painful. Please don't try to salvage CSS from your old site, just start fresh in your new system. Do not try to migrate composite pages or listings.

What if your website does not provide an export? Use wget, still works and is better than httrack. sed/awk/regex are your friend. archivebox (WARC).

Document your steps for your own sanity.

To manage json, jq or jello can be used. sq is a Swiss knife for json/sql/csv. emuto is a hybrid between jq and GraphQL.

Normalize import/export. We have `plone.exportimport` in core now.

In the future I would like a plone exporter script that accepts a regex and exports only matching pages. Switch backends: ZODB, relstorage, nick, quantum-db. Sitewide search/replace/sed. Sneakernet is useful in difficult countries where you cannot send data over the internet: so export to a usb stick.

A backup is only a backup if it regularly gets restored so you know that it works.

  • Keeping content and URL stability is a superpower.
  • Assuming that export/import/backup/restore/migration are rare occurrences, is wrong.
  • Quick export/import is very useful.

Do small migrations, treat it as maintenance. Don't be too far behind. Large migrations one every five years will be costly. Do a small migration every year. Do your part. Clients should also do their part, by budgeting this yearly. That is how budgeting works. Use every iteration to review custom code.

Make your sites live long and prosper.

29 Nov 2024 8:58pm GMT

Maurits van Rees: Fred van Dijk: Run Plone in containers on your own cluster with coolify.io

Link to talk information on Plone conference website.

Sorry, I ran out of time trying to set up https://coolify.io

So let's talk about another problem. Running applications (stacks) in containers is the future. Well: abstraction and isolation is the future, and containers is the current phase.

I am on the Plone A/I team, with Paul, Kim, Erico. All senior sysadmins, so we kept things running. In 2022 we worked on containerisation. Kubernetes was the kool kid then, but Docker Swarm was easier. Checkout Erico's training with new cookieplone templates.

Doing devops well is hard. You have a high workload, but still need to keep learning new stuff to keep up with what is changing.

I want to plug Coolify, which is a full open source product. "Self-hosting with super powers." The main developer, Andras Bacsal, believes in open source and 'hates' pay by usage cloud providers with a vengeance.

Coolify is still docker swarm. We also want Kubernetes support. But we still need sysadmins. Someone will still need to install coolify, and keep it updated.

I would like to run an online DevOps course somewhere January-March 2025. 4-6 meetings of 2 hours, maybe Friday afternoon. Talk through devops and sysadmin concepts, show docker swarm, try coolify, etc.

29 Nov 2024 7:58pm GMT