Conditions | 34 |
Total Lines | 282 |
Code Lines | 185 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like tool.cli.cli() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.
There are several approaches to avoid long parameter lists:
1 | #!/usr/bin/env python |
||
60 | @click.group( |
||
61 | context_settings={"help_option_names": ["-h", "--help"]}, |
||
62 | cls=DYMGroup, |
||
63 | short_help="Main Isomer CLI" |
||
64 | ) |
||
65 | @click.option( |
||
66 | "--instance", |
||
67 | "-i", |
||
68 | default="default", |
||
69 | help="Name of instance to act on", |
||
70 | metavar="<name>", |
||
71 | ) |
||
72 | @click.option( |
||
73 | "--env", |
||
74 | "--environment", |
||
75 | "-e", |
||
76 | help="Override environment to act on (CAUTION!)", |
||
77 | default=None, |
||
78 | type=click.Choice(["blue", "green", "current", "other"]), |
||
79 | ) |
||
80 | @click.option("--quiet", default=False, help="Suppress all output", is_flag=True) |
||
81 | @click.option( |
||
82 | "--no-colors", "-nc", default=False, help="Do not use colorful output", is_flag=True |
||
83 | ) |
||
84 | @click.option( |
||
85 | "--console-level", |
||
86 | "--clog", |
||
87 | default=None, |
||
88 | help="Log level to use (0-100)", |
||
89 | metavar="<level>", |
||
90 | ) |
||
91 | @click.option( |
||
92 | "--file-level", |
||
93 | "--flog", |
||
94 | default=None, |
||
95 | help="Log level to use (0-100)", |
||
96 | metavar="<level>", |
||
97 | ) |
||
98 | @click.option("--no-log", default=False, is_flag=True, help="Do not log to file") |
||
99 | @click.option("--log-path", default=None, help="Logfile path") |
||
100 | @click.option("--log-file", default=None, help="Logfile name") |
||
101 | @click.option("--dbhost", default=None, help=db_host_help, metavar=db_host_metavar) |
||
102 | @click.option("--dbname", default=None, help=db_help, metavar=db_metavar) |
||
103 | @click.option("--prefix-path", "-p", default=None, help="Use different system prefix") |
||
104 | @click.option("--config-path", "-c", default="/etc/isomer", |
||
105 | help="System configuration path") |
||
106 | @click.option("--fat-logo", "--fat", hidden=True, is_flag=True, default=False) |
||
107 | @click.pass_context |
||
108 | def cli( |
||
109 | ctx, |
||
110 | instance, |
||
111 | env, |
||
112 | quiet, |
||
113 | no_colors, |
||
114 | console_level, |
||
115 | file_level, |
||
116 | no_log, |
||
117 | log_path, |
||
118 | log_file, |
||
119 | dbhost, |
||
120 | dbname, |
||
121 | prefix_path, |
||
122 | config_path, |
||
123 | fat_logo, |
||
124 | ): |
||
125 | """Isomer Management Tool |
||
126 | |||
127 | This tool supports various operations to manage Isomer instances. |
||
128 | |||
129 | Most of the commands are grouped. To obtain more information about the |
||
130 | groups' available sub commands/groups, try |
||
131 | |||
132 | iso [group] |
||
133 | |||
134 | To display details of a command or its subgroups, try |
||
135 | |||
136 | iso [group] [subgroup] [..] [command] --help |
||
137 | |||
138 | To get a map of all available commands, try |
||
139 | |||
140 | iso cmdmap |
||
141 | """ |
||
142 | |||
143 | ctx.obj["quiet"] = quiet |
||
144 | |||
145 | def _set_verbosity(): |
||
146 | if quiet: |
||
147 | console_setting = 100 |
||
148 | else: |
||
149 | console_setting = int( |
||
150 | console_level if console_level is not None else 20 |
||
151 | ) |
||
152 | |||
153 | if no_log: |
||
154 | file_setting = 100 |
||
155 | else: |
||
156 | file_setting = int(file_level if file_level is not None else 20) |
||
157 | |||
158 | global_setting = min(console_setting, file_setting) |
||
159 | set_verbosity(global_setting, console_setting, file_setting) |
||
160 | |||
161 | def _set_logger(): |
||
162 | if log_path is not None or log_file is not None: |
||
163 | set_logfile(log_path, instance, log_file) |
||
164 | |||
165 | if no_colors is False: |
||
166 | set_color() |
||
167 | |||
168 | _set_verbosity() |
||
169 | _set_logger() |
||
170 | |||
171 | ctx.obj["instance"] = instance |
||
172 | |||
173 | log("Running with Python", sys.version.replace("\n", ""), sys.platform, lvl=verbose) |
||
174 | log("Interpreter executable:", sys.executable, lvl=verbose) |
||
175 | |||
176 | set_etc_path(config_path) |
||
177 | configuration = load_configuration() |
||
178 | |||
179 | if configuration is not None: |
||
180 | ctx.obj["config"] = configuration |
||
181 | else: |
||
182 | if ctx.invoked_subcommand not in ("version", "cmdmap"): |
||
183 | log("No configuration found. Most commands won't work. " |
||
184 | "Use 'iso system configure' to generate a configuration.", lvl=warn) |
||
185 | return |
||
186 | |||
187 | set_prefix_path(configuration['meta']['prefix']) |
||
188 | |||
189 | instances = load_instances() |
||
190 | |||
191 | ctx.obj["instances"] = instances |
||
192 | |||
193 | if instance not in instances: |
||
194 | log( |
||
195 | "No instance configuration called %s found! Using fresh defaults." |
||
196 | % instance, |
||
197 | lvl=warn, |
||
198 | ) |
||
199 | instance_configuration = instance_template |
||
200 | else: |
||
201 | instance_configuration = instances[instance] |
||
202 | |||
203 | if file_level is None and console_level is None: |
||
204 | instance_log_level = int(instance_configuration["loglevel"]) |
||
205 | |||
206 | set_verbosity(instance_log_level, file_level=instance_log_level) |
||
207 | log("Instance log level set to", instance_log_level, lvl=verbose) |
||
208 | |||
209 | ctx.obj["instance_configuration"] = instance_configuration |
||
210 | |||
211 | instance_environment = instance_configuration["environment"] |
||
212 | |||
213 | if env is not None: |
||
214 | if env == "current": |
||
215 | ctx.obj["acting_environment"] = instance_environment |
||
216 | elif env == "other": |
||
217 | ctx.obj["acting_environment"] = ( |
||
218 | "blue" if instance_environment == "green" else "blue" |
||
219 | ) |
||
220 | else: |
||
221 | ctx.obj["acting_environment"] = env |
||
222 | env = ctx.obj["acting_environment"] |
||
223 | else: |
||
224 | env = instance_configuration["environment"] |
||
225 | ctx.obj["acting_environment"] = None |
||
226 | |||
227 | def get_environment_toggle(platform, toggles): |
||
228 | """Checks well known methods to determine if the other environment should be |
||
229 | booted instead of the default environment.""" |
||
230 | |||
231 | def temp_file_toggle(): |
||
232 | """Check by looking for a state file in /tmp""" |
||
233 | |||
234 | state_filename = "/tmp/isomer_toggle_%s" % instance_configuration["name"] |
||
235 | log("Checking for override state file ", state_filename, lvl=debug) |
||
236 | |||
237 | if os.path.exists(state_filename): |
||
238 | log("Environment override state file found!", lvl=warn) |
||
239 | return True |
||
240 | else: |
||
241 | log("Environment override state file not found", lvl=debug) |
||
242 | return False |
||
243 | |||
244 | def gpio_switch_toggle(): |
||
245 | """Check by inspection of a GPIO pin for a closed switch""" |
||
246 | |||
247 | log("Checking for override GPIO switch on channel ", RPI_GPIO_CHANNEL, |
||
248 | lvl=debug) |
||
249 | |||
250 | if platform != "rpi": |
||
251 | log( |
||
252 | "Environment toggle: " |
||
253 | "GPIO switch can only be handled on Raspberry Pi!", |
||
254 | lvl=critical |
||
255 | ) |
||
256 | return False |
||
257 | else: |
||
258 | try: |
||
259 | import RPi.GPIO as GPIO |
||
260 | except ImportError: |
||
261 | log("RPi Python module not found. " |
||
262 | "This only works on a Raspberry Pi!", lvl=critical) |
||
263 | return False |
||
264 | |||
265 | GPIO.setmode(GPIO.BOARD) |
||
266 | GPIO.setup(RPI_GPIO_CHANNEL, GPIO.IN) |
||
267 | |||
268 | state = GPIO.input(RPI_GPIO_CHANNEL) is True |
||
269 | |||
270 | if state: |
||
271 | log("Environment override switch active!", lvl=warn) |
||
272 | else: |
||
273 | log("Environment override switch not active", lvl=debug) |
||
274 | |||
275 | return state |
||
276 | |||
277 | toggle = False |
||
278 | if "temp_file" in toggles: |
||
279 | toggle = toggle or temp_file_toggle() |
||
280 | if "gpio_switch" in toggles: |
||
281 | toggle = toggle or gpio_switch_toggle() |
||
282 | |||
283 | if toggle: |
||
284 | log("Booting other Environment per user request.") |
||
285 | else: |
||
286 | log("Booting active environment", lvl=debug) |
||
287 | |||
288 | return toggle |
||
289 | |||
290 | #log(configuration['meta'], pretty=True) |
||
291 | #log(instance_configuration, pretty=True) |
||
292 | |||
293 | if get_environment_toggle(configuration["meta"]["platform"], |
||
294 | instance_configuration['environment_toggles'] |
||
295 | ): |
||
296 | if env == 'blue': |
||
297 | env = 'green' |
||
298 | else: |
||
299 | env = 'blue' |
||
300 | |||
301 | ctx.obj["environment"] = env |
||
302 | |||
303 | if not fat_logo: |
||
304 | log("<> Isomer", version_info, " [%s|%s]" % (instance, env), lvl=99) |
||
305 | else: |
||
306 | from isomer.misc import logo |
||
307 | |||
308 | pad = len(logo.split("\n", maxsplit=1)[0]) |
||
309 | log(("Isomer %s" % version_info).center(pad), lvl=99) |
||
310 | for line in logo.split("\n"): |
||
311 | log(line, lvl=99) |
||
312 | |||
313 | if dbname is None: |
||
314 | dbname = instance_configuration["environments"][env]["database"] |
||
315 | if dbname in ("", None) and ctx.invoked_subcommand in ( |
||
316 | "config", |
||
317 | "db", |
||
318 | "environment", |
||
319 | "plugin", |
||
320 | ): |
||
321 | log( |
||
322 | "Database for this instance environment is unset, " |
||
323 | "you probably have to install the environment first.", |
||
324 | lvl=warn, |
||
325 | ) |
||
326 | |||
327 | if dbhost is None: |
||
328 | dbhost = "%s:%i" % ( |
||
329 | instance_configuration["database_host"], |
||
330 | instance_configuration["database_port"], |
||
331 | ) |
||
332 | |||
333 | ctx.obj["dbhost"] = dbhost |
||
334 | ctx.obj["dbname"] = dbname |
||
335 | |||
336 | set_instance(instance, env, prefix_path) |
||
337 | |||
338 | if log_path is None and log_file is None: |
||
339 | log_path = get_log_path() |
||
340 | |||
341 | set_logfile(log_path, instance, log_file) |
||
342 | |||
360 |