Skip to content

Commit adc0d9e

Browse files
authored
docs: Rewrite Handling Paths chapter (pathlib vs utils) (#6116)
## Description Updates the docs chapter "Handling Paths" describing how to modernise old code and intentionally includes historical details. Examples should further guide contributors while refactoring. Also moved the guide from the contribution guide into the dev docs.
2 parents 52b102c + 528d5e6 commit adc0d9e

File tree

4 files changed

+69
-25
lines changed

4 files changed

+69
-25
lines changed

CONTRIBUTING.rst

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -286,31 +286,6 @@ according to the specifications required by the project.
286286
Similarly, run ``poe format-docs`` and ``poe lint-docs`` to ensure consistent
287287
documentation formatting and check for any issues.
288288

289-
Handling Paths
290-
~~~~~~~~~~~~~~
291-
292-
A great deal of convention deals with the handling of **paths**. Paths are
293-
stored internally—in the database, for instance—as byte strings (i.e., ``bytes``
294-
instead of ``str`` in Python 3). This is because POSIX operating systems’ path
295-
names are only reliably usable as byte strings—operating systems typically
296-
recommend but do not require that filenames use a given encoding, so violations
297-
of any reported encoding are inevitable. On Windows, the strings are always
298-
encoded with UTF-8; on Unix, the encoding is controlled by the filesystem. Here
299-
are some guidelines to follow:
300-
301-
- If you have a Unicode path or you’re not sure whether something is Unicode or
302-
not, pass it through ``bytestring_path`` function in the ``beets.util`` module
303-
to convert it to bytes.
304-
- Pass every path name through the ``syspath`` function (also in ``beets.util``)
305-
before sending it to any *operating system* file operation (``open``, for
306-
example). This is necessary to use long filenames (which, maddeningly, must be
307-
Unicode) on Windows. This allows us to consistently store bytes in the
308-
database but use the native encoding rule on both POSIX and Windows.
309-
- Similarly, the ``displayable_path`` utility function converts bytestring paths
310-
to a Unicode string for displaying to the user. Every time you want to print
311-
out a string to the terminal or log it with the ``logging`` module, feed it
312-
through this function.
313-
314289
Editor Settings
315290
~~~~~~~~~~~~~~~
316291

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ For packagers:
2626

2727
Other changes:
2828

29+
- The documentation chapter :doc:`dev/paths` has been moved to the "For
30+
Developers" section and revised to reflect current best practices (pathlib
31+
usage).
32+
2933
2.5.1 (October 14, 2025)
3034
------------------------
3135

docs/dev/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ configuration files, respectively.
1818

1919
plugins/index
2020
library
21+
paths
2122
importer
2223
cli
2324
../api/index

docs/dev/paths.rst

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
Handling Paths
2+
==============
3+
4+
``pathlib`` provides a clean, cross-platform API for working with filesystem
5+
paths.
6+
7+
Use the ``.filepath`` property on ``Item`` and ``Album`` library objects to
8+
access paths as ``pathlib.Path`` objects. This produces a readable, native
9+
representation suitable for printing, logging, or further processing.
10+
11+
Normalize paths using ``Path(...).expanduser().resolve()``, which expands ``~``
12+
and resolves symlinks.
13+
14+
Cross-platform differences—such as path separators, Unicode handling, and
15+
long-path support (Windows) are automatically managed by ``pathlib``.
16+
17+
When storing paths in the database, however, convert them to bytes with
18+
``bytestring_path()``. Paths in Beets are currently stored as bytes, although
19+
there are plans to eventually store ``pathlib.Path`` objects directly. To access
20+
media file paths in their stored form, use the ``.path`` property on ``Item``
21+
and ``Album``.
22+
23+
Legacy utilities
24+
----------------
25+
26+
Historically, Beets used custom utilities to ensure consistent behavior across
27+
Linux, macOS, and Windows before ``pathlib`` became reliable:
28+
29+
- ``syspath()``: worked around Windows Unicode and long-path limitations by
30+
converting to a system-safe string (adding the ``\\?\`` prefix where needed).
31+
- ``normpath()``: normalized slashes and removed ``./`` or ``..`` parts but did
32+
not expand ``~``.
33+
- ``bytestring_path()``: converted paths to bytes for database storage (still
34+
used for that purpose today).
35+
- ``displayable_path()``: converted byte paths to Unicode for display or
36+
logging.
37+
38+
These functions remain safe to use in legacy code, but new code should rely
39+
solely on ``pathlib.Path``.
40+
41+
Examples
42+
--------
43+
44+
Old style
45+
46+
.. code-block:: python
47+
48+
displayable_path(item.path)
49+
normpath("~/Music/../Artist")
50+
syspath(path)
51+
52+
New style
53+
54+
.. code-block:: python
55+
56+
item.filepath
57+
Path("~/Music/../Artist").expanduser().resolve()
58+
Path(path)
59+
60+
When storing paths in the database
61+
62+
.. code-block:: python
63+
64+
path_bytes = bytestring_path(Path("/some/path/to/file.mp3"))

0 commit comments

Comments
 (0)