Added tests for the system config function.
[electrum-nvc.git] / lib / simple_config.py
1 import ast
2 import threading
3 import os
4
5 from util import user_dir, print_error, print_msg
6
7 SYSTEM_CONFIG_PATH = "/etc/electrum.conf"
8
9 config = None
10
11
12 def get_config():
13     global config
14     return config
15
16
17 def set_config(c):
18     global config
19     config = c
20
21
22 class SimpleConfig(object):
23     """
24     The SimpleConfig class is responsible for handling operations involving
25     configuration files.
26
27     There are 3 different sources of possible configuration values:
28         1. Command line options.
29         2. User configuration (in the user's config directory)
30         3. System configuration (in /etc/)
31     They are taken in order (1. overrides config options set in 2., that
32     override config set in 3.)
33     """
34     def __init__(self, options=None, read_system_config_function=None,
35                  read_user_config_function=None, read_user_dir_function=None):
36
37         # This is the holder of actual options for the current user.
38         self.current_options = {}
39         # This lock needs to be acquired for updating and reading the config in
40         # a thread-safe way.
41         self.lock = threading.RLock()
42         # The path for the config directory. This is set later by init_path()
43         self.path = None
44
45         if options is None:
46             options = {}  # Having a mutable as a default value is a bad idea.
47
48         # The following two functions are there for dependency injection when
49         # testing.
50         if read_system_config_function is None:
51             read_system_config_function = read_system_config
52         if read_user_config_function is None:
53             read_user_config_function = read_user_config
54         if read_user_dir_function is None:
55             self.user_dir = user_dir
56         else:
57             self.user_dir = read_user_dir_function
58
59         # Save the command-line keys to make sure we don't override them.
60         self.command_line_keys = options.keys()
61         # Save the system config keys to make sure we don't override them.
62         self.system_config_keys = []
63
64         if options.get('portable') is not True:
65             # system conf
66             system_config = read_system_config_function()
67             self.system_config_keys = system_config.keys()
68             self.current_options.update(system_config)
69
70         # update the current options with the command line options last (to
71         # override both others).
72         self.current_options.update(options)
73
74         # init path
75         self.init_path()
76
77         # user config.
78         self.user_config = read_user_config_function(self.path)
79         # The user config is overwritten by the current config!
80         self.user_config.update(self.current_options)
81         self.current_options = self.user_config
82
83         set_config(self)  # Make a singleton instance of 'self'
84
85     def init_path(self):
86         # Read electrum path in the command line configuration
87         self.path = self.current_options.get('electrum_path')
88
89         # If not set, use the user's default data directory.
90         if self.path is None:
91             self.path = self.user_dir()
92
93         # Make directory if it does not yet exist.
94         if not os.path.exists(self.path):
95             os.mkdir(self.path)
96
97         print_error( "electrum directory", self.path)
98
99     def set_key(self, key, value, save = True):
100         if not self.is_modifiable(key):
101             print "Warning: not changing key '%s' because it is not modifiable" \
102                   " (passed as command line option or defined in /etc/electrum.conf)"%key
103             return
104
105         with self.lock:
106             self.user_config[key] = value
107             self.current_options[key] = value
108             if save:
109                 self.save_user_config()
110
111         return
112
113     def get(self, key, default=None):
114         out = None
115         with self.lock:
116             out = self.current_options.get(key, default)
117         return out
118
119     def is_modifiable(self, key):
120         if key in self.command_line_keys:
121             return False
122         if key in self.system_config_keys:
123             return False
124         return True
125
126     def save_user_config(self):
127         if not self.path: return
128
129         path = os.path.join(self.path, "config")
130         s = repr(self.user_config)
131         f = open(path,"w")
132         f.write( s )
133         f.close()
134         if self.get('gui') != 'android':
135             import stat
136             os.chmod(path, stat.S_IREAD | stat.S_IWRITE)
137
138 def read_system_config(path=SYSTEM_CONFIG_PATH):
139     """Parse and return the system config settings in /etc/electrum.conf."""
140     result = {}
141     if os.path.exists(path):
142         try:
143             import ConfigParser
144         except ImportError:
145             print "cannot parse electrum.conf. please install ConfigParser"
146             return
147
148         p = ConfigParser.ConfigParser()
149         try:
150             p.read(path)
151             for k, v in p.items('client'):
152                 result[k] = v
153         except (ConfigParser.NoSectionError, ConfigParser.MissingSectionHeaderError):
154             pass
155
156     return result
157
158 def read_user_config(path):
159     """Parse and store the user config settings in electrum.conf into user_config[]."""
160     if not path: return
161
162     config_path = os.path.join(path, "config")
163     if os.path.exists(config_path):
164         try:
165             with open(config_path, "r") as f:
166                 data = f.read()
167         except IOError:
168             return
169         try:
170             d = ast.literal_eval( data )  #parse raw data from reading wallet file
171         except Exception:
172             print_msg("Error: Cannot read config file.")
173             return
174
175         return d