nkjnm

Dependencies:   MAX44000 nexpaq_mdk

Fork of LED_Demo by Maxim nexpaq

Committer:
nexpaq
Date:
Sat Sep 17 16:32:05 2016 +0000
Revision:
1:55a6170b404f
checking in for sharing

Who changed what in which revision?

UserRevisionLine numberNew contents of line
nexpaq 1:55a6170b404f 1 """
nexpaq 1:55a6170b404f 2 mbed SDK
nexpaq 1:55a6170b404f 3 Copyright (c) 2011-2013 ARM Limited
nexpaq 1:55a6170b404f 4
nexpaq 1:55a6170b404f 5 Licensed under the Apache License, Version 2.0 (the "License");
nexpaq 1:55a6170b404f 6 you may not use this file except in compliance with the License.
nexpaq 1:55a6170b404f 7 You may obtain a copy of the License at
nexpaq 1:55a6170b404f 8
nexpaq 1:55a6170b404f 9 http://www.apache.org/licenses/LICENSE-2.0
nexpaq 1:55a6170b404f 10
nexpaq 1:55a6170b404f 11 Unless required by applicable law or agreed to in writing, software
nexpaq 1:55a6170b404f 12 distributed under the License is distributed on an "AS IS" BASIS,
nexpaq 1:55a6170b404f 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
nexpaq 1:55a6170b404f 14 See the License for the specific language governing permissions and
nexpaq 1:55a6170b404f 15 limitations under the License.
nexpaq 1:55a6170b404f 16 """
nexpaq 1:55a6170b404f 17 import sys
nexpaq 1:55a6170b404f 18 import inspect
nexpaq 1:55a6170b404f 19 import os
nexpaq 1:55a6170b404f 20 import argparse
nexpaq 1:55a6170b404f 21 import math
nexpaq 1:55a6170b404f 22 from os import listdir, remove, makedirs
nexpaq 1:55a6170b404f 23 from shutil import copyfile
nexpaq 1:55a6170b404f 24 from os.path import isdir, join, exists, split, relpath, splitext, abspath
nexpaq 1:55a6170b404f 25 from os.path import commonprefix, normpath
nexpaq 1:55a6170b404f 26 from subprocess import Popen, PIPE, STDOUT, call
nexpaq 1:55a6170b404f 27 import json
nexpaq 1:55a6170b404f 28 from collections import OrderedDict
nexpaq 1:55a6170b404f 29 import logging
nexpaq 1:55a6170b404f 30
nexpaq 1:55a6170b404f 31 def compile_worker(job):
nexpaq 1:55a6170b404f 32 """Standard task runner used for compiling
nexpaq 1:55a6170b404f 33
nexpaq 1:55a6170b404f 34 Positional argumets:
nexpaq 1:55a6170b404f 35 job - a dict containing a list of commands and the remaining arguments
nexpaq 1:55a6170b404f 36 to run_cmd
nexpaq 1:55a6170b404f 37 """
nexpaq 1:55a6170b404f 38 results = []
nexpaq 1:55a6170b404f 39 for command in job['commands']:
nexpaq 1:55a6170b404f 40 try:
nexpaq 1:55a6170b404f 41 _, _stderr, _rc = run_cmd(command, work_dir=job['work_dir'],
nexpaq 1:55a6170b404f 42 chroot=job['chroot'])
nexpaq 1:55a6170b404f 43 except KeyboardInterrupt:
nexpaq 1:55a6170b404f 44 raise ToolException
nexpaq 1:55a6170b404f 45
nexpaq 1:55a6170b404f 46 results.append({
nexpaq 1:55a6170b404f 47 'code': _rc,
nexpaq 1:55a6170b404f 48 'output': _stderr,
nexpaq 1:55a6170b404f 49 'command': command
nexpaq 1:55a6170b404f 50 })
nexpaq 1:55a6170b404f 51
nexpaq 1:55a6170b404f 52 return {
nexpaq 1:55a6170b404f 53 'source': job['source'],
nexpaq 1:55a6170b404f 54 'object': job['object'],
nexpaq 1:55a6170b404f 55 'commands': job['commands'],
nexpaq 1:55a6170b404f 56 'results': results
nexpaq 1:55a6170b404f 57 }
nexpaq 1:55a6170b404f 58
nexpaq 1:55a6170b404f 59 def cmd(command, check=True, verbose=False, shell=False, cwd=None):
nexpaq 1:55a6170b404f 60 """A wrapper to run a command as a blocking job"""
nexpaq 1:55a6170b404f 61 text = command if shell else ' '.join(command)
nexpaq 1:55a6170b404f 62 if verbose:
nexpaq 1:55a6170b404f 63 print text
nexpaq 1:55a6170b404f 64 return_code = call(command, shell=shell, cwd=cwd)
nexpaq 1:55a6170b404f 65 if check and return_code != 0:
nexpaq 1:55a6170b404f 66 raise Exception('ERROR %d: "%s"' % (return_code, text))
nexpaq 1:55a6170b404f 67
nexpaq 1:55a6170b404f 68
nexpaq 1:55a6170b404f 69 def run_cmd(command, work_dir=None, chroot=None, redirect=False):
nexpaq 1:55a6170b404f 70 """Run a command in the forground
nexpaq 1:55a6170b404f 71
nexpaq 1:55a6170b404f 72 Positional arguments:
nexpaq 1:55a6170b404f 73 command - the command to run
nexpaq 1:55a6170b404f 74
nexpaq 1:55a6170b404f 75 Keyword arguments:
nexpaq 1:55a6170b404f 76 work_dir - the working directory to run the command in
nexpaq 1:55a6170b404f 77 chroot - the chroot to run the command in
nexpaq 1:55a6170b404f 78 redirect - redirect the stderr to a pipe to be used later
nexpaq 1:55a6170b404f 79 """
nexpaq 1:55a6170b404f 80 if chroot:
nexpaq 1:55a6170b404f 81 # Conventions managed by the web team for the mbed.org build system
nexpaq 1:55a6170b404f 82 chroot_cmd = [
nexpaq 1:55a6170b404f 83 '/usr/sbin/chroot', '--userspec=33:33', chroot
nexpaq 1:55a6170b404f 84 ]
nexpaq 1:55a6170b404f 85 for element in command:
nexpaq 1:55a6170b404f 86 chroot_cmd += [element.replace(chroot, '')]
nexpaq 1:55a6170b404f 87
nexpaq 1:55a6170b404f 88 logging.debug("Running command %s", ' '.join(chroot_cmd))
nexpaq 1:55a6170b404f 89 command = chroot_cmd
nexpaq 1:55a6170b404f 90 work_dir = None
nexpaq 1:55a6170b404f 91
nexpaq 1:55a6170b404f 92 try:
nexpaq 1:55a6170b404f 93 process = Popen(command, stdout=PIPE,
nexpaq 1:55a6170b404f 94 stderr=STDOUT if redirect else PIPE, cwd=work_dir)
nexpaq 1:55a6170b404f 95 _stdout, _stderr = process.communicate()
nexpaq 1:55a6170b404f 96 except OSError:
nexpaq 1:55a6170b404f 97 print "[OS ERROR] Command: "+(' '.join(command))
nexpaq 1:55a6170b404f 98 raise
nexpaq 1:55a6170b404f 99
nexpaq 1:55a6170b404f 100 return _stdout, _stderr, process.returncode
nexpaq 1:55a6170b404f 101
nexpaq 1:55a6170b404f 102
nexpaq 1:55a6170b404f 103 def run_cmd_ext(command):
nexpaq 1:55a6170b404f 104 """ A version of run command that checks if the command exists befor running
nexpaq 1:55a6170b404f 105
nexpaq 1:55a6170b404f 106 Positional arguments:
nexpaq 1:55a6170b404f 107 command - the command line you are trying to invoke
nexpaq 1:55a6170b404f 108 """
nexpaq 1:55a6170b404f 109 assert is_cmd_valid(command[0])
nexpaq 1:55a6170b404f 110 process = Popen(command, stdout=PIPE, stderr=PIPE)
nexpaq 1:55a6170b404f 111 _stdout, _stderr = process.communicate()
nexpaq 1:55a6170b404f 112 return _stdout, _stderr, process.returncode
nexpaq 1:55a6170b404f 113
nexpaq 1:55a6170b404f 114
nexpaq 1:55a6170b404f 115 def is_cmd_valid(command):
nexpaq 1:55a6170b404f 116 """ Verify that a command exists and is executable
nexpaq 1:55a6170b404f 117
nexpaq 1:55a6170b404f 118 Positional arguments:
nexpaq 1:55a6170b404f 119 command - the command to check
nexpaq 1:55a6170b404f 120 """
nexpaq 1:55a6170b404f 121 caller = get_caller_name()
nexpaq 1:55a6170b404f 122 cmd_path = find_cmd_abspath(command)
nexpaq 1:55a6170b404f 123 if not cmd_path:
nexpaq 1:55a6170b404f 124 error("%s: Command '%s' can't be found" % (caller, command))
nexpaq 1:55a6170b404f 125 if not is_exec(cmd_path):
nexpaq 1:55a6170b404f 126 error("%s: Command '%s' resolves to file '%s' which is not executable"
nexpaq 1:55a6170b404f 127 % (caller, command, cmd_path))
nexpaq 1:55a6170b404f 128 return True
nexpaq 1:55a6170b404f 129
nexpaq 1:55a6170b404f 130
nexpaq 1:55a6170b404f 131 def is_exec(path):
nexpaq 1:55a6170b404f 132 """A simple check to verify that a path to an executable exists
nexpaq 1:55a6170b404f 133
nexpaq 1:55a6170b404f 134 Positional arguments:
nexpaq 1:55a6170b404f 135 path - the executable
nexpaq 1:55a6170b404f 136 """
nexpaq 1:55a6170b404f 137 return os.access(path, os.X_OK) or os.access(path+'.exe', os.X_OK)
nexpaq 1:55a6170b404f 138
nexpaq 1:55a6170b404f 139
nexpaq 1:55a6170b404f 140 def find_cmd_abspath(command):
nexpaq 1:55a6170b404f 141 """ Returns the absolute path to a command.
nexpaq 1:55a6170b404f 142 None is returned if no absolute path was found.
nexpaq 1:55a6170b404f 143
nexpaq 1:55a6170b404f 144 Positional arguhments:
nexpaq 1:55a6170b404f 145 command - the command to find the path of
nexpaq 1:55a6170b404f 146 """
nexpaq 1:55a6170b404f 147 if exists(command) or exists(command + '.exe'):
nexpaq 1:55a6170b404f 148 return os.path.abspath(command)
nexpaq 1:55a6170b404f 149 if not 'PATH' in os.environ:
nexpaq 1:55a6170b404f 150 raise Exception("Can't find command path for current platform ('%s')"
nexpaq 1:55a6170b404f 151 % sys.platform)
nexpaq 1:55a6170b404f 152 path_env = os.environ['PATH']
nexpaq 1:55a6170b404f 153 for path in path_env.split(os.pathsep):
nexpaq 1:55a6170b404f 154 cmd_path = '%s/%s' % (path, command)
nexpaq 1:55a6170b404f 155 if exists(cmd_path) or exists(cmd_path + '.exe'):
nexpaq 1:55a6170b404f 156 return cmd_path
nexpaq 1:55a6170b404f 157
nexpaq 1:55a6170b404f 158
nexpaq 1:55a6170b404f 159 def mkdir(path):
nexpaq 1:55a6170b404f 160 """ a wrapped makedirs that only tries to create a directory if it does not
nexpaq 1:55a6170b404f 161 exist already
nexpaq 1:55a6170b404f 162
nexpaq 1:55a6170b404f 163 Positional arguments:
nexpaq 1:55a6170b404f 164 path - the path to maybe create
nexpaq 1:55a6170b404f 165 """
nexpaq 1:55a6170b404f 166 if not exists(path):
nexpaq 1:55a6170b404f 167 makedirs(path)
nexpaq 1:55a6170b404f 168
nexpaq 1:55a6170b404f 169
nexpaq 1:55a6170b404f 170 def copy_file(src, dst):
nexpaq 1:55a6170b404f 171 """ Implement the behaviour of "shutil.copy(src, dst)" without copying the
nexpaq 1:55a6170b404f 172 permissions (this was causing errors with directories mounted with samba)
nexpaq 1:55a6170b404f 173
nexpaq 1:55a6170b404f 174 Positional arguments:
nexpaq 1:55a6170b404f 175 src - the source of the copy operation
nexpaq 1:55a6170b404f 176 dst - the destination of the copy operation
nexpaq 1:55a6170b404f 177 """
nexpaq 1:55a6170b404f 178 if isdir(dst):
nexpaq 1:55a6170b404f 179 _, base = split(src)
nexpaq 1:55a6170b404f 180 dst = join(dst, base)
nexpaq 1:55a6170b404f 181 copyfile(src, dst)
nexpaq 1:55a6170b404f 182
nexpaq 1:55a6170b404f 183
nexpaq 1:55a6170b404f 184 def delete_dir_files(directory):
nexpaq 1:55a6170b404f 185 """ A function that does rm -rf
nexpaq 1:55a6170b404f 186
nexpaq 1:55a6170b404f 187 Positional arguments:
nexpaq 1:55a6170b404f 188 directory - the directory to remove
nexpaq 1:55a6170b404f 189 """
nexpaq 1:55a6170b404f 190 if not exists(directory):
nexpaq 1:55a6170b404f 191 return
nexpaq 1:55a6170b404f 192
nexpaq 1:55a6170b404f 193 for element in listdir(directory):
nexpaq 1:55a6170b404f 194 to_remove = join(directory, element)
nexpaq 1:55a6170b404f 195 if not isdir(to_remove):
nexpaq 1:55a6170b404f 196 remove(file)
nexpaq 1:55a6170b404f 197
nexpaq 1:55a6170b404f 198
nexpaq 1:55a6170b404f 199 def get_caller_name(steps=2):
nexpaq 1:55a6170b404f 200 """
nexpaq 1:55a6170b404f 201 When called inside a function, it returns the name
nexpaq 1:55a6170b404f 202 of the caller of that function.
nexpaq 1:55a6170b404f 203
nexpaq 1:55a6170b404f 204 Keyword arguments:
nexpaq 1:55a6170b404f 205 steps - the number of steps up the stack the calling function is
nexpaq 1:55a6170b404f 206 """
nexpaq 1:55a6170b404f 207 return inspect.stack()[steps][3]
nexpaq 1:55a6170b404f 208
nexpaq 1:55a6170b404f 209
nexpaq 1:55a6170b404f 210 def error(msg):
nexpaq 1:55a6170b404f 211 """Fatal error, abort hard
nexpaq 1:55a6170b404f 212
nexpaq 1:55a6170b404f 213 Positional arguments:
nexpaq 1:55a6170b404f 214 msg - the message to print before crashing
nexpaq 1:55a6170b404f 215 """
nexpaq 1:55a6170b404f 216 print("ERROR: %s" % msg)
nexpaq 1:55a6170b404f 217 sys.exit(1)
nexpaq 1:55a6170b404f 218
nexpaq 1:55a6170b404f 219
nexpaq 1:55a6170b404f 220 def rel_path(path, base, dot=False):
nexpaq 1:55a6170b404f 221 """Relative path calculation that optionaly always starts with a dot
nexpaq 1:55a6170b404f 222
nexpaq 1:55a6170b404f 223 Positional arguments:
nexpaq 1:55a6170b404f 224 path - the path to make relative
nexpaq 1:55a6170b404f 225 base - what to make the path relative to
nexpaq 1:55a6170b404f 226
nexpaq 1:55a6170b404f 227 Keyword arguments:
nexpaq 1:55a6170b404f 228 dot - if True, the path will always start with a './'
nexpaq 1:55a6170b404f 229 """
nexpaq 1:55a6170b404f 230 final_path = relpath(path, base)
nexpaq 1:55a6170b404f 231 if dot and not final_path.startswith('.'):
nexpaq 1:55a6170b404f 232 final_path = './' + final_path
nexpaq 1:55a6170b404f 233 return final_path
nexpaq 1:55a6170b404f 234
nexpaq 1:55a6170b404f 235
nexpaq 1:55a6170b404f 236 class ToolException(Exception):
nexpaq 1:55a6170b404f 237 """A class representing an exception throw by the tools"""
nexpaq 1:55a6170b404f 238 pass
nexpaq 1:55a6170b404f 239
nexpaq 1:55a6170b404f 240 class NotSupportedException(Exception):
nexpaq 1:55a6170b404f 241 """A class a toolchain not supporting a particular target"""
nexpaq 1:55a6170b404f 242 pass
nexpaq 1:55a6170b404f 243
nexpaq 1:55a6170b404f 244 class InvalidReleaseTargetException(Exception):
nexpaq 1:55a6170b404f 245 pass
nexpaq 1:55a6170b404f 246
nexpaq 1:55a6170b404f 247 def split_path(path):
nexpaq 1:55a6170b404f 248 """spilt a file name into it's directory name, base name, and extension
nexpaq 1:55a6170b404f 249
nexpaq 1:55a6170b404f 250 Positional arguments:
nexpaq 1:55a6170b404f 251 path - the file name to split
nexpaq 1:55a6170b404f 252 """
nexpaq 1:55a6170b404f 253 base, has_ext = split(path)
nexpaq 1:55a6170b404f 254 name, ext = splitext(has_ext)
nexpaq 1:55a6170b404f 255 return base, name, ext
nexpaq 1:55a6170b404f 256
nexpaq 1:55a6170b404f 257
nexpaq 1:55a6170b404f 258 def get_path_depth(path):
nexpaq 1:55a6170b404f 259 """ Given a path, return the number of directory levels present.
nexpaq 1:55a6170b404f 260 This roughly translates to the number of path separators (os.sep) + 1.
nexpaq 1:55a6170b404f 261 Ex. Given "path/to/dir", this would return 3
nexpaq 1:55a6170b404f 262 Special cases: "." and "/" return 0
nexpaq 1:55a6170b404f 263
nexpaq 1:55a6170b404f 264 Positional arguments:
nexpaq 1:55a6170b404f 265 path - the path to calculate the depth of
nexpaq 1:55a6170b404f 266 """
nexpaq 1:55a6170b404f 267 normalized_path = normpath(path)
nexpaq 1:55a6170b404f 268 path_depth = 0
nexpaq 1:55a6170b404f 269 head, tail = split(normalized_path)
nexpaq 1:55a6170b404f 270
nexpaq 1:55a6170b404f 271 while tail and tail != '.':
nexpaq 1:55a6170b404f 272 path_depth += 1
nexpaq 1:55a6170b404f 273 head, tail = split(head)
nexpaq 1:55a6170b404f 274
nexpaq 1:55a6170b404f 275 return path_depth
nexpaq 1:55a6170b404f 276
nexpaq 1:55a6170b404f 277
nexpaq 1:55a6170b404f 278 def args_error(parser, message):
nexpaq 1:55a6170b404f 279 """Abort with an error that was generated by the arguments to a CLI program
nexpaq 1:55a6170b404f 280
nexpaq 1:55a6170b404f 281 Positional arguments:
nexpaq 1:55a6170b404f 282 parser - the ArgumentParser object that parsed the command line
nexpaq 1:55a6170b404f 283 message - what went wrong
nexpaq 1:55a6170b404f 284 """
nexpaq 1:55a6170b404f 285 parser.error(message)
nexpaq 1:55a6170b404f 286 sys.exit(2)
nexpaq 1:55a6170b404f 287
nexpaq 1:55a6170b404f 288
nexpaq 1:55a6170b404f 289 def construct_enum(**enums):
nexpaq 1:55a6170b404f 290 """ Create your own pseudo-enums
nexpaq 1:55a6170b404f 291
nexpaq 1:55a6170b404f 292 Keyword arguments:
nexpaq 1:55a6170b404f 293 * - a member of the Enum you are creating and it's value
nexpaq 1:55a6170b404f 294 """
nexpaq 1:55a6170b404f 295 return type('Enum', (), enums)
nexpaq 1:55a6170b404f 296
nexpaq 1:55a6170b404f 297
nexpaq 1:55a6170b404f 298 def check_required_modules(required_modules, verbose=True):
nexpaq 1:55a6170b404f 299 """ Function checks for Python modules which should be "importable"
nexpaq 1:55a6170b404f 300 before test suite can be used.
nexpaq 1:55a6170b404f 301 @return returns True if all modules are installed already
nexpaq 1:55a6170b404f 302 """
nexpaq 1:55a6170b404f 303 import imp
nexpaq 1:55a6170b404f 304 not_installed_modules = []
nexpaq 1:55a6170b404f 305 for module_name in required_modules:
nexpaq 1:55a6170b404f 306 try:
nexpaq 1:55a6170b404f 307 imp.find_module(module_name)
nexpaq 1:55a6170b404f 308 except ImportError:
nexpaq 1:55a6170b404f 309 # We also test against a rare case: module is an egg file
nexpaq 1:55a6170b404f 310 try:
nexpaq 1:55a6170b404f 311 __import__(module_name)
nexpaq 1:55a6170b404f 312 except ImportError as exc:
nexpaq 1:55a6170b404f 313 not_installed_modules.append(module_name)
nexpaq 1:55a6170b404f 314 if verbose:
nexpaq 1:55a6170b404f 315 print "Error: %s" % exc
nexpaq 1:55a6170b404f 316
nexpaq 1:55a6170b404f 317 if verbose:
nexpaq 1:55a6170b404f 318 if not_installed_modules:
nexpaq 1:55a6170b404f 319 print ("Warning: Module(s) %s not installed. Please install " + \
nexpaq 1:55a6170b404f 320 "required module(s) before using this script.")\
nexpaq 1:55a6170b404f 321 % (', '.join(not_installed_modules))
nexpaq 1:55a6170b404f 322
nexpaq 1:55a6170b404f 323 if not_installed_modules:
nexpaq 1:55a6170b404f 324 return False
nexpaq 1:55a6170b404f 325 else:
nexpaq 1:55a6170b404f 326 return True
nexpaq 1:55a6170b404f 327
nexpaq 1:55a6170b404f 328 def dict_to_ascii(dictionary):
nexpaq 1:55a6170b404f 329 """ Utility function: traverse a dictionary and change all the strings in
nexpaq 1:55a6170b404f 330 the dictionary to ASCII from Unicode. Useful when reading ASCII JSON data,
nexpaq 1:55a6170b404f 331 because the JSON decoder always returns Unicode string. Based on
nexpaq 1:55a6170b404f 332 http://stackoverflow.com/a/13105359
nexpaq 1:55a6170b404f 333
nexpaq 1:55a6170b404f 334 Positional arguments:
nexpaq 1:55a6170b404f 335 dictionary - The dict that contains some Unicode that should be ASCII
nexpaq 1:55a6170b404f 336 """
nexpaq 1:55a6170b404f 337 if isinstance(dictionary, dict):
nexpaq 1:55a6170b404f 338 return OrderedDict([(dict_to_ascii(key), dict_to_ascii(value))
nexpaq 1:55a6170b404f 339 for key, value in dictionary.iteritems()])
nexpaq 1:55a6170b404f 340 elif isinstance(dictionary, list):
nexpaq 1:55a6170b404f 341 return [dict_to_ascii(element) for element in dictionary]
nexpaq 1:55a6170b404f 342 elif isinstance(dictionary, unicode):
nexpaq 1:55a6170b404f 343 return dictionary.encode('ascii')
nexpaq 1:55a6170b404f 344 else:
nexpaq 1:55a6170b404f 345 return dictionary
nexpaq 1:55a6170b404f 346
nexpaq 1:55a6170b404f 347 def json_file_to_dict(fname):
nexpaq 1:55a6170b404f 348 """ Read a JSON file and return its Python representation, transforming all
nexpaq 1:55a6170b404f 349 the strings from Unicode to ASCII. The order of keys in the JSON file is
nexpaq 1:55a6170b404f 350 preserved.
nexpaq 1:55a6170b404f 351
nexpaq 1:55a6170b404f 352 Positional arguments:
nexpaq 1:55a6170b404f 353 fname - the name of the file to parse
nexpaq 1:55a6170b404f 354 """
nexpaq 1:55a6170b404f 355 try:
nexpaq 1:55a6170b404f 356 with open(fname, "r") as file_obj:
nexpaq 1:55a6170b404f 357 return dict_to_ascii(json.load(file_obj,
nexpaq 1:55a6170b404f 358 object_pairs_hook=OrderedDict))
nexpaq 1:55a6170b404f 359 except (ValueError, IOError):
nexpaq 1:55a6170b404f 360 sys.stderr.write("Error parsing '%s':\n" % fname)
nexpaq 1:55a6170b404f 361 raise
nexpaq 1:55a6170b404f 362
nexpaq 1:55a6170b404f 363 # Wowza, double closure
nexpaq 1:55a6170b404f 364 def argparse_type(casedness, prefer_hyphen=False):
nexpaq 1:55a6170b404f 365 def middle(lst, type_name):
nexpaq 1:55a6170b404f 366 def parse_type(string):
nexpaq 1:55a6170b404f 367 """ validate that an argument passed in (as string) is a member of
nexpaq 1:55a6170b404f 368 the list of possible arguments. Offer a suggestion if the case of
nexpaq 1:55a6170b404f 369 the string, or the hyphens/underscores do not match the expected
nexpaq 1:55a6170b404f 370 style of the argument.
nexpaq 1:55a6170b404f 371 """
nexpaq 1:55a6170b404f 372 if prefer_hyphen:
nexpaq 1:55a6170b404f 373 newstring = casedness(string).replace("_", "-")
nexpaq 1:55a6170b404f 374 else:
nexpaq 1:55a6170b404f 375 newstring = casedness(string).replace("-", "_")
nexpaq 1:55a6170b404f 376 if string in lst:
nexpaq 1:55a6170b404f 377 return string
nexpaq 1:55a6170b404f 378 elif string not in lst and newstring in lst:
nexpaq 1:55a6170b404f 379 raise argparse.ArgumentTypeError(
nexpaq 1:55a6170b404f 380 "{0} is not a supported {1}. Did you mean {2}?".format(
nexpaq 1:55a6170b404f 381 string, type_name, newstring))
nexpaq 1:55a6170b404f 382 else:
nexpaq 1:55a6170b404f 383 raise argparse.ArgumentTypeError(
nexpaq 1:55a6170b404f 384 "{0} is not a supported {1}. Supported {1}s are:\n{2}".
nexpaq 1:55a6170b404f 385 format(string, type_name, columnate(lst)))
nexpaq 1:55a6170b404f 386 return parse_type
nexpaq 1:55a6170b404f 387 return middle
nexpaq 1:55a6170b404f 388
nexpaq 1:55a6170b404f 389 # short cuts for the argparse_type versions
nexpaq 1:55a6170b404f 390 argparse_uppercase_type = argparse_type(str.upper, False)
nexpaq 1:55a6170b404f 391 argparse_lowercase_type = argparse_type(str.lower, False)
nexpaq 1:55a6170b404f 392 argparse_uppercase_hyphen_type = argparse_type(str.upper, True)
nexpaq 1:55a6170b404f 393 argparse_lowercase_hyphen_type = argparse_type(str.lower, True)
nexpaq 1:55a6170b404f 394
nexpaq 1:55a6170b404f 395 def argparse_force_type(case):
nexpaq 1:55a6170b404f 396 """ validate that an argument passed in (as string) is a member of the list
nexpaq 1:55a6170b404f 397 of possible arguments after converting it's case.
nexpaq 1:55a6170b404f 398 """
nexpaq 1:55a6170b404f 399 def middle(lst, type_name):
nexpaq 1:55a6170b404f 400 """ The parser type generator"""
nexpaq 1:55a6170b404f 401 def parse_type(string):
nexpaq 1:55a6170b404f 402 """ The parser type"""
nexpaq 1:55a6170b404f 403 for option in lst:
nexpaq 1:55a6170b404f 404 if case(string) == case(option):
nexpaq 1:55a6170b404f 405 return option
nexpaq 1:55a6170b404f 406 raise argparse.ArgumentTypeError(
nexpaq 1:55a6170b404f 407 "{0} is not a supported {1}. Supported {1}s are:\n{2}".
nexpaq 1:55a6170b404f 408 format(string, type_name, columnate(lst)))
nexpaq 1:55a6170b404f 409 return parse_type
nexpaq 1:55a6170b404f 410 return middle
nexpaq 1:55a6170b404f 411
nexpaq 1:55a6170b404f 412 # these two types convert the case of their arguments _before_ validation
nexpaq 1:55a6170b404f 413 argparse_force_uppercase_type = argparse_force_type(str.upper)
nexpaq 1:55a6170b404f 414 argparse_force_lowercase_type = argparse_force_type(str.lower)
nexpaq 1:55a6170b404f 415
nexpaq 1:55a6170b404f 416 def argparse_many(func):
nexpaq 1:55a6170b404f 417 """ An argument parser combinator that takes in an argument parser and
nexpaq 1:55a6170b404f 418 creates a new parser that accepts a comma separated list of the same thing.
nexpaq 1:55a6170b404f 419 """
nexpaq 1:55a6170b404f 420 def wrap(string):
nexpaq 1:55a6170b404f 421 """ The actual parser"""
nexpaq 1:55a6170b404f 422 return [func(s) for s in string.split(",")]
nexpaq 1:55a6170b404f 423 return wrap
nexpaq 1:55a6170b404f 424
nexpaq 1:55a6170b404f 425 def argparse_filestring_type(string):
nexpaq 1:55a6170b404f 426 """ An argument parser that verifies that a string passed in corresponds
nexpaq 1:55a6170b404f 427 to a file"""
nexpaq 1:55a6170b404f 428 if exists(string):
nexpaq 1:55a6170b404f 429 return string
nexpaq 1:55a6170b404f 430 else:
nexpaq 1:55a6170b404f 431 raise argparse.ArgumentTypeError(
nexpaq 1:55a6170b404f 432 "{0}"" does not exist in the filesystem.".format(string))
nexpaq 1:55a6170b404f 433
nexpaq 1:55a6170b404f 434 def columnate(strings, separator=", ", chars=80):
nexpaq 1:55a6170b404f 435 """ render a list of strings as a in a bunch of columns
nexpaq 1:55a6170b404f 436
nexpaq 1:55a6170b404f 437 Positional arguments:
nexpaq 1:55a6170b404f 438 strings - the strings to columnate
nexpaq 1:55a6170b404f 439
nexpaq 1:55a6170b404f 440 Keyword arguments;
nexpaq 1:55a6170b404f 441 separator - the separation between the columns
nexpaq 1:55a6170b404f 442 chars - the maximum with of a row
nexpaq 1:55a6170b404f 443 """
nexpaq 1:55a6170b404f 444 col_width = max(len(s) for s in strings)
nexpaq 1:55a6170b404f 445 total_width = col_width + len(separator)
nexpaq 1:55a6170b404f 446 columns = math.floor(chars / total_width)
nexpaq 1:55a6170b404f 447 output = ""
nexpaq 1:55a6170b404f 448 for i, string in zip(range(len(strings)), strings):
nexpaq 1:55a6170b404f 449 append = string
nexpaq 1:55a6170b404f 450 if i != len(strings) - 1:
nexpaq 1:55a6170b404f 451 append += separator
nexpaq 1:55a6170b404f 452 if i % columns == columns - 1:
nexpaq 1:55a6170b404f 453 append += "\n"
nexpaq 1:55a6170b404f 454 else:
nexpaq 1:55a6170b404f 455 append = append.ljust(total_width)
nexpaq 1:55a6170b404f 456 output += append
nexpaq 1:55a6170b404f 457 return output
nexpaq 1:55a6170b404f 458
nexpaq 1:55a6170b404f 459 def argparse_dir_not_parent(other):
nexpaq 1:55a6170b404f 460 """fail if argument provided is a parent of the specified directory"""
nexpaq 1:55a6170b404f 461 def parse_type(not_parent):
nexpaq 1:55a6170b404f 462 """The parser type"""
nexpaq 1:55a6170b404f 463 abs_other = abspath(other)
nexpaq 1:55a6170b404f 464 abs_not_parent = abspath(not_parent)
nexpaq 1:55a6170b404f 465 if abs_not_parent == commonprefix([abs_not_parent, abs_other]):
nexpaq 1:55a6170b404f 466 raise argparse.ArgumentTypeError(
nexpaq 1:55a6170b404f 467 "{0} may not be a parent directory of {1}".format(
nexpaq 1:55a6170b404f 468 not_parent, other))
nexpaq 1:55a6170b404f 469 else:
nexpaq 1:55a6170b404f 470 return not_parent
nexpaq 1:55a6170b404f 471 return parse_type