def swap(env, args, kwargs): """FILE1 FILE2@Swaps filenames of FILE1 and FILE2.""" temp_popen = subprocess.Popen(["mktemp", "-d"], stdout=subprocess.PIPE) tempfile = temp_popen.communicate()[0].replace("\n", "") # set up proper paths to files curdir = os.getcwd() file1 = os.path.join(curdir, args[0]) file2 = os.path.join(curdir, args[1]) # 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, tempfile) # move 2 to 1 shutil.move(file2, file1) # move temp to 1 shutil.move(tempfile + "/" + os.path.basename(file1), file2) return
def multiply(env, args, kwargs): """[STRING,...] {num:N}@Prints its input N times.""" try: return args * kwargs["num"] except TypeError: if not isinstance(kwargs["num"], int): raise ErgonomicaError( "[ergo: ArgumentError]: Non-integer specified as num to command 'multiply'." ) except KeyError: raise ErgonomicaError("[ergo: ArgumentError]: No 'num' specified.")
def size(env, args, kwargs): """[FILE,...] {unit:UNIT}@Prints the size of each file. If unit specified, displays size in that unit (B, kB, MB,...).""" out = [] size_factor = 1 if "unit" in kwargs: unit = kwargs["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 args: 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
def whoami(env, args, kwargs): """@Return the user.""" if (args, kwargs) != ([], {}): raise ErgonomicaError( "[ergo: ArgumentError]: Arguments passed to 'whoami' (no arguments should be passed)." ) return env.user
def title(env, args, kwargs): """TITLE@Set the title of the current terminal window to TITLE.""" if len(args) != 1: raise ErgonomicaError( "[ergo: ArgumentError]: Incorrect number of arguments passed to 'title'." ) sys.stdout.write("\x1b]2;%s\x07" % (args[0])) return
def echo(env, args, kwargs): """[STRING,...] {ind:[INT,...]}@Prints its input. If ind specified, returns the items of its input with the specified indices.""" try: return [args[i] for i in kwargs["ind"]] except KeyError: return args except IndexError: raise ErgonomicaError( "[ergo: ArgumentError]: Indices passed not valid for passed list.")
def export(env, args, kwargs): """[EXP,..]@Append a line to .ergo_profile.""" try: open(os.path.join(os.path.expanduser("~"), ".ergo", ".ergo_profile"), 'a').write(" ".join(args) + "\n") except IOError: raise ErgonomicaError( "[ergo: ConfigError]: .ergo_profile could not be found. Please run ergo_setup." )
def addline(env, args, kwargs): """[LINE,...] {file:filename}@Adds all LINEs to file filename. Note that newlines must be included.""" try: _file = kwargs["file"] if _file[0] not in ["/", "~"]: _file = os.path.join(env.directory, _file) for line in args: open(kwargs["file"], "a").write(line) return except KeyError: raise ErgonomicaError( "[ergo: ArgumentError]: No file set for addline.")
def rm(env, args, kwargs): """[FILE,...]@Remove FILEs (works for directories as well).""" for x in args: path = os.path.expanduser(x) if os.path.exists(path): if os.path.isdir(path): shutil.rmtree(path) else: os.remove(path) else: raise ErgonomicaError("[ergo: NoSuchFileOrDirectoryError]: '%s'." % (path))
def mkdir(env, args, kwargs): """[DIR,...]@Make DIRs.""" for directory in args: try: os.mkdir(os.path.expanduser(directory)) except OSError: if errno.EEXIST: if kwargs.get("overwrite") == 'true': shutil.rmtree(os.path.expanduser(directory)) os.mkdir(os.path.expanduser(directory)) else: raise ErgonomicaError( '[ergo: DirectoryExist]') # TODO issue #42
def size(env, args, kwargs): """[FILE,...] {unit:UNIT}@Prints the size of each file. If unit specified, displays size in that unit (B, kB, MB,...).""" out = [] try: unit = kwargs["unit"] size_factor = 1 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 args: try: size = os.path.getsize(item) out.append(item + " : " + str(size / 1024**size_factor) + " " + SIZES[size_factor]) except OSError: raise ErgonomicaError( "[ergo: NoSuchFileError]: No such file '%s'." % (item)) except KeyError: for item in args: try: size = 0 if item[0] in ["/", "~"]: size = os.path.getsize(item) else: size = os.path.getsize(env.directory + "/" + item) size_factor = int(math.floor(math.log(size) / 6.93147)) out.append(item + " : " + str(size / 1024**size_factor) + " " + SIZES[size_factor]) except OSError: raise ErgonomicaError( "[ergo: NoSuchFileError]: No such file '%s'." % (item)) return out
def cd(env, args, kwargs): """[DIR]@Changes to directory DIR. If none specified, changes to ~.""" try: if args == []: os.chdir(os.path.expanduser("~")) elif args[0][0] in ["~", "/"]: os.chdir(os.path.expanduser(args[0])) else: os.chdir(os.path.join(env.directory, args[0])) env.directory = os.getcwd() except OSError: _, error, _ = sys.exc_info() raise ErgonomicaError( "[ergo: NoSuchDirectoryError] No such directory '%s'." % (re.findall(r"'(.*?)'", str(error))[0]))
def get_code_blocks(string): lines = re.split(r"(?:\"[^\"]*\"|.)+", string) blocks = [] for line in lines: if line == "": pass elif line[0] != " ": blocks.append(line) else: if (not line.startswith(" ")) and (line.startswith(" ")): raise ErgonomicaError( "[ergo: SyntaxError]: Incorrect indentation on line '%s'." % line) else: blocks[-1] += line[3:] + "\n" return blocks
def find(env, args, kwargs): """[DIR] {name:PATTERN}@Finds a file with name matching PATTERN. If no DIR specified, chooses current directory.""" try: pattern = kwargs["name"] except KeyError: pattern = "*" try: path = args[0] except IndexError: path = env.directory if not os.path.isdir(path): raise ErgonomicaError( "[ergo: NoSuchDirectoryError]: No such directory '%s'." % (path)) result = [] for root, dirs, files in os.walk(path): for dir in dirs: if fnmatch.fnmatch(os.path.join(root, dir), pattern): result.append(os.path.join(root, dir)) for name in files: if fnmatch.fnmatch(name, pattern): result.append(os.path.join(root, name)) return [env.theme["files"] + x for x in list(set(result))]
def ergo_help(env, args, kwargs): """[COMMAND,...]@Display all ergonomica commands. If COMMANDs specified, returns the docstrings and arguments for them.""" out = "" if args == []: return globalization_query("help_welcome_message", env.LANG) for arg in args: if arg == "syntax": out += """In Ergonomica, commands are of the form command arg1 arg2,... {kwarg1:val1,kwarg2:val2,...} For example, finding all files in the root directory that match the regular expression 'e.*o': find / {name:e.*o} Note that you can call a command by the first three letters of its name. For example, instead of edit important_code.py you can type edi important_code.py If a command is not defined in Ergonomica, ergonomica will fallback to BASH (but with Ergonomica syntax). Arguments are the same. If a flag requires a value (like -f file), there will be a kwarg with that flag that takes that value. If it does not require a value, simply supply 't' or 'true' for the value. For example, the command git commit --interactive -m "Making the world a better place" The Ergonomica equivalent for this command would be git commit {-interactive:t,m:"Making the world a better place"} or git commit {-interactive:true,m:"Making the world a better place"} To "pipe" in Ergonomica, one uses the '->' symbol. Commands may be put together as a chain command1 -> command2 -> command3 The last command in the chain will have its output printed. Each command outputs "args", and certain operators (defined later) allow for piping of kwargs. To make a command accept the arguments from the last command, one uses --arg as one of the arguments. For example, to list all files and remove them, one would run ls -> rm --arg (analagous to) rm file1.txt file2.mp3,... To accept kwargs, one uses --kw. To process pipes of arguments, there are "operators", denoted by parenthases, e.g., (filter) x.endswith(".py") The available operators are: (map) python_expression: applies a python expression to each argument. For example, ls -> (map) x + ' is on my computer.' # adds ' is on my computer' to each directory listing (filter) python_expression: returns all arguments such that python_expression is true. For example, ifconfig -> (match) .*broadcast.* # shows lines that contain "broadcast" in ifconfig (ip address on wifi cards) (match) regular_expression: returns all arguments that match regexp regular_expression. For example, ls -> (match) .*\.py # display all .py files (reverse): reverses all arguments (splice): splice arguments from last and current pipes. For example, echo a b -> echo c d -> (splice) # returns a c b d (split): splits all arguments by spaces and flattens list echo "hello world" -> (splice) # returns hello world (kw): sets each first argument to the value of the second in kwargs. For example, echo a 2 b 3 -> (kw) -> set --kw # sets a to 2 and b to 3\n\n""" elif arg == "commands": pruned_verbs = {} for item in env.verbs: if item not in pruned_verbs: pruned_verbs[item] = env.verbs[item] for item in pruned_verbs: docstring = env.verbs[item].__doc__.split("@") out += "%-36s | %29s\n" % (item + " " + docstring[0], docstring[1]) elif arg in env.verbs: docstring = env.verbs[arg].__doc__.split("@") out += "%-26s | %29s\n" % (arg + " " + docstring[0], docstring[1]) else: print("arg is", arg) raise ErgonomicaError( "[ergo: HelpError]: No such help directive '%s'." % (arg)) return out + "\nVisit https://github.com/ergonomica/ergonomica/wiki for more documentation."
def run_operator(block, pipe): operator = get_operator(block) # (map) -- map an operator to a list of operands if operator == "map": try: func = eval("lambda x: " + block.replace("(map)", "")) except Exception as error: raise ErgonomicaError( "[ergo: OperatorError]: Error in parsing command for operator 'map'." + str(error)) try: pipe.setstack_args([x for x in map(func, pipe.getstack_args(-1))]) except TypeError: if pipe.getstack_args(-1) is None: raise ErgonomicaError( "[ergo: OperatorError]: Error in parsing command for operator 'map'." ) except Exception as error: raise ErgonomicaError("[ergo: OperatorError]: " + (str(error))) #raise error return pipe.args[-1] # (filter) -- return all arguments that match the specified function elif operator == "filter": try: func = eval("lambda x: " + block.replace("(filter)", "")) except SyntaxError: raise ErgonomicaError( "[ergo: OperatorError]: SyntaxError in operator 'filter'.") pipe.lastlast_args = pipe.getstack_args(-1) try: pipe.setstack_args([x for x in pipe.getstack_args(-1) if func(x)]) except TypeError as error: if pipe.getstack_args(-1) is None: raise ErgonomicaError( "[ergo: OperatorError]: No arguments provided to operator 'filter'." ) raise error return pipe.getstack_args(-1) # (match) -- return all arguments that match the specified regexp elif operator == "match": exp = block.replace("(match)", "").strip() pipe.setstack_args( [x for x in pipe.getstack_args(-1) if re.findall(exp, x.strip())]) return pipe.getstack_args(-1) # (reverse) -- reverse the order of all arguments elif operator == "reverse": pipe.setstack_args(pipe.getstack_args(-1)[::-1]) return pipe.getstack_args(-1) # (splice) -- splice the last and 2nd last argument lists together elif operator == "splice": pipe.setstack_args( list( filter( None, sum( itertools.izip_longest(pipe.getstack_args(-2), pipe.getstack_args(-1)), ())))) return pipe.getstack_args(-1) # (split) -- split input strings by spaces elif operator == "split": pipe.setstack_args([ item for sublist in [x.split() for x in pipe.getstack_args(-1)] for item in sublist ]) return pipe.getstack_args(-1) # (kw) -- map the last and 2nd last argument lists into a dictionary elif operator == "kw": pipe.setstack_kwargs({ pipe.getstack_args(-1)[i]: pipe.getstack_args(-1)[i + 1] for i in range(len(pipe.getstack_args(-1)) - 1) }) return pipe.getstack_kwargs(-1) else: return False
def multiply(env, args, kwargs): """[STRING,...] {num:N}@Prints its input N times.""" try: return args * int(kwargs["num"]) except KeyError: raise ErgonomicaError("[ergo: ArgumentError]: No 'num' specified.")