Fundamentals 13 min read

Modern Python Standard Library: pathlib, secrets, zoneinfo, dataclasses, logging, f‑strings, tomllib, and setuptools

This article reviews recent Python standard‑library enhancements, showing how pathlib replaces os.path, secrets supersedes os.urandom, zoneinfo replaces pytz, dataclasses improve on namedtuple, proper logging outperforms print, f‑strings beat format, tomllib replaces tomli, and setuptools supersedes distutils, with code examples for each.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Modern Python Standard Library: pathlib, secrets, zoneinfo, dataclasses, logging, f‑strings, tomllib, and setuptools

Each new Python release adds modules and better ways to work with the language; it is time to adopt these improvements.

Pathlib instead of os.path – Path objects provide an object‑oriented, readable API. Example:

from pathlib import Path<br/>import os.path<br/><br/># old way<br/>two_dirs_up = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))<br/># new way<br/>two_dirs_up = Path(__file__).resolve().parent.parent

Path objects expose useful properties such as absolute() , name , root , parent , suffix , and is_absolute() :

readme = Path("README.md").resolve()<br/>print(f"Absolute path: {readme.absolute()}")<br/>print(f"File name: {readme.name}")<br/>print(f"Path root: {readme.root}")<br/>print(f"Parent directory: {readme.parent}")<br/>print(f"File extension: {readme.suffix}")<br/>print(f"Is it absolute: {readme.is_absolute()}")

Pathlib also supports the / operator for joining paths:

etc = Path('/etc')<br/>joined = etc / "cron.d" / "anacron"<br/>print(f"Exists? - {joined.exists()}")

Secrets instead of os.urandom – The secrets module provides cryptographically strong tokens:

# old way<br/>import os<br/>length = 64<br/>value = os.urandom(length)<br/>print(f"Bytes: {value}")<br/>print(f"Hex: {value.hex()}")<br/><br/># new way<br/>import secrets<br/>value = secrets.token_bytes(length)<br/>print(f"Bytes: {value}")<br/>value = secrets.token_hex(length)<br/>print(f"Hex: {value}")

Zoneinfo instead of pytz – Since Python 3.9, the built‑in zoneinfo module replaces the external pytz library:

# old way<br/>from datetime import datetime<br/>import pytz<br/>dt = datetime(2022, 6, 4)<br/>nyc = pytz.timezone("America/New_York")<br/>localized = nyc.localize(dt)<br/>print(f"Datetime: {localized}, Timezone: {localized.tzname()}, TZ Info: {localized.tzinfo}")<br/><br/># new way<br/>from datetime import datetime<br/>from zoneinfo import ZoneInfo<br/>nyc = ZoneInfo("America/New_York")<br/>localized = datetime(2022, 6, 4, tzinfo=nyc)<br/>print(f"Datetime: {localized}, Timezone: {localized.tzname()}, TZ Info: {localized.tzinfo}")

On systems lacking zone data, install the tzdata package.

Dataclasses instead of namedtuple – Dataclasses offer mutability, auto‑generated __repr__ , __eq__ , __init__ , default values, inheritance, and optional frozen and slots :

# old way<br/>from typing import NamedTuple<br/>import sys<br/>User = NamedTuple("User", [("name", str), ("surname", str), ("password", bytes)])<br/>u = User("John", "Doe", b'...')<br/>print(f"Size: {sys.getsizeof(u)}")<br/><br/># new way<br/>from dataclasses import dataclass<br/>@dataclass()<br/>class User:<br/>    name: str<br/>    surname: str<br/>    password: bytes<br/><br/>u = User("John", "Doe", b'...')<br/>print(u)<br/>print(f"Size: {sys.getsizeof(u)}, {sys.getsizeof(u) + sys.getsizeof(vars(u))}")

Dataclasses are larger in memory but have comparable attribute‑access speed.

Proper logging instead of print – Use the logging module for production‑ready output:

import logging<br/>logging.basicConfig(filename='application.log', level=logging.WARNING,<br/>    format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',<br/>    datefmt='%H:%M:%S')<br/><br/>logging.error("Some serious error occurred.")<br/>logging.warning('Some warning.')

f‑strings instead of format – f‑strings are more readable and faster, though % formatting can be useful for deferred logging:

# old style logging<br/>logger.error("Message: %s", things)<br/># f‑string logging (evaluated immediately)<br/>logger.error(f"Message: {things}")

tomllib instead of tomli – From Python 3.11, tomllib is built‑in for parsing TOML files:

import tomllib<br/>with open("pyproject.toml", "rb") as f:<br/>    config = tomllib.load(f)<br/>print(config)<br/><br/>toml_string = """[project]\nname = \"another-app\"\ndescription = \"Example Package\"\nversion = \"0.1.1\"\n"""<br/>config = tomllib.loads(toml_string)<br/>print(config)

setuptools instead of distutils – distutils is deprecated; migrate to setuptools as recommended by PEP 632.

In summary, regularly consult Python release notes for new modules, deprecations, and removals, and incorporate these modern tools and best practices into your projects.

Loggingstandard-librarypathlibdataclassesf-strings
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.