tox-dev / platformdirs

A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir".
https://platformdirs.readthedocs.io
MIT License
568 stars 49 forks source link

Optionally redirect `user_*_dir()` calls to `site_*_dir()` for root on Unix #213

Open PhrozenByte opened 1 year ago

PhrozenByte commented 1 year ago

This is a follow-up to #188 concerning the second suggestion I made there:

platformdirs.user_runtime_dir() currently yields /run/user/0/… for root on Unix. While not strictly false, this is a rather uncommon result; for root I'd rather expect just /run/….

Due to this @kemzeb just recently added platformdirs.site_runtime_dir() yielding /run/… on Unix (thank you @kemzeb! :heart:). I absolutely think that this is the right approach. However, platformdirs.user_runtime_dir() still yields /run/user/0/… for root on Unix - which is not wrong (it might be the correct answer, see #188), but unexpected.

This gets crucial for software that can run as both unprivileged users, and root, like Borg. For unprivileged users platformdirs.user_runtime_dir() is always the right answer, but for root it's usually platformdirs.site_runtime_dir(). I believe that this is true for most software that can run as both unprivileged users, and root:

[…] root is different: Software running as root is often intentionally NOT bound to root being logged in - in many systems root can't login at all. This is due to root's special role in Unix and a major difference to any other user. […] This doesn't mean that there might not indeed be software whose lifetime is bound to root being logged in [(i.e. platformdirs.user_runtime_dir() is the right answer for root, too)] - but that's the exception rather than the rule.

AFAIK platformdirs doesn't address root's special role in Unix […] right now, but it should.

To better account for root's special role on Unix I'd like to suggest the following:

As additional feature it might be helpful to add a global switch (disabled by default) which basically redirects calls to platformdirs.user_*_dir() to their platformdirs.site_*_dir() equivalent (i.e. not just for user_runtime_dir(), but config, cache, state, … too) for root unless stated otherwise (i.e. add a option to each method next to the global switch).

Even though I believe that most software would want to enable this global switch, I suggest disabling it by default to preserve BC. However, we should document this and the switch's usage.

What are your opinions on this?

Just to make this clear: One can easily implement root's special role in the software using platformdirs, i.e. add such a switch. Thus it's definitely no major issue. However, if you decide not to implement such a switch I'd strongly vote for at least documenting root's special role in Unix and that most projects rather want to use platformdirs.site_*_dir() for root.

Cc: @ThomasWaldmann

gaborbernat commented 1 year ago

Even though I believe that most software would want to enable this global switch, I

Ho would this global switch work?

PhrozenByte commented 1 year ago

I don't really have an exact implementation in mind. The idea is that users can change it once (i.e. once for their app) and aren't required to declare it on every invocation. My initial idea (at the time I created #188) was using a class attribute:

>>> import os
>>> os.getuid()
0
>>> from platformdirs import PlatformDirs
>>> PlatformDirs.use_site_dirs_for_root = True
>>> PlatformDirs.site_runtime_dir("SuperApp", "Acme")
'/run/SuperApp'
>>> PlatformDirs.user_runtime_dir("SuperApp", "Acme")
'/run/SuperApp'
>>> PlatformDirs.user_runtime_dir("SuperApp", "Acme", use_site_dirs_for_root=False)
'/run/user/0/SuperApp'

However, as I said, I'm indifferent about the concrete implementation. use_site_dirs_for_root might be a bit too descriptive and too long. Some devs don't like class attributes. If a "super global switch" isn't considered desirable, a regular instance attribute would work just fine too:

>>> from platformdirs import PlatformDirs
>>> dirs = PlatformDirs("SuperApp", "Acme", use_site_dirs_for_root=True)
>>> dirs.user_runtime_dir
'/var/run/SuperApp'
>>> dirs.site_runtime_dir
'/var/run/SuperApp'

It should be what you think fits best.