def __call__(self, parser, namespace, values, option_string=None): _eases, _shapes = argutils.easers ease = shape = t0 = t1 = '' items = [] if values: items = [i.split('=') for i in values.split(',')] valid = [len(x) == 2 for x in items] e = "unrecognized merge format: {}".format(values) assert all(valid), ToolError(e, parser.print_help) for key, value in items: if key == 't0': t0 = params.animation.times.type_t0(value) elif key == 't1': t1 = params.animation.times.type_t0(value) elif key == 'ease': ease = argutils.choice(value, _eases, 'ease') elif key == 'shape': shape = argutils.choice(value, _shapes, 'shape') else: e = "unknown merge option/argument: {}={}".format(key, value) raise ToolError(e, parser.print_help) setattr(namespace, self.dest, (t0, t1, ease, shape))
def choice(self, obj, choices, arg=None, convert=str): """checks if a object is in a container of available choices emulates argparse 'choices' function :param obj: `object`, object to check :param choices: `iter`, container of available choices :param arg: `str`, argument used for the call :param convert: `type`, convert object to specified type :return: `obj` if `obj` is in choices :raise: `ToolError` if `obj` not in `choices` or `convert` fails """ choices = [str(x) for x in choices] arg = self._arg(arg) e = ("{arg}: invalid choice: {obj} (choose from {choices})".format( arg=arg, obj=obj, choices=', '.join(choices))) if str(obj) in choices: try: return convert(obj) except ValueError: raise ToolError(e, self.print_help) raise ToolError(e, self.print_help)
def search(self, paths, schema_file=None, msg=None): """searches for valid json files from a list of files or directories :param paths: `str`/`iter`, files/directories to search in; if `str`,, the object is auto converted to a list :param schema_file: `str`, path to json schema file (for validation) :param msg: `str`, override default status message :yields: available json files :raise: `ToolError` if an object in `paths` is invalid """ paths = utils.make_iter(paths) for i, path_in in enumerate(paths, start=1): path = utils.full_path(path_in) is_file_or_dir = os.path.isdir(path) or os.path.isfile(path) e = "not a valid file or directory : " + path_in assert is_file_or_dir, ToolError(e, self.print_help) msg = msg or "checking for json content" msgutils.status(msg, src=path, count=i) if os.path.isfile(path): if self.data(path, schema_file): yield path elif os.path.isdir(path): file_paths = utils.walk_path(path) for path_ in file_paths: if self.data(path_, schema_file): yield path_
def search_raw(self, paths): """searches for RAW file types from a list of files or directories :param paths: `str`/`iter`, files/directories to search in; if `str`,, the object is auto converted to a list :yield: applicable RAW files :raise: `ToolError` if invalid file or directory specified """ paths = utils.make_iter(paths) def valid(p): return re.match(self.raw_pattern, p) for i, path in enumerate(paths, start=1): path = utils.full_path(path) is_file_or_dir = os.path.isdir(path) or os.path.isfile(path) e = "not a valid file or directory : " + path assert is_file_or_dir, ToolError(e, self.print_help) msg = "checking for RAW image data" msgutils.status(msg, src=path, count=i) if os.path.isfile(path) and valid(path): yield path elif os.path.isdir(path): file_paths = utils.walk_path(path, pattern=self.raw_pattern) for file_path in file_paths: yield file_path
def __call__(self, data=None, recipe_file=None, animate=False): """:raise: `ToolError` if invalid view parameter is found in data""" _data = {} if recipe_file: _data.update(jsonutils.data(recipe_file)) if data: _data.update(data) for param, value in _data.items(): if param in self._unsupported: self.unsupported_data[param] = value continue name = key_cls(param) e = "invalid parameter: " + param assert hasattr(self, name), ToolError(e, print_help_obj) cls = getattr(self, name) if animate: if not cls.animation: continue cls = cls.anim cls(value)
def all_or_none(self, allow_none=True, index=None, **kwargs): """requires all kwargs have values :param kwargs: keyword args parsed through for values :param allow_none: `bool`,, allows all kwargs to not have value :param index: `int`, indicates that kwargs are a subset of an array and changes the assertion error to include an index number :return: dict object of kwargs if values present, empty dict if not :raise: `ToolError` specified `kwargs` that both do/don't have values """ def arg_str(l): return ', '.join([self._arg(x) for x in l]) values = kwargs.values() any_ = utils.any_(values, iter_=False) all_ = utils.all_(values, iter_=False) set_args = [k for k, v in kwargs.items() if v] not_set_args = [k for k, v in kwargs.items() if not v] req = [k for k in set_args if k not in not_set_args] req_args = kwargs if set_args else {} must = not (any_ and not all_) if allow_none else all_ e = ("{set_}: requires additional arguments; " "{not_} not set".format(set_=arg_str(req), not_=arg_str(not_set_args))) if index: e = "index {}: ".format(index) + e assert must, ToolError(e, self.print_help) return req_args
def in_range(self, obj, arg=None, range_=(0, 0), type_=int, clip=True): """verifies if a number is in range of the specified range :param obj: `int`/`float`, object to check :param arg: `str`, argument used for the call :param range_: `tuple`, range to check if object is in :param type_: `int`/`float`, expected type :param clip: `bool`, adjust value to be within requested range :return: ``self.number(obj)`` if number in range :raise: `ToolError` if `obj` not in range """ min_, max_ = range_ num = self.number(obj, arg=arg, type_=type_) num = int(round(num)) if type_ is int else num arg = self._arg(arg) lt_gt = self.lt_gt(num, min_, max_) if not clip: e = self.bad_range.format(obj=obj, arg=arg, min_=min_, max_=max_) assert lt_gt is not False, ToolError(e, self.print_help) if lt_gt is False: max_closer = (num - min_) > abs(num - max_) num = max_ if max_closer else min_ return num
def cmd_picture_put(self, args): """put (update) user picture information :param args: `argparse.Namespace`, input args from Web Tool argparse :raise: `ToolError` if specified picture ID failed to ``GET`` """ self._set_print_help(args) msgutils.msg("updating information for picture {}".format( args.picture_id)) before = self._match_album_picture(self.username, args.picture_id) e = "could not perform lookup for picture {}".format(args.picture_id) assert before, ToolError(e, self.print_help) msgutils.msg("before:") msgutils.dumps(before) album_id = before['album_id'] self.put_picture(album_id=album_id, picture_id=args.picture_id, caption=args.caption) after = self.get_picture(album_id=album_id, picture_id=args.picture_id) msgutils.msg("after:") msgutils.dumps(after)
def search(self, paths): """search a file or directory for recipe files :param paths: `list`, list of files or directories to search :yield: valid recipe files :raise: `ToolError` if a specified path is invalid """ paths = utils.make_iter(paths) msg = "searching for valid v{} recipe files".format(self._version) isdir = os.path.isdir isfile = os.path.isfile for i, path in enumerate(paths, start=1): msgutils.status(msg, src=path, count=i) path = os.path.expanduser(path) e = "not a valid file or directory : " + path assert (isdir(path) or isfile(path)), ToolError(e, self.print_help) if os.path.isfile(path) and self.verify(path): yield os.path.abspath(path) elif os.path.isdir(path): file_paths = utils.walk_path(path, ext='json') for file_path in file_paths: if self.verify(file_path): yield os.path.abspath(file_path)
def error(self, message): """ overrides error function provided by argparse :param message: message to display when error occurs :raise: `ToolError` if error occurs """ raise ToolError(message, self.print_help)
def _assert_points(self, points, param): """checks if a animation line contains enough data for a given task :raise: `ToolError` if no or bad amount of animation data points found """ e = param + ": missing animation data" assert points, ToolError(e, self.print_help)
def _assert_response(self, resp, code=200): """:raise: `ToolError` (w/ web msg) if unexpected web response code""" e = 'HTTP {} - {}'.format(resp['code'], resp['msg']) if 'data' in resp and 'message' in resp['data']: e = resp['data']['message'] assert resp['code'] == code, ToolError(e, self.print_help)
def append(self, param, time=None, value=None, dt1=None, dv1=None, t0=None, v0=None, view=None, initial_value=None, default_value=None): """given available parameters, determine the next best value to append :param param: `str`, animation parameter to perform function on :param time: `float`, moment in time to apply value :param value: `float`/`int`, parameter value to apply :param dt1: `list`, succeeding time handle pair values :param dv1: `list`, succeeding value handle pair values :param t0: `int`/`float`, start time, in seconds :param v0: `int`/`float`, start value :param view: the recipe's view parameter value for the corresponding animation parameter :param initial_value: the starting value of the animation parameter :param default_value: the default value of the parameter defined in the LFP schema :return: best guessed t0/v0 values :raise: `ToolError` if no appropriate start value found """ if not any_(t0): t0 = self._auto_buffer if any_(dt1) and any_(time): t0 += time - dt1 elif any_(time): t0 += time if not any_(v0): if any_(dv1) and any_(value): v0 = value - dv1 elif any_(value): v0 = value elif any_(initial_value): v0 = initial_value elif any_(view): v0 = view elif any_(default_value): v0 = default_value else: e = param + ": no starting value found; specify --v0" ToolError(e, self.print_help) return t0, v0
def verify_image_paths(self, lfp_path, image_paths): """verifies all images referenced in an unpacked LFP are present :param lfp_path: `str`, lfp_path being checked :param image_paths: `list`, absolute paths to images :raise: `ToolError` if any LFP image file is missing """ for path in image_paths: e = "missing {} to pack LFP file {}".format(path, lfp_path) assert os.path.isfile(path), ToolError(e, self.print_help)
def extant_file_type(path): """`argparse` type: checks if path exists on the file system :param path: path to check :return: path if exists :raise: `ToolError` if file does not exist """ err = "file does not exist: {}".format(path) assert os.path.exists(path), ToolError(err, parser.print_help) return path
def __init__(self, path, print_help=object, store_raw=False): self.print_help = self.set_print_help(print_help) self.path = path self._store_raw = store_raw self.file_size = os.path.getsize(path) try: self.blobs = self._get_blobs except Exception as e: raise ToolError(e, self.print_help) else: e = "not a valid LFP file : " + self.path assert self.blobs, ToolError(e, self.print_help) self._master = {} self._ref_md = {} self.picture = {} self.private = [] self.public = [] self.master = self._get_master
def _assert_src(self, src, paths, cmd, raw_in=False, range_=(0, 0)): """:raise: `ToolError`, if no valid LFPs found for current command""" s = 'RAW' if raw_in else 'LFP' e = cmd + ": no valid {} files found".format(s) if range_[1]: a, b = range_ e += " with range: {} - {}".format(a, b) e += ": " + ','.join(paths) assert src, ToolError(e, self.print_help)
def album_description_type(desc): """`argparse` type: checks if the album description is <= 3000 chars :param desc: `str`, to check :return: album description if appropriate length :raise: `ToolError` if description longer than 3000 chars """ err = ("album description too long (length: {}, max: 3000): \"{}\"". format(len(desc), desc)) assert len(desc) <= 3000, ToolError(err, parser.print_help) return desc
def album_name_type(name): """`argparse` type: checks if the album name is <= 140 chars :param name: `str`, to check :return: album if appropriate length :raise: `ToolError` if name longer than 140 chars """ err = ("album name too long (length: {}, max: 140): \"{}\"".format( len(name), name)) assert len(name) <= 140, ToolError(err, parser.print_help) return name
def mkdir(self, dir_path): """make a directory :param dir_path: `str`, input directory to create :raise: `ToolError` if there is an `OSError` exception """ try: os.makedirs(dir_path) return dir_path except OSError as e: raise ToolError(e, self.print_help)
def _rep_sanity(self, action, rep, rep_type, options): """checks imagerep/depthrep compatibility with the chosen action :raise: `ToolError` if representation not in available options """ arg = '--' + action.replace('_', '-') opt = ', '.join(options) e = ("{}: invalid {} choice: {} (choose from {})".format( arg, rep_type, rep, opt)) assert rep in options, ToolError(e, self.print_help)
def flush(self): """validates loaded recipe and writes recipe file to disk :raise: `ToolError` if `self.path` is not set""" self.dependencies() self.validate() self.zulu_time() e = "recipe output file not set" assert self.path, ToolError(e, print_help_obj) store = self.store store.update(self.unsupported_data) utils.write(self.path, od(sorted(store.items())))
def _assert_t0_lt_t1(self, t0, t1, param=''): """checks if t0 value is less than t1 value :raise: `ToolError` if t0 greater than t1 """ param = "{}: ".format(param) if param else "" if any_(t1): t0 = t0 if any_(t0) else self._auto_buffer e = param + "t0/t1 duration is less than .5 seconds; " e += "t0={}, t1={}".format(t0, t1) assert t0 + .5 < t1, ToolError(e, self.print_help)
def assert_len(self, props, len_=1): """assert that values in a dictionary are of an acceptable length :param props: `dict`, dictionary to parse through :param len_: acceptable length of dictionary values :raise: `ToolError` if `len_` does not match length of `props` """ for key, value in props.items(): arg = self._arg(key) e = arg + ": only {} value can be specified".format(len_) l = len(value) if hasattr(value, '__len__') else 1 assert l == len_, ToolError(e, self.print_help)
def json_file(self, file_path_in): """verifies that a inputted file is a valid JSON file :param file_path_in: `str`, input path to check :return: file path if valid :raise: `ToolError` if `file_path_in` does not contain JSON data """ file_path = utils.full_path(file_path_in) json_data = jsonutils.data(file_path) e = "invalid JSON file: " + file_path_in assert json_data or json_data == {}, ToolError(e, self.print_help) return file_path
def picture_caption_type(caption): """`argparse` type: checks if the picture caption is <= 140 chars :param caption: `str`, to check :return: picture caption if appropriate length :raise: `ToolError` if caption longer than 140 chars """ err = ( "picture caption too long (length: {}, max: 140): \"{}\"".format( len(caption), caption)) assert len(caption) <= 140, ToolError(err, parser.print_help) return caption
def __call__(self, *args, **kwargs): """:raise: `ToolError` if `args[0]` not a valid control point format""" values = args[0] if isinstance(values, dict): x = values['x'] y = values['y'] elif isinstance(values, tuple): x, y = values else: e = "invalid control point format: " + str(values) raise ToolError(e, print_help_obj) self.x(x) self.y(y)
def boolean(self, obj, arg=None): """checks if object is boolean :param obj: `object`, object to check :param arg: `str`, argument used for the call :return: `obj` if bool :raise: `ToolError` if `obj` not boolean """ if isinstance(obj, bool): return obj arg = self._arg(arg) e = "{}: expected boolean value: {}".format(arg, obj) raise ToolError(e, self.print_help)
def zulu_time(self, str_, arg=None): """verifies that the inputted string matches zulu time format :param str_: `type`, object to check :param arg: `str`, argument used for the call :return: string version of object :raise: `ToolError` if `str_` not zulu time key format """ try: _ = datetime.datetime.strptime(str_, '%Y-%m-%dT%H:%M:%S.%fZ') return str_ except Exception as e: arg = self._arg(arg) e = "{arg}: {e}".format(arg=arg, e=e) raise ToolError(e, self.print_help)
def unsigned_number(self, obj, arg=None): """verifies if an object is an unsigned int or float :param obj: `int`/`float`, object to check :param arg: `str`, argument used for the call :return: ``self.number(obj)`` if unsigned :raise: `ToolError` if number is not unsigned """ num = self.number(obj, arg=arg) if num >= 0: return num arg = self._arg(arg) e = self.bad_range.format(arg=arg, obj=obj, min_=0, max_='..') raise ToolError(e, self.print_help)