Added support for site-local packages
[zeroinstall/solver.git] / zeroinstall / support / basedir.py
blob3992852753901bdfdfde587c1cd4fa05696d645b
1 """
2 Support code for the freedesktop.org basedir spec.
4 This module provides functions for locating configuration files.
6 @see: U{http://freedesktop.org/wiki/Standards/basedir-spec}
8 @var home: The value of $HOME (or '/' if not set). If we're running as root and
9 $HOME isn't owned by root, then this will be root's home from /etc/passwd
10 instead.
11 """
13 # Copyright (C) 2009, Thomas Leonard
14 # See the README file for details, or visit http://0install.net.
16 from zeroinstall import _
17 import os
19 home = os.environ.get('HOME', '/')
21 try:
22 _euid = os.geteuid()
23 except AttributeError:
24 pass # Windows?
25 else:
26 if _euid == 0:
27 # We're running as root. Ensure that $HOME really is root's home,
28 # not the user's home, or we're likely to fill it will unreadable
29 # root-owned files.
30 home_owner = os.stat(home).st_uid
31 if home_owner != 0:
32 import pwd
33 from logging import info
34 old_home = home
35 home = pwd.getpwuid(0).pw_dir or '/'
36 info(_("$HOME (%(home)s) is owned by user %(user)d, but we are root (0). Using %(root_home)s instead."), {'home': old_home, 'user': home_owner, 'root_home': home})
37 del old_home
38 del home_owner
40 portable_base = os.environ.get('ZEROINSTALL_PORTABLE_BASE')
41 if portable_base:
42 xdg_data_dirs = [os.path.join(portable_base, "data")]
43 xdg_cache_dirs = [os.path.join(portable_base, "cache")]
44 xdg_config_dirs = [os.path.join(portable_base, "config")]
45 else:
46 if os.name == "nt":
47 from win32com.shell import shell, shellcon
48 appData = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0)
49 localAppData = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, 0, 0)
50 commonAppData = shell.SHGetFolderPath(0, shellcon.CSIDL_COMMON_APPDATA, 0, 0)
52 _default_paths = {
53 'DATA' : [appData, commonAppData],
54 'CACHE' : [localAppData, commonAppData],
55 'CONFIG' : [appData, commonAppData],
57 else:
58 _default_paths = {
59 'DATA' : [os.path.join(home, '.local', 'share'), '/usr/local/share', '/usr/share'],
60 'CACHE' : [os.path.join(home, '.cache'), '/var/cache'],
61 'CONFIG' : [os.path.join(home, '.config'), '/etc/xdg'],
64 def _get_path(home_var, dirs_var, default_paths):
65 paths = default_paths
67 x = os.environ.get(home_var, None)
68 if x is not None:
69 paths[0] = x
71 x = os.environ.get(dirs_var, None)
72 if x is not None:
73 paths[1:] = filter(None, x.split(os.path.pathsep))
75 return paths
77 xdg_data_dirs = _get_path('XDG_DATA_HOME', 'XDG_DATA_DIRS', _default_paths['DATA'])
78 xdg_cache_dirs = _get_path('XDG_CACHE_HOME', 'XDG_CACHE_DIRS', _default_paths['CACHE'])
79 xdg_config_dirs = _get_path('XDG_CONFIG_HOME', 'XDG_CONFIG_DIRS', _default_paths['CONFIG'])
81 # Maybe we should get rid of these?
82 xdg_data_home = xdg_data_dirs[0]
83 xdg_cache_home = xdg_cache_dirs[0]
84 xdg_config_home = xdg_config_dirs[0]
86 def save_config_path(*resource):
87 """Ensure $XDG_CONFIG_HOME/<resource>/ exists, and return its path.
88 'resource' should normally be the name of your application. Use this
89 when SAVING configuration settings. Use the xdg_config_dirs variable
90 for loading."""
91 resource = os.path.join(*resource)
92 assert not os.path.isabs(resource)
93 path = os.path.join(xdg_config_home, resource)
94 if not os.path.isdir(path):
95 os.makedirs(path, 0o700)
96 return path
98 def load_config_paths(*resource):
99 """Returns an iterator which gives each directory named 'resource' in the
100 configuration search path. Information provided by earlier directories should
101 take precedence over later ones (ie, the user's config dir comes first)."""
102 resource = os.path.join(*resource)
103 for config_dir in xdg_config_dirs:
104 path = os.path.join(config_dir, resource)
105 if os.path.exists(path): yield path
107 def load_first_config(*resource):
108 """Returns the first result from load_config_paths, or None if there is nothing
109 to load."""
110 for x in load_config_paths(*resource):
111 return x
112 return None
114 def save_cache_path(*resource):
115 """Ensure $XDG_CACHE_HOME/<resource>/ exists, and return its path.
116 'resource' should normally be the name of your application."""
117 resource = os.path.join(*resource)
118 assert not os.path.isabs(resource)
119 path = os.path.join(xdg_cache_home, resource)
120 if not os.path.isdir(path):
121 os.makedirs(path, 0o700)
122 return path
124 def load_cache_paths(*resource):
125 """Returns an iterator which gives each directory named 'resource' in the
126 cache search path. Information provided by earlier directories should
127 take precedence over later ones (ie, the user's cache dir comes first)."""
128 resource = os.path.join(*resource)
129 for cache_dir in xdg_cache_dirs:
130 path = os.path.join(cache_dir, resource)
131 if os.path.exists(path): yield path
133 def load_first_cache(*resource):
134 """Returns the first result from load_cache_paths, or None if there is nothing
135 to load."""
136 for x in load_cache_paths(*resource):
137 return x
138 return None
140 def load_data_paths(*resource):
141 """Returns an iterator which gives each directory named 'resource' in the
142 shared data search path. Information provided by earlier directories should
143 take precedence over later ones.
144 @since: 0.28"""
145 resource = os.path.join(*resource)
146 for data_dir in xdg_data_dirs:
147 path = os.path.join(data_dir, resource)
148 if os.path.exists(path): yield path
150 def load_first_data(*resource):
151 """Returns the first result from load_data_paths, or None if there is nothing
152 to load.
153 @since: 0.28"""
154 for x in load_data_paths(*resource):
155 return x
156 return None
158 def save_data_path(*resource):
159 """Ensure $XDG_DATA_HOME/<resource>/ exists, and return its path.
160 'resource' should normally be the name of your application."""
161 resource = os.path.join(*resource)
162 assert not os.path.isabs(resource)
163 path = os.path.join(xdg_data_home, resource)
164 if not os.path.isdir(path):
165 os.makedirs(path, 0o700)
166 return path