December of 2021
Life Management⚑
Music Management⚑
- New: Introduce how I manage my music library.
MusicBrainz⚑
-
New: How to contribute to MusicBrainz.
MusicBrainz is an open music encyclopedia that collects music metadata and makes it available to the public.
MusicBrainz aims to be:
- The ultimate source of music information by allowing anyone to contribute and releasing the data under open licenses.
- The universal lingua franca for music by providing a reliable and unambiguous form of music identification, enabling both people and machines to have meaningful conversations about music.
Like Wikipedia, MusicBrainz is maintained by a global community of users and we want everyone — including you — to participate and contribute.
Coding⚑
Generic Coding Practices⚑
Program Versioning⚑
-
New: Define how to use versioning in software.
A long article on how to use versioning both as a developer and as a consumer. It includes:
- Deciding what version system to use for your programs.
- How to evolve your code version.
- Deciding how to manage the versions of your dependencies.
- A huge rant on Upper version pinning.
- When to use lower version pinning.
- How to automatically upgrade and test your dependencies.
- Monitor your dependencies evolution.
Keep a Changelog⚑
-
New: Introduce the Changelog practice.
A changelog is a file which contains a curated, chronologically ordered list of notable changes for each version of a project.
It's purpose is to make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project.
In the article we added:
- Guidelines on how to build good changelogs
- How to reduce the effort required to maintain a changelog
-
New: Introduce Semantic Versioning.
Semantic Versioning is a way to define your program's version based on the type of changes you've introduced. It's defined as a three-number string (separated with a period) in the format of
MAJOR.MINOR.PATCH
.Also included in the article is:
Calendar Versioning⚑
-
New: Introduce Calendar Versioning.
Calendar Versioning is a versioning convention based on your project's release calendar, instead of arbitrary numbers.
CalVer suggests version number to be in format of:
YEAR.MONTH.sequence
. For example,20.1
indicates a release in 2020 January, while20.5.2
indicates a release that occurred in 2020 May, while the2
indicates this is the third release of the month.
Python⚑
Pydantic Field Types⚑
-
New: Using constrained strings in list attributes.
import re import pydantic from pydantic import Field from typing import List class Regex(pydantic.ConstrainedStr): regex = re.compile("^[0-9a-z_]*$") class Data(pydantic.BaseModel): regex: List[Regex] data = Data(**{"regex": ["abc", "123", "asdf"]}) print(data) print(data.json())
Pipenv⚑
-
New: Introduce the pipenv package manager.
Pipenv is a tool that aims to bring the best of all packaging worlds (bundler, composer, npm, cargo, yarn, etc.) to the Python world.
Poetry⚑
-
New: Deeply introduce Poetry, a python package manager.
Poetry is a command line program that helps you declare, manage and install dependencies of Python projects, ensuring you have the right stack everywhere.
-
New: Debugging why a package is not updated to the latest version.
- New: Checking what package is using a dependency.
- New: Try to use
pass
as a keyring backend to store the PYPI token. -
Correction: Warn against upper version pinning.
The main problem is that
poetry add
does upper pinning of dependencies by default, which is a really bad idea.
Dash⚑
-
New: Test programs that use
sh
.sh
can be patched in your tests the typical way, withunittest.mock.patch()
:from unittest.mock import patch import sh def get_something(): return sh.pwd() @patch("sh.pwd", create=True) def test_something(pwd): pwd.return_value = "/" assert get_something() == "/"
Code Styling⚑
-
New: Introduce the awesome, life saving library
pydantic_factories
.Pydantic factories is a library offers powerful mock data generation capabilities for pydantic based models and dataclasses. It automatically creates FactoryBoy factories from a pydantic model.
from datetime import date, datetime from typing import List, Union from pydantic import BaseModel, UUID4 from pydantic_factories import ModelFactory class Person(BaseModel): id: UUID4 name: str hobbies: List[str] age: Union[float, int] birthday: Union[datetime, date] class PersonFactory(ModelFactory): __model__ = Person result = PersonFactory.build()
-
Reorganization: Moved the semantic versioning commit guidelines to the semver article.
Package Management⚑
-
New: Compare Poetry, Pipenv and PDM package management tools.
Pipenv has broad support. It is an official project of the Python Packaging Authority, alongside pip. It's also supported by the Heroku Python buildpack, which is useful for anyone with Heroku or Dokku-based deployment strategies.
Poetry is a one-stop shop for dependency management and package management. It simplifies creating a package, managing its dependencies, and publishing it. Compared to Pipenv, Poetry's separate add and install commands are more explicit, and it's faster for everything except for a full dependency install.
I liked Poetry most, and in the end I didn't analyze
pdm
. -
New: Describe what a dependency solver does.
A Solver tries to find a working set of dependencies that all agree with each other. By looking back in time, it’s happy to solve very old versions of packages if newer ones are supposed to be incompatible. This can be helpful, but is slow, and also means you can easily get a very ancient set of packages when you thought you were getting the latest versions.
In the section we compare Pip's and Poetry's solver.
-
It does upper version capping by default, which is becoming a big problem in the Python environment.
This is specially useless when you add dependencies that follow CalVer.
poetry add
packaging will still do^21
for the version it adds. You shouldn’t be capping versions, but you really shouldn’t be capping CalVer.It's equally troublesome that it upper pins the python version.
GitPython⚑
-
New: Clone a repository.
from git import Repo Repo.clone_from(git_url, repo_dir)
-
New: Create a branch.
new_branch = repo.create_head('new_branch') assert repo.active_branch != new_branch # It's not checked out yet repo.head.reference = new_branch assert not repo.head.is_detached
-
New: Get the latest commit of a repository.
repo.head.object.hexsha
Pytest⚑
-
New: Introduce goodconf the pyndantic YAML friendly configuration management.
goodconf is a thin wrapper over Pydantic's settings management. Allows you to define configuration variables and load them from environment or JSON/YAML file. Also generates initial configuration files and documentation for your defined configuration.
-
New: Capture deprecation warnings.
Python and its ecosystem does not have an assumption of strict SemVer, and has a tradition of providing deprecation warnings. If you have good CI, you should be able to catch warnings even before your users see them. Try the following pytest configuration:
[tool.pytest.ini_options] filterwarnings = ["error"]
This will turn warnings into errors and allow your CI to break before users break.
Other sections added are:
Python Snippets⚑
-
New: Capture the stdout of a function.
import io from contextlib import redirect_stdout f = io.StringIO() with redirect_stdout(f): do_something(my_object) out = f.getvalue()
-
import tempfile dirpath = tempfile.mkdtemp()
-
New: Change the working directory of a test.
import unittest import os from src.main import get_cwd class TestMain(unittest.TestCase): def test_get_cwd(self): os.chdir('src') print('testing get_cwd()') current_dir = get_cwd() self.assertIsNotNone(current_dir) self.assertEqual(current_dir, 'src')
-
New: Copy a directory.
import shutil shutil.copytree('bar', 'foo')
-
Correction: Use fixture to change the working directory of a test.
The previous code didn't work, instead use the next fixture:
@pytest.fixture(name="change_test_dir") def change_test_dir_(request: SubRequest) -> Any: os.chdir(request.fspath.dirname) yield os.chdir(request.config.invocation_dir)
-
regex = re.compile( r"(?<![-\.\d])(?:0{0,2}?[0-9]\.|1\d?\d?\.|2[0-5]?[0-5]?\.){3}" r'(?:0{0,2}?[0-9]|1\d?\d?|2[0-5]?[0-5]?)(?![\.\d])"^[0-9]{1,3}*$' )
-
New: Remove the elements of a list from another.
>>> set([1,2,6,8]) - set([2,3,5,8]) set([1, 6])
-
New: Change the logging level of a library.
sh_logger = logging.getLogger("sh") sh_logger.setLevel(logging.WARN)
-
New: Get all subdirectories of a directory.
[x[0] for x in os.walk(directory)]
-
New: Move a file.
import os os.rename("path/to/current/file.foo", "path/to/new/destination/for/file.foo")
-
New: Copy a file.
import shutil shutil.copyfile(src_file, dest_file)
mkdocstrings⚑
-
New: How to write good test docstrings.
Both without a template and using the Given When Then style.
questionary⚑
-
New: Conditionally skip questions.
Sometimes it is helpful to be able to skip a question based on a condition. To avoid the need for an if around the question, you can pass the condition when you create the question:
import questionary DISABLED = True response = questionary.confirm("Are you amazed?").skip_if(DISABLED, default=True).ask()
-
New: Don't highlight the selected option by default.
If you don't want to highlight the default choice in the
select
question use the next style:from questionary import Style choice = select( "Question title: ", choices=['a', 'b', 'c'], default='a', style=Style([("selected", "noreverse")]), ).ask()
SQLite⚑
-
New: Get the columns of a database.
PRAGMA table_info(table_name);
DevOps⚑
Continuous Integration⚑
Dependency managers⚑
- Correction: Deprecate in favour of Poetry.