99# the GNU General License
1010# ----------------------------------------------------------------------
1111
12- from . errors import Error
12+ import json
1313
14- TRUE = true = True
15- FALSE = false = False
14+ from typing import Dict
15+ from typing import List
16+ from typing import Any
1617
18+ from .errors import Error
1719
1820__all__ = ['Option' , 'Options' , 'ANYTYPE' ]
1921
2022
21- class ANYTYPE ( object ) :
23+ class ANYTYPE :
2224 """ Dummy class to signal any value
2325 """
2426 pass
@@ -64,31 +66,45 @@ def __str__(self):
6466 % (self .value , self .option , self .type )
6567
6668
69+ class InvalidConfigInitialization (Error ):
70+ def __init__ (self , invalid_value ):
71+ self .invalid_value = invalid_value
72+
73+ def __str__ (self ):
74+ return "Invalid value for config initialization"
75+
76+
6777# ----------------------------------------------------------------------
6878# This class interfaces an Options Container
6979# ----------------------------------------------------------------------
70- class Option (object ):
71- """ A simple container
80+ class Option :
81+ """ A simple container for options with optional type checking
82+ on vale assignation.
7283 """
73- def __init__ (self , name , type_ , value = None ):
84+ def __init__ (self , name : str , type_ , value = None ):
7485 self .name = name
7586 self .type = type_
7687 self .value = value
77- self .stack = [] # An option stack
88+ self .stack : List [ Any ] = [] # An option stack
7889
7990 @property
80- def value (self ):
91+ def value (self ) -> Any :
8192 return self .__value
8293
8394 @value .setter
8495 def value (self , value ):
85- if self .type is not None and not isinstance (value , self .type ):
96+ if value is not None and self .type is not None and not isinstance (value , self .type ):
8697 try :
87- value = eval (value )
98+ if isinstance (value , str ) and self .type == bool :
99+ value = {'false' : False , 'true' : True }[value .lower ()]
100+ else :
101+ value = self .type (value )
88102 except TypeError :
89103 pass
90104 except ValueError :
91105 pass
106+ except KeyError :
107+ pass
92108
93109 if value is not None and not isinstance (value , self .type ):
94110 raise InvalidValueError (self .name , self .type , value )
@@ -102,63 +118,81 @@ def push(self, value=None):
102118 self .stack .append (self .value )
103119 self .value = value
104120
105- def pop (self ):
106- result = self .value
107-
108- try :
109- self .value = self .stack .pop ()
110- except IndexError :
121+ def pop (self ) -> Any :
122+ if not self .stack :
111123 raise OptionStackUnderflowError (self .name )
112124
125+ result = self .value
126+ self .value = self .stack .pop ()
113127 return result
114128
115129
116130# ----------------------------------------------------------------------
117131# This class interfaces an Options Container
118132# ----------------------------------------------------------------------
119- class Options (object ):
120- def __init__ (self ):
121- self .options = None
122- self .reset ()
133+ class Options :
134+ """ Class to store config options.
135+ """
136+ def __init__ (self , init_value = None ):
137+ self ._options : Dict [str , Option ] = {}
123138
124- def reset (self ):
125- if self .options is None :
126- self .options = {}
139+ if init_value is not None :
140+ if isinstance (init_value , dict ):
141+ self ._options = init_value
142+ elif isinstance (init_value , str ):
143+ self ._options = json .loads (init_value )
144+ else :
145+ raise InvalidConfigInitialization (invalid_value = init_value )
127146
128- for opt in list (self . options . keys ()): # converts to list since dict will change size during iteration
129- self .remove_option ( opt )
147+ def reset (self ):
148+ self ._options . clear ( )
130149
131150 def add_option (self , name , type_ = None , default_value = None ):
132- if name in self .options . keys () :
151+ if name in self ._options :
133152 raise DuplicatedOptionError (name )
134153
135154 if type_ is None and default_value is not None :
136155 type_ = type (default_value )
137156 elif type_ is ANYTYPE :
138157 type_ = None
139158
140- self .options [name ] = Option (name , type_ , default_value )
141- setattr (self , name , self .options [name ])
142-
143- def has_option (self , name ):
144- """ Returns whether the given option is defined in this class.
145- """
146- return hasattr (self , name )
159+ self ._options [name ] = Option (name , type_ , default_value )
147160
148161 def add_option_if_not_defined (self , name , type_ = None , default_value = None ):
149- if self .has_option ( name ) :
162+ if name in self ._options :
150163 return
151164 self .add_option (name , type_ , default_value )
152165
153- def remove_option (self , name ):
154- if name not in self .options .keys ():
155- raise UndefinedOptionError (name )
166+ def __delattr__ (self , item : str ):
167+ del self [item ]
168+
169+ def __getattr__ (self , item : str ):
170+ return self [item ].value
171+
172+ def __setattr__ (self , key : str , value : Any ):
173+ if key == '_options' :
174+ self .__dict__ [key ] = value
175+ return
176+
177+ self [key ] = value
178+
179+ def __getitem__ (self , item : str ) -> Option :
180+ if item not in self ._options :
181+ raise UndefinedOptionError (option_name = item )
182+
183+ return self ._options [item ]
184+
185+ def __delitem__ (self , item ):
186+ if item not in self ._options :
187+ raise UndefinedOptionError (item )
188+
189+ del self ._options [item ]
156190
157- del self .options [name ]
158- delattr (self , name )
191+ def __setitem__ (self , key : str , value : Any ):
192+ if key not in self ._options :
193+ raise UndefinedOptionError (option_name = key )
159194
160- def option (self , name ):
161- if name not in self .options .keys ():
162- raise UndefinedOptionError (name )
195+ self ._options [key ].value = value
163196
164- return self .options [name ]
197+ def __contains__ (self , item : str ):
198+ return item in self ._options
0 commit comments