Summary
The best CPython commit of 2023
Django version 5
2FA gets mandatory on pypi for 2024
A proposal to clean up the logging module API
The best CPython commit of 2023
Performance has been a busy topic this year, and we mentioned last month the future JIT that was planned for 3.13.
With change GH-113464, Brandt Bucher offers the whole community the Xmas gift of the first JIT related merge proposal.
Even if you don't intend to read the code, I strongly encourage you to read the commit message as it's made with a cheerful holiday spirit.
Since 3.13 alpha 2 is out of the oven, it's all well in sync.
It's also a good time to remind you once again that 3.13 will deprecate a lot of things, although there has been some push back on that side, as people reminded Victor Stinner he is, as usual, a bit too enthusiastic about deleting stuff.
And since I'm on the topic, 3.11.7 and 3.12.1 were also released. Those are bug fixes of course, but if you don't follow my advice to stay one version behind, at least I hope you waited until 3.12.1 because 3.x.0 are always full of holes.
Ladies and gentlemen, this is Django No 5
Since you know now that beginners should use Django and not Flask, maybe you will be very interested that the latest release of the web framework dropped this month.
On the menu:
The ORM used to allow dynamic default values for any field, but expected a Python function to provide them. We can now declare that the database should generate them. E.G:
from django.db import models
from django.db.models.functions import Now, Pi
class MyModel(models.Model):
age = models.IntegerField(db_default=18)
created = models.DateTimeField(db_default=Now())
circumference = models.FloatField(db_default=2 * Pi())
Following in the same trend, the ORM now has a way for you to define fields generated on the database side, such as a field made from other fields. E.G:
from django.db import models
from django.db.models import F
class Square(models.Model):
side = models.IntegerField()
area = models.GeneratedField(
expression=F("side") * F("side"),
output_field=models.BigIntegerField(),
db_persist=True,
)
Templates for form rendering have been simplified: you can now decide the granularity of what part of the form you want to customize, using field groups.
UUIDField
uses a correct UUID column on MariaDB 10.7.Django now supports oracledb version 1.3.2 or higher, but cx_Oracle support is deprecated.
The asynchronous work in progress continues, with many more features supporting it, such as authentication or view decorators.
Django 5 drops support for Python 3.9 and bellow, though, so if you use those, stay on the 4.2.X series.
You get 2FA, and you get 2FA, and you get 2FA!
New users of pypi had recently to activate 2FA to be able to manage their account. This test has proven successful, and given the regular attacks on pypi, 2FA will be rolled out for all users in 2024.
You have the choice between a security key like a yubikey, or a TOTP code.
I just tested the procedure, and it took only a few minutes, despite having 4 yubikeys and 2 TOTP apps to configure.
Very well implemented.
A proposal to clean up logging API
I have a love/hate relationship with the stdlib logging module. On one hand, it means Python ships with a rich, versatile, fully functional logging module out of the box, that is so configurable you can pretty much do anything with it, and it's thread safe by default! It's kinda amazing, and rare enough to state it.
On the other hand, the API is terrible. It's verbose, counterintuitive, has bad default values, and is written in camel Case.
The draft of PEP 8106 aims to change this very last point, and at least provide a version of logging that would be PEP8 compliant. The idea is not to remove the old API, but at to come up with aliases that don't make you feel like you coding in 1998 Java.
So logging.basicConfig
would also be available as logging.basic_config
, Logger.setLevel
would be available also as Logger.set_level
, and so on.
The PEP is very bare bone and doesn't have a sponsor yet, so I wouldn't expect it to go very far. Plus, I'm not saying this is the right solution. An os.path
to pathlib
new wrapper module would sit better with me.
But I like the fact it's shaking this tree.