1# This file is part of QuTiP: Quantum Toolbox in Python.
2#
3#    Copyright (c) 2011 and later, QuSTaR.
4#    All rights reserved.
5#
6#    Redistribution and use in source and binary forms, with or without
7#    modification, are permitted provided that the following conditions are
8#    met:
9#
10#    1. Redistributions of source code must retain the above copyright notice,
11#       this list of conditions and the following disclaimer.
12#
13#    2. Redistributions in binary form must reproduce the above copyright
14#       notice, this list of conditions and the following disclaimer in the
15#       documentation and/or other materials provided with the distribution.
16#
17#    3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names
18#       of its contributors may be used to endorse or promote products derived
19#       from this software without specific prior written permission.
20#
21#    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22#    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23#    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24#    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25#    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26#    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27#    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28#    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29#    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30#    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31#    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32###############################################################################
33import os
34import qutip
35import qutip.settings as qset
36import qutip.solver as def_options
37import warnings
38try:
39    import ConfigParser as configparser #py27
40except:
41    import configparser #py3x
42from functools import partial
43
44
45def getcomplex(self, section, option):
46    return complex(self.get(section, option))
47
48
49configparser.ConfigParser.getcomplex = getcomplex
50
51
52sections = [('qutip', qset)]
53
54
55def full_path(rc_file):
56    rc_file = os.path.expanduser(rc_file)
57    if os.path.isabs(rc_file):
58        return rc_file
59    qutip_conf_dir = os.path.join(os.path.expanduser("~"), '.qutip')
60    return os.path.join(qutip_conf_dir, rc_file)
61
62
63def has_qutip_rc():
64    """
65    Checks to see if the qutiprc file exists in the default
66    location, i.e. HOME/.qutip/qutiprc
67    """
68    qutip_conf_dir = os.path.join(os.path.expanduser("~"), '.qutip')
69    if os.path.exists(qutip_conf_dir):
70        qutip_rc_file = os.path.join(qutip_conf_dir, "qutiprc")
71        qrc_exists = os.path.isfile(qutip_rc_file)
72        if qrc_exists:
73            return True, qutip_rc_file
74        else:
75            return False, ''
76    else:
77        return False, ''
78
79
80def generate_qutiprc(rc_file="qutiprc"):
81    """
82    Generate a blank qutiprc file.
83    """
84    # Check for write access to home dir
85    qutip_rc_file = full_path(rc_file)
86    qutip_conf_dir = os.path.dirname(qutip_rc_file)
87    os.makedirs(qutip_conf_dir, exist_ok=True)
88
89    if os.path.isfile(qutip_rc_file):
90        config = configparser.ConfigParser()
91        config.read(qutip_rc_file)
92        modified = False
93        for section, settings_object in sections:
94            if not config.has_section(section):
95                config.add_section(section)
96                modified = True
97        if modified:
98            with open(qutip_rc_file, 'w') as cfgfile:
99                config.write(cfgfile)
100
101        return modified
102
103    with open(qutip_rc_file,'w') as cfgfile:
104        config = configparser.ConfigParser()
105        for section, settings_object in sections:
106            config.add_section(section)
107        config.write(cfgfile)
108    return True
109
110
111def get_reader(val, config):
112    # The type of the read value is the same as the one presently loaded.
113    if isinstance(val, bool):
114        reader = config.getboolean
115    elif isinstance(val, int):
116        reader = config.getint
117    elif isinstance(val, float):
118        reader = config.getfloat
119    elif isinstance(val, complex):
120        reader = config.getcomplex
121    elif isinstance(val, str):
122        reader = config.get
123    return reader
124
125
126def has_rc_key(key, section=None, rc_file="qutiprc"):
127    """
128    Verify if key exist in section of rc_file
129    """
130    rc_file = full_path(rc_file)
131    if not os.path.isfile(rc_file):
132        return False
133    config = configparser.ConfigParser()
134    config.read(rc_file)
135    if section is None:
136        search_sections = [section for section, _ in sections]
137    else:
138        search_sections = [section]
139    for section in search_sections:
140        if config.has_section(section):
141            opts = config.options(section)
142            if key in opts:
143                return True
144    return False
145
146
147def write_rc_key(key, value, section='qutip', rc_file="qutiprc"):
148    """
149    Writes a single key value to the qutiprc file
150
151    Parameters
152    ----------
153    key : str
154        The key name to be written.
155    value : int/float/bool
156        Value corresponding to given key.
157    section : str
158        String for which settings object the key belong to.
159        Default : qutip
160    rc_file : str
161        String specifying file location.
162        Default : qutiprc
163    """
164    rc_file = full_path(rc_file)
165    if not os.access(rc_file, os.W_OK):
166        warnings.warn("Does not have permissions to write config file")
167        return
168    config = configparser.ConfigParser()
169    config.read(rc_file)
170    if not config.has_section(section):
171        config.add_section(section)
172    config.set(section, key, str(value))
173
174    with open(rc_file, 'w') as cfgfile:
175        config.write(cfgfile)
176
177
178def read_rc_key(key, datatype, section='qutip', rc_file="qutiprc"):
179    """
180    Writes a single key value to the qutiprc file
181
182    Parameters
183    ----------
184    key : str
185        The key name to be written.
186    datatype :
187        Type of the value corresponding to given key.
188        One of [int, float, bool, complex, str]
189    section : str
190        String for which settings object the key belong to.
191    rc_file : str
192        String specifying file location.
193    """
194    rc_file = full_path(rc_file)
195    config = configparser.ConfigParser()
196    config.read(rc_file)
197    if not config.has_section(section):
198        raise ValueError("key not found")
199    reader = get_reader(datatype(0), config)
200    opts = config.options(section)
201    if key not in opts:
202        raise ValueError("key not found")
203    return reader(section, key)
204
205
206def write_rc_object(rc_file, section, object):
207    """
208    Writes all keys and values corresponding to one object qutiprc file.
209
210    Parameters
211    ----------
212    rc_file : str
213        String specifying file location.
214    section : str
215        Tags for the saved data.
216    object : Object
217        Object to save. All attribute's type must be one of bool, int, float,
218        complex, str.
219    """
220    generate_qutiprc(rc_file)
221    config = configparser.ConfigParser()
222    config.read(full_path(rc_file))
223    if not config.has_section(section):
224        config.add_section(section)
225    keys = object.__all
226    for key in keys:
227        config.set(section, key, str(getattr(object, key)))
228    with open(full_path(rc_file), 'w') as cfgfile:
229        config.write(cfgfile)
230    return
231
232
233def load_rc_object(rc_file, section, object):
234    """
235    Read keys and values corresponding to one settings location
236    to the qutiprc file.
237
238    Parameters
239    ----------
240    rc_file : str
241        String specifying file location.
242    section : str
243        Tags for the saved data.
244    object : Object
245        Object to overwrite. All attribute's type must be one of bool, int,
246        float, complex, str.
247    """
248    config = configparser.ConfigParser()
249    config.read(full_path(rc_file))
250    if not config.has_section(section):
251        raise configparser.NoSectionError(section)
252    keys = object.__all
253    opts = config.options(section)
254    for op in opts:
255        if op in keys:
256            reader = get_reader(getattr(object, op), config)
257            setattr(object, op, reader(section, op))
258        else:
259            warnings.warn("Invalid qutip config variable in qutiprc: " + op)
260
261
262def write_rc_qset(rc_file):
263    """
264    Writes qutip.settings in a qutiprc file.
265
266    Parameters
267    ----------
268    rc_file : str
269        String specifying file location.
270    """
271    write_rc_object(rc_file, "qutip", qset)
272
273
274def load_rc_qset(rc_file):
275    """
276    Read qutip.settings to a qutiprc file.
277
278    Parameters
279    ----------
280    rc_file : str
281        String specifying file location.
282    """
283    load_rc_object(rc_file, "qutip", qset)
284
285
286def write_rc_config(rc_file):
287    """
288    Writes all keys and values to the qutiprc file.
289
290    Parameters
291    ----------
292    rc_file : str
293        String specifying file location.
294    """
295    generate_qutiprc(rc_file)
296
297    config = configparser.ConfigParser()
298    config.read(full_path(rc_file))
299    for section, settings_object in sections:
300        keys = settings_object.__all
301        for key in keys:
302            config.set(section, key, str(getattr(settings_object, key)))
303
304    with open(full_path(rc_file), 'w') as cfgfile:
305        config.write(cfgfile)
306    return
307
308
309def load_rc_config(rc_file):
310    """
311    Loads the configuration data from the
312    qutiprc file
313    """
314    config = configparser.ConfigParser()
315    config.read(full_path(rc_file))
316    for section, settings_object in sections:
317        if config.has_section(section):
318            keys = settings_object.__all
319            opts = config.options(section)
320            for op in opts:
321                if op in keys:
322                    reader = get_reader(getattr(settings_object, op), config)
323                    setattr(settings_object, op,
324                            reader(section, op))
325                else:
326                    warnings.warn("Invalid qutip config variable in qutiprc: "
327                                  + op)
328                    # raise Exception('Invalid config variable in qutiprc.')
329        else:
330            warnings.warn("Section " + section + " not found ")
331            # raise configparser.NoSectionError('qutip')
332
333    if config.has_section('compiler'):
334        _valid_keys = ['CC', 'CXX']
335        opts = config.options('compiler')
336        for op in opts:
337            up_op = op.upper()
338            if up_op in _valid_keys:
339                os.environ[up_op] = config.get('compiler', op)
340            else:
341                # raise Exception('Invalid config variable in qutiprc.')
342                warnings.warn("Invalid compiler config variable in qutiprc: "
343                              + op)
344