diff --git a/args.py b/args.py new file mode 100644 index 0000000..ac95a5c --- /dev/null +++ b/args.py @@ -0,0 +1,398 @@ +# -*- coding: utf-8 -*- + +""" +args +~~~~ + +This module provides the CLI argument interface for clint. + +""" + +import os +from sys import argv +from glob import glob +from collections import OrderedDict + +def _expand_path(path): + """Expands directories and globs in given path.""" + + paths = [] + path = os.path.expanduser(path) + path = os.path.expandvars(path) + + if os.path.isdir(path): + + for (dir, dirs, files) in os.walk(path): + for file in files: + paths.append(os.path.join(dir, file)) + else: + paths.extend(glob(path)) + + return paths + + +def _is_collection(obj): + """Tests if an object is a collection. Strings don't count.""" + + if isinstance(obj, basestring): + return False + + return hasattr(obj, '__getitem__') + + + +class ArgsList(object): + """CLI Argument management.""" + + def __init__(self, args=None, no_argv=False): + if not args: + if not no_argv: + self._args = argv[1:] + else: + self._args = [] + else: + self._args = args + + + def __len__(self): + return len(self._args) + + + def __repr__(self): + return '' % (repr(self._args)) + + + def __getitem__(self, i): + try: + return self.all[i] + except IndexError: + return None + + + def __contains__(self, x): + return self.first(x) is not None + + + def get(self, x): + """Returns argument at given index, else none.""" + try: + return self.all[x] + except IndexError: + return None + + + def get_with(self, x): + """Returns first argument that contains given string.""" + return self.all[self.first_with(x)] + + + def remove(self, x): + """Removes given arg (or list thereof) from Args object.""" + + def _remove(x): + found = self.first(x) + if found is not None: + self._args.pop(found) + + if _is_collection(x): + for item in x: + _remove(x) + else: + _remove(x) + + + def pop(self, x): + """Removes and Returns value at given index, else none.""" + try: + return self._args.pop(x) + except IndexError: + return None + + + def any_contain(self, x): + """Tests if given string is contained in any stored argument.""" + + return bool(self.first_with(x)) + + + def contains(self, x): + """Tests if given object is in arguments list. + Accepts strings and lists of strings.""" + + return self.__contains__(x) + + + def first(self, x): + """Returns first found index of given value (or list of values)""" + + def _find( x): + try: + return self.all.index(str(x)) + except ValueError: + return None + + if _is_collection(x): + for item in x: + found = _find(item) + if found is not None: + return found + return None + else: + return _find(x) + + + def first_with(self, x): + """Returns first found index containing value (or list of values)""" + + def _find(x): + try: + for arg in self.all: + if x in arg: + return self.all.index(arg) + except ValueError: + return None + + if _is_collection(x): + for item in x: + found = _find(item) + if found: + return found + return None + else: + return _find(x) + + + def first_without(self, x): + """Returns first found index not containing value (or list of values)""" + + def _find(x): + try: + for arg in self.all: + if x not in arg: + return self.all.index(arg) + except ValueError: + return None + + if _is_collection(x): + for item in x: + found = _find(item) + if found: + return found + return None + else: + return _find(x) + + + def start_with(self, x): + """Returns all arguments beginning with given string (or list thereof)""" + + _args = [] + + for arg in self.all: + if _is_collection(x): + for _x in x: + if arg.startswith(x): + _args.append(arg) + break + else: + if arg.startswith(x): + _args.append(arg) + + return ArgsList(_args, no_argv=True) + + + def contains_at(self, x, index): + """Tests if given [list of] string is at given index.""" + + try: + if _is_collection(x): + for _x in x: + if (_x in self.all[index]) or (_x == self.all[index]): + return True + else: + return False + else: + return (x in self.all[index]) + + except IndexError: + return False + + + def has(self, x): + """Returns true if argument exists at given index. + Accepts: integer. + """ + + try: + self.all[x] + return True + except IndexError: + return False + + + def value_after(self, x): + """Returns value of argument after given found argument (or list thereof).""" + + try: + try: + i = self.all.index(x) + except ValueError: + return None + + return self.all[i + 1] + + except IndexError: + return None + + + @property + def grouped(self): + """Extracts --flag groups from argument list. + Returns {format: Args, ...} + """ + + collection = OrderedDict(_=ArgsList(no_argv=True)) + + _current_group = None + + for arg in self.all: + if arg.startswith('-'): + _current_group = arg + collection.setdefault(arg, ArgsList(no_argv=True)) + else: + if _current_group: + collection[_current_group]._args.append(arg) + else: + collection['_']._args.append(arg) + + return collection + + + @property + def last(self): + """Returns last argument.""" + + try: + return self.all[-1] + except IndexError: + return None + + + @property + def all(self): + """Returns all arguments.""" + + return self._args + + + def all_with(self, x): + """Returns all arguments containing given string (or list thereof)""" + + _args = [] + + for arg in self.all: + if _is_collection(x): + for _x in x: + if _x in arg: + _args.append(arg) + break + else: + if x in arg: + _args.append(arg) + + return ArgsList(_args, no_argv=True) + + + def all_without(self, x): + """Returns all arguments not containing given string (or list thereof)""" + + _args = [] + + for arg in self.all: + if _is_collection(x): + for _x in x: + if _x not in arg: + _args.append(arg) + break + else: + if x not in arg: + _args.append(arg) + + return ArgsList(_args, no_argv=True) + + + @property + def flags(self): + """Returns Arg object including only flagged arguments.""" + + return self.start_with('-') + + + @property + def not_flags(self): + """Returns Arg object excluding flagged arguments.""" + + return self.all_without('-') + + + @property + def files(self, absolute=False): + """Returns an expanded list of all valid paths that were passed in.""" + + _paths = [] + + for arg in self.all: + for path in _expand_path(arg): + if os.path.exists(path): + if absolute: + _paths.append(os.path.abspath(path)) + else: + _paths.append(path) + + return _paths + + + @property + def not_files(self): + """Returns a list of all arguments that aren't files/globs.""" + + _args = [] + + for arg in self.all: + if not len(_expand_path(arg)): + if not os.path.exists(arg): + _args.append(arg) + + return ArgsList(_args, no_argv=True) + + @property + def copy(self): + """Returns a copy of Args object for temporary manipulation.""" + + return ArgsList(self.all) + + +args = ArgsList() +get = args.get +get_with = args.get_with +remove = args.remove +pop = args.pop +any_contain = args.any_contain +contains = args.contains +first = args.first +first_with = args.first_with +first_without = args.first_without +start_with = args.start_with +contains_at = args.contains_at +has = args.has +value_after = args.value_after +grouped = args.grouped +last = args.last +all = args.all +all_with = args.all_with +all_without = args.all_without +flags = args.flags +not_flags = args.not_flags +files = args.files +not_files = args.not_files +copy = args.copy \ No newline at end of file