Skip to content

Commit ae73d34

Browse files
📚 docs: split usage guide into tutorial, how-to, and reference (#441)
The existing `usage.rst` documentation mixed tutorial content with reference material in a single 661-line file. New users looking to get started had to wade through parameter tables and edge cases, while experienced users searching for specific parameters had to scan through introductory examples. 📖 This made the documentation harder to navigate and maintain as the library evolved. The reorganization follows the [Divio documentation system](https://documentation.divio.com/) by splitting the content into three focused pages. `usage.rst` becomes a tutorial teaching core concepts through real-world examples from Black, Poetry, virtualenv, and tox. `howto.rst` provides recipes for common tasks like handling write errors and platform-specific considerations. `parameters.rst` serves as a reference for all configuration options including the previously undocumented `use_site_for_root` parameter. ✨ This structure lets users quickly find what they need based on their goal. All examples now use `_path` variants instead of `_dir` for consistency with modern Python practices. Real-world code examples link directly to source files in production projects so users can see how established tools use `platformdirs`. Closes #433 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 816747e commit ae73d34

7 files changed

Lines changed: 577 additions & 361 deletions

File tree

.proselintrc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"checks": {
3+
"lexical_illusions": false,
4+
"typography": false
5+
}
6+
}

docs/howto.rst

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
How-to guides
2+
=============
3+
4+
This page provides recipes for common tasks and platform-specific guidance.
5+
6+
Common patterns
7+
---------------
8+
9+
.. _creating-directories-safely:
10+
11+
Creating directories safely
12+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
14+
Always create parent directories before writing files:
15+
16+
.. code-block:: python
17+
18+
from pathlib import Path
19+
from platformdirs import user_data_path
20+
21+
data_dir = user_data_path("MyApp")
22+
db_file = data_dir / "data.db"
23+
24+
# Create directory if it doesn't exist
25+
db_file.parent.mkdir(parents=True, exist_ok=True)
26+
27+
# Now safe to write
28+
db_file.write_bytes(b"...")
29+
30+
Or use ``ensure_exists=True`` to create directories automatically:
31+
32+
.. code-block:: python
33+
34+
from platformdirs import PlatformDirs
35+
36+
dirs = PlatformDirs("MyApp", ensure_exists=True)
37+
db_file = dirs.user_data_path / "data.db"
38+
db_file.write_bytes(b"...") # Directory already exists
39+
40+
Handling write errors
41+
~~~~~~~~~~~~~~~~~~~~~
42+
43+
Directory paths may not be writable due to permissions or disk space:
44+
45+
.. code-block:: python
46+
47+
from platformdirs import user_data_path
48+
from pathlib import Path
49+
import tempfile
50+
51+
data_dir = user_data_path("MyApp")
52+
data_file = data_dir / "data.json"
53+
54+
try:
55+
data_dir.mkdir(parents=True, exist_ok=True)
56+
data_file.write_text('{"key": "value"}')
57+
except (OSError, PermissionError) as e:
58+
# Fallback to temp directory
59+
temp_dir = Path(tempfile.gettempdir()) / "MyApp"
60+
temp_dir.mkdir(parents=True, exist_ok=True)
61+
data_file = temp_dir / "data.json"
62+
data_file.write_text('{"key": "value"}')
63+
64+
Checking directory writability
65+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66+
67+
Test if a directory is writable before using it:
68+
69+
.. code-block:: python
70+
71+
from platformdirs import user_cache_path
72+
import os
73+
74+
cache_dir = user_cache_path("MyApp")
75+
cache_dir.mkdir(parents=True, exist_ok=True)
76+
77+
if os.access(cache_dir, os.W_OK):
78+
# Directory is writable
79+
cache_file = cache_dir / "cache.dat"
80+
cache_file.write_bytes(b"...")
81+
else:
82+
# Handle read-only directory
83+
print(f"Warning: {cache_dir} is not writable")
84+
85+
Cleaning up cache
86+
~~~~~~~~~~~~~~~~~
87+
88+
Implement cache cleanup based on age or size:
89+
90+
.. code-block:: python
91+
92+
from platformdirs import user_cache_path
93+
import time
94+
95+
cache_dir = user_cache_path("MyApp")
96+
max_age_days = 30
97+
98+
if cache_dir.exists():
99+
now = time.time()
100+
for item in cache_dir.rglob("*"):
101+
if item.is_file():
102+
age_days = (now - item.stat().st_mtime) / 86400
103+
if age_days > max_age_days:
104+
item.unlink()
105+
106+
.. _versioned-data-migration:
107+
108+
Versioned data migration
109+
~~~~~~~~~~~~~~~~~~~~~~~~~
110+
111+
When using the ``version`` parameter, handle migration between versions:
112+
113+
.. code-block:: python
114+
115+
from platformdirs import user_data_path
116+
import shutil
117+
118+
current_version = "2.0"
119+
previous_version = "1.0"
120+
121+
current_dir = user_data_path("MyApp", version=current_version)
122+
previous_dir = user_data_path("MyApp", version=previous_version)
123+
124+
if not current_dir.exists() and previous_dir.exists():
125+
# Migrate data from previous version
126+
current_dir.mkdir(parents=True, exist_ok=True)
127+
for item in previous_dir.iterdir():
128+
shutil.copy2(item, current_dir / item.name)
129+
130+
Platform-specific considerations
131+
---------------------------------
132+
133+
Windows
134+
~~~~~~~
135+
136+
Windows Store Python sandbox
137+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
138+
139+
Applications installed via the Microsoft Store run in a restricted sandbox environment.
140+
``platformdirs`` handles this automatically, but be aware that:
141+
142+
- File system access is limited to specific directories.
143+
- Some APIs may return sandbox-specific paths.
144+
- Network access may require additional permissions.
145+
146+
Roaming vs local profiles
147+
^^^^^^^^^^^^^^^^^^^^^^^^^^
148+
149+
Use ``roaming=True`` for settings that should sync across domain-joined machines. Use local
150+
(default) for machine-specific data like caches.
151+
152+
.. code-block:: python
153+
154+
from platformdirs import user_config_path, user_cache_path
155+
156+
# Synced across domain computers
157+
roaming_cfg = user_config_path("MyApp", roaming=True)
158+
159+
# Local to this machine
160+
local_cache = user_cache_path("MyApp")
161+
162+
Environment variable overrides
163+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
164+
165+
Windows supports environment variable overrides for certain directories:
166+
167+
- ``WIN_PD_OVERRIDE_*`` - Override specific directory types
168+
- ``PLATFORMDIRS_*`` - Alternative override mechanism
169+
170+
See the source code for complete list of supported variables.
171+
172+
macOS
173+
~~~~~
174+
175+
Data and config location
176+
^^^^^^^^^^^^^^^^^^^^^^^^^
177+
178+
On macOS, ``user_data_dir`` and ``user_config_dir`` both return
179+
``~/Library/Application Support/AppName`` by default. Use subdirectories to separate concerns:
180+
181+
.. code-block:: python
182+
183+
from platformdirs import user_data_path
184+
185+
app_dir = user_data_path("MyApp")
186+
config_dir = app_dir / "config"
187+
databases_dir = app_dir / "databases"
188+
189+
XDG support on macOS
190+
^^^^^^^^^^^^^^^^^^^^
191+
192+
macOS now supports XDG environment variables. If ``XDG_DATA_HOME`` is set, ``platformdirs``
193+
will honor it instead of using ``~/Library``:
194+
195+
.. code-block:: python
196+
197+
import os
198+
from platformdirs import user_data_dir
199+
200+
# Without XDG
201+
user_data_dir("MyApp") # ~/Library/Application Support/MyApp
202+
203+
# With XDG
204+
os.environ["XDG_DATA_HOME"] = os.path.expanduser("~/.local/share")
205+
user_data_dir("MyApp") # ~/.local/share/MyApp
206+
207+
Linux/Unix
208+
~~~~~~~~~~
209+
210+
Permission requirements
211+
^^^^^^^^^^^^^^^^^^^^^^^^
212+
213+
Writing to ``site_*`` directories typically requires root privileges. Normal users can only
214+
read from these locations:
215+
216+
.. code-block:: python
217+
218+
from platformdirs import site_config_path, user_config_path
219+
import os
220+
221+
site_cfg = site_config_path("MyApp") / "defaults.conf"
222+
223+
# Check if we can write to site config
224+
if os.access(site_cfg.parent, os.W_OK):
225+
# Running as root or have special permissions
226+
site_cfg.write_text("[defaults]\n")
227+
else:
228+
# Normal user - use user config instead
229+
user_cfg = user_config_path("MyApp") / "config.conf"
230+
user_cfg.parent.mkdir(parents=True, exist_ok=True)
231+
user_cfg.write_text("[user_settings]\n")
232+
233+
XDG Base Directory Specification
234+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
235+
236+
``platformdirs`` fully supports the XDG Base Directory Specification. Users can override
237+
default paths by setting environment variables:
238+
239+
- ``XDG_DATA_HOME`` - defaults to ``~/.local/share``
240+
- ``XDG_CONFIG_HOME`` - defaults to ``~/.config``
241+
- ``XDG_CACHE_HOME`` - defaults to ``~/.cache``
242+
- ``XDG_STATE_HOME`` - defaults to ``~/.local/state``
243+
- ``XDG_RUNTIME_DIR`` - defaults to ``/run/user/$UID``
244+
- ``XDG_DATA_DIRS`` - defaults to ``/usr/local/share:/usr/share``
245+
- ``XDG_CONFIG_DIRS`` - defaults to ``/etc/xdg``
246+
247+
Running as root
248+
^^^^^^^^^^^^^^^
249+
250+
When running as root (uid 0), ``user_*`` directories default to root's home directory
251+
(``/root``). Use ``use_site_for_root=True`` to redirect to system directories instead:
252+
253+
.. code-block:: python
254+
255+
from platformdirs import PlatformDirs
256+
import os
257+
258+
# System service that should use /var/lib instead of /root
259+
if os.geteuid() == 0:
260+
dirs = PlatformDirs("myservice", use_site_for_root=True)
261+
# user_data_dir now returns /usr/local/share/myservice
262+
else:
263+
dirs = PlatformDirs("myservice")
264+
# user_data_dir returns ~/.local/share/myservice
265+
266+
Android
267+
~~~~~~~
268+
269+
App-specific storage
270+
^^^^^^^^^^^^^^^^^^^^
271+
272+
All directories are within your app's private storage (``/data/data/<package_name>/``).
273+
Data is automatically removed when the app is uninstalled.
274+
275+
.. code-block:: python
276+
277+
from platformdirs import user_data_path
278+
279+
# Returns /data/data/com.example.myapp/files/MyApp
280+
data_dir = user_data_path("MyApp")
281+
282+
External storage not supported
283+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
284+
285+
``platformdirs`` does not provide paths for external/shared storage (SD cards,
286+
``/storage/emulated/0/``). Use Android-specific APIs for shared storage:
287+
288+
.. code-block:: python
289+
290+
# For shared/external storage on Android, use Android APIs directly
291+
# platformdirs only handles app-private directories
292+
293+
Storage permissions
294+
^^^^^^^^^^^^^^^^^^^
295+
296+
App-private directories provided by ``platformdirs`` don't require storage permissions.
297+
For external storage access, your app needs appropriate Android permissions.

docs/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Features
2020
:caption: Contents
2121

2222
usage
23+
howto
24+
parameters
2325
api
2426
platforms
2527
changelog

0 commit comments

Comments
 (0)