• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

johntruckenbrodt / pyroSAR / 8967262012

06 May 2024 09:54AM UTC coverage: 52.274% (+0.4%) from 51.879%
8967262012

Pull #305

github

johntruckenbrodt
Merge branch 'main' of https://github.com/johntruckenbrodt/pyroSAR into feature/config
Pull Request #305: revise configuration handling

154 of 174 new or added lines in 5 files covered. (88.51%)

1 existing line in 1 file now uncovered.

3643 of 6969 relevant lines covered (52.27%)

0.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.33
/pyroSAR/config.py
1
# -*- coding: utf-8 -*-
2
###############################################################################
3
# pyroSAR configuration handling
4

5
# Copyright (c) 2018-2024, the pyroSAR Developers.
6

7
# This file is part of the pyroSAR Project. It is subject to the
8
# license terms in the LICENSE.txt file found in the top-level
9
# directory of this distribution and at
10
# https://github.com/johntruckenbrodt/pyroSAR/blob/master/LICENSE.txt.
11
# No part of the pyroSAR project, including this file, may be
12
# copied, modified, propagated, or distributed except according
13
# to the terms contained in the LICENSE.txt file.
14
###############################################################################
15
import os
1✔
16
import json
1✔
17

18
import configparser as ConfigParser
1✔
19

20
__LOCAL__ = ['acquisition_mode', 'coordinates', 'cycleNumber', 'frameNumber',
1✔
21
             'lines', 'orbit', 'orbitNumber_abs', 'orbitNumber_rel',
22
             'polarizations', 'product', 'projection', 'samples',
23
             'sensor', 'spacing', 'start', 'stop']
24

25

26
class Singleton(type):
1✔
27
    """
28
    Define an Instance operation that lets clients access its unique instance.
29
    https://sourcemaking.com/design_patterns/singleton/python/1
30
    """
31
    
32
    def __init__(cls, name, bases, attrs, **kwargs):
1✔
33
        super().__init__(name, bases, attrs)
1✔
34
        cls._instance = None
1✔
35
    
36
    def __call__(cls, *args, **kwargs):
1✔
37
        if cls._instance is None:
1✔
38
            cls._instance = super().__call__(*args, **kwargs)
1✔
39
        return cls._instance
1✔
40

41

42
class ConfigHandler(metaclass=Singleton):
1✔
43
    """
44
    ConfigHandler is a configuration handler for pyroSAR. It is intended to be called by a class's '__init__' and
45
    set or get the configuration parameters throughout an entire package.
46
    The primary goal with ConfigHandler is to load a single, consistent configuration environment to be passed 
47
    amongst ALL objects within a package.
48
        
49
    ConfigHandler is a SINGLETON, meaning once instantiated, THE SAME OBJECT
50
    will be returned to every class object calling it.
51

52
    Parameters
53
    ----------
54
    path : str or None
55
        A path where the .pyrosar directory will be created. If None (default) it will be created in the user home
56
        directory.
57
    config_fname : str
58
        Name of the config file. Default is 'config.ini'.
59
    
60
    Methods
61
    -------
62
    make_dir : Create a .pyrosar directory in home directory.
63
    create_config : Create a config.ini file in .pyrosar directory.
64
    open : Open the config.ini file.
65
    add_section : Create a new section in the configuration.
66
    set : Set an option in the configuration.
67
    remove_option : Remove an option in the configuration.
68

69
    Notes
70
    -----
71
    The syntax is the same as in ConfigParser. Here, keys are called options.
72

73
    """
74
    
75
    # Define __setter to control changeable keys (optional)
76
    # __setter = ["etc", "auxdata"]
77
    
78
    def __init__(self):
1✔
79
        path = os.path.join(os.path.expanduser('~'), '.pyrosar')
1✔
80
        
81
        self.__GLOBAL = {
1✔
82
            'path': path,
83
            'config_fname': 'config.ini',
84
            'config': os.path.join(path, 'config.ini'),
85
        }
86
        
87
        if not os.path.isfile(self.__GLOBAL['config']):
1✔
88
            self.__create_config()
1✔
89
        
90
        self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
1✔
91
        self.parser.optionxform = str
1✔
92
        self.parser.read(self.__GLOBAL['config'])
1✔
93
    
94
    def __create_config(self):
1✔
95
        """
96
        Create a config.ini file in .pyrosar directory.
97

98
        Returns
99
        -------
100
        None
101
        """
102
        
103
        if not os.path.exists(self.__GLOBAL['path']):
1✔
104
            os.makedirs(self.__GLOBAL['path'])
1✔
105
        
106
        with open(self.__GLOBAL['config'], 'w'):
1✔
107
            pass
1✔
108
    
109
    def __str__(self):
1✔
110
        items = []
×
111
        for section in self.parser.sections():
×
112
            items.append('  Section: {0}\n'.format(section))
×
113
            
114
            for options in self.parser.options(section):
×
115
                items.append('    x {0} :: {1} :: {2}\n'
×
116
                             .format(options,
117
                                     self.parser.get(section, options),
118
                                     str(type(options))))
NEW
119
        out = f'Class    : {self.__class__.__name__}\n' \
×
120
              f'Path     : {self.__GLOBAL["config"]}\n' \
121
              f'Sections : {len(self.parser.sections())}\n' \
122
              f'Contents : \n{"".join(items)}'
123
        
124
        return out
×
125
    
126
    def __getitem__(self, section):
1✔
127
        if not self.parser.has_section(section):
1✔
128
            raise AttributeError('Section {0} does not exist.'.format(str(section)))
×
129
        return dict(self.parser.items(section))
1✔
130
    
131
    @property
1✔
132
    def sections(self):
1✔
133
        return self.parser.sections()
1✔
134
    
135
    def keys(self, section):
1✔
136
        """
137
        Get all keys (options) of a section.
138

139
        Parameters
140
        ----------
141
        section : str
142
            Section name.
143

144
        Returns
145
        -------
146
        list : options (keys) of a section.
147

148
        """
149
        return self.parser.options(section)
1✔
150
    
151
    def open(self):
1✔
152
        """
153
        Open the config.ini file. This method will open the config.ini
154
        file in an external standard app (text editor).
155

156
        Returns
157
        -------
158
        os.startfile
159

160
        """
161
        
162
        os.startfile(self.__GLOBAL['config'])
×
163
    
164
    def add_section(self, section):
1✔
165
        """
166
        Create a new section in the configuration.
167

168
        Parameters
169
        ----------
170
        section : str
171
            Section name
172

173
        Returns
174
        -------
175
        None
176

177
        """
178
        if not self.parser.has_section(section):
1✔
179
            self.parser.add_section(section)
1✔
180
            self.write()
1✔
181
        else:
182
            raise RuntimeError('section already exists')
×
183
    
184
    @property
1✔
185
    def file(self):
1✔
186
        return self.__GLOBAL['config']
1✔
187
    
188
    def set(self, section, key, value, overwrite=False):
1✔
189
        """
190
        Set an option.
191

192
        Parameters
193
        ----------
194
        section : str
195
            Section name.
196
        key : str
197
            the attribute name
198
        value :
199
            the attribute value
200
        overwrite : bool
201
            If True and the defined key exists the value will be overwritten.
202

203
        Returns
204
        -------
205

206
        """
207
        if not self.parser.has_section(section):
1✔
208
            raise AttributeError('Section {0} does not exist.'.format(str(section)))
1✔
209
        
210
        if isinstance(value, list):
1✔
211
            value = json.dumps(value)
×
212
        
213
        if key in self.parser.options(section) and not overwrite:
1✔
214
            raise RuntimeError('Value already exists.')
1✔
215
        
216
        self.parser.set(section, key, value)
1✔
217
        self.write()
1✔
218
    
219
    def remove_option(self, section, key):
1✔
220
        """
221
        Remove an option and key.
222

223
        Parameters
224
        ----------
225
        section : str
226
            Section name.
227
        key : str
228
            Key value.
229
        
230
        Returns
231
        -------
232
        
233
        """
234
        if not self.parser.has_section(section):
1✔
235
            raise AttributeError('Section {0} does not exist.'.format(str(section)))
1✔
236
        
237
        if key not in self.parser.options(section):
1✔
238
            raise AttributeError('Key {0} does not exist.'.format(str(key)))
1✔
239
        
240
        self.parser.remove_option(section, key)
1✔
241
        self.write()
1✔
242
    
243
    def remove_section(self, section):
1✔
244
        """
245
        remove a section
246
        
247
        Parameters
248
        ----------
249
        section: str
250
            Section name.
251

252
        Returns
253
        -------
254

255
        """
256
        self.parser.remove_section(section)
1✔
257
        self.write()
1✔
258
    
259
    def write(self):
1✔
260
        with open(self.__GLOBAL['config'], 'w', encoding='utf8') as out:
1✔
261
            self.parser.write(out)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc