def swap(argc): """swap: Swap the names/contents of two files. Usage: swap <file>FILE1 <file>FILE2 """ tempdir = tempfile.mkdtemp() # set up proper paths to files curdir = os.getcwd() file1 = os.path.join(curdir, argc.args['FILE1']) file2 = os.path.join(curdir, argc.args['FILE2']) # check existence of files if not (os.path.exists(file1)): raise ErgonomicaError("[ergo: FileError]: No such file '%s'." % file1) elif not (os.path.exists(file2)): raise ErgonomicaError("[ergo: FileError]: No such file '%s'." % file2) # move 1 to temp shutil.move(file1, tempdir) # move 2 to 1 shutil.move(file2, file1) # move temp to 1 shutil.move(tempdir + "/" + os.path.basename(file1), file2) return
def shuffle(array): if not isinstance(array, list): raise ErgonomicaError("[ergo: shuffle]: TypeError: Non-list passed.") array2 = [x for x in array] # copy since arrays are mutable random.shuffle(array2) return array2
def uuid(argc): """uuid: Yields a Universal Unique ID (UUID). Usage: uuid [int | hex | words] [LENGTH] """ if not argc.args['LENGTH']: length = 4 else: try: length = int(argc.args['LENGTH']) except ValueError: raise ErgonomicaError("[ergo: uuid]: UUID length must be an int.") if argc.args['hex']: charset = '0123456789abcdef' return "".join([random.choice(charset) for x in range(length)]) elif argc.args['words']: rw = RandomWords() return " ".join([str(x) for x in rw.random_words(count=length)]) else: charset = "0123456789" return "".join([random.choice(charset) for x in range(length)])
def randint(lower, upper=None): if not upper: lower, upper = 0, lower if not isinstance(lower, int): raise ErgonomicaError( "[ergo: randint]: TypeError: '{}' not an integer.".format( str(lower))) elif not isinstance(upper, int): raise ErgonomicaError( "[ergo: randint]: TypeError: '{}' not an integer.".format( str(upper))) return random.randint(lower, upper)
def obj_set(arr, order=True): new_arr = [] for i in arr: contained = False if not order: if not (isinstance(i, list) or isinstance(i, str) or isinstance(i, str)): raise ErgonomicaError("[ergo: ~=]: Non-iterable passed.") for j in new_arr: if order: if i == j: if contained: break else: contained = True else: if set(i) == set(j): if contained: break else: contained = True if not contained: new_arr.append(i) return new_arr
def rm(argc): """rm: Remove files and directories. Usage: rm <file/directory>[FILES...] """ for _file in argc.args['FILES']: if _file[0] == "/": path = _file elif _file[0] == "~": path = os.path.expanduser(_file) else: path = os.path.join(argc.env.directory, _file) if os.path.exists(path) or os.path.islink(path): if os.path.isdir(path): shutil.rmtree(path) elif os.path.islink(path): os.unlink(path) else: os.remove(path) else: raise ErgonomicaError( "[ergo: rm]: NoSuchFileOrDirectoryError: '%s'." % (path))
def flatten(arr): if not isinstance(arr, list): raise ErgonomicaError("[ergo: flatten]: Non-list passed.") out = [] for i in arr: if isinstance(i, list): out += flatten(i) else: out.append(i) return out
def edit_func(funcname): if not hasattr(namespace[funcname], 'body'): raise ErgonomicaError( '[ergo: edit_func]: Function passed not a valid Ergonomica function! Perhaps it\'s a builtin?' ) # initialize tempfile and write current function body filename = tempfile.mktemp() open(filename, 'w').write(_ast_to_string(namespace[funcname].body)) # start fsupdate thread threading.Thread(target=lambda: func_update(funcname, filename)).start() # return the tempfile name return filename
def _slice(*args): if len(args) == 2: if not isinstance(args[0], int): raise ErgonomicaError( "[ergo: slice]: TypeError: Index '{}' not an integer.".format( str(args[0]))) try: return args[1][args[0]] except IndexError: raise ErgonomicaError( "[ergo: slice]: IndexError: Index {} out of range.".format( str(args[0]))) elif len(args) == 3: if not isinstance(args[0], int): raise ErgonomicaError( "[ergo: slice]: TypeError: Index '{}' not an integer.".format( str(args[0]))) if not isinstance(args[1], int): raise ErgonomicaError( "[ergo: slice]: TypeError: Index '{}' not an integer.".format( str(args[1]))) return args[2][args[0]:args[1]]
def arglist(_function): if isinstance(_function, function): return _function.args # they don't have args attributes since they're not actual Python functions if isinstance(_function, types.BuiltinFunctionType): return [] elif isinstance(_function, types.FunctionType): return inspect.getargspec(_function).args else: try: return inspect.getargspec(function.__call__).args except AttributeError: raise ErgonomicaError( "[ergo]: TypeError: '{}' is not a function.".format( str(function)))
def __init__(self, argspec=(), args=(), outer=None): argv_read = False args = list(args) # so we can pop from it for i in argspec: if i.startswith("*"): if argv_read: raise ErgonomicaError( "[ergo: SyntaxError]: Multiple argv arguments.") else: argv_read = True self.update({i[1:]: args}) else: self.update({i: args.pop()}) self.outer = outer
def ls(argc): """ ls: List files in a directory. Usage: ls [DIR] [-c | --count-files] [-d | --date] [-h | --hide-dotfiles] Options: -d --date Show file creation dates. -h --hide-dotfiles Ignore dotfiles. -c --count-files Return the number of files in a directory. Examples: ls $ """ file_filter = lambda x: True if argc.args['--hide-dotfiles']: file_filter = lambda x: not x.startswith(".") # date processing from numerical time date = lambda t: str(datetime.datetime.fromtimestamp(creation_date(t))) +\ " " if argc.args['--date'] else "" if not argc.args['DIR']: argc.args['DIR'] = "." if not os.path.isdir(expand_path(argc.env, argc.args['DIR'])): raise ErgonomicaError( "[ergo: ls]: [DirectoryError]: No such directory '{}'.".format( expand_path(argc.env, argc.args['DIR']))) files = [ date(x) + x for x in os.listdir(expand_path(argc.env, argc.args['DIR'])) if file_filter(x) ] if argc.args['--count-files']: return len(files) else: return files
def color(argc): """color: Easily print terminal color codes. Usage: color COLOR color bg COLOR """ if argc.args['bg']: module = colorama.Back else: module = colorama.Fore try: return getattr(module, argc.args['COLOR'].upper()) except AttributeError: raise ErgonomicaError("[ergo: color]: No such color '{}'.".format( argc.args['COLOR'])) return
def size(argc): """size: Return the sizes of files. Usage: size [-h] FILE size [-h] [-u UNIT] FILE Options: -u, --unit Specify the unit of size in which to display the file. -h, --human-readable Print the size along with units (i.e., human readable) """ path = os.path.expanduser(argc.args['FILE']) if not os.path.exists(path): raise ErgonomicaError("[ergo: NoSuchFileError]: No such file '%s'." % (argc.args['FILE'])) size = file_or_dir_size(path) if argc.args['--unit']: unit = argc.args["UNIT"] if unit in SHORT_SIZES: size_factor = SHORT_SIZES.index(unit) elif unit in SIZES: size_factor = SIZES.index(unit) elif unit in NAME_SIZES: size_factor = NAME_SIZES.index(unit) else: # automatically calculate the best unit, falling # back to largest size if too large size_factor = len(SHORT_SIZES) - 1 for i in range(len(SIZES)): if (1024 ** i) >= size: size_factor = i - 1 break if argc.args['--human-readable']: return argc.args['FILE'] + ": " + str(size / 1024.0 ** size_factor) + " " + SIZES[size_factor] else: return (size / 1024.0 ** size_factor)
def cd(argc): """cd: Changes the directory. Usage: cd <directory>[DIR] """ if not argc.args['DIR']: argc.args['DIR'] = "~" try: os.chdir(expand_path(argc.env, argc.args['DIR'])) except OSError: raise ErgonomicaError( "[ergo: cd]: [DirectoryError]: No such directory '{}'.".format( expand_path(argc.env, argc.args['DIR']))) argc.env.directory = os.getcwd() return None
def ls(argc): """ ls: List files in a directory. Usage: ls [DIR] [-c | --count-files] [-d | --date] [-a | --all] Options: -d --date Show file creation dates. -a --all Do not ignore files and directories starting with a `.` character.. -c --count-files Return the number of files in a directory. Examples: ls $ """ # date processing from numerical time date = lambda t: str(datetime.datetime.fromtimestamp(creation_date(t))) +\ " " if argc.args['--date'] else "" if not argc.args['DIR']: argc.args['DIR'] = "." if not os.path.isdir(expand_path(argc.env, argc.args['DIR'])): raise ErgonomicaError( "[ergo: ls]: [DirectoryError]: No such directory '{}'.".format( expand_path(argc.env, argc.args['DIR']))) files = [ date(x) + x for x in os.listdir(expand_path(argc.env, argc.args['DIR'])) if argc.args['--all'] or (not (x.startswith(".") or x == "~")) ] if argc.args['--count-files']: return len(files) else: return files
def environment(argc): """ environment: Configure environment variables. Usage: environment set VARIABLE VALUE environment get VARIABLE """ if argc.args['set']: vars(argc.env)[argc.args['VARIABLE']] = argc.args['VALUE'] if argc.args['VARIABLE'] == 'path': os.environ['PATH'] = argc.args['VALUE'] elif argc.args['VARIABLE'] == 'pypath': sys.path = argc.args['VALUE'].split(os.pathsep) elif argc.args['get']: try: return vars(argc.env)[argc.args['VARIABLE']] except KeyError: raise ErgonomicaError( '[ergo]: [environment]: No such environment variable "{}".'. format(argc.args['VARIABLE']))
def _range(argc): """range: Construct a range of integers. Usage: range END range START END range START END STEP """ end = float(argc.args['END']) try: if argc.args['START']: start = float(argc.args['START']) if argc.args['STEP']: step = float(argc.args['STEP']) return [x for x in frange(start, end, step)] else: return [x for x in frange(start, end, 1)] else: return [x for x in frange(0, end, 1)] except ValueError: # TODO: have this give the actual offending number raise ErgonomicaError("[ergo: range]: Non-number passed.")
def size(argc): """size: Return the sizes of files. Usage: size FILE... size [-u UNIT] FILE... Options: -u, --unit Specify the unit of size in which to display the file. """ out = [] size_factor = 1 if argc.args['--unit']: unit = argc.args["UNIT"] if unit in SHORT_SIZES: size_factor = SHORT_SIZES.index(unit) elif unit in SIZES: size_factor = SIZES.index(unit) elif unit in NAME_SIZES: size_factor = NAME_SIZES.index(unit) for item in argc.args['FILE']: try: path = os.path.expanduser(item) if not os.path.exists(path): raise OSError size = file_or_dir_size(path) out.append(item + ": " + str(size / 1024**size_factor) + " " + SIZES[size_factor]) except OSError: raise ErgonomicaError( "[ergo: NoSuchFileError]: No such file '%s'." % (item)) return out[0] if len(out) == 1 else out
def eval(x, ns, at_top=False): global namespace, PRINT_OVERRIDE, ENV if at_top: PRINT_OVERRIDE = False while True: if x == []: raise ErgonomicaError("syntax-error", "Empty expression!") if isinstance(x, Symbol): try: return ns.find(x)[x] except AttributeError as error: raise ErgonomicaError( "[ergo]: NameError: No such variable {}.".format(x)) elif isinstance(x, str): return x elif not isinstance(x, list): return x elif x[0] == "if": if len(x) > 4: # elif statements i = 0 while True: if i == len(x): break item = x[i] if item in ["if", "elif"]: if eval(x[i + 1], ns): exp = x[i + 2] break i += 3 elif item in ["else"]: exp = x[i + 1] break elif len(x) == 3: (_, conditional, then) = x exp = (then if eval(conditional, ns) else None) else: raise ErgonomicaError( "[ergo: SyntaxError]: Wrong number of arguments for `if`. Should be: `if conditional then_expr [elif conditional then_expr] [else]`." ) return eval(exp, ns) elif x[0] == "set": if len(x) == 3: (_, name, body) = x name = Symbol(name) ns[name] = eval(body, ns) return None else: raise ErgonomicaError( "[ergo: SyntaxError]: Wrong number of arguments for `set`. Should be: `set symbol value`." ) elif x[0] == "with": # with (expr) as varname (body_expression) if len(x) == 5: (_, expr, _, varname, body_expression) = x name = Symbol(varname) copied_ns = copy(ns) copied_ns[name] = eval(expr, ns) return eval(body_expression, copied_ns) else: raise Ergonomica( "[ergo: SyntaxError]: Wrong number of arguments for `with`. Should be: `with (expr) as varname (body_expression)`." ) elif x[0] == "global": (_, name, body) = x name = Symbol(name) namespace[name] = eval(body, ns) return None elif x[0] == "lambda": if len(x) > 2: argspec = x[1] body = x[2] return function(argspec, body, ns) else: raise ErgonomicaError( "[ergo: SyntaxError]: Wrong number of arguments for `lambda`. Should be: lambda argspec body...." ) else: try: if isinstance(eval(x[0], ns), function): p = eval(x[0], ns) ns = Namespace(p.args[::-1], [eval(y, ns) for y in x[1:]], p.ns) x = p.body continue # if arglist(eval(x[0], ns)) == ['argc']: # return eval(x[0], ns)(ArgumentsContainer(ENV, namespace, docopt(eval(x[0], ns).__doc__, [eval(i, ns) for i in x[1:]]))) return eval(x[0], ns)(*[eval(i, ns) for i in x[1:]]) except Exception as e: if isinstance(e, ErgonomicaError): if not e.args[0].startswith( "[ergo]: NameError: No such variable {}.".format( x[0])): # then it's not actually a unknown command---it's an error from something else raise else: print("[ergo]: Error on line:") print(x) raise # presumably the command isn't found ENV.update_env() if os.path.islink("~"): if os.path.realpath("~") != os.path.expanduser("~"): print( "[ergo]: Fatal error! There is already a symlink in this directory named `~` which does not link to your home folder! Refusing to run..." ) elif os.path.exists("~"): print( "[ergo]: Fatal error! There is already an item in this directory named `~` which is not a symlink to your home folder! Refusing to run..." ) else: os.symlink(os.path.expanduser("~"), "~") try: if isinstance(x[0], str) and x[0].startswith("%") or at_top: PRINT_OVERRIDE = at_top if x[0].startswith("%"): x[0] = x[0][1:] # trim off percent sign try: r = os.system(" ".join([ quote(y) for y in [x[0]] + expand_typed_args([eval(i, ns) for i in x[1:]]) ])) os.unlink("~") return r except Exception as e: os.unlink("~") raise e else: try: p = subprocess.Popen( [x[0]] + expand_typed_args([eval(i, ns) for i in x[1:]]), stdout=subprocess.PIPE, universal_newlines=True) p.wait() os.unlink("~") except Exception as e: os.unlink("~") raise e try: cur = [ line[:-1].encode().decode('utf-8') for line in iter(p.stdout.readline, "") ] if len(cur) == 1: return cur[0] else: return cur except KeyboardInterrupt as e: p.terminate() raise e # TODO: instead of checking for a FileNotFoundError, check if the command is in the user's PATH. except FileNotFoundError as e: raise ErgonomicaError( "[ergo]: Unknown command '{}'.".format(x[0])) except OSError as e: # on Python2 raise ErgonomicaError( "[ergo]: Unknown command '{}'.".format(x[0]))
def eval(x, ns, at_top=False): global namespace, PRINT_OVERRIDE, ENV if at_top: PRINT_OVERRIDE = False while True: if x == []: return if isinstance(x, Symbol): try: return ns.find(x)[x] except AttributeError as error: raise ErgonomicaError( "[ergo]: NameError: No such variable {}.".format(x)) elif isinstance(x, str): return x elif not isinstance(x, list): return x elif x[0] == "if": if len(x) > 4: # elif statements i = 0 while True: if i == len(x): break item = x[i] if item in ["if", "elif"]: if eval(x[i + 1], ns): exp = x[i + 2] break i += 3 elif item in ["else"]: exp = x[i + 1] break elif len(x) == 3: (_, conditional, then) = x exp = (then if eval(conditional, ns) else None) else: raise ErgonomicaError( "[ergo: SyntaxError]: Wrong number of arguments for `if`. Should be: if conditional then [else]." ) return eval(exp, ns) elif x[0] == "set": if len(x) == 3: (_, name, body) = x name = Symbol(name) ns[name] = eval(body, ns) return None else: raise ErgonomicaError( "[ergo: SyntaxError]: Wrong number of arguments for `set`. Should be: set symbol value." ) elif x[0] == "global": (_, name, body) = x name = Symbol(name) namespace[name] = eval(body, ns) return None elif x[0] == "lambda": if len(x) > 2: argspec = x[1] body = x[2] return function(argspec, body, ns) else: raise ErgonomicaError( "[ergo: SyntaxError]: Wrong number of arguments for `lambda`. Should be: lambda argspec body...." ) else: try: if isinstance(eval(x[0], ns), function): p = eval(x[0], ns) ns = Namespace(p.args, [eval(y, ns) for y in x[1:]], p.ns) x = p.body continue if arglist(eval(x[0], ns)) == ['argc']: return eval(x[0], ns)(ArgumentsContainer( ENV, namespace, docopt( eval(x[0], ns).__doc__, [eval(i, ns) for i in x[1:]]))) return eval(x[0], ns)(*[eval(i, ns) for i in x[1:]]) except ErgonomicaError as e: if not e.args[0].startswith( "[ergo]: NameError: No such variable {}.".format( x[0])): # then it's not actually a unknown command---it's an error from something else raise e # presumably the command isn't found ENV.update_env() try: if isinstance(x[0], str) and x[0].startswith("%") or at_top: PRINT_OVERRIDE = at_top if x[0].startswith("%"): x[0] = x[0][1:] # trim off percent sign return os.system(" ".join([ quote(y) for y in [x[0]] + expand_typed_args([eval(i, ns) for i in x[1:]]) ])) else: p = subprocess.Popen( [x[0]] + expand_typed_args([eval(i, ns) for i in x[1:]]), stdout=subprocess.PIPE, universal_newlines=True) try: cur = [ line[:-1] for line in iter(p.stdout.readline, "") ] if len(cur) == 1: return cur[0] else: return cur except KeyboardInterrupt as e: p.terminate() raise e except FileNotFoundError: raise ErgonomicaError( "[ergo]: Unknown command '{}'.".format(x[0])) except OSError: # on Python2 raise ErgonomicaError( "[ergo]: Unknown command '{}'.".format(x[0]))