From 217c6215bc39c9322afa0c035cd62a6eb4e1c53a Mon Sep 17 00:00:00 2001 From: Jan David Date: Fri, 25 Sep 2015 18:58:36 +0200 Subject: [PATCH 01/12] Add sh dependency The workflow makes some system calls, and sh provides an easy-to-use interface for that. Because we don't want to trouble the user with the installation of Python modules, the source of sh is included with the workflow. --- sh.py | 2352 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2352 insertions(+) create mode 100644 sh.py diff --git a/sh.py b/sh.py new file mode 100644 index 0000000..3588546 --- /dev/null +++ b/sh.py @@ -0,0 +1,2352 @@ +""" +http://amoffat.github.io/sh/ +""" +#=============================================================================== +# Copyright (C) 2011-2015 by Andrew Moffat +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +#=============================================================================== + + +__version__ = "1.11" +__project_url__ = "https://github.com/amoffat/sh" + + + +import platform + +if "windows" in platform.system().lower(): + raise ImportError("sh %s is currently only supported on linux and osx. \ +please install pbs 0.110 (http://pypi.python.org/pypi/pbs) for windows \ +support." % __version__) + + +import sys +IS_PY3 = sys.version_info[0] == 3 + +import traceback +import os +import re +from glob import glob as original_glob +import time +from types import ModuleType +from functools import partial +import inspect +from contextlib import contextmanager + +from locale import getpreferredencoding +DEFAULT_ENCODING = getpreferredencoding() or "UTF-8" + + +if IS_PY3: + from io import StringIO + from io import BytesIO as cStringIO + from queue import Queue, Empty + + # for some reason, python 3.1 removed the builtin "callable", wtf + if not hasattr(__builtins__, "callable"): + def callable(ob): + return hasattr(ob, "__call__") +else: + from StringIO import StringIO + from cStringIO import OutputType as cStringIO + from Queue import Queue, Empty + +IS_OSX = platform.system() == "Darwin" +THIS_DIR = os.path.dirname(os.path.realpath(__file__)) +SH_LOGGER_NAME = "sh" + + +import errno +import warnings + +import pty +import termios +import signal +import gc +import select +import threading +import tty +import fcntl +import struct +import resource +from collections import deque +import logging +import weakref + + +# TODO remove with contexts in next version +def with_context_warning(): + warnings.warn(""" +with contexts are deprecated because they are not thread safe. they will be \ +removed in the next version. use subcommands instead \ +http://amoffat.github.io/sh/#sub-commands. see \ +https://github.com/amoffat/sh/issues/195 +""".strip(), stacklevel=3) + + + +if IS_PY3: + raw_input = input + unicode = str + basestring = str + + +_unicode_methods = set(dir(unicode())) + + +def encode_to_py3bytes_or_py2str(s): + """ takes anything and attempts to return a py2 string or py3 bytes. this + is typically used when creating command + arguments to be executed via + os.exec* """ + + fallback_encoding = "utf8" + + if IS_PY3: + # if we're already bytes, do nothing + if isinstance(s, bytes): + pass + else: + s = str(s) + try: + s = bytes(s, DEFAULT_ENCODING) + except UnicodeEncodeError: + s = bytes(s, fallback_encoding) + else: + # attempt to convert the thing to unicode from the system's encoding + try: + s = unicode(s, DEFAULT_ENCODING) + # if the thing is already unicode, or it's a number, it can't be + # coerced to unicode with an encoding argument, but if we leave out + # the encoding argument, it will convert it to a string, then to unicode + except TypeError: + s = unicode(s) + + # now that we have guaranteed unicode, encode to our system encoding, + # but attempt to fall back to something + try: + s = s.encode(DEFAULT_ENCODING) + except: + s = s.encode(fallback_encoding) + return s + + +class ErrorReturnCode(Exception): + """ base class for all exceptions as a result of a command's exit status + being deemed an error. this base class is dynamically subclassed into + derived classes with the format: ErrorReturnCode_NNN where NNN is the exit + code number. the reason for this is it reduces boiler plate code when + testing error return codes: + + try: + some_cmd() + except ErrorReturnCode_12: + print("couldn't do X") + + vs: + try: + some_cmd() + except ErrorReturnCode as e: + if e.exit_code == 12: + print("couldn't do X") + + it's not much of a savings, but i believe it makes the code easier to read """ + + truncate_cap = 750 + + def __init__(self, full_cmd, stdout, stderr): + self.full_cmd = full_cmd + self.stdout = stdout + self.stderr = stderr + + + if self.stdout is None: + exc_stdout = "" + else: + exc_stdout = self.stdout[:self.truncate_cap] + out_delta = len(self.stdout) - len(exc_stdout) + if out_delta: + exc_stdout += ("... (%d more, please see e.stdout)" % out_delta).encode() + + if self.stderr is None: + exc_stderr = "" + else: + exc_stderr = self.stderr[:self.truncate_cap] + err_delta = len(self.stderr) - len(exc_stderr) + if err_delta: + exc_stderr += ("... (%d more, please see e.stderr)" % err_delta).encode() + + msg = "\n\n RAN: %r\n\n STDOUT:\n%s\n\n STDERR:\n%s" % \ + (full_cmd, exc_stdout.decode(DEFAULT_ENCODING, "replace"), + exc_stderr.decode(DEFAULT_ENCODING, "replace")) + super(ErrorReturnCode, self).__init__(msg) + + +class SignalException(ErrorReturnCode): pass +class TimeoutException(Exception): + """ the exception thrown when a command is killed because a specified + timeout (via _timeout) was hit """ + def __init__(self, exit_code): + self.exit_code = exit_code + super(Exception, self).__init__() + +SIGNALS_THAT_SHOULD_THROW_EXCEPTION = ( + signal.SIGABRT, + signal.SIGBUS, + signal.SIGFPE, + signal.SIGILL, + signal.SIGINT, + signal.SIGKILL, + signal.SIGPIPE, + signal.SIGQUIT, + signal.SIGSEGV, + signal.SIGTERM, + signal.SIGSYS, +) + + +# we subclass AttributeError because: +# https://github.com/ipython/ipython/issues/2577 +# https://github.com/amoffat/sh/issues/97#issuecomment-10610629 +class CommandNotFound(AttributeError): pass + + + + +rc_exc_regex = re.compile("(ErrorReturnCode|SignalException)_((\d+)|SIG\w+)") +rc_exc_cache = {} + + +def get_exc_from_name(name): + """ takes an exception name, like: + + ErrorReturnCode_1 + SignalException_9 + SignalException_SIGHUP + + and returns the corresponding exception. this is primarily used for + importing exceptions from sh into user code, for instance, to capture those + exceptions """ + + exc = None + try: + return rc_exc_cache[name] + except KeyError: + m = rc_exc_regex.match(name) + if m: + base = m.group(1) + rc_or_sig_name = m.group(2) + + if base == "SignalException": + try: + rc = -int(rc_or_sig_name) + except ValueError: + rc = -getattr(signal, rc_or_sig_name) + else: + rc = int(rc_or_sig_name) + + exc = get_rc_exc(rc) + return exc + + +def get_rc_exc(rc_or_sig_name): + """ takes a exit code, signal number, or signal name, and produces an + exception that corresponds to that return code. positive return codes yield + ErrorReturnCode exception, negative return codes yield SignalException + + we also cache the generated exception so that only one signal of that type + exists, preserving identity """ + + try: + rc = int(rc_or_sig_name) + except ValueError: + rc = -getattr(signal, rc_or_sig_name) + + try: + return rc_exc_cache[rc] + except KeyError: + pass + + if rc > 0: + name = "ErrorReturnCode_%d" % rc + base = ErrorReturnCode + else: + name = "SignalException_%d" % abs(rc) + base = SignalException + + exc = type(name, (base,), {"exit_code": rc}) + rc_exc_cache[rc] = exc + return exc + + + + +def which(program): + def is_exe(fpath): + return (os.path.exists(fpath) and + os.access(fpath, os.X_OK) and + os.path.isfile(os.path.realpath(fpath))) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + if "PATH" not in os.environ: + return None + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + return None + +def resolve_program(program): + path = which(program) + if not path: + # our actual command might have a dash in it, but we can't call + # that from python (we have to use underscores), so we'll check + # if a dash version of our underscore command exists and use that + # if it does + if "_" in program: + path = which(program.replace("_", "-")) + if not path: + return None + return path + + +# we add this thin wrapper to glob.glob because of a specific edge case where +# glob does not expand to anything. for example, if you try to do +# glob.glob("*.py") and there are no *.py files in the directory, glob.glob +# returns an empty list. this empty list gets passed to the command, and +# then the command fails with a misleading error message. this thin wrapper +# ensures that if there is no expansion, we pass in the original argument, +# so that when the command fails, the error message is clearer +def glob(arg): + return original_glob(arg) or arg + + + +class Logger(object): + """ provides a memory-inexpensive logger. a gotcha about python's builtin + logger is that logger objects are never garbage collected. if you create a + thousand loggers with unique names, they'll sit there in memory until your + script is done. with sh, it's easy to create loggers with unique names if + we want our loggers to include our command arguments. for example, these + are all unique loggers: + + ls -l + ls -l /tmp + ls /tmp + + so instead of creating unique loggers, and without sacrificing logging + output, we use this class, which maintains as part of its state, the logging + "context", which will be the very unique name. this allows us to get a + logger with a very general name, eg: "command", and have a unique name + appended to it via the context, eg: "ls -l /tmp" """ + def __init__(self, name, context=None): + self.name = name + if context: + context = context.replace("%", "%%") + self.context = context + self.log = logging.getLogger("%s.%s" % (SH_LOGGER_NAME, name)) + + def _format_msg(self, msg, *args): + if self.context: + msg = "%s: %s" % (self.context, msg) + return msg % args + + def get_child(self, name, context): + new_name = self.name + "." + name + new_context = self.context + "." + context + l = Logger(new_name, new_context) + return l + + def info(self, msg, *args): + self.log.info(self._format_msg(msg, *args)) + + def debug(self, msg, *args): + self.log.debug(self._format_msg(msg, *args)) + + def error(self, msg, *args): + self.log.error(self._format_msg(msg, *args)) + + def exception(self, msg, *args): + self.log.exception(self._format_msg(msg, *args)) + + +def friendly_truncate(s, max_len): + if len(s) > max_len: + s = "%s...(%d more)" % (s[:max_len], len(s) - max_len) + return s + + +class RunningCommand(object): + """ this represents an executing Command object. it is returned as the + result of __call__() being executed on a Command instance. this creates a + reference to a OProc instance, which is a low-level wrapper around the + process that was exec'd + + this is the class that gets manipulated the most by user code, and so it + implements various convenience methods and logical mechanisms for the + underlying process. for example, if a user tries to access a + backgrounded-process's stdout/err, the RunningCommand object is smart enough + to know to wait() on the process to finish first. and when the process + finishes, RunningCommand is smart enough to translate exit codes to + exceptions. """ + + def __init__(self, cmd, call_args, stdin, stdout, stderr): + # self.ran is used for auditing what actually ran. for example, in + # exceptions, or if you just want to know what was ran after the + # command ran + if IS_PY3: + self.ran = " ".join([arg.decode(DEFAULT_ENCODING, "ignore") for arg in cmd]) + else: + self.ran = " ".join(cmd) + + + friendly_cmd = friendly_truncate(self.ran, 20) + friendly_call_args = friendly_truncate(str(call_args), 20) + + # we're setting up the logger string here, instead of __repr__ because + # we reserve __repr__ to behave as if it was evaluating the child + # process's output + logger_str = "" % (friendly_cmd, + friendly_call_args) + + self.log = Logger("command", logger_str) + self.call_args = call_args + self.cmd = cmd + + self.process = None + self._process_completed = False + should_wait = True + spawn_process = True + + + # with contexts shouldn't run at all yet, they prepend + # to every command in the context + if call_args["with"]: + spawn_process = False + Command._prepend_stack.append(self) + + + if call_args["piped"] or call_args["iter"] or call_args["iter_noblock"]: + should_wait = False + + # we're running in the background, return self and let us lazily + # evaluate + if call_args["bg"]: + should_wait = False + + # redirection + if call_args["err_to_out"]: + stderr = OProc.STDOUT + + + # set up which stream should write to the pipe + # TODO, make pipe None by default and limit the size of the Queue + # in oproc.OProc + pipe = OProc.STDOUT + if call_args["iter"] == "out" or call_args["iter"] is True: + pipe = OProc.STDOUT + elif call_args["iter"] == "err": + pipe = OProc.STDERR + + if call_args["iter_noblock"] == "out" or call_args["iter_noblock"] is True: + pipe = OProc.STDOUT + elif call_args["iter_noblock"] == "err": + pipe = OProc.STDERR + + + # there's currently only one case where we wouldn't spawn a child + # process, and that's if we're using a with-context with our command + if spawn_process: + self.log.info("starting process") + self.process = OProc(self.log, cmd, stdin, stdout, stderr, + self.call_args, pipe) + + if should_wait: + self.wait() + + + def wait(self): + if not self._process_completed: + self._process_completed = True + + exit_code = self.process.wait() + if self.process.timed_out: + # if we timed out, our exit code represents a signal, which is + # negative, so let's make it positive to store in our + # TimeoutException + raise TimeoutException(-exit_code) + else: + self.handle_command_exit_code(exit_code) + + # https://github.com/amoffat/sh/issues/185 + if self.call_args["done"]: + self.call_args["done"](self) + + return self + + + def handle_command_exit_code(self, code): + """ here we determine if we had an exception, or an error code that we + weren't expecting to see. if we did, we create and raise an exception + """ + if (code not in self.call_args["ok_code"] and (code > 0 or -code in + SIGNALS_THAT_SHOULD_THROW_EXCEPTION)): + exc = get_rc_exc(code) + raise exc(self.ran, self.process.stdout, self.process.stderr) + + + + @property + def stdout(self): + self.wait() + return self.process.stdout + + @property + def stderr(self): + self.wait() + return self.process.stderr + + @property + def exit_code(self): + self.wait() + return self.process.exit_code + + @property + def pid(self): + return self.process.pid + + def __len__(self): + return len(str(self)) + + def __enter__(self): + """ we don't actually do anything here because anything that should have + been done would have been done in the Command.__call__ call. + essentially all that has to happen is the comand be pushed on the + prepend stack. """ + with_context_warning() + + def __iter__(self): + return self + + def next(self): + """ allow us to iterate over the output of our command """ + + # we do this because if get blocks, we can't catch a KeyboardInterrupt + # so the slight timeout allows for that. + while True: + try: + chunk = self.process._pipe_queue.get(True, 0.001) + except Empty: + if self.call_args["iter_noblock"]: + return errno.EWOULDBLOCK + else: + if chunk is None: + self.wait() + raise StopIteration() + try: + return chunk.decode(self.call_args["encoding"], + self.call_args["decode_errors"]) + except UnicodeDecodeError: + return chunk + + # python 3 + __next__ = next + + def __exit__(self, typ, value, traceback): + if self.call_args["with"] and Command._prepend_stack: + Command._prepend_stack.pop() + + def __str__(self): + """ in python3, should return unicode. in python2, should return a + string of bytes """ + if IS_PY3: + return self.__unicode__() + else: + return unicode(self).encode(self.call_args["encoding"]) + + def __unicode__(self): + """ a magic method defined for python2. calling unicode() on a + RunningCommand object will call this """ + if self.process and self.stdout: + return self.stdout.decode(self.call_args["encoding"], + self.call_args["decode_errors"]) + elif IS_PY3: + return "" + else: + return unicode("") + + def __eq__(self, other): + return unicode(self) == unicode(other) + __hash__ = None # Avoid DeprecationWarning in Python < 3 + + def __contains__(self, item): + return item in str(self) + + def __getattr__(self, p): + # let these three attributes pass through to the OProc object + if p in ("signal", "terminate", "kill"): + if self.process: + return getattr(self.process, p) + else: + raise AttributeError + + # see if strings have what we're looking for. we're looking at the + # method names explicitly because we don't want to evaluate self unless + # we absolutely have to, the reason being, in python2, hasattr swallows + # exceptions, and if we try to run hasattr on a command that failed and + # is being run with _iter=True, the command will be evaluated, throw an + # exception, but hasattr will discard it + if p in _unicode_methods: + return getattr(unicode(self), p) + + raise AttributeError + + def __repr__(self): + """ in python3, should return unicode. in python2, should return a + string of bytes """ + try: + return str(self) + except UnicodeDecodeError: + if self.process: + if self.stdout: + return repr(self.stdout) + return repr("") + + def __long__(self): + return long(str(self).strip()) + + def __float__(self): + return float(str(self).strip()) + + def __int__(self): + return int(str(self).strip()) + + + +def output_redirect_is_filename(out): + return out \ + and not callable(out) \ + and not hasattr(out, "write") \ + and not isinstance(out, (cStringIO, StringIO)) + + + + + + +class Command(object): + """ represents an un-run system program, like "ls" or "cd". because it + represents the program itself (and not a running instance of it), it should + hold very little state. in fact, the only state it does hold is baked + arguments. + + when a Command object is called, the result that is returned is a + RunningCommand object, which represents the Command put into an execution + state. """ + _prepend_stack = [] + + _call_args = { + # currently unsupported + #"fg": False, # run command in foreground + + # run a command in the background. commands run in the background + # ignore SIGHUP and do not automatically exit when the parent process + # ends + "bg": False, + + "with": False, # prepend the command to every command after it + "in": None, + "out": None, # redirect STDOUT + "err": None, # redirect STDERR + "err_to_out": None, # redirect STDERR to STDOUT + + # stdin buffer size + # 1 for line, 0 for unbuffered, any other number for that amount + "in_bufsize": 0, + # stdout buffer size, same values as above + "out_bufsize": 1, + "err_bufsize": 1, + + # this is how big the output buffers will be for stdout and stderr. + # this is essentially how much output they will store from the process. + # we use a deque, so if it overflows past this amount, the first items + # get pushed off as each new item gets added. + # + # NOTICE + # this is not a *BYTE* size, this is a *CHUNK* size...meaning, that if + # you're buffering out/err at 1024 bytes, the internal buffer size will + # be "internal_bufsize" CHUNKS of 1024 bytes + "internal_bufsize": 3 * 1024 ** 2, + + "env": None, + "piped": None, + "iter": None, + "iter_noblock": None, + "ok_code": 0, + "cwd": None, + + # the separator delimiting between a long-argument's name and its value + # for example, --arg=derp, '=' is the long_sep + "long_sep": "=", + + # this is for programs that expect their input to be from a terminal. + # ssh is one of those programs + "tty_in": False, + "tty_out": True, + + "encoding": DEFAULT_ENCODING, + "decode_errors": "strict", + + # how long the process should run before it is auto-killed + "timeout": 0, + "timeout_signal": signal.SIGKILL, + + # TODO write some docs on "long-running processes" + # these control whether or not stdout/err will get aggregated together + # as the process runs. this has memory usage implications, so sometimes + # with long-running processes with a lot of data, it makes sense to + # set these to true + "no_out": False, + "no_err": False, + "no_pipe": False, + + # if any redirection is used for stdout or stderr, internal buffering + # of that data is not stored. this forces it to be stored, as if + # the output is being T'd to both the redirected destination and our + # internal buffers + "tee": None, + + # will be called when a process terminates without exception. this + # option also puts the command in the background, since it doesn't make + # sense to have an un-backgrounded command with a done callback + "done": None, + + # a tuple (rows, columns) of the desired size of both the stdout and + # stdin ttys, if ttys are being used + "tty_size": (20, 80), + } + + # these are arguments that cannot be called together, because they wouldn't + # make any sense + _incompatible_call_args = ( + #("fg", "bg", "Command can't be run in the foreground and background"), + ("err", "err_to_out", "Stderr is already being redirected"), + ("piped", "iter", "You cannot iterate when this command is being piped"), + ("piped", "no_pipe", "Using a pipe doesn't make sense if you've \ +disabled the pipe"), + ("no_out", "iter", "You cannot iterate over output if there is no \ +output"), + ) + + + # this method exists because of the need to have some way of letting + # manual object instantiation not perform the underscore-to-dash command + # conversion that resolve_program uses. + # + # there are 2 ways to create a Command object. using sh.Command() + # or by using sh.. the method fed into sh.Command must be taken + # literally, and so no underscore-dash conversion is performed. the one + # for sh. must do the underscore-dash converesion, because we + # can't type dashes in method names + @classmethod + def _create(cls, program, **default_kwargs): + path = resolve_program(program) + if not path: + raise CommandNotFound(program) + + cmd = cls(path) + if default_kwargs: + cmd = cmd.bake(**default_kwargs) + + return cmd + + + def __init__(self, path): + found = which(path) + if not found: + raise CommandNotFound(path) + + self._path = encode_to_py3bytes_or_py2str(found) + + self._partial = False + self._partial_baked_args = [] + self._partial_call_args = {} + + # bugfix for functools.wraps. issue #121 + self.__name__ = str(self) + + + def __getattribute__(self, name): + # convenience + getattr = partial(object.__getattribute__, self) + + if name.startswith("_"): + return getattr(name) + if name == "bake": + return getattr("bake") + if name.endswith("_"): + name = name[:-1] + + return getattr("bake")(name) + + + @staticmethod + def _extract_call_args(kwargs, to_override={}): + kwargs = kwargs.copy() + call_args = {} + for parg, default in Command._call_args.items(): + key = "_" + parg + + if key in kwargs: + call_args[parg] = kwargs[key] + del kwargs[key] + elif parg in to_override: + call_args[parg] = to_override[parg] + + # test for incompatible call args + s1 = set(call_args.keys()) + for args in Command._incompatible_call_args: + args = list(args) + error = args.pop() + + if s1.issuperset(args): + raise TypeError("Invalid special arguments %r: %s" % (args, error)) + + return call_args, kwargs + + + def _aggregate_keywords(self, keywords, sep, raw=False): + processed = [] + for k, v in keywords.items(): + # we're passing a short arg as a kwarg, example: + # cut(d="\t") + if len(k) == 1: + if v is not False: + processed.append(encode_to_py3bytes_or_py2str("-" + k)) + if v is not True: + processed.append(encode_to_py3bytes_or_py2str(v)) + + # we're doing a long arg + else: + if not raw: + k = k.replace("_", "-") + + if v is True: + processed.append(encode_to_py3bytes_or_py2str("--" + k)) + elif v is False: + pass + else: + arg = encode_to_py3bytes_or_py2str("--%s%s%s" % (k, sep, v)) + processed.append(arg) + return processed + + + def _compile_args(self, args, kwargs, sep): + processed_args = [] + + # aggregate positional args + for arg in args: + if isinstance(arg, (list, tuple)): + if not arg: + warnings.warn("Empty list passed as an argument to %r. \ +If you're using glob.glob(), please use sh.glob() instead." % self._path, stacklevel=3) + for sub_arg in arg: + processed_args.append(encode_to_py3bytes_or_py2str(sub_arg)) + elif isinstance(arg, dict): + processed_args += self._aggregate_keywords(arg, sep, raw=True) + else: + processed_args.append(encode_to_py3bytes_or_py2str(arg)) + + # aggregate the keyword arguments + processed_args += self._aggregate_keywords(kwargs, sep) + + return processed_args + + + # TODO needs documentation + def bake(self, *args, **kwargs): + fn = Command(self._path) + fn._partial = True + + call_args, kwargs = self._extract_call_args(kwargs) + + pruned_call_args = call_args + for k, v in Command._call_args.items(): + try: + if pruned_call_args[k] == v: + del pruned_call_args[k] + except KeyError: + continue + + fn._partial_call_args.update(self._partial_call_args) + fn._partial_call_args.update(pruned_call_args) + fn._partial_baked_args.extend(self._partial_baked_args) + sep = pruned_call_args.get("long_sep", self._call_args["long_sep"]) + fn._partial_baked_args.extend(self._compile_args(args, kwargs, sep)) + return fn + + def __str__(self): + """ in python3, should return unicode. in python2, should return a + string of bytes """ + if IS_PY3: + return self.__unicode__() + else: + return self.__unicode__().encode(DEFAULT_ENCODING) + + + def __eq__(self, other): + try: + return str(self) == str(other) + except: + return False + __hash__ = None # Avoid DeprecationWarning in Python < 3 + + + def __repr__(self): + """ in python3, should return unicode. in python2, should return a + string of bytes """ + return "" % str(self) + + + def __unicode__(self): + """ a magic method defined for python2. calling unicode() on a + self will call this """ + baked_args = " ".join(item.decode(DEFAULT_ENCODING) for item in self._partial_baked_args) + if baked_args: + baked_args = " " + baked_args + return self._path.decode(DEFAULT_ENCODING) + baked_args + + def __enter__(self): + with_context_warning() + self(_with=True) + + def __exit__(self, typ, value, traceback): + Command._prepend_stack.pop() + + + def __call__(self, *args, **kwargs): + kwargs = kwargs.copy() + args = list(args) + + cmd = [] + + # aggregate any 'with' contexts + call_args = Command._call_args.copy() + for prepend in self._prepend_stack: + # don't pass the 'with' call arg + pcall_args = prepend.call_args.copy() + try: + del pcall_args["with"] + except: + pass + + call_args.update(pcall_args) + cmd.extend(prepend.cmd) + + cmd.append(self._path) + + # here we extract the special kwargs and override any + # special kwargs from the possibly baked command + tmp_call_args, kwargs = self._extract_call_args(kwargs, self._partial_call_args) + call_args.update(tmp_call_args) + + if not getattr(call_args["ok_code"], "__iter__", None): + call_args["ok_code"] = [call_args["ok_code"]] + + + if call_args["done"]: + call_args["bg"] = True + + # check if we're piping via composition + stdin = call_args["in"] + if args: + first_arg = args.pop(0) + if isinstance(first_arg, RunningCommand): + # it makes sense that if the input pipe of a command is running + # in the background, then this command should run in the + # background as well + if first_arg.call_args["bg"]: + call_args["bg"] = True + + if first_arg.call_args["piped"] == "direct": + stdin = first_arg.process + else: + stdin = first_arg.process._pipe_queue + + else: + args.insert(0, first_arg) + + processed_args = self._compile_args(args, kwargs, call_args["long_sep"]) + + # makes sure our arguments are broken up correctly + split_args = self._partial_baked_args + processed_args + + final_args = split_args + + cmd.extend(final_args) + + + # stdout redirection + stdout = call_args["out"] + if output_redirect_is_filename(stdout): + stdout = open(str(stdout), "wb") + + # stderr redirection + stderr = call_args["err"] + if output_redirect_is_filename(stderr): + stderr = open(str(stderr), "wb") + + + return RunningCommand(cmd, call_args, stdin, stdout, stderr) + + + + +def _start_daemon_thread(fn, *args): + thrd = threading.Thread(target=fn, args=args) + thrd.daemon = True + thrd.start() + return thrd + + +def setwinsize(fd, rows_cols): + """ set the terminal size of a tty file descriptor. borrowed logic + from pexpect.py """ + rows, cols = rows_cols + TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561) + + s = struct.pack('HHHH', rows, cols, 0, 0) + fcntl.ioctl(fd, TIOCSWINSZ, s) + +def construct_streamreader_callback(process, handler): + """ here we're constructing a closure for our streamreader callback. this + is used in the case that we pass a callback into _out or _err, meaning we + want to our callback to handle each bit of output + + we construct the closure based on how many arguments it takes. the reason + for this is to make it as easy as possible for people to use, without + limiting them. a new user will assume the callback takes 1 argument (the + data). as they get more advanced, they may want to terminate the process, + or pass some stdin back, and will realize that they can pass a callback of + more args """ + + + # implied arg refers to the "self" that methods will pass in. we need to + # account for this implied arg when figuring out what function the user + # passed in based on number of args + implied_arg = 0 + + partial_args = 0 + handler_to_inspect = handler + + if isinstance(handler, partial): + partial_args = len(handler.args) + handler_to_inspect = handler.func + + if inspect.ismethod(handler_to_inspect): + implied_arg = 1 + num_args = len(inspect.getargspec(handler_to_inspect).args) + + else: + if inspect.isfunction(handler_to_inspect): + num_args = len(inspect.getargspec(handler_to_inspect).args) + + # is an object instance with __call__ method + else: + implied_arg = 1 + num_args = len(inspect.getargspec(handler_to_inspect.__call__).args) + + + net_args = num_args - implied_arg - partial_args + + handler_args = () + + # just the chunk + if net_args == 1: + handler_args = () + + # chunk, stdin + if net_args == 2: + handler_args = (process.stdin,) + + # chunk, stdin, process + elif net_args == 3: + # notice we're only storing a weakref, to prevent cyclic references + # (where the process holds a streamreader, and a streamreader holds a + # handler-closure with a reference to the process + handler_args = (process.stdin, weakref.ref(process)) + + def fn(chunk): + # this is pretty ugly, but we're evaluating the process at call-time, + # because it's a weakref + args = handler_args + if len(args) == 2: + args = (handler_args[0], handler_args[1]()) + return handler(chunk, *args) + + return fn + + + +def handle_process_exit_code(exit_code): + """ this should only ever be called once for each child process """ + # if we exited from a signal, let our exit code reflect that + if os.WIFSIGNALED(exit_code): + return -os.WTERMSIG(exit_code) + # otherwise just give us a normal exit code + elif os.WIFEXITED(exit_code): + return os.WEXITSTATUS(exit_code) + else: + raise RuntimeError("Unknown child exit status!") + + + + +class OProc(object): + """ this class is instantiated by RunningCommand for a command to be exec'd. + it handles all the nasty business involved with correctly setting up the + input/output to the child process. it gets its name for subprocess.Popen + (process open) but we're calling ours OProc (open process) """ + + _default_window_size = (24, 80) + + # used in redirecting + STDOUT = -1 + STDERR = -2 + + def __init__(self, parent_log, cmd, stdin, stdout, stderr, call_args, pipe): + """ + cmd is the full string that will be exec'd. it includes the program + name and all its arguments + + stdin, stdout, stderr are what the child will use for standard + input/output/err + + call_args is a mapping of all the special keyword arguments to apply + to the child process + """ + + self.call_args = call_args + + # I had issues with getting 'Input/Output error reading stdin' from dd, + # until I set _tty_out=False + if self.call_args["piped"] == "direct": + self.call_args["tty_out"] = False + + self._single_tty = self.call_args["tty_in"] and self.call_args["tty_out"] + + # this logic is a little convoluted, but basically this top-level + # if/else is for consolidating input and output TTYs into a single + # TTY. this is the only way some secure programs like ssh will + # output correctly (is if stdout and stdin are both the same TTY) + if self._single_tty: + self._stdin_fd, self._slave_stdin_fd = pty.openpty() + + self._stdout_fd = self._stdin_fd + self._slave_stdout_fd = self._slave_stdin_fd + + self._stderr_fd = self._stdin_fd + self._slave_stderr_fd = self._slave_stdin_fd + + # do not consolidate stdin and stdout. this is the most common use- + # case + else: + # this check here is because we may be doing "direct" piping + # (_piped="direct"), and so our stdin might be an instance of + # OProc + if isinstance(stdin, OProc): + self._slave_stdin_fd = stdin._stdout_fd + self._stdin_fd = None + elif self.call_args["tty_in"]: + self._slave_stdin_fd, self._stdin_fd = pty.openpty() + # tty_in=False is the default + else: + self._slave_stdin_fd, self._stdin_fd = os.pipe() + + + # tty_out=True is the default + if self.call_args["tty_out"]: + self._stdout_fd, self._slave_stdout_fd = pty.openpty() + else: + self._stdout_fd, self._slave_stdout_fd = os.pipe() + + # unless STDERR is going to STDOUT, it ALWAYS needs to be a pipe, + # and never a PTY. the reason for this is not totally clear to me, + # but it has to do with the fact that if STDERR isn't set as the + # CTTY (because STDOUT is), the STDERR buffer won't always flush + # by the time the process exits, and the data will be lost. + # i've only seen this on OSX. + if stderr is not OProc.STDOUT: + self._stderr_fd, self._slave_stderr_fd = os.pipe() + + + # this is a hack, but what we're doing here is intentionally throwing an + # OSError exception if our child processes's directory doesn't exist, + # but we're doing it BEFORE we fork. the reason for before the fork is + # error handling. i'm currently too lazy to implement what + # subprocess.py did and set up a error pipe to handle exceptions that + # happen in the child between fork and exec. it has only been seen in + # the wild for a missing cwd, so we'll handle it here. + cwd = self.call_args["cwd"] + if cwd is not None and not os.path.exists(cwd): + os.chdir(cwd) + + + gc_enabled = gc.isenabled() + if gc_enabled: + gc.disable() + self.pid = os.fork() + + + # child + if self.pid == 0: # pragma: no cover + try: + # ignoring SIGHUP lets us persist even after the parent process + # exits. only ignore if we're backgrounded + if self.call_args["bg"] is True: + signal.signal(signal.SIGHUP, signal.SIG_IGN) + + # this piece of ugliness is due to a bug where we can lose output + # if we do os.close(self._slave_stdout_fd) in the parent after + # the child starts writing. + # see http://bugs.python.org/issue15898 + if IS_OSX: + time.sleep(0.01) + + os.setsid() + + if self.call_args["tty_out"]: + # set raw mode, so there isn't any weird translation of + # newlines to \r\n and other oddities. we're not outputting + # to a terminal anyways + # + # we HAVE to do this here, and not in the parent process, + # because we have to guarantee that this is set before the + # child process is run, and we can't do it twice. + tty.setraw(self._slave_stdout_fd) + + + # if the parent-side fd for stdin exists, close it. the case + # where it may not exist is if we're using piped="direct" + if self._stdin_fd: + os.close(self._stdin_fd) + + if not self._single_tty: + os.close(self._stdout_fd) + if stderr is not OProc.STDOUT: + os.close(self._stderr_fd) + + + if cwd: + os.chdir(cwd) + + os.dup2(self._slave_stdin_fd, 0) + os.dup2(self._slave_stdout_fd, 1) + + # we're not directing stderr to stdout? then set self._slave_stderr_fd to + # fd 2, the common stderr fd + if stderr is OProc.STDOUT: + os.dup2(self._slave_stdout_fd, 2) + else: + os.dup2(self._slave_stderr_fd, 2) + + # don't inherit file descriptors + max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0] + os.closerange(3, max_fd) + + + # set our controlling terminal. tty_out defaults to true + if self.call_args["tty_out"]: + tmp_fd = os.open(os.ttyname(1), os.O_RDWR) + os.close(tmp_fd) + + + if self.call_args["tty_out"]: + setwinsize(1, self.call_args["tty_size"]) + + # actually execute the process + if self.call_args["env"] is None: + os.execv(cmd[0], cmd) + else: + os.execve(cmd[0], cmd, self.call_args["env"]) + + # we must ensure that we ALWAYS exit the child process, otherwise + # the parent process code will be executed twice on exception + # https://github.com/amoffat/sh/issues/202 + # + # if your parent process experiences an exit code 255, it is most + # likely that an exception occurred between the fork of the child + # and the exec. this should be reported. + finally: + os._exit(255) + + # parent + else: + if gc_enabled: + gc.enable() + + # used to determine what exception to raise. if our process was + # killed via a timeout counter, we'll raise something different than + # a SIGKILL exception + self.timed_out = False + + self.started = time.time() + self.cmd = cmd + + # exit code should only be manipulated from within self._wait_lock + # to prevent race conditions + self.exit_code = None + + self.stdin = stdin or Queue() + + # _pipe_queue is used internally to hand off stdout from one process + # to another. by default, all stdout from a process gets dumped + # into this pipe queue, to be consumed in real time (hence the + # thread-safe Queue), or at a potentially later time + self._pipe_queue = Queue() + + # this is used to prevent a race condition when we're waiting for + # a process to end, and the OProc's internal threads are also checking + # for the processes's end + self._wait_lock = threading.Lock() + + # these are for aggregating the stdout and stderr. we use a deque + # because we don't want to overflow + self._stdout = deque(maxlen=self.call_args["internal_bufsize"]) + self._stderr = deque(maxlen=self.call_args["internal_bufsize"]) + + if self.call_args["tty_in"]: + setwinsize(self._stdin_fd, self.call_args["tty_size"]) + + + self.log = parent_log.get_child("process", repr(self)) + + os.close(self._slave_stdin_fd) + if not self._single_tty: + os.close(self._slave_stdout_fd) + if stderr is not OProc.STDOUT: + os.close(self._slave_stderr_fd) + + self.log.debug("started process") + + + if self.call_args["tty_in"]: + attr = termios.tcgetattr(self._stdin_fd) + attr[3] &= ~termios.ECHO + termios.tcsetattr(self._stdin_fd, termios.TCSANOW, attr) + + # this represents the connection from a Queue object (or whatever + # we're using to feed STDIN) to the process's STDIN fd + self._stdin_stream = None + if not isinstance(self.stdin, OProc): + self._stdin_stream = \ + StreamWriter(self.log.get_child("streamwriter", + "stdin"), self._stdin_fd, self.stdin, + self.call_args["in_bufsize"], + self.call_args["encoding"], + self.call_args["tty_in"]) + + stdout_pipe = None + if pipe is OProc.STDOUT and not self.call_args["no_pipe"]: + stdout_pipe = self._pipe_queue + + + # this represents the connection from a process's STDOUT fd to + # wherever it has to go, sometimes a pipe Queue (that we will use + # to pipe data to other processes), and also an internal deque + # that we use to aggregate all the output + save_stdout = not self.call_args["no_out"] and \ + (self.call_args["tee"] in (True, "out") or stdout is None) + + + # if we're piping directly into another process's filedescriptor, we + # bypass reading from the stdout stream altogether, because we've + # already hooked up this processes's stdout fd to the other + # processes's stdin fd + self._stdout_stream = None + if self.call_args["piped"] != "direct": + if callable(stdout): + stdout = construct_streamreader_callback(self, stdout) + self._stdout_stream = \ + StreamReader(self.log.get_child("streamreader", + "stdout"), self._stdout_fd, stdout, self._stdout, + self.call_args["out_bufsize"], + self.call_args["encoding"], + self.call_args["decode_errors"], stdout_pipe, + save_data=save_stdout) + + if stderr is OProc.STDOUT or self._single_tty: + self._stderr_stream = None + else: + stderr_pipe = None + if pipe is OProc.STDERR and not self.call_args["no_pipe"]: + stderr_pipe = self._pipe_queue + + save_stderr = not self.call_args["no_err"] and \ + (self.call_args["tee"] in ("err",) or stderr is None) + + if callable(stderr): + stderr = construct_streamreader_callback(self, stderr) + + self._stderr_stream = StreamReader(Logger("streamreader"), + self._stderr_fd, stderr, self._stderr, + self.call_args["err_bufsize"], self.call_args["encoding"], + self.call_args["decode_errors"], stderr_pipe, + save_data=save_stderr) + + + # start the main io threads + # stdin thread is not needed if we are connecting from another process's stdout pipe + self._input_thread = None + if self._stdin_stream: + self._input_thread = _start_daemon_thread(self.input_thread, + self._stdin_stream) + + self._output_thread = _start_daemon_thread(self.output_thread, + self._stdout_stream, self._stderr_stream, + self.call_args["timeout"], self.started, + self.call_args["timeout_signal"]) + + + def __repr__(self): + return "" % (self.pid, self.cmd[:500]) + + + def change_in_bufsize(self, buf): + self._stdin_stream.stream_bufferer.change_buffering(buf) + + def change_out_bufsize(self, buf): + self._stdout_stream.stream_bufferer.change_buffering(buf) + + def change_err_bufsize(self, buf): + self._stderr_stream.stream_bufferer.change_buffering(buf) + + + def input_thread(self, stdin): + """ this is run in a separate thread. it writes into our process's + stdin (a streamwriter) and waits the process to end AND everything that + can be written to be written """ + done = False + while not done and self.is_alive(): + self.log.debug("%r ready for more input", stdin) + done = stdin.write() + + stdin.close() + + + def output_thread(self, stdout, stderr, timeout, started, timeout_exc): + """ this function is run in a separate thread. it reads from the + process's stdout stream (a streamreader), and waits for it to claim that + its done """ + + readers = [] + errors = [] + + if stdout is not None: + readers.append(stdout) + errors.append(stdout) + if stderr is not None: + readers.append(stderr) + errors.append(stderr) + + # this is our select loop for polling stdout or stderr that is ready to + # be read and processed. if one of those streamreaders indicate that it + # is done altogether being read from, we remove it from our list of + # things to poll. when no more things are left to poll, we leave this + # loop and clean up + while readers: + outputs, inputs, err = select.select(readers, [], errors, 0.1) + + # stdout and stderr + for stream in outputs: + self.log.debug("%r ready to be read from", stream) + done = stream.read() + if done: + readers.remove(stream) + + for stream in err: + pass + + # test if the process has been running too long + if timeout: + now = time.time() + if now - started > timeout: + self.log.debug("we've been running too long") + self.timed_out = True + self.signal(timeout_exc) + + + # this is here because stdout may be the controlling TTY, and + # we can't close it until the process has ended, otherwise the + # child will get SIGHUP. typically, if we've broken out of + # the above loop, and we're here, the process is just about to + # end, so it's probably ok to aggressively poll self.is_alive() + # + # the other option to this would be to do the CTTY close from + # the method that does the actual os.waitpid() call, but the + # problem with that is that the above loop might still be + # running, and closing the fd will cause some operation to + # fail. this is less complex than wrapping all the ops + # in the above loop with out-of-band fd-close exceptions + while self.is_alive(): + time.sleep(0.001) + + if stdout: + stdout.close() + + if stderr: + stderr.close() + + + @property + def stdout(self): + return "".encode(self.call_args["encoding"]).join(self._stdout) + + @property + def stderr(self): + return "".encode(self.call_args["encoding"]).join(self._stderr) + + + def signal(self, sig): + self.log.debug("sending signal %d", sig) + try: + os.kill(self.pid, sig) + except OSError: + pass + + def kill(self): + self.log.debug("killing") + self.signal(signal.SIGKILL) + + def terminate(self): + self.log.debug("terminating") + self.signal(signal.SIGTERM) + + + def is_alive(self): + """ polls if our child process has completed, without blocking. this + method has side-effects, such as setting our exit_code, if we happen to + see our child exit while this is running """ + + if self.exit_code is not None: + return False + + # what we're doing here essentially is making sure that the main thread + # (or another thread), isn't calling .wait() on the process. because + # .wait() calls os.waitpid(self.pid, 0), we can't do an os.waitpid + # here...because if we did, and the process exited while in this + # thread, the main thread's os.waitpid(self.pid, 0) would raise OSError + # (because the process ended in another thread). + # + # so essentially what we're doing is, using this lock, checking if + # we're calling .wait(), and if we are, let .wait() get the exit code + # and handle the status, otherwise let us do it. + acquired = self._wait_lock.acquire(False) + if not acquired: + if self.exit_code is not None: + return False + return True + + try: + # WNOHANG is just that...we're calling waitpid without hanging... + # essentially polling the process. the return result is (0, 0) if + # there's no process status, so we check that pid == self.pid below + # in order to determine how to proceed + pid, exit_code = os.waitpid(self.pid, os.WNOHANG) + if pid == self.pid: + self.exit_code = handle_process_exit_code(exit_code) + return False + + # no child process + except OSError: + return False + else: + return True + finally: + self._wait_lock.release() + + + def wait(self): + """ waits for the process to complete, handles the exit code """ + + self.log.debug("acquiring wait lock to wait for completion") + # using the lock in a with-context blocks, which is what we want if + # we're running wait() + with self._wait_lock: + self.log.debug("got wait lock") + + if self.exit_code is None: + self.log.debug("exit code not set, waiting on pid") + pid, exit_code = os.waitpid(self.pid, 0) # blocks + self.exit_code = handle_process_exit_code(exit_code) + else: + self.log.debug("exit code already set (%d), no need to wait", self.exit_code) + + # we may not have a thread for stdin, if the pipe has been connected + # via _piped="direct" + if self._input_thread: + self._input_thread.join() + + # wait for our stdout and stderr streamreaders to finish reading and + # aggregating the process output + self._output_thread.join() + + return self.exit_code + + + + +class DoneReadingForever(Exception): pass +class NotYetReadyToRead(Exception): pass + + +def determine_how_to_read_input(input_obj): + """ given some kind of input object, return a function that knows how to + read chunks of that input object. + + each reader function should return a chunk and raise a DoneReadingForever + exception, or return None, when there's no more data to read + + NOTE: the function returned does not need to care much about the requested + buffering type (eg, unbuffered vs newline-buffered). the StreamBufferer + will take care of that. these functions just need to return a + reasonably-sized chunk of data. """ + + get_chunk = None + + if isinstance(input_obj, Queue): + log_msg = "queue" + get_chunk = get_queue_chunk_reader(input_obj) + + elif callable(input_obj): + log_msg = "callable" + get_chunk = get_callable_chunk_reader(input_obj) + + # also handles stringio + elif hasattr(input_obj, "read"): + log_msg = "file descriptor" + get_chunk = get_file_chunk_reader(input_obj) + + elif isinstance(input_obj, basestring): + log_msg = "string" + get_chunk = get_iter_string_reader(input_obj) + + else: + log_msg = "general iterable" + get_chunk = get_iter_chunk_reader(iter(input_obj)) + + return get_chunk, log_msg + + + +def get_queue_chunk_reader(stdin): + def fn(): + try: + chunk = stdin.get(True, 0.01) + except Empty: + raise NotYetReadyToRead + if chunk is None: + raise DoneReadingForever + return chunk + return fn + + +def get_callable_chunk_reader(stdin): + def fn(): + try: + return stdin() + except: + raise DoneReadingForever + return fn + + +def get_iter_string_reader(stdin): + """ return an iterator that returns a chunk of a string every time it is + called. notice that even though bufsize_type might be line buffered, we're + not doing any line buffering here. that's because our StreamBufferer + handles all buffering. we just need to return a reasonable-sized chunk. """ + bufsize = 1024 + iter_str = (stdin[i:i + bufsize] for i in range(0, len(stdin), bufsize)) + return get_iter_chunk_reader(iter_str) + + +def get_iter_chunk_reader(stdin): + def fn(): + try: + if IS_PY3: + chunk = stdin.__next__() + else: + chunk = stdin.next() + return chunk + except StopIteration: + raise DoneReadingForever + return fn + +def get_file_chunk_reader(stdin): + bufsize = 1024 + + def fn(): + chunk = stdin.read(bufsize) + if not chunk: + raise DoneReadingForever + else: + return chunk + return fn + + +def bufsize_type_to_bufsize(bf_type): + """ for a given bufsize type, return the actual bufsize we will read. + notice that although 1 means "newline-buffered", we're reading a chunk size + of 1024. this is because we have to read something. we let a + StreamBufferer instance handle splitting our chunk on newlines """ + + # newlines + if bf_type == 1: + bufsize = 1024 + # unbuffered + elif bf_type == 0: + bufsize = 1 + # or buffered by specific amount + else: + bufsize = bf_type + + return bufsize + + + +class StreamWriter(object): + """ StreamWriter reads from some input (the stdin param) and writes to a fd + (the stream param). the stdin may be a Queue, a callable, something with + the "read" method, a string, or an iterable """ + + def __init__(self, log, stream, stdin, bufsize_type, encoding, tty_in): + self.stream = stream + self.stdin = stdin + + self.log = log + self.encoding = encoding + self.tty_in = tty_in + + + self.stream_bufferer = StreamBufferer(bufsize_type, self.encoding) + self.get_chunk, log_msg = determine_how_to_read_input(stdin) + self.log.debug("parsed stdin as a %s", log_msg) + + + def fileno(self): + """ defining this allows us to do select.select on an instance of this + class """ + return self.stream + + + + def write(self): + """ attempt to get a chunk of data to write to our child process's + stdin, then write it. the return value answers the questions "are we + done writing forever?" """ + + # get_chunk may sometimes return bytes, and sometimes returns trings + # because of the nature of the different types of STDIN objects we + # support + try: + chunk = self.get_chunk() + if chunk is None: + raise DoneReadingForever + + except DoneReadingForever: + self.log.debug("done reading") + + if self.tty_in: + # EOF time + try: + char = termios.tcgetattr(self.stream)[6][termios.VEOF] + except: + char = chr(4).encode() + os.write(self.stream, char) + + return True + + except NotYetReadyToRead: + self.log.debug("received no data") + return False + + # if we're not bytes, make us bytes + if IS_PY3 and hasattr(chunk, "encode"): + chunk = chunk.encode(self.encoding) + + for proc_chunk in self.stream_bufferer.process(chunk): + self.log.debug("got chunk size %d: %r", len(proc_chunk), + proc_chunk[:30]) + + self.log.debug("writing chunk to process") + try: + os.write(self.stream, proc_chunk) + except OSError: + self.log.debug("OSError writing stdin chunk") + return True + + + def close(self): + self.log.debug("closing, but flushing first") + chunk = self.stream_bufferer.flush() + self.log.debug("got chunk size %d to flush: %r", len(chunk), chunk[:30]) + try: + if chunk: + os.write(self.stream, chunk) + + if not self.tty_in: + self.log.debug("we used a TTY, so closing the stream") + os.close(self.stream) + + except OSError: + pass + + + +def determine_how_to_feed_output(handler, encoding, decode_errors): + if callable(handler): + process, finish = get_callback_chunk_consumer(handler, encoding, + decode_errors) + elif isinstance(handler, cStringIO): + process, finish = get_cstringio_chunk_consumer(handler) + elif isinstance(handler, StringIO): + process, finish = get_stringio_chunk_consumer(handler, encoding, + decode_errors) + elif hasattr(handler, "write"): + process, finish = get_file_chunk_consumer(handler) + else: + process = lambda chunk: False + finish = lambda: None + + return process, finish + + +def get_file_chunk_consumer(handler): + def process(chunk): + handler.write(chunk) + # we should flush on an fd. chunk is already the correctly-buffered + # size, so we don't need the fd buffering as well + handler.flush() + return False + + def finish(): + if hasattr(handler, "flush"): + handler.flush() + + return process, finish + +def get_callback_chunk_consumer(handler, encoding, decode_errors): + def process(chunk): + # try to use the encoding first, if that doesn't work, send + # the bytes, because it might be binary + try: + chunk = chunk.decode(encoding, decode_errors) + except UnicodeDecodeError: + pass + return handler(chunk) + + def finish(): + pass + + return process, finish + +def get_cstringio_chunk_consumer(handler): + def process(chunk): + handler.write(chunk) + return False + + def finish(): + pass + + return process, finish + + +def get_stringio_chunk_consumer(handler, encoding, decode_errors): + def process(chunk): + handler.write(chunk.decode(encoding, decode_errors)) + return False + + def finish(): + pass + + return process, finish + + +class StreamReader(object): + """ reads from some output (the stream) and sends what it just read to the + handler. """ + def __init__(self, log, stream, handler, buffer, bufsize_type, encoding, + decode_errors, pipe_queue=None, save_data=True): + self.stream = stream + self.buffer = buffer + self.save_data = save_data + self.encoding = encoding + self.decode_errors = decode_errors + + self.pipe_queue = None + if pipe_queue: + self.pipe_queue = weakref.ref(pipe_queue) + + self.log = log + + self.stream_bufferer = StreamBufferer(bufsize_type, self.encoding, + self.decode_errors) + self.bufsize = bufsize_type_to_bufsize(bufsize_type) + + self.process_chunk, self.finish_chunk_processor = \ + determine_how_to_feed_output(handler, encoding, decode_errors) + + self.should_quit = False + + + def fileno(self): + """ defining this allows us to do select.select on an instance of this + class """ + return self.stream + + def close(self): + chunk = self.stream_bufferer.flush() + self.log.debug("got chunk size %d to flush: %r", len(chunk), chunk[:30]) + if chunk: + self.write_chunk(chunk) + + self.finish_chunk_processor() + + if self.pipe_queue and self.save_data: + self.pipe_queue().put(None) + + try: + os.close(self.stream) + except OSError: + pass + + + def write_chunk(self, chunk): + # in PY3, the chunk coming in will be bytes, so keep that in mind + + if not self.should_quit: + self.should_quit = self.process_chunk(chunk) + + + if self.save_data: + self.buffer.append(chunk) + + if self.pipe_queue: + self.log.debug("putting chunk onto pipe: %r", chunk[:30]) + self.pipe_queue().put(chunk) + + + def read(self): + # if we're PY3, we're reading bytes, otherwise we're reading + # str + try: + chunk = os.read(self.stream, self.bufsize) + except OSError as e: + self.log.debug("got errno %d, done reading", e.errno) + return True + if not chunk: + self.log.debug("got no chunk, done reading") + return True + + self.log.debug("got chunk size %d: %r", len(chunk), chunk[:30]) + for chunk in self.stream_bufferer.process(chunk): + self.write_chunk(chunk) + + + + +class StreamBufferer(object): + """ this is used for feeding in chunks of stdout/stderr, and breaking it up + into chunks that will actually be put into the internal buffers. for + example, if you have two processes, one being piped to the other, and you + want that, first process to feed lines of data (instead of the chunks + however they come in), OProc will use an instance of this class to chop up + the data and feed it as lines to be sent down the pipe """ + + def __init__(self, buffer_type, encoding=DEFAULT_ENCODING, + decode_errors="strict"): + # 0 for unbuffered, 1 for line, everything else for that amount + self.type = buffer_type + self.buffer = [] + self.n_buffer_count = 0 + self.encoding = encoding + self.decode_errors = decode_errors + + # this is for if we change buffering types. if we change from line + # buffered to unbuffered, its very possible that our self.buffer list + # has data that was being saved up (while we searched for a newline). + # we need to use that up, so we don't lose it + self._use_up_buffer_first = False + + # the buffering lock is used because we might chance the buffering + # types from a different thread. for example, if we have a stdout + # callback, we might use it to change the way stdin buffers. so we + # lock + self._buffering_lock = threading.RLock() + self.log = Logger("stream_bufferer") + + + def change_buffering(self, new_type): + # TODO, when we stop supporting 2.6, make this a with context + self.log.debug("acquiring buffering lock for changing buffering") + self._buffering_lock.acquire() + self.log.debug("got buffering lock for changing buffering") + try: + if new_type == 0: + self._use_up_buffer_first = True + + self.type = new_type + finally: + self._buffering_lock.release() + self.log.debug("released buffering lock for changing buffering") + + + def process(self, chunk): + # MAKE SURE THAT THE INPUT IS PY3 BYTES + # THE OUTPUT IS ALWAYS PY3 BYTES + + # TODO, when we stop supporting 2.6, make this a with context + self.log.debug("acquiring buffering lock to process chunk (buffering: %d)", self.type) + self._buffering_lock.acquire() + self.log.debug("got buffering lock to process chunk (buffering: %d)", self.type) + try: + # we've encountered binary, permanently switch to N size buffering + # since matching on newline doesn't make sense anymore + if self.type == 1: + try: + chunk.decode(self.encoding, self.decode_errors) + except: + self.log.debug("detected binary data, changing buffering") + self.change_buffering(1024) + + # unbuffered + if self.type == 0: + if self._use_up_buffer_first: + self._use_up_buffer_first = False + to_write = self.buffer + self.buffer = [] + to_write.append(chunk) + return to_write + + return [chunk] + + # line buffered + # we must decode the bytes before we try to match on newline + elif self.type == 1: + total_to_write = [] + chunk = chunk.decode(self.encoding, self.decode_errors) + while True: + newline = chunk.find("\n") + if newline == -1: + break + + chunk_to_write = chunk[:newline + 1] + if self.buffer: + # this is ugly, but it's designed to take the existing + # bytes buffer, join it together, tack on our latest + # chunk, then convert the whole thing to a string. + # it's necessary, i'm sure. read the whole block to + # see why. + chunk_to_write = "".encode(self.encoding).join(self.buffer) \ + + chunk_to_write.encode(self.encoding) + chunk_to_write = chunk_to_write.decode(self.encoding) + + self.buffer = [] + self.n_buffer_count = 0 + + chunk = chunk[newline + 1:] + total_to_write.append(chunk_to_write.encode(self.encoding)) + + if chunk: + self.buffer.append(chunk.encode(self.encoding)) + self.n_buffer_count += len(chunk) + return total_to_write + + # N size buffered + else: + total_to_write = [] + while True: + overage = self.n_buffer_count + len(chunk) - self.type + if overage >= 0: + ret = "".encode(self.encoding).join(self.buffer) + chunk + chunk_to_write = ret[:self.type] + chunk = ret[self.type:] + total_to_write.append(chunk_to_write) + self.buffer = [] + self.n_buffer_count = 0 + else: + self.buffer.append(chunk) + self.n_buffer_count += len(chunk) + break + return total_to_write + finally: + self._buffering_lock.release() + self.log.debug("released buffering lock for processing chunk (buffering: %d)", self.type) + + + def flush(self): + self.log.debug("acquiring buffering lock for flushing buffer") + self._buffering_lock.acquire() + self.log.debug("got buffering lock for flushing buffer") + try: + ret = "".encode(self.encoding).join(self.buffer) + self.buffer = [] + return ret + finally: + self._buffering_lock.release() + self.log.debug("released buffering lock for flushing buffer") + + + +@contextmanager +def pushd(path): + """ pushd is just a specialized form of args, where we're passing in the + current working directory """ + with args(_cwd=path): + yield + + +@contextmanager +def args(*args, **kwargs): + """ allows us to temporarily override all the special keyword parameters in + a with context """ + call_args = Command._call_args + old_args = call_args.copy() + + for key,value in kwargs.items(): + key = key.lstrip("_") + call_args[key] = value + + yield + call_args.update(old_args) + + + +class Environment(dict): + """ this allows lookups to names that aren't found in the global scope to be + searched for as a program name. for example, if "ls" isn't found in this + module's scope, we consider it a system program and try to find it. + + we use a dict instead of just a regular object as the base class because the + exec() statement used in this file requires the "globals" argument to be a + dictionary """ + + + # this is a list of all of the names that the sh module exports that will + # not resolve to functions. we don't want to accidentally shadow real + # commands with functions/imports that we define in sh.py. for example, + # "import time" may override the time system program + whitelist = set([ + "Command", + "CommandNotFound", + "DEFAULT_ENCODING", + "DoneReadingForever", + "ErrorReturnCode", + "NotYetReadyToRead", + "SignalException", + "TimeoutException", + "__project_url__", + "__version__", + "args", + "glob", + "pushd", + ]) + + def __init__(self, globs, baked_args={}): + self.globs = globs + self.baked_args = baked_args + self.disable_whitelist = False + + def __setitem__(self, k, v): + self.globs[k] = v + + def __getitem__(self, k): + # if we first import "_disable_whitelist" from sh, we can import + # anything defined in the global scope of sh.py. this is useful for our + # tests + if k == "_disable_whitelist": + self.disable_whitelist = True + return None + + # we're trying to import something real (maybe), see if it's in our + # global scope + if k in self.whitelist or self.disable_whitelist: + try: + return self.globs[k] + except KeyError: + pass + + # somebody tried to be funny and do "from sh import *" + if k == "__all__": + raise AttributeError("Cannot import * from sh. \ +Please import sh or import programs individually.") + + + # check if we're naming a dynamically generated ReturnCode exception + exc = get_exc_from_name(k) + if exc: + return exc + + + # https://github.com/ipython/ipython/issues/2577 + # https://github.com/amoffat/sh/issues/97#issuecomment-10610629 + if k.startswith("__") and k.endswith("__"): + raise AttributeError + + # how about an environment variable? + try: + return os.environ[k] + except KeyError: + pass + + # is it a custom builtin? + builtin = getattr(self, "b_" + k, None) + if builtin: + return builtin + + # it must be a command then + # we use _create instead of instantiating the class directly because + # _create uses resolve_program, which will automatically do underscore- + # to-dash conversions. instantiating directly does not use that + return Command._create(k, **self.baked_args) + + + # methods that begin with "b_" are custom builtins and will override any + # program that exists in our path. this is useful for things like + # common shell builtins that people are used to, but which aren't actually + # full-fledged system binaries + + def b_cd(self, path): + os.chdir(path) + + def b_which(self, program): + return which(program) + + + + +def run_repl(env): # pragma: no cover + banner = "\n>> sh v{version}\n>> https://github.com/amoffat/sh\n" + + print(banner.format(version=__version__)) + while True: + try: + line = raw_input("sh> ") + except (ValueError, EOFError): + break + + try: + exec(compile(line, "", "single"), env, env) + except SystemExit: + break + except: + print(traceback.format_exc()) + + # cleans up our last line + print("") + + + + +# this is a thin wrapper around THIS module (we patch sys.modules[__name__]). +# this is in the case that the user does a "from sh import whatever" +# in other words, they only want to import certain programs, not the whole +# system PATH worth of commands. in this case, we just proxy the +# import lookup to our Environment class +class SelfWrapper(ModuleType): + def __init__(self, self_module, baked_args={}): + # this is super ugly to have to copy attributes like this, + # but it seems to be the only way to make reload() behave + # nicely. if i make these attributes dynamic lookups in + # __getattr__, reload sometimes chokes in weird ways... + for attr in ["__builtins__", "__doc__", "__name__", "__package__"]: + setattr(self, attr, getattr(self_module, attr, None)) + + # python 3.2 (2.7 and 3.3 work fine) breaks on osx (not ubuntu) + # if we set this to None. and 3.3 needs a value for __path__ + self.__path__ = [] + self.__self_module = self_module + self.__env = Environment(globals(), baked_args) + + def __setattr__(self, name, value): + if hasattr(self, "__env"): + self.__env[name] = value + else: + ModuleType.__setattr__(self, name, value) + + def __getattr__(self, name): + if name == "__env": + raise AttributeError + return self.__env[name] + + # accept special keywords argument to define defaults for all operations + # that will be processed with given by return SelfWrapper + def __call__(self, **kwargs): + return SelfWrapper(self.__self_module, kwargs) + + + +# we're being run as a stand-alone script +if __name__ == "__main__": # pragma: no cover + try: + arg = sys.argv.pop(1) + except: + arg = None + + if arg == "test": + import subprocess + + def run_test(version, locale): + py_version = "python%s" % version + py_bin = which(py_version) + + if py_bin: + print("Testing %s, locale %r" % (py_version.capitalize(), + locale)) + + env = os.environ.copy() + env["LANG"] = locale + p = subprocess.Popen([py_bin, os.path.join(THIS_DIR, "test.py")] + + sys.argv[1:], env=env) + return_code = p.wait() + + if return_code != 0: + exit(1) + else: + print("Couldn't find %s, skipping" % py_version.capitalize()) + + versions = ("2.6", "2.7", "3.1", "3.2", "3.3", "3.4") + locales = ("en_US.UTF-8", "C") + for locale in locales: + for version in versions: + run_test(version, locale) + + else: + env = Environment(globals()) + run_repl(env) + +# we're being imported from somewhere +else: + self = sys.modules[__name__] + sys.modules[__name__] = SelfWrapper(self) From 6da80fbb5c802d2e612b52e39a5064525ad437e8 Mon Sep 17 00:00:00 2001 From: Jan David Date: Fri, 25 Sep 2015 19:00:53 +0200 Subject: [PATCH 02/12] Add Alfred-Workflow dependency Alfred-Workflow is a library that makes it incredible easy to develop feature-rich workflows for Alfred. Following its installation instructions, its source has been placed within the project folder to be shipped with the workflow, once again unburdening the user from installing a Python module. --- workflow/__init__.py | 108 ++ workflow/background.py | 252 ++++ workflow/update.py | 363 +++++ workflow/version | 1 + workflow/web.py | 644 +++++++++ workflow/workflow.py | 2879 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 4247 insertions(+) create mode 100644 workflow/__init__.py create mode 100644 workflow/background.py create mode 100644 workflow/update.py create mode 100644 workflow/version create mode 100644 workflow/web.py create mode 100644 workflow/workflow.py diff --git a/workflow/__init__.py b/workflow/__init__.py new file mode 100644 index 0000000..5de1a96 --- /dev/null +++ b/workflow/__init__.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# Copyright (c) 2014 Dean Jackson +# +# MIT Licence. See http://opensource.org/licenses/MIT +# +# Created on 2014-02-15 +# + +""" +A Python helper library for `Alfred 2 `_ Workflow +authors. +""" + +import os + +__title__ = 'Alfred-Workflow' +__version__ = open(os.path.join(os.path.dirname(__file__), 'version')).read() +__author__ = 'Dean Jackson' +__licence__ = 'MIT' +__copyright__ = 'Copyright 2014 Dean Jackson' + + +# Workflow objects +from .workflow import Workflow, manager + +# Exceptions +from .workflow import PasswordNotFound, KeychainError + +# Icons +from .workflow import ( + ICON_ACCOUNT, + ICON_BURN, + ICON_CLOCK, + ICON_COLOR, + ICON_COLOUR, + ICON_EJECT, + ICON_ERROR, + ICON_FAVORITE, + ICON_FAVOURITE, + ICON_GROUP, + ICON_HELP, + ICON_HOME, + ICON_INFO, + ICON_NETWORK, + ICON_NOTE, + ICON_SETTINGS, + ICON_SWIRL, + ICON_SWITCH, + ICON_SYNC, + ICON_TRASH, + ICON_USER, + ICON_WARNING, + ICON_WEB, +) + +# Filter matching rules +from .workflow import ( + MATCH_ALL, + MATCH_ALLCHARS, + MATCH_ATOM, + MATCH_CAPITALS, + MATCH_INITIALS, + MATCH_INITIALS_CONTAIN, + MATCH_INITIALS_STARTSWITH, + MATCH_STARTSWITH, + MATCH_SUBSTRING, +) + +__all__ = [ + 'Workflow', + 'manager', + 'PasswordNotFound', + 'KeychainError', + 'ICON_ACCOUNT', + 'ICON_BURN', + 'ICON_CLOCK', + 'ICON_COLOR', + 'ICON_COLOUR', + 'ICON_EJECT', + 'ICON_ERROR', + 'ICON_FAVORITE', + 'ICON_FAVOURITE', + 'ICON_GROUP', + 'ICON_HELP', + 'ICON_HOME', + 'ICON_INFO', + 'ICON_NETWORK', + 'ICON_NOTE', + 'ICON_SETTINGS', + 'ICON_SWIRL', + 'ICON_SWITCH', + 'ICON_SYNC', + 'ICON_TRASH', + 'ICON_USER', + 'ICON_WARNING', + 'ICON_WEB', + 'MATCH_ALL', + 'MATCH_ALLCHARS', + 'MATCH_ATOM', + 'MATCH_CAPITALS', + 'MATCH_INITIALS', + 'MATCH_INITIALS_CONTAIN', + 'MATCH_INITIALS_STARTSWITH', + 'MATCH_STARTSWITH', + 'MATCH_SUBSTRING', +] diff --git a/workflow/background.py b/workflow/background.py new file mode 100644 index 0000000..bcfa74d --- /dev/null +++ b/workflow/background.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# Copyright © 2014 deanishe@deanishe.net +# +# MIT Licence. See http://opensource.org/licenses/MIT +# +# Created on 2014-04-06 +# + +""" +Run background tasks +""" + +from __future__ import print_function, unicode_literals + +import sys +import os +import subprocess +import pickle + +from workflow import Workflow + +__all__ = ['is_running', 'run_in_background'] + +_wf = None + + +def wf(): + global _wf + if _wf is None: + _wf = Workflow() + return _wf + + +def _arg_cache(name): + """Return path to pickle cache file for arguments + + :param name: name of task + :type name: ``unicode`` + :returns: Path to cache file + :rtype: ``unicode`` filepath + + """ + + return wf().cachefile('{0}.argcache'.format(name)) + + +def _pid_file(name): + """Return path to PID file for ``name`` + + :param name: name of task + :type name: ``unicode`` + :returns: Path to PID file for task + :rtype: ``unicode`` filepath + + """ + + return wf().cachefile('{0}.pid'.format(name)) + + +def _process_exists(pid): + """Check if a process with PID ``pid`` exists + + :param pid: PID to check + :type pid: ``int`` + :returns: ``True`` if process exists, else ``False`` + :rtype: ``Boolean`` + """ + + try: + os.kill(pid, 0) + except OSError: # not running + return False + return True + + +def is_running(name): + """ + Test whether task is running under ``name`` + + :param name: name of task + :type name: ``unicode`` + :returns: ``True`` if task with name ``name`` is running, else ``False`` + :rtype: ``Boolean`` + + """ + pidfile = _pid_file(name) + if not os.path.exists(pidfile): + return False + + with open(pidfile, 'rb') as file_obj: + pid = int(file_obj.read().strip()) + + if _process_exists(pid): + return True + + elif os.path.exists(pidfile): + os.unlink(pidfile) + + return False + + +def _background(stdin='/dev/null', stdout='/dev/null', + stderr='/dev/null'): # pragma: no cover + """Fork the current process into a background daemon. + + :param stdin: where to read input + :type stdin: filepath + :param stdout: where to write stdout output + :type stdout: filepath + :param stderr: where to write stderr output + :type stderr: filepath + + """ + + # Do first fork. + try: + pid = os.fork() + if pid > 0: + sys.exit(0) # Exit first parent. + except OSError as e: + wf().logger.critical("fork #1 failed: ({0:d}) {1}".format( + e.errno, e.strerror)) + sys.exit(1) + # Decouple from parent environment. + os.chdir(wf().workflowdir) + os.umask(0) + os.setsid() + # Do second fork. + try: + pid = os.fork() + if pid > 0: + sys.exit(0) # Exit second parent. + except OSError as e: + wf().logger.critical("fork #2 failed: ({0:d}) {1}".format( + e.errno, e.strerror)) + sys.exit(1) + # Now I am a daemon! + # Redirect standard file descriptors. + si = file(stdin, 'r', 0) + so = file(stdout, 'a+', 0) + se = file(stderr, 'a+', 0) + if hasattr(sys.stdin, 'fileno'): + os.dup2(si.fileno(), sys.stdin.fileno()) + if hasattr(sys.stdout, 'fileno'): + os.dup2(so.fileno(), sys.stdout.fileno()) + if hasattr(sys.stderr, 'fileno'): + os.dup2(se.fileno(), sys.stderr.fileno()) + + +def run_in_background(name, args, **kwargs): + """Pickle arguments to cache file, then call this script again via + :func:`subprocess.call`. + + :param name: name of task + :type name: ``unicode`` + :param args: arguments passed as first argument to :func:`subprocess.call` + :param \**kwargs: keyword arguments to :func:`subprocess.call` + :returns: exit code of sub-process + :rtype: ``int`` + + When you call this function, it caches its arguments and then calls + ``background.py`` in a subprocess. The Python subprocess will load the + cached arguments, fork into the background, and then run the command you + specified. + + This function will return as soon as the ``background.py`` subprocess has + forked, returning the exit code of *that* process (i.e. not of the command + you're trying to run). + + If that process fails, an error will be written to the log file. + + If a process is already running under the same name, this function will + return immediately and will not run the specified command. + + """ + + if is_running(name): + wf().logger.info('Task `{0}` is already running'.format(name)) + return + + argcache = _arg_cache(name) + + # Cache arguments + with open(argcache, 'wb') as file_obj: + pickle.dump({'args': args, 'kwargs': kwargs}, file_obj) + wf().logger.debug('Command arguments cached to `{0}`'.format(argcache)) + + # Call this script + cmd = ['/usr/bin/python', __file__, name] + wf().logger.debug('Calling {0!r} ...'.format(cmd)) + retcode = subprocess.call(cmd) + if retcode: # pragma: no cover + wf().logger.error('Failed to call task in background') + else: + wf().logger.debug('Executing task `{0}` in background...'.format(name)) + return retcode + + +def main(wf): # pragma: no cover + """ + Load cached arguments, fork into background, then call + :meth:`subprocess.call` with cached arguments + + """ + + name = wf.args[0] + argcache = _arg_cache(name) + if not os.path.exists(argcache): + wf.logger.critical('No arg cache found : {0!r}'.format(argcache)) + return 1 + + # Load cached arguments + with open(argcache, 'rb') as file_obj: + data = pickle.load(file_obj) + + # Cached arguments + args = data['args'] + kwargs = data['kwargs'] + + # Delete argument cache file + os.unlink(argcache) + + pidfile = _pid_file(name) + + # Fork to background + _background() + + # Write PID to file + with open(pidfile, 'wb') as file_obj: + file_obj.write('{0}'.format(os.getpid())) + + # Run the command + try: + wf.logger.debug('Task `{0}` running'.format(name)) + wf.logger.debug('cmd : {0!r}'.format(args)) + + retcode = subprocess.call(args, **kwargs) + + if retcode: + wf.logger.error('Command failed with [{0}] : {1!r}'.format( + retcode, args)) + + finally: + if os.path.exists(pidfile): + os.unlink(pidfile) + wf.logger.debug('Task `{0}` finished'.format(name)) + + +if __name__ == '__main__': # pragma: no cover + wf().run(main) diff --git a/workflow/update.py b/workflow/update.py new file mode 100644 index 0000000..b946e79 --- /dev/null +++ b/workflow/update.py @@ -0,0 +1,363 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# Copyright © 2014 Fabio Niephaus , +# Dean Jackson +# +# MIT Licence. See http://opensource.org/licenses/MIT +# +# Created on 2014-08-16 +# + +""" +Self-updating from GitHub + +.. versionadded:: 1.9 + +.. note:: + + This module is not intended to be used directly. Automatic updates + are controlled by the ``update_settings`` :class:`dict` passed to + :class:`~workflow.workflow.Workflow` objects. + +""" + +from __future__ import print_function, unicode_literals + +import os +import tempfile +import re +import subprocess + +import workflow +import web + +# __all__ = [] + + +RELEASES_BASE = 'https://api.github.com/repos/{0}/releases' + + +_wf = None + + +def wf(): + global _wf + if _wf is None: + _wf = workflow.Workflow() + return _wf + + +class Version(object): + """Mostly semantic versioning + + The main difference to proper :ref:`semantic versioning ` + is that this implementation doesn't require a minor or patch version. + """ + + #: Match version and pre-release/build information in version strings + match_version = re.compile(r'([0-9\.]+)(.+)?').match + + def __init__(self, vstr): + self.vstr = vstr + self.major = 0 + self.minor = 0 + self.patch = 0 + self.suffix = '' + self.build = '' + self._parse(vstr) + + def _parse(self, vstr): + if vstr.startswith('v'): + m = self.match_version(vstr[1:]) + else: + m = self.match_version(vstr) + if not m: + raise ValueError('Invalid version number: {0}'.format(vstr)) + + version, suffix = m.groups() + parts = self._parse_dotted_string(version) + self.major = parts.pop(0) + if len(parts): + self.minor = parts.pop(0) + if len(parts): + self.patch = parts.pop(0) + if not len(parts) == 0: + raise ValueError('Invalid version (too long) : {0}'.format(vstr)) + + if suffix: + # Build info + idx = suffix.find('+') + if idx > -1: + self.build = suffix[idx+1:] + suffix = suffix[:idx] + if suffix: + if not suffix.startswith('-'): + raise ValueError( + 'Invalid suffix : `{0}`. Must start with `-`'.format( + suffix)) + self.suffix = suffix[1:] + + # wf().logger.debug('version str `{}` -> {}'.format(vstr, repr(self))) + + def _parse_dotted_string(self, s): + """Parse string ``s`` into list of ints and strings""" + parsed = [] + parts = s.split('.') + for p in parts: + if p.isdigit(): + p = int(p) + parsed.append(p) + return parsed + + @property + def tuple(self): + """Return version number as a tuple of major, minor, patch, pre-release + """ + + return (self.major, self.minor, self.patch, self.suffix) + + def __lt__(self, other): + if not isinstance(other, Version): + raise ValueError('Not a Version instance: {0!r}'.format(other)) + t = self.tuple[:3] + o = other.tuple[:3] + if t < o: + return True + if t == o: # We need to compare suffixes + if self.suffix and not other.suffix: + return True + if other.suffix and not self.suffix: + return False + return (self._parse_dotted_string(self.suffix) < + self._parse_dotted_string(other.suffix)) + # t > o + return False + + def __eq__(self, other): + if not isinstance(other, Version): + raise ValueError('Not a Version instance: {0!r}'.format(other)) + return self.tuple == other.tuple + + def __ne__(self, other): + return not self.__eq__(other) + + def __gt__(self, other): + if not isinstance(other, Version): + raise ValueError('Not a Version instance: {0!r}'.format(other)) + return other.__lt__(self) + + def __le__(self, other): + if not isinstance(other, Version): + raise ValueError('Not a Version instance: {0!r}'.format(other)) + return not other.__lt__(self) + + def __ge__(self, other): + return not self.__lt__(other) + + def __str__(self): + vstr = '{0}.{1}.{2}'.format(self.major, self.minor, self.patch) + if self.suffix: + vstr += '-{0}'.format(self.suffix) + if self.build: + vstr += '+{0}'.format(self.build) + return vstr + + def __repr__(self): + return "Version('{0}')".format(str(self)) + + +def download_workflow(url): + """Download workflow at ``url`` to a local temporary file + + :param url: URL to .alfredworkflow file in GitHub repo + :returns: path to downloaded file + + """ + + filename = url.split("/")[-1] + + if (not url.endswith('.alfredworkflow') or + not filename.endswith('.alfredworkflow')): + raise ValueError('Attachment `{}` not a workflow'.format(filename)) + + local_path = os.path.join(tempfile.gettempdir(), filename) + + wf().logger.debug( + 'Downloading updated workflow from `{0}` to `{1}` ...'.format( + url, local_path)) + + response = web.get(url) + + with open(local_path, 'wb') as output: + output.write(response.content) + + return local_path + + +def build_api_url(slug): + """Generate releases URL from GitHub slug + + :param slug: Repo name in form ``username/repo`` + :returns: URL to the API endpoint for the repo's releases + + """ + + if len(slug.split('/')) != 2: + raise ValueError('Invalid GitHub slug : {0}'.format(slug)) + + return RELEASES_BASE.format(slug) + + +def get_valid_releases(github_slug): + """Return list of all valid releases + + :param github_slug: ``username/repo`` for workflow's GitHub repo + :returns: list of dicts. Each :class:`dict` has the form + ``{'version': '1.1', 'download_url': 'http://github.com/...'}`` + + + A valid release is one that contains one ``.alfredworkflow`` file. + + If the GitHub version (i.e. tag) is of the form ``v1.1``, the leading + ``v`` will be stripped. + + """ + + api_url = build_api_url(github_slug) + releases = [] + + wf().logger.debug('Retrieving releases list from `{0}` ...'.format( + api_url)) + + def retrieve_releases(): + wf().logger.info( + 'Retrieving releases for `{0}` ...'.format(github_slug)) + return web.get(api_url).json() + + slug = github_slug.replace('/', '-') + for release in wf().cached_data('gh-releases-{0}'.format(slug), + retrieve_releases): + version = release['tag_name'] + download_urls = [] + for asset in release.get('assets', []): + url = asset.get('browser_download_url') + if not url or not url.endswith('.alfredworkflow'): + continue + download_urls.append(url) + + # Validate release + if release['prerelease']: + wf().logger.warning( + 'Invalid release {0} : pre-release detected'.format(version)) + continue + if not download_urls: + wf().logger.warning( + 'Invalid release {0} : No workflow file'.format(version)) + continue + if len(download_urls) > 1: + wf().logger.warning( + 'Invalid release {0} : multiple workflow files'.format(version)) + continue + + wf().logger.debug('Release `{0}` : {1}'.format(version, url)) + releases.append({'version': version, 'download_url': download_urls[0]}) + + return releases + + +def check_update(github_slug, current_version): + """Check whether a newer release is available on GitHub + + :param github_slug: ``username/repo`` for workflow's GitHub repo + :param current_version: the currently installed version of the + workflow. :ref:`Semantic versioning ` is required. + :type current_version: ``unicode`` + :returns: ``True`` if an update is available, else ``False`` + + If an update is available, its version number and download URL will + be cached. + + """ + + releases = get_valid_releases(github_slug) + + wf().logger.info('{0} releases for {1}'.format(len(releases), + github_slug)) + + if not len(releases): + raise ValueError('No valid releases for {0}'.format(github_slug)) + + # GitHub returns releases newest-first + latest_release = releases[0] + + # (latest_version, download_url) = get_latest_release(releases) + vr = Version(latest_release['version']) + vl = Version(current_version) + wf().logger.debug('Latest : {0!r} Installed : {1!r}'.format(vr, vl)) + if vr > vl: + + wf().cache_data('__workflow_update_status', { + 'version': latest_release['version'], + 'download_url': latest_release['download_url'], + 'available': True + }) + + return True + + wf().cache_data('__workflow_update_status', { + 'available': False + }) + return False + + +def install_update(github_slug, current_version): + """If a newer release is available, download and install it + + :param github_slug: ``username/repo`` for workflow's GitHub repo + :param current_version: the currently installed version of the + workflow. :ref:`Semantic versioning ` is required. + :type current_version: ``unicode`` + + If an update is available, it will be downloaded and installed. + + :returns: ``True`` if an update is installed, else ``False`` + + """ + # TODO: `github_slug` and `current_version` are both unusued. + + update_data = wf().cached_data('__workflow_update_status', max_age=0) + + if not update_data or not update_data.get('available'): + wf().logger.info('No update available') + return False + + local_file = download_workflow(update_data['download_url']) + + wf().logger.info('Installing updated workflow ...') + subprocess.call(['open', local_file]) + + update_data['available'] = False + wf().cache_data('__workflow_update_status', update_data) + return True + + +if __name__ == '__main__': # pragma: nocover + import sys + + def show_help(): + print('Usage : update.py (check|install) github_slug version') + sys.exit(1) + + if len(sys.argv) != 4: + show_help() + + action, github_slug, version = sys.argv[1:] + + if action not in ('check', 'install'): + show_help() + + if action == 'check': + check_update(github_slug, version) + elif action == 'install': + install_update(github_slug, version) diff --git a/workflow/version b/workflow/version new file mode 100644 index 0000000..9f76d37 --- /dev/null +++ b/workflow/version @@ -0,0 +1 @@ +1.13 \ No newline at end of file diff --git a/workflow/web.py b/workflow/web.py new file mode 100644 index 0000000..153833b --- /dev/null +++ b/workflow/web.py @@ -0,0 +1,644 @@ +# encoding: utf-8 +# +# Copyright (c) 2014 Dean Jackson +# +# MIT Licence. See http://opensource.org/licenses/MIT +# +# Created on 2014-02-15 +# + +""" +A lightweight HTTP library with a requests-like interface. +""" + +from __future__ import print_function + +import codecs +import json +import mimetypes +import os +import random +import re +import socket +import string +import unicodedata +import urllib +import urllib2 +import urlparse +import zlib + + +USER_AGENT = u'Alfred-Workflow/1.11 (http://www.deanishe.net)' + +# Valid characters for multipart form data boundaries +BOUNDARY_CHARS = string.digits + string.ascii_letters + +# HTTP response codes +RESPONSES = { + 100: 'Continue', + 101: 'Switching Protocols', + 200: 'OK', + 201: 'Created', + 202: 'Accepted', + 203: 'Non-Authoritative Information', + 204: 'No Content', + 205: 'Reset Content', + 206: 'Partial Content', + 300: 'Multiple Choices', + 301: 'Moved Permanently', + 302: 'Found', + 303: 'See Other', + 304: 'Not Modified', + 305: 'Use Proxy', + 307: 'Temporary Redirect', + 400: 'Bad Request', + 401: 'Unauthorized', + 402: 'Payment Required', + 403: 'Forbidden', + 404: 'Not Found', + 405: 'Method Not Allowed', + 406: 'Not Acceptable', + 407: 'Proxy Authentication Required', + 408: 'Request Timeout', + 409: 'Conflict', + 410: 'Gone', + 411: 'Length Required', + 412: 'Precondition Failed', + 413: 'Request Entity Too Large', + 414: 'Request-URI Too Long', + 415: 'Unsupported Media Type', + 416: 'Requested Range Not Satisfiable', + 417: 'Expectation Failed', + 500: 'Internal Server Error', + 501: 'Not Implemented', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Gateway Timeout', + 505: 'HTTP Version Not Supported' +} + + +def str_dict(dic): + """Convert keys and values in ``dic`` into UTF-8-encoded :class:`str` + + :param dic: :class:`dict` of Unicode strings + :returns: :class:`dict` + + """ + if isinstance(dic, CaseInsensitiveDictionary): + dic2 = CaseInsensitiveDictionary() + else: + dic2 = {} + for k, v in dic.items(): + if isinstance(k, unicode): + k = k.encode('utf-8') + if isinstance(v, unicode): + v = v.encode('utf-8') + dic2[k] = v + return dic2 + + +class NoRedirectHandler(urllib2.HTTPRedirectHandler): + """Prevent redirections""" + + def redirect_request(self, *args): + return None + + +# Adapted from https://gist.github.com/babakness/3901174 +class CaseInsensitiveDictionary(dict): + """ + Dictionary that enables case insensitive searching while preserving + case sensitivity when keys are listed, ie, via keys() or items() methods. + + Works by storing a lowercase version of the key as the new key and + stores the original key-value pair as the key's value + (values become dictionaries). + + """ + + def __init__(self, initval=None): + + if isinstance(initval, dict): + for key, value in initval.iteritems(): + self.__setitem__(key, value) + + elif isinstance(initval, list): + for (key, value) in initval: + self.__setitem__(key, value) + + def __contains__(self, key): + return dict.__contains__(self, key.lower()) + + def __getitem__(self, key): + return dict.__getitem__(self, key.lower())['val'] + + def __setitem__(self, key, value): + return dict.__setitem__(self, key.lower(), {'key': key, 'val': value}) + + def get(self, key, default=None): + try: + v = dict.__getitem__(self, key.lower()) + except KeyError: + return default + else: + return v['val'] + + def update(self, other): + for k, v in other.items(): + self[k] = v + + def items(self): + return [(v['key'], v['val']) for v in dict.itervalues(self)] + + def keys(self): + return [v['key'] for v in dict.itervalues(self)] + + def values(self): + return [v['val'] for v in dict.itervalues(self)] + + def iteritems(self): + for v in dict.itervalues(self): + yield v['key'], v['val'] + + def iterkeys(self): + for v in dict.itervalues(self): + yield v['key'] + + def itervalues(self): + for v in dict.itervalues(self): + yield v['val'] + + +class Response(object): + """ + Returned by :func:`request` / :func:`get` / :func:`post` functions. + + A simplified version of the ``Response`` object in the ``requests`` library. + + >>> r = request('http://www.google.com') + >>> r.status_code + 200 + >>> r.encoding + ISO-8859-1 + >>> r.content # bytes + ... + >>> r.text # unicode, decoded according to charset in HTTP header/meta tag + u' ...' + >>> r.json() # content parsed as JSON + + """ + + def __init__(self, request): + """Call `request` with :mod:`urllib2` and process results. + + :param request: :class:`urllib2.Request` instance + + """ + + self.request = request + self.url = None + self.raw = None + self._encoding = None + self.error = None + self.status_code = None + self.reason = None + self.headers = CaseInsensitiveDictionary() + self._content = None + self._gzipped = False + + # Execute query + try: + self.raw = urllib2.urlopen(request) + except urllib2.HTTPError as err: + self.error = err + try: + self.url = err.geturl() + # sometimes (e.g. when authentication fails) + # urllib can't get a URL from an HTTPError + # This behaviour changes across Python versions, + # so no test cover (it isn't important). + except AttributeError: # pragma: no cover + pass + self.status_code = err.code + else: + self.status_code = self.raw.getcode() + self.url = self.raw.geturl() + self.reason = RESPONSES.get(self.status_code) + + # Parse additional info if request succeeded + if not self.error: + headers = self.raw.info() + self.transfer_encoding = headers.getencoding() + self.mimetype = headers.gettype() + for key in headers.keys(): + self.headers[key.lower()] = headers.get(key) + + # Is content gzipped? + # Transfer-Encoding appears to not be used in the wild + # (contrary to the HTTP standard), but no harm in testing + # for it + if ('gzip' in headers.get('content-encoding', '') or + 'gzip' in headers.get('transfer-encoding', '')): + self._gzipped = True + + def json(self): + """Decode response contents as JSON. + + :returns: object decoded from JSON + :rtype: :class:`list` / :class:`dict` + + """ + + return json.loads(self.content, self.encoding or 'utf-8') + + @property + def encoding(self): + """Text encoding of document or ``None`` + + :returns: :class:`str` or ``None`` + + """ + + if not self._encoding: + self._encoding = self._get_encoding() + + return self._encoding + + @property + def content(self): + """Raw content of response (i.e. bytes) + + :returns: Body of HTTP response + :rtype: :class:`str` + + """ + + if not self._content: + + # Decompress gzipped content + if self._gzipped: + decoder = zlib.decompressobj(16 + zlib.MAX_WBITS) + self._content = decoder.decompress(self.raw.read()) + + else: + self._content = self.raw.read() + + return self._content + + @property + def text(self): + """Unicode-decoded content of response body. + + If no encoding can be determined from HTTP headers or the content + itself, the encoded response body will be returned instead. + + :returns: Body of HTTP response + :rtype: :class:`unicode` or :class:`str` + + """ + + if self.encoding: + return unicodedata.normalize('NFC', unicode(self.content, + self.encoding)) + return self.content + + def iter_content(self, chunk_size=4096, decode_unicode=False): + """Iterate over response data. + + .. versionadded:: 1.6 + + :param chunk_size: Number of bytes to read into memory + :type chunk_size: ``int`` + :param decode_unicode: Decode to Unicode using detected encoding + :type decode_unicode: ``Boolean`` + :returns: iterator + + """ + + def decode_stream(iterator, r): + + decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace') + + for chunk in iterator: + data = decoder.decode(chunk) + if data: + yield data + + data = decoder.decode(b'', final=True) + if data: + yield data # pragma: nocover + + def generate(): + + if self._gzipped: + decoder = zlib.decompressobj(16 + zlib.MAX_WBITS) + + while True: + chunk = self.raw.read(chunk_size) + if not chunk: + break + + if self._gzipped: + chunk = decoder.decompress(chunk) + + yield chunk + + chunks = generate() + + if decode_unicode and self.encoding: + chunks = decode_stream(chunks, self) + + return chunks + + def save_to_path(self, filepath): + """Save retrieved data to file at ``filepath`` + + .. versionadded: 1.9.6 + + :param filepath: Path to save retrieved data. + + """ + + filepath = os.path.abspath(filepath) + dirname = os.path.dirname(filepath) + if not os.path.exists(dirname): + os.makedirs(dirname) + + with open(filepath, 'wb') as fileobj: + for data in self.iter_content(): + fileobj.write(data) + + def raise_for_status(self): + """Raise stored error if one occurred. + + error will be instance of :class:`urllib2.HTTPError` + """ + + if self.error is not None: + raise self.error + return + + def _get_encoding(self): + """Get encoding from HTTP headers or content. + + :returns: encoding or `None` + :rtype: ``unicode`` or ``None`` + + """ + + headers = self.raw.info() + encoding = None + + if headers.getparam('charset'): + encoding = headers.getparam('charset') + + # HTTP Content-Type header + for param in headers.getplist(): + if param.startswith('charset='): + encoding = param[8:] + break + + # Encoding declared in document should override HTTP headers + if self.mimetype == 'text/html': # sniff HTML headers + m = re.search("""""", + self.content) + if m: + encoding = m.group(1) + + elif ((self.mimetype.startswith('application/') or + self.mimetype.startswith('text/')) and + 'xml' in self.mimetype): + m = re.search("""]*\?>""", + self.content) + if m: + encoding = m.group(1) + + # Format defaults + if self.mimetype == 'application/json' and not encoding: + # The default encoding for JSON + encoding = 'utf-8' + + elif self.mimetype == 'application/xml' and not encoding: + # The default for 'application/xml' + encoding = 'utf-8' + + if encoding: + encoding = encoding.lower() + + return encoding + + +def request(method, url, params=None, data=None, headers=None, cookies=None, + files=None, auth=None, timeout=60, allow_redirects=False): + """Initiate an HTTP(S) request. Returns :class:`Response` object. + + :param method: 'GET' or 'POST' + :type method: ``unicode`` + :param url: URL to open + :type url: ``unicode`` + :param params: mapping of URL parameters + :type params: :class:`dict` + :param data: mapping of form data ``{'field_name': 'value'}`` or + :class:`str` + :type data: :class:`dict` or :class:`str` + :param headers: HTTP headers + :type headers: :class:`dict` + :param cookies: cookies to send to server + :type cookies: :class:`dict` + :param files: files to upload (see below). + :type files: :class:`dict` + :param auth: username, password + :type auth: ``tuple`` + :param timeout: connection timeout limit in seconds + :type timeout: ``int`` + :param allow_redirects: follow redirections + :type allow_redirects: ``Boolean`` + :returns: :class:`Response` object + + + The ``files`` argument is a dictionary:: + + {'fieldname' : { 'filename': 'blah.txt', + 'content': '', + 'mimetype': 'text/plain'} + } + + * ``fieldname`` is the name of the field in the HTML form. + * ``mimetype`` is optional. If not provided, :mod:`mimetypes` will + be used to guess the mimetype, or ``application/octet-stream`` + will be used. + + """ + + # TODO: cookies + # TODO: any way to force GET or POST? + socket.setdefaulttimeout(timeout) + + # Default handlers + openers = [] + + if not allow_redirects: + openers.append(NoRedirectHandler()) + + if auth is not None: # Add authorisation handler + username, password = auth + password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() + password_manager.add_password(None, url, username, password) + auth_manager = urllib2.HTTPBasicAuthHandler(password_manager) + openers.append(auth_manager) + + # Install our custom chain of openers + opener = urllib2.build_opener(*openers) + urllib2.install_opener(opener) + + if not headers: + headers = CaseInsensitiveDictionary() + else: + headers = CaseInsensitiveDictionary(headers) + + if 'user-agent' not in headers: + headers['user-agent'] = USER_AGENT + + # Accept gzip-encoded content + encodings = [s.strip() for s in + headers.get('accept-encoding', '').split(',')] + if 'gzip' not in encodings: + encodings.append('gzip') + + headers['accept-encoding'] = ', '.join(encodings) + + if files: + if not data: + data = {} + new_headers, data = encode_multipart_formdata(data, files) + headers.update(new_headers) + elif data and isinstance(data, dict): + data = urllib.urlencode(str_dict(data)) + + # Make sure everything is encoded text + headers = str_dict(headers) + + if isinstance(url, unicode): + url = url.encode('utf-8') + + if params: # GET args (POST args are handled in encode_multipart_formdata) + + scheme, netloc, path, query, fragment = urlparse.urlsplit(url) + + if query: # Combine query string and `params` + url_params = urlparse.parse_qs(query) + # `params` take precedence over URL query string + url_params.update(params) + params = url_params + + query = urllib.urlencode(str_dict(params), doseq=True) + url = urlparse.urlunsplit((scheme, netloc, path, query, fragment)) + + req = urllib2.Request(url, data, headers) + return Response(req) + + +def get(url, params=None, headers=None, cookies=None, auth=None, + timeout=60, allow_redirects=True): + """Initiate a GET request. Arguments as for :func:`request`. + + :returns: :class:`Response` instance + + """ + + return request('GET', url, params, headers=headers, cookies=cookies, + auth=auth, timeout=timeout, allow_redirects=allow_redirects) + + +def post(url, params=None, data=None, headers=None, cookies=None, files=None, + auth=None, timeout=60, allow_redirects=False): + """Initiate a POST request. Arguments as for :func:`request`. + + :returns: :class:`Response` instance + + """ + return request('POST', url, params, data, headers, cookies, files, auth, + timeout, allow_redirects) + + +def encode_multipart_formdata(fields, files): + """Encode form data (``fields``) and ``files`` for POST request. + + :param fields: mapping of ``{name : value}`` pairs for normal form fields. + :type fields: :class:`dict` + :param files: dictionary of fieldnames/files elements for file data. + See below for details. + :type files: :class:`dict` of :class:`dicts` + :returns: ``(headers, body)`` ``headers`` is a :class:`dict` of HTTP headers + :rtype: 2-tuple ``(dict, str)`` + + The ``files`` argument is a dictionary:: + + {'fieldname' : { 'filename': 'blah.txt', + 'content': '', + 'mimetype': 'text/plain'} + } + + - ``fieldname`` is the name of the field in the HTML form. + - ``mimetype`` is optional. If not provided, :mod:`mimetypes` will be used to guess the mimetype, or ``application/octet-stream`` will be used. + + """ + + def get_content_type(filename): + """Return or guess mimetype of ``filename``. + + :param filename: filename of file + :type filename: unicode/string + :returns: mime-type, e.g. ``text/html`` + :rtype: :class::class:`str` + + """ + + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' + + boundary = '-----' + ''.join(random.choice(BOUNDARY_CHARS) + for i in range(30)) + CRLF = '\r\n' + output = [] + + # Normal form fields + for (name, value) in fields.items(): + if isinstance(name, unicode): + name = name.encode('utf-8') + if isinstance(value, unicode): + value = value.encode('utf-8') + output.append('--' + boundary) + output.append('Content-Disposition: form-data; name="%s"' % name) + output.append('') + output.append(value) + + # Files to upload + for name, d in files.items(): + filename = d[u'filename'] + content = d[u'content'] + if u'mimetype' in d: + mimetype = d[u'mimetype'] + else: + mimetype = get_content_type(filename) + if isinstance(name, unicode): + name = name.encode('utf-8') + if isinstance(filename, unicode): + filename = filename.encode('utf-8') + if isinstance(mimetype, unicode): + mimetype = mimetype.encode('utf-8') + output.append('--' + boundary) + output.append('Content-Disposition: form-data; ' + 'name="%s"; filename="%s"' % (name, filename)) + output.append('Content-Type: %s' % mimetype) + output.append('') + output.append(content) + + output.append('--' + boundary + '--') + output.append('') + body = CRLF.join(output) + headers = { + 'Content-Type': 'multipart/form-data; boundary=%s' % boundary, + 'Content-Length': str(len(body)), + } + return (headers, body) diff --git a/workflow/workflow.py b/workflow/workflow.py new file mode 100644 index 0000000..fe65a81 --- /dev/null +++ b/workflow/workflow.py @@ -0,0 +1,2879 @@ +# encoding: utf-8 +# +# Copyright (c) 2014 Dean Jackson +# +# MIT Licence. See http://opensource.org/licenses/MIT +# +# Created on 2014-02-15 +# + +""" +The :class:`Workflow` object is the main interface to this library. + +See :ref:`setup` in the :ref:`user-manual` for an example of how to set +up your Python script to best utilise the :class:`Workflow` object. + +""" + +from __future__ import print_function, unicode_literals + +import binascii +from contextlib import contextmanager +import cPickle +import errno +import json +import logging +import logging.handlers +import os +import pickle +import plistlib +import re +import shutil +import signal +import string +import subprocess +import sys +import time +import unicodedata + +try: + import xml.etree.cElementTree as ET +except ImportError: # pragma: no cover + import xml.etree.ElementTree as ET + + +#: Sentinel for properties that haven't been set yet (that might +#: correctly have the value ``None``) +UNSET = object() + +#################################################################### +# Standard system icons +#################################################################### + +# These icons are default OS X icons. They are super-high quality, and +# will be familiar to users. +# This library uses `ICON_ERROR` when a workflow dies in flames, so +# in my own workflows, I use `ICON_WARNING` for less fatal errors +# (e.g. bad user input, no results etc.) + +# The system icons are all in this directory. There are many more than +# are listed here + +ICON_ROOT = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources' + +ICON_ACCOUNT = os.path.join(ICON_ROOT, 'Accounts.icns') +ICON_BURN = os.path.join(ICON_ROOT, 'BurningIcon.icns') +ICON_CLOCK = os.path.join(ICON_ROOT, 'Clock.icns') +ICON_COLOR = os.path.join(ICON_ROOT, 'ProfileBackgroundColor.icns') +ICON_COLOUR = ICON_COLOR # Queen's English, if you please +ICON_EJECT = os.path.join(ICON_ROOT, 'EjectMediaIcon.icns') +# Shown when a workflow throws an error +ICON_ERROR = os.path.join(ICON_ROOT, 'AlertStopIcon.icns') +ICON_FAVORITE = os.path.join(ICON_ROOT, 'ToolbarFavoritesIcon.icns') +ICON_FAVOURITE = ICON_FAVORITE +ICON_GROUP = os.path.join(ICON_ROOT, 'GroupIcon.icns') +ICON_HELP = os.path.join(ICON_ROOT, 'HelpIcon.icns') +ICON_HOME = os.path.join(ICON_ROOT, 'HomeFolderIcon.icns') +ICON_INFO = os.path.join(ICON_ROOT, 'ToolbarInfo.icns') +ICON_NETWORK = os.path.join(ICON_ROOT, 'GenericNetworkIcon.icns') +ICON_NOTE = os.path.join(ICON_ROOT, 'AlertNoteIcon.icns') +ICON_SETTINGS = os.path.join(ICON_ROOT, 'ToolbarAdvanced.icns') +ICON_SWIRL = os.path.join(ICON_ROOT, 'ErasingIcon.icns') +ICON_SWITCH = os.path.join(ICON_ROOT, 'General.icns') +ICON_SYNC = os.path.join(ICON_ROOT, 'Sync.icns') +ICON_TRASH = os.path.join(ICON_ROOT, 'TrashIcon.icns') +ICON_USER = os.path.join(ICON_ROOT, 'UserIcon.icns') +ICON_WARNING = os.path.join(ICON_ROOT, 'AlertCautionIcon.icns') +ICON_WEB = os.path.join(ICON_ROOT, 'BookmarkIcon.icns') + +#################################################################### +# non-ASCII to ASCII diacritic folding. +# Used by `fold_to_ascii` method +#################################################################### + +ASCII_REPLACEMENTS = { + 'À': 'A', + 'Á': 'A', + 'Â': 'A', + 'Ã': 'A', + 'Ä': 'A', + 'Å': 'A', + 'Æ': 'AE', + 'Ç': 'C', + 'È': 'E', + 'É': 'E', + 'Ê': 'E', + 'Ë': 'E', + 'Ì': 'I', + 'Í': 'I', + 'Î': 'I', + 'Ï': 'I', + 'Ð': 'D', + 'Ñ': 'N', + 'Ò': 'O', + 'Ó': 'O', + 'Ô': 'O', + 'Õ': 'O', + 'Ö': 'O', + 'Ø': 'O', + 'Ù': 'U', + 'Ú': 'U', + 'Û': 'U', + 'Ü': 'U', + 'Ý': 'Y', + 'Þ': 'Th', + 'ß': 'ss', + 'à': 'a', + 'á': 'a', + 'â': 'a', + 'ã': 'a', + 'ä': 'a', + 'å': 'a', + 'æ': 'ae', + 'ç': 'c', + 'è': 'e', + 'é': 'e', + 'ê': 'e', + 'ë': 'e', + 'ì': 'i', + 'í': 'i', + 'î': 'i', + 'ï': 'i', + 'ð': 'd', + 'ñ': 'n', + 'ò': 'o', + 'ó': 'o', + 'ô': 'o', + 'õ': 'o', + 'ö': 'o', + 'ø': 'o', + 'ù': 'u', + 'ú': 'u', + 'û': 'u', + 'ü': 'u', + 'ý': 'y', + 'þ': 'th', + 'ÿ': 'y', + 'Ł': 'L', + 'ł': 'l', + 'Ń': 'N', + 'ń': 'n', + 'Ņ': 'N', + 'ņ': 'n', + 'Ň': 'N', + 'ň': 'n', + 'Ŋ': 'ng', + 'ŋ': 'NG', + 'Ō': 'O', + 'ō': 'o', + 'Ŏ': 'O', + 'ŏ': 'o', + 'Ő': 'O', + 'ő': 'o', + 'Œ': 'OE', + 'œ': 'oe', + 'Ŕ': 'R', + 'ŕ': 'r', + 'Ŗ': 'R', + 'ŗ': 'r', + 'Ř': 'R', + 'ř': 'r', + 'Ś': 'S', + 'ś': 's', + 'Ŝ': 'S', + 'ŝ': 's', + 'Ş': 'S', + 'ş': 's', + 'Š': 'S', + 'š': 's', + 'Ţ': 'T', + 'ţ': 't', + 'Ť': 'T', + 'ť': 't', + 'Ŧ': 'T', + 'ŧ': 't', + 'Ũ': 'U', + 'ũ': 'u', + 'Ū': 'U', + 'ū': 'u', + 'Ŭ': 'U', + 'ŭ': 'u', + 'Ů': 'U', + 'ů': 'u', + 'Ű': 'U', + 'ű': 'u', + 'Ŵ': 'W', + 'ŵ': 'w', + 'Ŷ': 'Y', + 'ŷ': 'y', + 'Ÿ': 'Y', + 'Ź': 'Z', + 'ź': 'z', + 'Ż': 'Z', + 'ż': 'z', + 'Ž': 'Z', + 'ž': 'z', + 'ſ': 's', + 'Α': 'A', + 'Β': 'B', + 'Γ': 'G', + 'Δ': 'D', + 'Ε': 'E', + 'Ζ': 'Z', + 'Η': 'E', + 'Θ': 'Th', + 'Ι': 'I', + 'Κ': 'K', + 'Λ': 'L', + 'Μ': 'M', + 'Ν': 'N', + 'Ξ': 'Ks', + 'Ο': 'O', + 'Π': 'P', + 'Ρ': 'R', + 'Σ': 'S', + 'Τ': 'T', + 'Υ': 'U', + 'Φ': 'Ph', + 'Χ': 'Kh', + 'Ψ': 'Ps', + 'Ω': 'O', + 'α': 'a', + 'β': 'b', + 'γ': 'g', + 'δ': 'd', + 'ε': 'e', + 'ζ': 'z', + 'η': 'e', + 'θ': 'th', + 'ι': 'i', + 'κ': 'k', + 'λ': 'l', + 'μ': 'm', + 'ν': 'n', + 'ξ': 'x', + 'ο': 'o', + 'π': 'p', + 'ρ': 'r', + 'ς': 's', + 'σ': 's', + 'τ': 't', + 'υ': 'u', + 'φ': 'ph', + 'χ': 'kh', + 'ψ': 'ps', + 'ω': 'o', + 'А': 'A', + 'Б': 'B', + 'В': 'V', + 'Г': 'G', + 'Д': 'D', + 'Е': 'E', + 'Ж': 'Zh', + 'З': 'Z', + 'И': 'I', + 'Й': 'I', + 'К': 'K', + 'Л': 'L', + 'М': 'M', + 'Н': 'N', + 'О': 'O', + 'П': 'P', + 'Р': 'R', + 'С': 'S', + 'Т': 'T', + 'У': 'U', + 'Ф': 'F', + 'Х': 'Kh', + 'Ц': 'Ts', + 'Ч': 'Ch', + 'Ш': 'Sh', + 'Щ': 'Shch', + 'Ъ': "'", + 'Ы': 'Y', + 'Ь': "'", + 'Э': 'E', + 'Ю': 'Iu', + 'Я': 'Ia', + 'а': 'a', + 'б': 'b', + 'в': 'v', + 'г': 'g', + 'д': 'd', + 'е': 'e', + 'ж': 'zh', + 'з': 'z', + 'и': 'i', + 'й': 'i', + 'к': 'k', + 'л': 'l', + 'м': 'm', + 'н': 'n', + 'о': 'o', + 'п': 'p', + 'р': 'r', + 'с': 's', + 'т': 't', + 'у': 'u', + 'ф': 'f', + 'х': 'kh', + 'ц': 'ts', + 'ч': 'ch', + 'ш': 'sh', + 'щ': 'shch', + 'ъ': "'", + 'ы': 'y', + 'ь': "'", + 'э': 'e', + 'ю': 'iu', + 'я': 'ia', + # 'ᴀ': '', + # 'ᴁ': '', + # 'ᴂ': '', + # 'ᴃ': '', + # 'ᴄ': '', + # 'ᴅ': '', + # 'ᴆ': '', + # 'ᴇ': '', + # 'ᴈ': '', + # 'ᴉ': '', + # 'ᴊ': '', + # 'ᴋ': '', + # 'ᴌ': '', + # 'ᴍ': '', + # 'ᴎ': '', + # 'ᴏ': '', + # 'ᴐ': '', + # 'ᴑ': '', + # 'ᴒ': '', + # 'ᴓ': '', + # 'ᴔ': '', + # 'ᴕ': '', + # 'ᴖ': '', + # 'ᴗ': '', + # 'ᴘ': '', + # 'ᴙ': '', + # 'ᴚ': '', + # 'ᴛ': '', + # 'ᴜ': '', + # 'ᴝ': '', + # 'ᴞ': '', + # 'ᴟ': '', + # 'ᴠ': '', + # 'ᴡ': '', + # 'ᴢ': '', + # 'ᴣ': '', + # 'ᴤ': '', + # 'ᴥ': '', + 'ᴦ': 'G', + 'ᴧ': 'L', + 'ᴨ': 'P', + 'ᴩ': 'R', + 'ᴪ': 'PS', + 'ẞ': 'Ss', + 'Ỳ': 'Y', + 'ỳ': 'y', + 'Ỵ': 'Y', + 'ỵ': 'y', + 'Ỹ': 'Y', + 'ỹ': 'y', +} + +#################################################################### +# Smart-to-dumb punctuation mapping +#################################################################### + +DUMB_PUNCTUATION = { + '‘': "'", + '’': "'", + '‚': "'", + '“': '"', + '”': '"', + '„': '"', + '–': '-', + '—': '-' +} + + +#################################################################### +# Used by `Workflow.filter` +#################################################################### + +# Anchor characters in a name +#: Characters that indicate the beginning of a "word" in CamelCase +INITIALS = string.ascii_uppercase + string.digits + +#: Split on non-letters, numbers +split_on_delimiters = re.compile('[^a-zA-Z0-9]').split + +# Match filter flags +#: Match items that start with ``query`` +MATCH_STARTSWITH = 1 +#: Match items whose capital letters start with ``query`` +MATCH_CAPITALS = 2 +#: Match items with a component "word" that matches ``query`` +MATCH_ATOM = 4 +#: Match items whose initials (based on atoms) start with ``query`` +MATCH_INITIALS_STARTSWITH = 8 +#: Match items whose initials (based on atoms) contain ``query`` +MATCH_INITIALS_CONTAIN = 16 +#: Combination of :const:`MATCH_INITIALS_STARTSWITH` and +#: :const:`MATCH_INITIALS_CONTAIN` +MATCH_INITIALS = 24 +#: Match items if ``query`` is a substring +MATCH_SUBSTRING = 32 +#: Match items if all characters in ``query`` appear in the item in order +MATCH_ALLCHARS = 64 +#: Combination of all other ``MATCH_*`` constants +MATCH_ALL = 127 + + +#################################################################### +# Used by `Workflow.check_update` +#################################################################### + +# Number of days to wait between checking for updates to the workflow +DEFAULT_UPDATE_FREQUENCY = 1 + + +#################################################################### +# Lockfile and Keychain access errors +#################################################################### + +class AcquisitionError(Exception): + """Raised if a lock cannot be acquired.""" + + +class KeychainError(Exception): + """Raised by methods :meth:`Workflow.save_password`, + :meth:`Workflow.get_password` and :meth:`Workflow.delete_password` + when ``security`` CLI app returns an unknown error code. + + """ + + +class PasswordNotFound(KeychainError): + """Raised by method :meth:`Workflow.get_password` when ``account`` + is unknown to the Keychain. + + """ + + +class PasswordExists(KeychainError): + """Raised when trying to overwrite an existing account password. + + You should never receive this error: it is used internally + by the :meth:`Workflow.save_password` method to know if it needs + to delete the old password first (a Keychain implementation detail). + + """ + + +#################################################################### +# Helper functions +#################################################################### + +def isascii(text): + """Test if ``text`` contains only ASCII characters + + :param text: text to test for ASCII-ness + :type text: ``unicode`` + :returns: ``True`` if ``text`` contains only ASCII characters + :rtype: ``Boolean`` + """ + + try: + text.encode('ascii') + except UnicodeEncodeError: + return False + return True + + +#################################################################### +# Implementation classes +#################################################################### + +class SerializerManager(object): + """Contains registered serializers. + + .. versionadded:: 1.8 + + A configured instance of this class is available at + ``workflow.manager``. + + Use :meth:`register()` to register new (or replace + existing) serializers, which you can specify by name when calling + :class:`Workflow` data storage methods. + + See :ref:`manual-serialization` and :ref:`manual-persistent-data` + for further information. + + """ + + def __init__(self): + self._serializers = {} + + def register(self, name, serializer): + """Register ``serializer`` object under ``name``. + + Raises :class:`AttributeError` if ``serializer`` in invalid. + + .. note:: + + ``name`` will be used as the file extension of the saved files. + + :param name: Name to register ``serializer`` under + :type name: ``unicode`` or ``str`` + :param serializer: object with ``load()`` and ``dump()`` + methods + + """ + + # Basic validation + getattr(serializer, 'load') + getattr(serializer, 'dump') + + self._serializers[name] = serializer + + def serializer(self, name): + """Return serializer object for ``name`` or ``None`` if no such + serializer is registered + + :param name: Name of serializer to return + :type name: ``unicode`` or ``str`` + :returns: serializer object or ``None`` + + """ + + return self._serializers.get(name) + + def unregister(self, name): + """Remove registered serializer with ``name`` + + Raises a :class:`ValueError` if there is no such registered + serializer. + + :param name: Name of serializer to remove + :type name: ``unicode`` or ``str`` + :returns: serializer object + + """ + + if name not in self._serializers: + raise ValueError('No such serializer registered : {0}'.format(name)) + + serializer = self._serializers[name] + del self._serializers[name] + + return serializer + + @property + def serializers(self): + """Return names of registered serializers""" + return sorted(self._serializers.keys()) + + +class JSONSerializer(object): + """Wrapper around :mod:`json`. Sets ``indent`` and ``encoding``. + + .. versionadded:: 1.8 + + Use this serializer if you need readable data files. JSON doesn't + support Python objects as well as ``cPickle``/``pickle``, so be + careful which data you try to serialize as JSON. + + """ + + @classmethod + def load(cls, file_obj): + """Load serialized object from open JSON file. + + .. versionadded:: 1.8 + + :param file_obj: file handle + :type file_obj: ``file`` object + :returns: object loaded from JSON file + :rtype: object + + """ + + return json.load(file_obj) + + @classmethod + def dump(cls, obj, file_obj): + """Serialize object ``obj`` to open JSON file. + + .. versionadded:: 1.8 + + :param obj: Python object to serialize + :type obj: JSON-serializable data structure + :param file_obj: file handle + :type file_obj: ``file`` object + + """ + + return json.dump(obj, file_obj, indent=2, encoding='utf-8') + + +class CPickleSerializer(object): + """Wrapper around :mod:`cPickle`. Sets ``protocol``. + + .. versionadded:: 1.8 + + This is the default serializer and the best combination of speed and + flexibility. + + """ + + @classmethod + def load(cls, file_obj): + """Load serialized object from open pickle file. + + .. versionadded:: 1.8 + + :param file_obj: file handle + :type file_obj: ``file`` object + :returns: object loaded from pickle file + :rtype: object + + """ + + return cPickle.load(file_obj) + + @classmethod + def dump(cls, obj, file_obj): + """Serialize object ``obj`` to open pickle file. + + .. versionadded:: 1.8 + + :param obj: Python object to serialize + :type obj: Python object + :param file_obj: file handle + :type file_obj: ``file`` object + + """ + + return cPickle.dump(obj, file_obj, protocol=-1) + + +class PickleSerializer(object): + """Wrapper around :mod:`pickle`. Sets ``protocol``. + + .. versionadded:: 1.8 + + Use this serializer if you need to add custom pickling. + + """ + + @classmethod + def load(cls, file_obj): + """Load serialized object from open pickle file. + + .. versionadded:: 1.8 + + :param file_obj: file handle + :type file_obj: ``file`` object + :returns: object loaded from pickle file + :rtype: object + + """ + + return pickle.load(file_obj) + + @classmethod + def dump(cls, obj, file_obj): + """Serialize object ``obj`` to open pickle file. + + .. versionadded:: 1.8 + + :param obj: Python object to serialize + :type obj: Python object + :param file_obj: file handle + :type file_obj: ``file`` object + + """ + + return pickle.dump(obj, file_obj, protocol=-1) + + +# Set up default manager and register built-in serializers +manager = SerializerManager() +manager.register('cpickle', CPickleSerializer) +manager.register('pickle', PickleSerializer) +manager.register('json', JSONSerializer) + + +class Item(object): + """Represents a feedback item for Alfred. Generates Alfred-compliant + XML for a single item. + + You probably shouldn't use this class directly, but via + :meth:`Workflow.add_item`. See :meth:`~Workflow.add_item` + for details of arguments. + + """ + + def __init__(self, title, subtitle='', modifier_subtitles=None, + arg=None, autocomplete=None, valid=False, uid=None, + icon=None, icontype=None, type=None, largetext=None, + copytext=None): + """Arguments the same as for :meth:`Workflow.add_item`. + + """ + + self.title = title + self.subtitle = subtitle + self.modifier_subtitles = modifier_subtitles or {} + self.arg = arg + self.autocomplete = autocomplete + self.valid = valid + self.uid = uid + self.icon = icon + self.icontype = icontype + self.type = type + self.largetext = largetext + self.copytext = copytext + + @property + def elem(self): + """Create and return feedback item for Alfred. + + :returns: :class:`ElementTree.Element ` + instance for this :class:`Item` instance. + + """ + + # Attributes on element + attr = {} + if self.valid: + attr['valid'] = 'yes' + else: + attr['valid'] = 'no' + # Allow empty string for autocomplete. This is a useful value, + # as TABing the result will revert the query back to just the + # keyword + if self.autocomplete is not None: + attr['autocomplete'] = self.autocomplete + + # Optional attributes + for name in ('uid', 'type'): + value = getattr(self, name, None) + if value: + attr[name] = value + + root = ET.Element('item', attr) + ET.SubElement(root, 'title').text = self.title + ET.SubElement(root, 'subtitle').text = self.subtitle + + # Add modifier subtitles + for mod in ('cmd', 'ctrl', 'alt', 'shift', 'fn'): + if mod in self.modifier_subtitles: + ET.SubElement(root, 'subtitle', + {'mod': mod}).text = self.modifier_subtitles[mod] + + # Add arg as element instead of attribute on , as it's more + # flexible (newlines aren't allowed in attributes) + if self.arg: + ET.SubElement(root, 'arg').text = self.arg + + # Add icon if there is one + if self.icon: + if self.icontype: + attr = dict(type=self.icontype) + else: + attr = {} + ET.SubElement(root, 'icon', attr).text = self.icon + + if self.largetext: + ET.SubElement(root, 'text', + {'type': 'largetype'}).text = self.largetext + + if self.copytext: + ET.SubElement(root, 'text', + {'type': 'copy'}).text = self.copytext + + return root + + +class LockFile(object): + """Context manager to create lock files""" + + def __init__(self, protected_path, timeout=0, delay=0.05): + self.lockfile = protected_path + '.lock' + self.timeout = timeout + self.delay = delay + self._locked = False + + @property + def locked(self): + """`True` if file is locked by this instance.""" + return self._locked + + def acquire(self, blocking=True): + """Acquire the lock if possible. + + If the lock is in use and ``blocking`` is ``False``, return + ``False``. + + Otherwise, check every `self.delay` seconds until it acquires + lock or exceeds `self.timeout` and raises an exception. + + """ + start = time.time() + while True: + try: + fd = os.open(self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR) + with os.fdopen(fd, 'w') as fd: + fd.write('{0}'.format(os.getpid())) + break + except OSError as err: + if err.errno != errno.EEXIST: # pragma: no cover + raise + if self.timeout and (time.time() - start) >= self.timeout: + raise AcquisitionError('Lock acquisition timed out.') + if not blocking: + return False + time.sleep(self.delay) + + self._locked = True + return True + + def release(self): + """Release the lock by deleting `self.lockfile`.""" + self._locked = False + os.unlink(self.lockfile) + + def __enter__(self): + """Acquire lock.""" + self.acquire() + return self + + def __exit__(self, typ, value, traceback): + """Release lock.""" + self.release() + + def __del__(self): + """Clear up `self.lockfile`.""" + if self._locked: # pragma: no cover + self.release() + + +@contextmanager +def atomic_writer(file_path, mode): + """Atomic file writer. + + :param file_path: path of file to write to. + :type file_path: ``unicode`` + :param mode: sames as for `func:open` + :type mode: string + + .. versionadded:: 1.12 + + Context manager that ensures the file is only written if the write + succeeds. The data is first written to a temporary file. + + """ + + temp_suffix = '.aw.temp' + temp_file_path = file_path + temp_suffix + with open(temp_file_path, mode) as file_obj: + try: + yield file_obj + os.rename(temp_file_path, file_path) + finally: + try: + os.remove(temp_file_path) + except (OSError, IOError): + pass + + +class uninterruptible(object): + """Decorator that postpones SIGTERM until wrapped function is complete. + + .. versionadded:: 1.12 + + Since version 2.7, Alfred allows Script Filters to be killed. If + your workflow is killed in the middle of critical code (e.g. + writing data to disk), this may corrupt your workflow's data. + + Use this decorator to wrap critical functions that *must* complete. + If the script is killed while a wrapped function is executing, + the SIGTERM will be caught and handled after your function has + finished executing. + + Alfred-Workflow uses this internally to ensure its settings, data + and cache writes complete. + + .. important:: + + This decorator is NOT thread-safe. + + """ + + def __init__(self, func, class_name=''): + self.func = func + self._caught_signal = None + + def signal_handler(self, signum, frame): + """Called when process receives SIGTERM.""" + self._caught_signal = (signum, frame) + + def __call__(self, *args, **kwargs): + self._caught_signal = None + # Register handler for SIGTERM, then call `self.func` + self.old_signal_handler = signal.getsignal(signal.SIGTERM) + signal.signal(signal.SIGTERM, self.signal_handler) + + self.func(*args, **kwargs) + + # Restore old signal handler + signal.signal(signal.SIGTERM, self.old_signal_handler) + + # Handle any signal caught during execution + if self._caught_signal is not None: + signum, frame = self._caught_signal + if callable(self.old_signal_handler): + self.old_signal_handler(signum, frame) + elif self.old_signal_handler == signal.SIG_DFL: + sys.exit(0) + + def __get__(self, obj=None, klass=None): + return self.__class__(self.func.__get__(obj, klass), + klass.__name__) + + +class Settings(dict): + """A dictionary that saves itself when changed. + + Dictionary keys & values will be saved as a JSON file + at ``filepath``. If the file does not exist, the dictionary + (and settings file) will be initialised with ``defaults``. + + :param filepath: where to save the settings + :type filepath: :class:`unicode` + :param defaults: dict of default settings + :type defaults: :class:`dict` + + + An appropriate instance is provided by :class:`Workflow` instances at + :attr:`Workflow.settings`. + + """ + + def __init__(self, filepath, defaults=None): + + super(Settings, self).__init__() + self._filepath = filepath + self._nosave = False + if os.path.exists(self._filepath): + self._load() + elif defaults: + for key, val in defaults.items(): + self[key] = val + self.save() # save default settings + + def _load(self): + """Load cached settings from JSON file `self._filepath`""" + + self._nosave = True + with open(self._filepath, 'rb') as file_obj: + for key, value in json.load(file_obj, encoding='utf-8').items(): + self[key] = value + self._nosave = False + + def save(self): + """Save settings to JSON file specified in ``self._filepath`` + + If you're using this class via :attr:`Workflow.settings`, which + you probably are, ``self._filepath`` will be ``settings.json`` + in your workflow's data directory (see :attr:`~Workflow.datadir`). + """ + if self._nosave: + return + data = {} + for key, value in self.items(): + data[key] = value + with LockFile(self._filepath): + with atomic_writer(self._filepath, 'wb') as file_obj: + json.dump(data, file_obj, sort_keys=True, indent=2, + encoding='utf-8') + + # dict methods + def __setitem__(self, key, value): + super(Settings, self).__setitem__(key, value) + self.save() + + def __delitem__(self, key): + super(Settings, self).__delitem__(key) + self.save() + + def update(self, *args, **kwargs): + """Override :class:`dict` method to save on update.""" + super(Settings, self).update(*args, **kwargs) + self.save() + + def setdefault(self, key, value=None): + """Override :class:`dict` method to save on update.""" + ret = super(Settings, self).setdefault(key, value) + self.save() + return ret + + +class Workflow(object): + """Create new :class:`Workflow` instance. + + :param default_settings: default workflow settings. If no settings file + exists, :class:`Workflow.settings` will be pre-populated with + ``default_settings``. + :type default_settings: :class:`dict` + :param update_settings: settings for updating your workflow from GitHub. + This must be a :class:`dict` that contains ``github_slug`` and + ``version`` keys. ``github_slug`` is of the form ``username/repo`` + and ``version`` **must** correspond to the tag of a release. + See :ref:`updates` for more information. + :type update_settings: :class:`dict` + :param input_encoding: encoding of command line arguments + :type input_encoding: :class:`unicode` + :param normalization: normalisation to apply to CLI args. + See :meth:`Workflow.decode` for more details. + :type normalization: :class:`unicode` + :param capture_args: capture and act on ``workflow:*`` arguments. See + :ref:`Magic arguments ` for details. + :type capture_args: :class:`Boolean` + :param libraries: sequence of paths to directories containing + libraries. These paths will be prepended to ``sys.path``. + :type libraries: :class:`tuple` or :class:`list` + :param help_url: URL to webpage where a user can ask for help with + the workflow, report bugs, etc. This could be the GitHub repo + or a page on AlfredForum.com. If your workflow throws an error, + this URL will be displayed in the log and Alfred's debugger. It can + also be opened directly in a web browser with the ``workflow:help`` + :ref:`magic argument `. + :type help_url: :class:`unicode` or :class:`str` + + """ + + # Which class to use to generate feedback items. You probably + # won't want to change this + item_class = Item + + def __init__(self, default_settings=None, update_settings=None, + input_encoding='utf-8', normalization='NFC', + capture_args=True, libraries=None, + help_url=None): + + self._default_settings = default_settings or {} + self._update_settings = update_settings or {} + self._input_encoding = input_encoding + self._normalizsation = normalization + self._capture_args = capture_args + self.help_url = help_url + self._workflowdir = None + self._settings_path = None + self._settings = None + self._bundleid = None + self._name = None + self._cache_serializer = 'cpickle' + self._data_serializer = 'cpickle' + # info.plist should be in the directory above this one + self._info_plist = self.workflowfile('info.plist') + self._info = None + self._info_loaded = False + self._logger = None + self._items = [] + self._alfred_env = None + # Version number of the workflow + self._version = UNSET + # Version from last workflow run + self._last_version_run = UNSET + # Cache for regex patterns created for filter keys + self._search_pattern_cache = {} + # Magic arguments + #: The prefix for all magic arguments. Default is ``workflow:`` + self.magic_prefix = 'workflow:' + #: Mapping of available magic arguments. The built-in magic + #: arguments are registered by default. To add your own magic arguments + #: (or override built-ins), add a key:value pair where the key is + #: what the user should enter (prefixed with :attr:`magic_prefix`) + #: and the value is a callable that will be called when the argument + #: is entered. If you would like to display a message in Alfred, the + #: function should return a ``unicode`` string. + #: + #: By default, the magic arguments documented + #: :ref:`here ` are registered. + self.magic_arguments = {} + + self._register_default_magic() + + if libraries: + sys.path = libraries + sys.path + + #################################################################### + # API methods + #################################################################### + + # info.plist contents and alfred_* environment variables ---------- + + @property + def alfred_env(self): + """Alfred's environmental variables minus the ``alfred_`` prefix. + + .. versionadded:: 1.7 + + The variables Alfred 2.4+ exports are: + + ============================ ========================================= + Variable Description + ============================ ========================================= + alfred_preferences Path to Alfred.alfredpreferences + (where your workflows and settings are + stored). + alfred_preferences_localhash Machine-specific preferences are stored + in ``Alfred.alfredpreferences/preferences/local/`` + (see ``alfred_preferences`` above for + the path to ``Alfred.alfredpreferences``) + alfred_theme ID of selected theme + alfred_theme_background Background colour of selected theme in + format ``rgba(r,g,b,a)`` + alfred_theme_subtext Show result subtext. + ``0`` = Always, + ``1`` = Alternative actions only, + ``2`` = Selected result only, + ``3`` = Never + alfred_version Alfred version number, e.g. ``'2.4'`` + alfred_version_build Alfred build number, e.g. ``277`` + alfred_workflow_bundleid Bundle ID, e.g. + ``net.deanishe.alfred-mailto`` + alfred_workflow_cache Path to workflow's cache directory + alfred_workflow_data Path to workflow's data directory + alfred_workflow_name Name of current workflow + alfred_workflow_uid UID of workflow + ============================ ========================================= + + **Note:** all values are Unicode strings except ``version_build`` and + ``theme_subtext``, which are integers. + + :returns: ``dict`` of Alfred's environmental variables without the + ``alfred_`` prefix, e.g. ``preferences``, ``workflow_data``. + + """ + + if self._alfred_env is not None: + return self._alfred_env + + data = {} + + for key in ( + 'alfred_preferences', + 'alfred_preferences_localhash', + 'alfred_theme', + 'alfred_theme_background', + 'alfred_theme_subtext', + 'alfred_version', + 'alfred_version_build', + 'alfred_workflow_bundleid', + 'alfred_workflow_cache', + 'alfred_workflow_data', + 'alfred_workflow_name', + 'alfred_workflow_uid'): + + value = os.getenv(key) + + if isinstance(value, str): + if key in ('alfred_version_build', 'alfred_theme_subtext'): + value = int(value) + else: + value = self.decode(value) + + data[key[7:]] = value + + self._alfred_env = data + + return self._alfred_env + + @property + def info(self): + """:class:`dict` of ``info.plist`` contents.""" + + if not self._info_loaded: + self._load_info_plist() + return self._info + + @property + def bundleid(self): + """Workflow bundle ID from environmental vars or ``info.plist``. + + :returns: bundle ID + :rtype: ``unicode`` + + """ + + if not self._bundleid: + if self.alfred_env.get('workflow_bundleid'): + self._bundleid = self.alfred_env.get('workflow_bundleid') + else: + self._bundleid = unicode(self.info['bundleid'], 'utf-8') + + return self._bundleid + + @property + def name(self): + """Workflow name from Alfred's environmental vars or ``info.plist``. + + :returns: workflow name + :rtype: ``unicode`` + + """ + + if not self._name: + if self.alfred_env.get('workflow_name'): + self._name = self.decode(self.alfred_env.get('workflow_name')) + else: + self._name = self.decode(self.info['name']) + + return self._name + + @property + def version(self): + """Return the version of the workflow + + .. versionadded:: 1.9.10 + + Get the version from the ``update_settings`` dict passed on + instantiation or the ``version`` file located in the workflow's + root directory. Return ``None`` if neither exist or + :class:`ValueError` if the version number is invalid (i.e. not + semantic). + + :returns: Version of the workflow (not Alfred-Workflow) + :rtype: :class:`~workflow.update.Version` object + + """ + + if self._version is UNSET: + + version = None + # First check `update_settings` + if self._update_settings: + version = self._update_settings.get('version') + + # Fallback to `version` file + if not version: + filepath = self.workflowfile('version') + + if os.path.exists(filepath): + with open(filepath, 'rb') as fileobj: + version = fileobj.read() + + if version: + from update import Version + version = Version(version) + + self._version = version + + return self._version + + # Workflow utility methods ----------------------------------------- + + @property + def args(self): + """Return command line args as normalised unicode. + + Args are decoded and normalised via :meth:`~Workflow.decode`. + + The encoding and normalisation are the ``input_encoding`` and + ``normalization`` arguments passed to :class:`Workflow` (``UTF-8`` + and ``NFC`` are the defaults). + + If :class:`Workflow` is called with ``capture_args=True`` + (the default), :class:`Workflow` will look for certain + ``workflow:*`` args and, if found, perform the corresponding + actions and exit the workflow. + + See :ref:`Magic arguments ` for details. + + """ + + msg = None + args = [self.decode(arg) for arg in sys.argv[1:]] + + # Handle magic args + if len(args) and self._capture_args: + for name in self.magic_arguments: + key = '{0}{1}'.format(self.magic_prefix, name) + if key in args: + msg = self.magic_arguments[name]() + + if msg: + self.logger.debug(msg) + if not sys.stdout.isatty(): # Show message in Alfred + self.add_item(msg, valid=False, icon=ICON_INFO) + self.send_feedback() + sys.exit(0) + return args + + @property + def cachedir(self): + """Path to workflow's cache directory. + + The cache directory is a subdirectory of Alfred's own cache directory in + ``~/Library/Caches``. The full path is: + + ``~/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/`` + + :returns: full path to workflow's cache directory + :rtype: ``unicode`` + + """ + + if self.alfred_env.get('workflow_cache'): + dirpath = self.alfred_env.get('workflow_cache') + + else: + dirpath = os.path.join( + os.path.expanduser( + '~/Library/Caches/com.runningwithcrayons.Alfred-2/' + 'Workflow Data/'), + self.bundleid) + + return self._create(dirpath) + + @property + def datadir(self): + """Path to workflow's data directory. + + The data directory is a subdirectory of Alfred's own data directory in + ``~/Library/Application Support``. The full path is: + + ``~/Library/Application Support/Alfred 2/Workflow Data/`` + + :returns: full path to workflow data directory + :rtype: ``unicode`` + + """ + + if self.alfred_env.get('workflow_data'): + dirpath = self.alfred_env.get('workflow_data') + + else: + dirpath = os.path.join(os.path.expanduser( + '~/Library/Application Support/Alfred 2/Workflow Data/'), + self.bundleid) + + return self._create(dirpath) + + @property + def workflowdir(self): + """Path to workflow's root directory (where ``info.plist`` is). + + :returns: full path to workflow root directory + :rtype: ``unicode`` + + """ + + if not self._workflowdir: + # Try the working directory first, then the directory + # the library is in. CWD will be the workflow root if + # a workflow is being run in Alfred + candidates = [ + os.path.abspath(os.getcwdu()), + os.path.dirname(os.path.abspath(os.path.dirname(__file__)))] + + # climb the directory tree until we find `info.plist` + for dirpath in candidates: + + # Ensure directory path is Unicode + dirpath = self.decode(dirpath) + + while True: + if os.path.exists(os.path.join(dirpath, 'info.plist')): + self._workflowdir = dirpath + break + + elif dirpath == '/': + # no `info.plist` found + break + + # Check the parent directory + dirpath = os.path.dirname(dirpath) + + # No need to check other candidates + if self._workflowdir: + break + + if not self._workflowdir: + raise IOError("'info.plist' not found in directory tree") + + return self._workflowdir + + def cachefile(self, filename): + """Return full path to ``filename`` within your workflow's + :attr:`cache directory `. + + :param filename: basename of file + :type filename: ``unicode`` + :returns: full path to file within cache directory + :rtype: ``unicode`` + + """ + + return os.path.join(self.cachedir, filename) + + def datafile(self, filename): + """Return full path to ``filename`` within your workflow's + :attr:`data directory `. + + :param filename: basename of file + :type filename: ``unicode`` + :returns: full path to file within data directory + :rtype: ``unicode`` + + """ + + return os.path.join(self.datadir, filename) + + def workflowfile(self, filename): + """Return full path to ``filename`` in workflow's root dir + (where ``info.plist`` is). + + :param filename: basename of file + :type filename: ``unicode`` + :returns: full path to file within data directory + :rtype: ``unicode`` + + """ + + return os.path.join(self.workflowdir, filename) + + @property + def logfile(self): + """Return path to logfile + + :returns: path to logfile within workflow's cache directory + :rtype: ``unicode`` + + """ + + return self.cachefile('%s.log' % self.bundleid) + + @property + def logger(self): + """Create and return a logger that logs to both console and + a log file. + + Use :meth:`open_log` to open the log file in Console. + + :returns: an initialised :class:`~logging.Logger` + + """ + + if self._logger: + return self._logger + + # Initialise new logger and optionally handlers + logger = logging.getLogger('workflow') + + if not len(logger.handlers): # Only add one set of handlers + logfile = logging.handlers.RotatingFileHandler( + self.logfile, + maxBytes=1024*1024, + backupCount=0) + + console = logging.StreamHandler() + + fmt = logging.Formatter( + '%(asctime)s %(filename)s:%(lineno)s' + ' %(levelname)-8s %(message)s', + datefmt='%H:%M:%S') + + logfile.setFormatter(fmt) + console.setFormatter(fmt) + + logger.addHandler(logfile) + logger.addHandler(console) + + logger.setLevel(logging.DEBUG) + self._logger = logger + + return self._logger + + @logger.setter + def logger(self, logger): + """Set a custom logger. + + :param logger: The logger to use + :type logger: `~logging.Logger` instance + + """ + + self._logger = logger + + @property + def settings_path(self): + """Path to settings file within workflow's data directory. + + :returns: path to ``settings.json`` file + :rtype: ``unicode`` + + """ + + if not self._settings_path: + self._settings_path = self.datafile('settings.json') + return self._settings_path + + @property + def settings(self): + """Return a dictionary subclass that saves itself when changed. + + See :ref:`manual-settings` in the :ref:`user-manual` for more + information on how to use :attr:`settings` and **important + limitations** on what it can do. + + :returns: :class:`~workflow.workflow.Settings` instance + initialised from the data in JSON file at + :attr:`settings_path` or if that doesn't exist, with the + ``default_settings`` :class:`dict` passed to + :class:`Workflow` on instantiation. + :rtype: :class:`~workflow.workflow.Settings` instance + + """ + + if not self._settings: + self.logger.debug('Reading settings from `{0}` ...'.format( + self.settings_path)) + self._settings = Settings(self.settings_path, + self._default_settings) + return self._settings + + @property + def cache_serializer(self): + """Name of default cache serializer. + + .. versionadded:: 1.8 + + This serializer is used by :meth:`cache_data()` and + :meth:`cached_data()` + + See :class:`SerializerManager` for details. + + :returns: serializer name + :rtype: ``unicode`` + + """ + + return self._cache_serializer + + @cache_serializer.setter + def cache_serializer(self, serializer_name): + """Set the default cache serialization format. + + .. versionadded:: 1.8 + + This serializer is used by :meth:`cache_data()` and + :meth:`cached_data()` + + The specified serializer must already by registered with the + :class:`SerializerManager` at `~workflow.workflow.manager`, + otherwise a :class:`ValueError` will be raised. + + :param serializer_name: Name of default serializer to use. + :type serializer_name: + + """ + + if manager.serializer(serializer_name) is None: + raise ValueError( + 'Unknown serializer : `{0}`. Register your serializer ' + 'with `manager` first.'.format(serializer_name)) + + self.logger.debug( + 'default cache serializer set to `{0}`'.format(serializer_name)) + + self._cache_serializer = serializer_name + + @property + def data_serializer(self): + """Name of default data serializer. + + .. versionadded:: 1.8 + + This serializer is used by :meth:`store_data()` and + :meth:`stored_data()` + + See :class:`SerializerManager` for details. + + :returns: serializer name + :rtype: ``unicode`` + + """ + + return self._data_serializer + + @data_serializer.setter + def data_serializer(self, serializer_name): + """Set the default cache serialization format. + + .. versionadded:: 1.8 + + This serializer is used by :meth:`store_data()` and + :meth:`stored_data()` + + The specified serializer must already by registered with the + :class:`SerializerManager` at `~workflow.workflow.manager`, + otherwise a :class:`ValueError` will be raised. + + :param serializer_name: Name of serializer to use by default. + + """ + + if manager.serializer(serializer_name) is None: + raise ValueError( + 'Unknown serializer : `{0}`. Register your serializer ' + 'with `manager` first.'.format(serializer_name)) + + self.logger.debug( + 'default data serializer set to `{0}`'.format(serializer_name)) + + self._data_serializer = serializer_name + + def stored_data(self, name): + """Retrieve data from data directory. Returns ``None`` if there + are no data stored. + + .. versionadded:: 1.8 + + :param name: name of datastore + + """ + + metadata_path = self.datafile('.{0}.alfred-workflow'.format(name)) + + if not os.path.exists(metadata_path): + self.logger.debug('No data stored for `{0}`'.format(name)) + return None + + with open(metadata_path, 'rb') as file_obj: + serializer_name = file_obj.read().strip() + + serializer = manager.serializer(serializer_name) + + if serializer is None: + raise ValueError( + 'Unknown serializer `{0}`. Register a corresponding ' + 'serializer with `manager.register()` ' + 'to load this data.'.format(serializer_name)) + + self.logger.debug('Data `{0}` stored in `{1}` format'.format( + name, serializer_name)) + + filename = '{0}.{1}'.format(name, serializer_name) + data_path = self.datafile(filename) + + if not os.path.exists(data_path): + self.logger.debug('No data stored for `{0}`'.format(name)) + if os.path.exists(metadata_path): + os.unlink(metadata_path) + + return None + + with open(data_path, 'rb') as file_obj: + data = serializer.load(file_obj) + + self.logger.debug('Stored data loaded from : {0}'.format(data_path)) + + return data + + def store_data(self, name, data, serializer=None): + """Save data to data directory. + + .. versionadded:: 1.8 + + If ``data`` is ``None``, the datastore will be deleted. + + Note that the datastore does NOT support mutliple threads. + + :param name: name of datastore + :param data: object(s) to store. **Note:** some serializers + can only handled certain types of data. + :param serializer: name of serializer to use. If no serializer + is specified, the default will be used. See + :class:`SerializerManager` for more information. + :returns: data in datastore or ``None`` + + """ + + # Ensure deletion is not interrupted by SIGTERM + @uninterruptible + def delete_paths(paths): + """Clear one or more data stores""" + for path in paths: + if os.path.exists(path): + os.unlink(path) + self.logger.debug('Deleted data file : {0}'.format(path)) + + serializer_name = serializer or self.data_serializer + + # In order for `stored_data()` to be able to load data stored with + # an arbitrary serializer, yet still have meaningful file extensions, + # the format (i.e. extension) is saved to an accompanying file + metadata_path = self.datafile('.{0}.alfred-workflow'.format(name)) + filename = '{0}.{1}'.format(name, serializer_name) + data_path = self.datafile(filename) + + if data_path == self.settings_path: + raise ValueError( + 'Cannot save data to' + + '`{0}` with format `{1}`. '.format(name, serializer_name) + + "This would overwrite Alfred-Workflow's settings file.") + + serializer = manager.serializer(serializer_name) + + if serializer is None: + raise ValueError( + 'Invalid serializer `{0}`. Register your serializer with ' + '`manager.register()` first.'.format(serializer_name)) + + if data is None: # Delete cached data + delete_paths((metadata_path, data_path)) + return + + # Ensure write is not interrupted by SIGTERM + @uninterruptible + def _store(): + # Save file extension + with atomic_writer(metadata_path, 'wb') as file_obj: + file_obj.write(serializer_name) + + with atomic_writer(data_path, 'wb') as file_obj: + serializer.dump(data, file_obj) + + _store() + + self.logger.debug('Stored data saved at : {0}'.format(data_path)) + + def cached_data(self, name, data_func=None, max_age=60): + """Retrieve data from cache or re-generate and re-cache data if + stale/non-existant. If ``max_age`` is 0, return cached data no + matter how old. + + :param name: name of datastore + :param data_func: function to (re-)generate data. + :type data_func: ``callable`` + :param max_age: maximum age of cached data in seconds + :type max_age: ``int`` + :returns: cached data, return value of ``data_func`` or ``None`` + if ``data_func`` is not set + + """ + + serializer = manager.serializer(self.cache_serializer) + + cache_path = self.cachefile('%s.%s' % (name, self.cache_serializer)) + age = self.cached_data_age(name) + + if (age < max_age or max_age == 0) and os.path.exists(cache_path): + + with open(cache_path, 'rb') as file_obj: + self.logger.debug('Loading cached data from : %s', + cache_path) + return serializer.load(file_obj) + + if not data_func: + return None + + data = data_func() + self.cache_data(name, data) + + return data + + def cache_data(self, name, data): + """Save ``data`` to cache under ``name``. + + If ``data`` is ``None``, the corresponding cache file will be + deleted. + + :param name: name of datastore + :param data: data to store. This may be any object supported by + the cache serializer + + """ + + serializer = manager.serializer(self.cache_serializer) + + cache_path = self.cachefile('%s.%s' % (name, self.cache_serializer)) + + if data is None: + if os.path.exists(cache_path): + os.unlink(cache_path) + self.logger.debug('Deleted cache file : %s', cache_path) + return + + with atomic_writer(cache_path, 'wb') as file_obj: + serializer.dump(data, file_obj) + + self.logger.debug('Cached data saved at : %s', cache_path) + + def cached_data_fresh(self, name, max_age): + """Is data cached at `name` less than `max_age` old? + + :param name: name of datastore + :param max_age: maximum age of data in seconds + :type max_age: ``int`` + :returns: ``True`` if data is less than ``max_age`` old, else + ``False`` + + """ + + age = self.cached_data_age(name) + + if not age: + return False + + return age < max_age + + def cached_data_age(self, name): + """Return age of data cached at `name` in seconds or 0 if + cache doesn't exist + + :param name: name of datastore + :type name: ``unicode`` + :returns: age of datastore in seconds + :rtype: ``int`` + + """ + + cache_path = self.cachefile('%s.%s' % (name, self.cache_serializer)) + + if not os.path.exists(cache_path): + return 0 + + return time.time() - os.stat(cache_path).st_mtime + + def filter(self, query, items, key=lambda x: x, ascending=False, + include_score=False, min_score=0, max_results=0, + match_on=MATCH_ALL, fold_diacritics=True): + """Fuzzy search filter. Returns list of ``items`` that match ``query``. + + ``query`` is case-insensitive. Any item that does not contain the + entirety of ``query`` is rejected. + + .. warning:: + + If ``query`` is an empty string or contains only whitespace, + a :class:`ValueError` will be raised. + + :param query: query to test items against + :type query: ``unicode`` + :param items: iterable of items to test + :type items: ``list`` or ``tuple`` + :param key: function to get comparison key from ``items``. + Must return a ``unicode`` string. The default simply returns + the item. + :type key: ``callable`` + :param ascending: set to ``True`` to get worst matches first + :type ascending: ``Boolean`` + :param include_score: Useful for debugging the scoring algorithm. + If ``True``, results will be a list of tuples + ``(item, score, rule)``. + :type include_score: ``Boolean`` + :param min_score: If non-zero, ignore results with a score lower + than this. + :type min_score: ``int`` + :param max_results: If non-zero, prune results list to this length. + :type max_results: ``int`` + :param match_on: Filter option flags. Bitwise-combined list of + ``MATCH_*`` constants (see below). + :type match_on: ``int`` + :param fold_diacritics: Convert search keys to ASCII-only + characters if ``query`` only contains ASCII characters. + :type fold_diacritics: ``Boolean`` + :returns: list of ``items`` matching ``query`` or list of + ``(item, score, rule)`` `tuples` if ``include_score`` is ``True``. + ``rule`` is the ``MATCH_*`` rule that matched the item. + :rtype: ``list`` + + **Matching rules** + + By default, :meth:`filter` uses all of the following flags (i.e. + :const:`MATCH_ALL`). The tests are always run in the given order: + + 1. :const:`MATCH_STARTSWITH` : Item search key startswith + ``query``(case-insensitive). + 2. :const:`MATCH_CAPITALS` : The list of capital letters in item + search key starts with ``query`` (``query`` may be + lower-case). E.g., ``of`` would match ``OmniFocus``, + ``gc`` would match ``Google Chrome`` + 3. :const:`MATCH_ATOM` : Search key is split into "atoms" on + non-word characters (.,-,' etc.). Matches if ``query`` is + one of these atoms (case-insensitive). + 4. :const:`MATCH_INITIALS_STARTSWITH` : Initials are the first + characters of the above-described "atoms" (case-insensitive). + 5. :const:`MATCH_INITIALS_CONTAIN` : ``query`` is a substring of + the above-described initials. + 6. :const:`MATCH_INITIALS` : Combination of (4) and (5). + 7. :const:`MATCH_SUBSTRING` : Match if ``query`` is a substring + of item search key (case-insensitive). + 8. :const:`MATCH_ALLCHARS` : Matches if all characters in + ``query`` appear in item search key in the same order + (case-insensitive). + 9. :const:`MATCH_ALL` : Combination of all the above. + + + :const:`MATCH_ALLCHARS` is considerably slower than the other + tests and provides much less accurate results. + + **Examples:** + + To ignore :const:`MATCH_ALLCHARS` (tends to provide the worst + matches and is expensive to run), use + ``match_on=MATCH_ALL ^ MATCH_ALLCHARS``. + + To match only on capitals, use ``match_on=MATCH_CAPITALS``. + + To match only on startswith and substring, use + ``match_on=MATCH_STARTSWITH | MATCH_SUBSTRING``. + + **Diacritic folding** + + .. versionadded:: 1.3 + + If ``fold_diacritics`` is ``True`` (the default), and ``query`` + contains only ASCII characters, non-ASCII characters in search keys + will be converted to ASCII equivalents (e.g. **ü** -> **u**, + **ß** -> **ss**, **é** -> **e**). + + See :const:`ASCII_REPLACEMENTS` for all replacements. + + If ``query`` contains non-ASCII characters, search keys will not be + altered. + + """ + + if not query: + raise ValueError('Empty `query`') + + # Remove preceding/trailing spaces + query = query.strip() + + if not query: + raise ValueError('`query` contains only whitespace') + + # Use user override if there is one + fold_diacritics = self.settings.get('__workflow_diacritic_folding', + fold_diacritics) + + results = [] + + for item in items: + skip = False + score = 0 + words = [s.strip() for s in query.split(' ')] + value = key(item).strip() + if value == '': + continue + for word in words: + if word == '': + continue + s, rule = self._filter_item(value, word, match_on, + fold_diacritics) + + if not s: # Skip items that don't match part of the query + skip = True + score += s + + if skip: + continue + + if score: + # use "reversed" `score` (i.e. highest becomes lowest) and + # `value` as sort key. This means items with the same score + # will be sorted in alphabetical not reverse alphabetical order + results.append(((100.0 / score, value.lower(), score), + (item, score, rule))) + + # sort on keys, then discard the keys + results.sort(reverse=ascending) + results = [t[1] for t in results] + + if min_score: + results = [r for r in results if r[1] > min_score] + + if max_results and len(results) > max_results: + results = results[:max_results] + + # return list of ``(item, score, rule)`` + if include_score: + return results + # just return list of items + return [t[0] for t in results] + + def _filter_item(self, value, query, match_on, fold_diacritics): + """Filter ``value`` against ``query`` using rules ``match_on`` + + :returns: ``(score, rule)`` + + """ + + query = query.lower() + + if not isascii(query): + fold_diacritics = False + + if fold_diacritics: + value = self.fold_to_ascii(value) + + # pre-filter any items that do not contain all characters + # of ``query`` to save on running several more expensive tests + if not set(query) <= set(value.lower()): + + return (0, None) + + # item starts with query + if match_on & MATCH_STARTSWITH and value.lower().startswith(query): + score = 100.0 - (len(value) / len(query)) + + return (score, MATCH_STARTSWITH) + + # query matches capitalised letters in item, + # e.g. of = OmniFocus + if match_on & MATCH_CAPITALS: + initials = ''.join([c for c in value if c in INITIALS]) + if initials.lower().startswith(query): + score = 100.0 - (len(initials) / len(query)) + + return (score, MATCH_CAPITALS) + + # split the item into "atoms", i.e. words separated by + # spaces or other non-word characters + if (match_on & MATCH_ATOM or + match_on & MATCH_INITIALS_CONTAIN or + match_on & MATCH_INITIALS_STARTSWITH): + atoms = [s.lower() for s in split_on_delimiters(value)] + # print('atoms : %s --> %s' % (value, atoms)) + # initials of the atoms + initials = ''.join([s[0] for s in atoms if s]) + + if match_on & MATCH_ATOM: + # is `query` one of the atoms in item? + # similar to substring, but scores more highly, as it's + # a word within the item + if query in atoms: + score = 100.0 - (len(value) / len(query)) + + return (score, MATCH_ATOM) + + # `query` matches start (or all) of the initials of the + # atoms, e.g. ``himym`` matches "How I Met Your Mother" + # *and* "how i met your mother" (the ``capitals`` rule only + # matches the former) + if (match_on & MATCH_INITIALS_STARTSWITH and + initials.startswith(query)): + score = 100.0 - (len(initials) / len(query)) + + return (score, MATCH_INITIALS_STARTSWITH) + + # `query` is a substring of initials, e.g. ``doh`` matches + # "The Dukes of Hazzard" + elif (match_on & MATCH_INITIALS_CONTAIN and + query in initials): + score = 95.0 - (len(initials) / len(query)) + + return (score, MATCH_INITIALS_CONTAIN) + + # `query` is a substring of item + if match_on & MATCH_SUBSTRING and query in value.lower(): + score = 90.0 - (len(value) / len(query)) + + return (score, MATCH_SUBSTRING) + + # finally, assign a score based on how close together the + # characters in `query` are in item. + if match_on & MATCH_ALLCHARS: + search = self._search_for_query(query) + match = search(value) + if match: + score = 100.0 / ((1 + match.start()) * + (match.end() - match.start() + 1)) + + return (score, MATCH_ALLCHARS) + + # Nothing matched + return (0, None) + + def _search_for_query(self, query): + if query in self._search_pattern_cache: + return self._search_pattern_cache[query] + + # Build pattern: include all characters + pattern = [] + for c in query: + # pattern.append('[^{0}]*{0}'.format(re.escape(c))) + pattern.append('.*?{0}'.format(re.escape(c))) + pattern = ''.join(pattern) + search = re.compile(pattern, re.IGNORECASE).search + + self._search_pattern_cache[query] = search + return search + + def run(self, func): + """Call ``func`` to run your workflow + + :param func: Callable to call with ``self`` (i.e. the :class:`Workflow` + instance) as first argument. + + ``func`` will be called with :class:`Workflow` instance as first + argument. + + ``func`` should be the main entry point to your workflow. + + Any exceptions raised will be logged and an error message will be + output to Alfred. + + """ + + start = time.time() + + # Call workflow's entry function/method within a try-except block + # to catch any errors and display an error message in Alfred + try: + if self.version: + self.logger.debug('Workflow version : {0}'.format(self.version)) + + # Run update check if configured for self-updates. + # This call has to go in the `run` try-except block, as it will + # initialise `self.settings`, which will raise an exception + # if `settings.json` isn't valid. + + if self._update_settings: + self.check_update() + + # Run workflow's entry function/method + func(self) + + # Set last version run to current version after a successful + # run + self.set_last_version() + + except Exception as err: + self.logger.exception(err) + if self.help_url: + self.logger.info( + 'For assistance, see: {0}'.format(self.help_url)) + if not sys.stdout.isatty(): # Show error in Alfred + self._items = [] + if self._name: + name = self._name + elif self._bundleid: + name = self._bundleid + else: # pragma: no cover + name = os.path.dirname(__file__) + self.add_item("Error in workflow '%s'" % name, unicode(err), + icon=ICON_ERROR) + self.send_feedback() + return 1 + finally: + self.logger.debug('Workflow finished in {0:0.3f} seconds.'.format( + time.time() - start)) + return 0 + + # Alfred feedback methods ------------------------------------------ + + def add_item(self, title, subtitle='', modifier_subtitles=None, arg=None, + autocomplete=None, valid=False, uid=None, icon=None, + icontype=None, type=None, largetext=None, copytext=None): + """Add an item to be output to Alfred + + :param title: Title shown in Alfred + :type title: ``unicode`` + :param subtitle: Subtitle shown in Alfred + :type subtitle: ``unicode`` + :param modifier_subtitles: Subtitles shown when modifier + (CMD, OPT etc.) is pressed. Use a ``dict`` with the lowercase + keys ``cmd``, ``ctrl``, ``shift``, ``alt`` and ``fn`` + :type modifier_subtitles: ``dict`` + :param arg: Argument passed by Alfred as ``{query}`` when item is + actioned + :type arg: ``unicode`` + :param autocomplete: Text expanded in Alfred when item is TABbed + :type autocomplete: ``unicode`` + :param valid: Whether or not item can be actioned + :type valid: ``Boolean`` + :param uid: Used by Alfred to remember/sort items + :type uid: ``unicode`` + :param icon: Filename of icon to use + :type icon: ``unicode`` + :param icontype: Type of icon. Must be one of ``None`` , ``'filetype'`` + or ``'fileicon'``. Use ``'filetype'`` when ``icon`` is a filetype + such as ``'public.folder'``. Use ``'fileicon'`` when you wish to + use the icon of the file specified as ``icon``, e.g. + ``icon='/Applications/Safari.app', icontype='fileicon'``. + Leave as `None` if ``icon`` points to an actual + icon file. + :type icontype: ``unicode`` + :param type: Result type. Currently only ``'file'`` is supported + (by Alfred). This will tell Alfred to enable file actions for + this item. + :type type: ``unicode`` + :param largetext: Text to be displayed in Alfred's large text box + if user presses CMD+L on item. + :type largetext: ``unicode`` + :param copytext: Text to be copied to pasteboard if user presses + CMD+C on item. + :type copytext: ``unicode`` + :returns: :class:`Item` instance + + See the :ref:`script-filter-results` section of the documentation + for a detailed description of what the various parameters do and how + they interact with one another. + + See :ref:`icons` for a list of the supported system icons. + + .. note:: + + Although this method returns an :class:`Item` instance, you don't + need to hold onto it or worry about it. All generated :class:`Item` + instances are also collected internally and sent to Alfred when + :meth:`send_feedback` is called. + + The generated :class:`Item` is only returned in case you want to + edit it or do something with it other than send it to Alfred. + + """ + + item = self.item_class(title, subtitle, modifier_subtitles, arg, + autocomplete, valid, uid, icon, icontype, type, + largetext, copytext) + self._items.append(item) + return item + + def send_feedback(self): + """Print stored items to console/Alfred as XML.""" + root = ET.Element('items') + for item in self._items: + root.append(item.elem) + sys.stdout.write('\n') + sys.stdout.write(ET.tostring(root).encode('utf-8')) + sys.stdout.flush() + + #################################################################### + # Updating methods + #################################################################### + + @property + def first_run(self): + """Return ``True`` if it's the first time this version has run. + + .. versionadded:: 1.9.10 + + Raises a :class:`ValueError` if :attr:`version` isn't set. + + """ + + if not self.version: + raise ValueError('No workflow version set') + + if not self.last_version_run: + return True + + return self.version != self.last_version_run + + @property + def last_version_run(self): + """Return version of last version to run (or ``None``) + + .. versionadded:: 1.9.10 + + :returns: :class:`~workflow.update.Version` instance + or ``None`` + + """ + + if self._last_version_run is UNSET: + + version = self.settings.get('__workflow_last_version') + if version: + from update import Version + version = Version(version) + + self._last_version_run = version + + self.logger.debug('Last run version : {0}'.format( + self._last_version_run)) + + return self._last_version_run + + def set_last_version(self, version=None): + """Set :attr:`last_version_run` to current version + + .. versionadded:: 1.9.10 + + :param version: version to store (default is current version) + :type version: :class:`~workflow.update.Version` instance + or ``unicode`` + :returns: ``True`` if version is saved, else ``False`` + + """ + + if not version: + if not self.version: + self.logger.warning( + "Can't save last version: workflow has no version") + return False + + version = self.version + + if isinstance(version, basestring): + from update import Version + version = Version(version) + + self.settings['__workflow_last_version'] = str(version) + + self.logger.debug('Set last run version : {0}'.format(version)) + + return True + + @property + def update_available(self): + """Is an update available? + + .. versionadded:: 1.9 + + See :ref:`manual-updates` in the :ref:`user-manual` for detailed + information on how to enable your workflow to update itself. + + :returns: ``True`` if an update is available, else ``False`` + + """ + + update_data = self.cached_data('__workflow_update_status', max_age=0) + self.logger.debug('update_data : {0}'.format(update_data)) + + if not update_data or not update_data.get('available'): + return False + + return update_data['available'] + + def check_update(self, force=False): + """Call update script if it's time to check for a new release + + .. versionadded:: 1.9 + + The update script will be run in the background, so it won't + interfere in the execution of your workflow. + + See :ref:`manual-updates` in the :ref:`user-manual` for detailed + information on how to enable your workflow to update itself. + + :param force: Force update check + :type force: ``Boolean`` + + """ + + frequency = self._update_settings.get('frequency', + DEFAULT_UPDATE_FREQUENCY) + + if not force and not self.settings.get('__workflow_autoupdate', True): + self.logger.debug('Auto update turned off by user') + return + + # Check for new version if it's time + if (force or not self.cached_data_fresh( + '__workflow_update_status', frequency * 86400)): + + github_slug = self._update_settings['github_slug'] + # version = self._update_settings['version'] + version = str(self.version) + + from background import run_in_background + + # update.py is adjacent to this file + update_script = os.path.join(os.path.dirname(__file__), + b'update.py') + + cmd = ['/usr/bin/python', update_script, 'check', github_slug, + version] + + self.logger.info('Checking for update ...') + + run_in_background('__workflow_update_check', cmd) + + else: + self.logger.debug('Update check not due') + + def start_update(self): + """Check for update and download and install new workflow file + + .. versionadded:: 1.9 + + See :ref:`manual-updates` in the :ref:`user-manual` for detailed + information on how to enable your workflow to update itself. + + :returns: ``True`` if an update is available and will be + installed, else ``False`` + + """ + + import update + + github_slug = self._update_settings['github_slug'] + # version = self._update_settings['version'] + version = str(self.version) + + if not update.check_update(github_slug, version): + return False + + from background import run_in_background + + # update.py is adjacent to this file + update_script = os.path.join(os.path.dirname(__file__), + b'update.py') + + cmd = ['/usr/bin/python', update_script, 'install', github_slug, + version] + + self.logger.debug('Downloading update ...') + run_in_background('__workflow_update_install', cmd) + + return True + + #################################################################### + # Keychain password storage methods + #################################################################### + + def save_password(self, account, password, service=None): + """Save account credentials. + + If the account exists, the old password will first be deleted + (Keychain throws an error otherwise). + + If something goes wrong, a :class:`KeychainError` exception will + be raised. + + :param account: name of the account the password is for, e.g. + "Pinboard" + :type account: ``unicode`` + :param password: the password to secure + :type password: ``unicode`` + :param service: Name of the service. By default, this is the + workflow's bundle ID + :type service: ``unicode`` + + """ + if not service: + service = self.bundleid + + try: + self._call_security('add-generic-password', service, account, + '-w', password) + self.logger.debug('Saved password : %s:%s', service, account) + + except PasswordExists: + self.logger.debug('Password exists : %s:%s', service, account) + current_password = self.get_password(account, service) + + if current_password == password: + self.logger.debug('Password unchanged') + + else: + self.delete_password(account, service) + self._call_security('add-generic-password', service, + account, '-w', password) + self.logger.debug('save_password : %s:%s', service, account) + + def get_password(self, account, service=None): + """Retrieve the password saved at ``service/account``. Raise + :class:`PasswordNotFound` exception if password doesn't exist. + + :param account: name of the account the password is for, e.g. + "Pinboard" + :type account: ``unicode`` + :param service: Name of the service. By default, this is the workflow's + bundle ID + :type service: ``unicode`` + :returns: account password + :rtype: ``unicode`` + + """ + + if not service: + service = self.bundleid + + output = self._call_security('find-generic-password', service, + account, '-g') + + # Parsing of `security` output is adapted from python-keyring + # by Jason R. Coombs + # https://pypi.python.org/pypi/keyring + m = re.search( + r'password:\s*(?:0x(?P[0-9A-F]+)\s*)?(?:"(?P.*)")?', + output) + + if m: + groups = m.groupdict() + h = groups.get('hex') + password = groups.get('pw') + if h: + password = unicode(binascii.unhexlify(h), 'utf-8') + + self.logger.debug('Got password : %s:%s', service, account) + + return password + + def delete_password(self, account, service=None): + """Delete the password stored at ``service/account``. Raises + :class:`PasswordNotFound` if account is unknown. + + :param account: name of the account the password is for, e.g. + "Pinboard" + :type account: ``unicode`` + :param service: Name of the service. By default, this is the workflow's + bundle ID + :type service: ``unicode`` + + """ + + if not service: + service = self.bundleid + + self._call_security('delete-generic-password', service, account) + + self.logger.debug('Deleted password : %s:%s', service, account) + + #################################################################### + # Methods for workflow:* magic args + #################################################################### + + def _register_default_magic(self): + """Register the built-in magic arguments""" + # TODO: refactor & simplify + + # Wrap callback and message with callable + def callback(func, msg): + def wrapper(): + func() + return msg + + return wrapper + + self.magic_arguments['delcache'] = callback(self.clear_cache, + 'Deleted workflow cache') + self.magic_arguments['deldata'] = callback(self.clear_data, + 'Deleted workflow data') + self.magic_arguments['delsettings'] = callback( + self.clear_settings, 'Deleted workflow settings') + self.magic_arguments['reset'] = callback(self.reset, + 'Reset workflow') + self.magic_arguments['openlog'] = callback(self.open_log, + 'Opening workflow log file') + self.magic_arguments['opencache'] = callback( + self.open_cachedir, 'Opening workflow cache directory') + self.magic_arguments['opendata'] = callback( + self.open_datadir, 'Opening workflow data directory') + self.magic_arguments['openworkflow'] = callback( + self.open_workflowdir, 'Opening workflow directory') + self.magic_arguments['openterm'] = callback( + self.open_terminal, 'Opening workflow root directory in Terminal') + + # Diacritic folding + def fold_on(): + self.settings['__workflow_diacritic_folding'] = True + return 'Diacritics will always be folded' + + def fold_off(): + self.settings['__workflow_diacritic_folding'] = False + return 'Diacritics will never be folded' + + def fold_default(): + if '__workflow_diacritic_folding' in self.settings: + del self.settings['__workflow_diacritic_folding'] + return 'Diacritics folding reset' + + self.magic_arguments['foldingon'] = fold_on + self.magic_arguments['foldingoff'] = fold_off + self.magic_arguments['foldingdefault'] = fold_default + + # Updates + def update_on(): + self.settings['__workflow_autoupdate'] = True + return 'Auto update turned on' + + def update_off(): + self.settings['__workflow_autoupdate'] = False + return 'Auto update turned off' + + def do_update(): + if self.start_update(): + return 'Downloading and installing update ...' + else: + return 'No update available' + + self.magic_arguments['autoupdate'] = update_on + self.magic_arguments['noautoupdate'] = update_off + self.magic_arguments['update'] = do_update + + # Help + def do_help(): + if self.help_url: + self.open_help() + return 'Opening workflow help URL in browser' + else: + return 'Workflow has no help URL' + + def show_version(): + if self.version: + return 'Version: {0}'.format(self.version) + else: + return 'This workflow has no version number' + + def list_magic(): + """Display all available magic args in Alfred""" + isatty = sys.stderr.isatty() + for name in sorted(self.magic_arguments.keys()): + if name == 'magic': + continue + arg = '{0}{1}'.format(self.magic_prefix, name) + self.logger.debug(arg) + + if not isatty: + self.add_item(arg, icon=ICON_INFO) + + if not isatty: + self.send_feedback() + + self.magic_arguments['help'] = do_help + self.magic_arguments['magic'] = list_magic + self.magic_arguments['version'] = show_version + + def clear_cache(self, filter_func=lambda f: True): + """Delete all files in workflow's :attr:`cachedir`. + + :param filter_func: Callable to determine whether a file should be + deleted or not. ``filter_func`` is called with the filename + of each file in the data directory. If it returns ``True``, + the file will be deleted. + By default, *all* files will be deleted. + :type filter_func: ``callable`` + """ + self._delete_directory_contents(self.cachedir, filter_func) + + def clear_data(self, filter_func=lambda f: True): + """Delete all files in workflow's :attr:`datadir`. + + :param filter_func: Callable to determine whether a file should be + deleted or not. ``filter_func`` is called with the filename + of each file in the data directory. If it returns ``True``, + the file will be deleted. + By default, *all* files will be deleted. + :type filter_func: ``callable`` + """ + self._delete_directory_contents(self.datadir, filter_func) + + def clear_settings(self): + """Delete workflow's :attr:`settings_path`.""" + if os.path.exists(self.settings_path): + os.unlink(self.settings_path) + self.logger.debug('Deleted : %r', self.settings_path) + + def reset(self): + """Delete :attr:`settings `, :attr:`cache ` + and :attr:`data ` + + """ + + self.clear_cache() + self.clear_data() + self.clear_settings() + + def open_log(self): + """Open workflows :attr:`logfile` in standard + application (usually Console.app). + + """ + + subprocess.call(['open', self.logfile]) + + def open_cachedir(self): + """Open the workflow's :attr:`cachedir` in Finder.""" + subprocess.call(['open', self.cachedir]) + + def open_datadir(self): + """Open the workflow's :attr:`datadir` in Finder.""" + subprocess.call(['open', self.datadir]) + + def open_workflowdir(self): + """Open the workflow's :attr:`workflowdir` in Finder.""" + subprocess.call(['open', self.workflowdir]) + + def open_terminal(self): + """Open a Terminal window at workflow's :attr:`workflowdir`.""" + + subprocess.call(['open', '-a', 'Terminal', + self.workflowdir]) + + def open_help(self): + """Open :attr:`help_url` in default browser""" + subprocess.call(['open', self.help_url]) + + return 'Opening workflow help URL in browser' + + #################################################################### + # Helper methods + #################################################################### + + def decode(self, text, encoding=None, normalization=None): + """Return ``text`` as normalised unicode. + + If ``encoding`` and/or ``normalization`` is ``None``, the + ``input_encoding``and ``normalization`` parameters passed to + :class:`Workflow` are used. + + :param text: string + :type text: encoded or Unicode string. If ``text`` is already a + Unicode string, it will only be normalised. + :param encoding: The text encoding to use to decode ``text`` to + Unicode. + :type encoding: ``unicode`` or ``None`` + :param normalization: The nomalisation form to apply to ``text``. + :type normalization: ``unicode`` or ``None`` + :returns: decoded and normalised ``unicode`` + + :class:`Workflow` uses "NFC" normalisation by default. This is the + standard for Python and will work well with data from the web (via + :mod:`~workflow.web` or :mod:`json`). + + OS X, on the other hand, uses "NFD" normalisation (nearly), so data + coming from the system (e.g. via :mod:`subprocess` or + :func:`os.listdir`/:mod:`os.path`) may not match. You should either + normalise this data, too, or change the default normalisation used by + :class:`Workflow`. + + """ + + encoding = encoding or self._input_encoding + normalization = normalization or self._normalizsation + if not isinstance(text, unicode): + text = unicode(text, encoding) + return unicodedata.normalize(normalization, text) + + def fold_to_ascii(self, text): + """Convert non-ASCII characters to closest ASCII equivalent. + + .. versionadded:: 1.3 + + .. note:: This only works for a subset of European languages. + + :param text: text to convert + :type text: ``unicode`` + :returns: text containing only ASCII characters + :rtype: ``unicode`` + + """ + if isascii(text): + return text + text = ''.join([ASCII_REPLACEMENTS.get(c, c) for c in text]) + return unicode(unicodedata.normalize('NFKD', + text).encode('ascii', 'ignore')) + + def dumbify_punctuation(self, text): + """Convert non-ASCII punctuation to closest ASCII equivalent. + + This method replaces "smart" quotes and n- or m-dashes with their + workaday ASCII equivalents. This method is currently not used + internally, but exists as a helper method for workflow authors. + + .. versionadded: 1.9.7 + + :param text: text to convert + :type text: ``unicode`` + :returns: text with only ASCII punctuation + :rtype: ``unicode`` + + """ + if isascii(text): + return text + + text = ''.join([DUMB_PUNCTUATION.get(c, c) for c in text]) + return text + + def _delete_directory_contents(self, dirpath, filter_func): + """Delete all files in a directory + + :param dirpath: path to directory to clear + :type dirpath: ``unicode`` or ``str`` + :param filter_func function to determine whether a file shall be + deleted or not. + :type filter_func ``callable`` + """ + + if os.path.exists(dirpath): + for filename in os.listdir(dirpath): + if not filter_func(filename): + continue + path = os.path.join(dirpath, filename) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.unlink(path) + self.logger.debug('Deleted : %r', path) + + def _load_info_plist(self): + """Load workflow info from ``info.plist`` + + """ + + self._info = plistlib.readPlist(self._info_plist) + self._info_loaded = True + + def _create(self, dirpath): + """Create directory `dirpath` if it doesn't exist + + :param dirpath: path to directory + :type dirpath: ``unicode`` + :returns: ``dirpath`` argument + :rtype: ``unicode`` + + """ + + if not os.path.exists(dirpath): + os.makedirs(dirpath) + return dirpath + + def _call_security(self, action, service, account, *args): + """Call the ``security`` CLI app that provides access to keychains. + + + May raise `PasswordNotFound`, `PasswordExists` or `KeychainError` + exceptions (the first two are subclasses of `KeychainError`). + + :param action: The ``security`` action to call, e.g. + ``add-generic-password`` + :type action: ``unicode`` + :param service: Name of the service. + :type service: ``unicode`` + :param account: name of the account the password is for, e.g. + "Pinboard" + :type account: ``unicode`` + :param password: the password to secure + :type password: ``unicode`` + :param *args: list of command line arguments to be passed to + ``security`` + :type *args: `list` or `tuple` + :returns: ``(retcode, output)``. ``retcode`` is an `int`, ``output`` a + ``unicode`` string. + :rtype: `tuple` (`int`, ``unicode``) + + """ + + cmd = ['security', action, '-s', service, '-a', account] + list(args) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + retcode, output = p.wait(), p.stdout.read().strip().decode('utf-8') + if retcode == 44: # password does not exist + raise PasswordNotFound() + elif retcode == 45: # password already exists + raise PasswordExists() + elif retcode > 0: + err = KeychainError('Unknown Keychain error : %s' % output) + err.retcode = retcode + raise err + return output From 5700368356c0d3209cd7a60a67c471a936d645f9 Mon Sep 17 00:00:00 2001 From: Jan David Date: Fri, 25 Sep 2015 23:54:04 +0200 Subject: [PATCH 03/12] Add script that downloads templates A script has been added to download the templates from GitHub. The templates are checked out as a Git repository, making it really easy to download changes later on. If the repository does not exist yet, "git clone" is executed to create a local copy of the it. If it exists, "git pull" is invoked to get the latest version of the templates. --- gitignore-update.py | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 gitignore-update.py diff --git a/gitignore-update.py b/gitignore-update.py new file mode 100644 index 0000000..6adaf00 --- /dev/null +++ b/gitignore-update.py @@ -0,0 +1,52 @@ +# encoding: utf-8 + +import os +import sys +from sh import git, pwd, sh +from workflow import Workflow, ICON_SYNC, web + + +def main(wf): + return_value = 0 + + if not repo_exists(): + return_value = clone_repo() + else: + return_value = pull_repo() + + if return_value: + print "ERROR. Templates could not be downloaded." + else: + print "Templates have been successfully updated." + + +def clone_repo(): + return_value = 0 + + try: + return_value = git.clone("https://github.com/github/gitignore.git") + except: + return_value = -1 + + return return_value + + +def pull_repo(): + return_value = 0 + + try: + os.chdir("./gitignore") + return_code = git.pull() + except: + return_value = -1 + + return return_value + + +def repo_exists(): + return os.path.isdir("./gitignore") + + +if __name__ == u"__main__": + wf = Workflow() + sys.exit(wf.run(main)) From 758382d78c362464509c469d62e77d582754895b Mon Sep 17 00:00:00 2001 From: Jan David Date: Sat, 26 Sep 2015 21:12:23 +0200 Subject: [PATCH 04/12] Use the workflow's data directory for storage Alfred creates a data directory for each workflow. This directory should be used for persistent data, e.g. our template files, which the update script now does. Additionally, the script has been extended to make use of Alfred- Workflow's data API. The filenames are now saved using the persistent data API, making it incredible fast to get the list of available templates. --- gitignore-update.py | 56 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/gitignore-update.py b/gitignore-update.py index 6adaf00..32db0ce 100644 --- a/gitignore-update.py +++ b/gitignore-update.py @@ -1,18 +1,24 @@ # encoding: utf-8 import os +import re import sys from sh import git, pwd, sh from workflow import Workflow, ICON_SYNC, web +workflow = Workflow() +repo_dir = workflow.datafile("gitignore") + def main(wf): return_value = 0 - if not repo_exists(): - return_value = clone_repo() + if not os.path.isdir(repo_dir): + return_value = clone_repo(wf.datafile("")) else: - return_value = pull_repo() + return_value = pull_repo(repo_dir) + + update_templates(repo_dir) if return_value: print "ERROR. Templates could not be downloaded." @@ -20,10 +26,11 @@ def main(wf): print "Templates have been successfully updated." -def clone_repo(): +def clone_repo(parent_dir): return_value = 0 try: + os.chdir(parent_dir) return_value = git.clone("https://github.com/github/gitignore.git") except: return_value = -1 @@ -35,7 +42,7 @@ def pull_repo(): return_value = 0 try: - os.chdir("./gitignore") + os.chdir(repo_dir) return_code = git.pull() except: return_value = -1 @@ -43,10 +50,41 @@ def pull_repo(): return return_value -def repo_exists(): - return os.path.isdir("./gitignore") +def update_templates(): + wf.clear_data(lambda f: f.startswith("templates")) + store_template_names(repo_dir) + + +def store_template_names(): + templates = get_template_names(repo_dir) + templates.sort() + wf.store_data('templates', templates) + + +def get_template_names(): + file_names = get_file_names_in_dir(repo_dir) + templates = [] + + for f in file_names: + file_name = str(f) + if re.search(".\.gitignore$", file_name): + templates.append(file_name[:-10]) + + return templates + + +def get_file_names_in_dir(directory): + file_names = [] + + for root, subdirs, files in os.walk(directory): + for subdir in subdirs: + file_names.append(get_file_names_in_dir(subdir)) + + for f in files: + file_names.append(f) + + return file_names if __name__ == u"__main__": - wf = Workflow() - sys.exit(wf.run(main)) + sys.exit(workflow.run(main)) From 6d6d5425187499a948f5c361eac9732e4bbc5515 Mon Sep 17 00:00:00 2001 From: Jan David Date: Sat, 26 Sep 2015 21:45:22 +0200 Subject: [PATCH 05/12] Implement exception handling Exceptions are now handled gracefully, and preparations have been made to provide meaningful messages depending on the type of failure. Right now, however, only a single custom error message is implemented. --- gitignore-update.py | 53 +++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/gitignore-update.py b/gitignore-update.py index 32db0ce..d940ddc 100644 --- a/gitignore-update.py +++ b/gitignore-update.py @@ -11,54 +11,55 @@ def main(wf): - return_value = 0 - if not os.path.isdir(repo_dir): - return_value = clone_repo(wf.datafile("")) + clone_repo() else: - return_value = pull_repo(repo_dir) - - update_templates(repo_dir) + pull_repo() - if return_value: - print "ERROR. Templates could not be downloaded." - else: - print "Templates have been successfully updated." + update_templates() + print "Templates have been successfully updated." -def clone_repo(parent_dir): - return_value = 0 +def clone_repo(): try: - os.chdir(parent_dir) - return_value = git.clone("https://github.com/github/gitignore.git") + os.chdir(workflow.datafile("")) + git.clone("https://github.com/github/gitignore.git") except: - return_value = -1 - - return return_value + handle_exception() + return 0 def pull_repo(): - return_value = 0 - try: os.chdir(repo_dir) - return_code = git.pull() + git.pull() except: - return_value = -1 + handle_exception() + return 0 + - return return_value +def handle_exception(): + e = sys.exc_info()[0] + if e.__name__ == "ErrorReturnCode_128": + print "'git clone' failed due to an unknown reason. Please contact the support." + else: + print "An unknown error occured. Please contact the support." + sys.exit(-1) def update_templates(): - wf.clear_data(lambda f: f.startswith("templates")) - store_template_names(repo_dir) + workflow.clear_data(lambda f: f.startswith("templates")) + store_template_names() + return 0 def store_template_names(): - templates = get_template_names(repo_dir) + templates = get_template_names() templates.sort() - wf.store_data('templates', templates) + + workflow.store_data('templates', templates) + return 0 def get_template_names(): From 65be5455a6819604c551a0caa3e72ee250b421f2 Mon Sep 17 00:00:00 2001 From: Jan David Date: Sat, 26 Sep 2015 22:07:09 +0200 Subject: [PATCH 06/12] Comment script Since the script has been mostly finalized, comments have been added to describe its behavior. --- gitignore-update.py | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/gitignore-update.py b/gitignore-update.py index d940ddc..3b05678 100644 --- a/gitignore-update.py +++ b/gitignore-update.py @@ -11,6 +11,14 @@ def main(wf): + """ + Run script. + + This script checks whether or not the gitignore repository has already been + cloned, and either clones it or pulls the latest changes from the remote + repository. In both cases, the list of templates is stored in the persistent + storage provided by the Workflow's data API. + """ if not os.path.isdir(repo_dir): clone_repo() else: @@ -22,6 +30,14 @@ def main(wf): def clone_repo(): + """ + Clone the Git repository 'github/gitignore' to the data directory. + + This function clones the gitignore repository from GitHub and saves the + local copy in the workflow's data directory. It uses the module sh to invoke + the git executable. If the git executable cannot execute properly, an + exception is thrown. + """ try: os.chdir(workflow.datafile("")) git.clone("https://github.com/github/gitignore.git") @@ -31,6 +47,13 @@ def clone_repo(): def pull_repo(): + """ + Pull the recent changes from origin master. + + This function pulls all recent changes from the gitignore repository on + GitHub. It uses the module sh to invoke the git executable. If the git + executable cannot execute properly, an exception is thrown. + """ try: os.chdir(repo_dir) git.pull() @@ -40,6 +63,13 @@ def pull_repo(): def handle_exception(): + """ + Handle the last thrown exception. + + This function handles the last thrown exception. It compares the exception's + class to a number of known exceptions, and prints the respective error + message. If the exception is not known, a generic error message is printed. + """ e = sys.exc_info()[0] if e.__name__ == "ErrorReturnCode_128": print "'git clone' failed due to an unknown reason. Please contact the support." @@ -49,12 +79,26 @@ def handle_exception(): def update_templates(): + """ + Update the list of templates stored with the Workflow's data API. + + This function updates the list of templates that is stored with the + Workflow's data API. To avoid duplicate data entries, it first deletes any + existing data before saving the current list of templates. + """ workflow.clear_data(lambda f: f.startswith("templates")) store_template_names() return 0 def store_template_names(): + """ + Save the template names using the Workflow's data API. + + This function reads the names of the currently available templates from the + directory, and saves them in the persistent data storage provided by the + Workflow library. + """ templates = get_template_names() templates.sort() @@ -63,6 +107,13 @@ def store_template_names(): def get_template_names(): + """ + Return the names of all templates in the local repository. + + This function goes recursively through the local copy of the gitignore + repository, and returns the name of all templates within it. Templates are + identified by their file extension, which is '.gitignore'. + """ file_names = get_file_names_in_dir(repo_dir) templates = [] @@ -75,6 +126,15 @@ def get_template_names(): def get_file_names_in_dir(directory): + """ + Return the names of all files in the given directory. + + Arguments: + - directory: Path of the directory whose files should be returned + + This function goes recursively through the given directory and returns the + name of all files within it. + """ file_names = [] for root, subdirs, files in os.walk(directory): From 189aa910e55459a3bb93acff1be9b1afce351eae Mon Sep 17 00:00:00 2001 From: Jan David Date: Thu, 5 Nov 2015 15:26:11 +0100 Subject: [PATCH 07/12] Add script that selects templates A new script has been added that allows the user to select the templates he wants to combine into a single .gitignore file. --- gitignore-input.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 gitignore-input.py diff --git a/gitignore-input.py b/gitignore-input.py new file mode 100644 index 0000000..0230d63 --- /dev/null +++ b/gitignore-input.py @@ -0,0 +1,88 @@ +# encoding: utf-8 + +import argparse +import os +import sys +from workflow import Workflow, ICON_SYNC, ICON_WARNING, web + +workflow = Workflow() + + +def main(wf): + parser = argparse.ArgumentParser() + parser.add_argument('query', nargs='*', default=None) + + args = parser.parse_args(wf.args) + templates = wf.stored_data("templates") + + current_query = "" + + if len is None: + wf.add_item( + title="Templates missing", + subtitle="Please run gitignore-update to download the templates...", + icon=ICON_WARNING, + valid=False + ) + else: + if args.query: + query = args.query + input = query[-1] + current_query = " ".join(query[:-1]) + filtered_templates = [i for i in templates if input.lower() in i.lower()] + + if len(filtered_templates) >= 1: + templates = filtered_templates + + wf.add_item( + title="Build .gitignore file", + subtitle="Combine the chosen templates to a single .gitignore file...", + uid="build_gitignore", + arg=" ".join(query), + valid=True, + ) + + for i in templates: + add_template(i, query=current_query) + else: + for i in templates: + add_template(i) + + wf.send_feedback() + + +def add_template(template_name, query=""): + """ + Add template to output. + + This function adds the given template as a new item to the XML output. + """ + autocomplete = build_autocomplete(template_name, query) + + workflow.add_item( + title=template_name, + uid=template_name, + autocomplete=autocomplete, + valid=False + ) + + +def build_autocomplete(template_name, query): + """ + Build the autocomplete string. + + From the template name and the current query a new string is built that can + be used as the value for an item's autocomplete attribute. + """ + autocomplete = "" + + if len(query) > 0: + autocomplete = " ".join([query, template_name]) + else: + autocomplete = template_name + + return " ".join([autocomplete, ""]) + + +if __name__ == u"__main__": + sys.exit(workflow.run(main)) From a5c0357b310c0575bf007c59df2df373fd38dff4 Mon Sep 17 00:00:00 2001 From: Jan David Date: Wed, 25 Nov 2015 16:54:41 +0100 Subject: [PATCH 08/12] Add script to build .gitignore file The final stage of the workflow is the script that takes the templates selected by the user, and puts them into a single file. The file is stored in /tmp, where it will automatically get deleted if it is older than three days. --- gitignore-build.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 gitignore-build.py diff --git a/gitignore-build.py b/gitignore-build.py new file mode 100644 index 0000000..c5513a6 --- /dev/null +++ b/gitignore-build.py @@ -0,0 +1,50 @@ +# encoding: utf-8 + +import glob +import hashlib +import os +import sys +from workflow import Workflow, ICON_SYNC, ICON_WARNING, web + +workflow = Workflow() +repo_dir = workflow.datafile("gitignore") + +def main(wf): + if len(sys.argv) < 2: + print "No templates were selected, so nothing was built." + return + + if not os.path.isdir(repo_dir): + print "Please run gitignore-update first to download the templates." + + templates = sys.argv[1:] + + tmp_file_name = hashlib.md5(" ".join(templates)).hexdigest() + tmp_file_path = "/tmp/" + tmp_file_name + + if os.path.isfile(tmp_file_path): + os.system("open %s" % tmp_file_path) + return + + formatted_templates = set() + + for t in templates: + formatted_templates.add(t.lower() + ".gitignore") + + for root, dirs, files in os.walk(repo_dir): + for name in files: + if name.lower() in formatted_templates: + with open(os.path.join(root, name)) as in_file: + with open(tmp_file_path, "a+") as out_file: + out_file.write("### %s\n\n" % name) + for line in in_file: + out_file.write(line) + out_file.write("\n\n") + + print "Successfully built .gitignore file. Have fun!" + os.system("open %s" % tmp_file_path) + return + + +if __name__ == u"__main__": + sys.exit(workflow.run(main)) From dff20f2397e62ada0bc59e2ffd9973907f2f9d3b Mon Sep 17 00:00:00 2001 From: Jan David Date: Wed, 25 Nov 2015 17:29:36 +0100 Subject: [PATCH 09/12] Extend README The README file has been extended to include instructions on how to use the workflow. --- README.md | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 186e372..a4af42b 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,48 @@ **gitignore** is an [Alfred](https://www.alfredapp.com) workflow that lets you create _.gitignore_ files from Alfred's input interface. It uses templates -provided by [GitHub](https://github.com), allowing you to simply type in the -names of all templates you want to combine into your own _.gitignore_ file. +provided by [GitHub](https://github.com), and combines them into a single +.gitignore file. -# License +## Requirements + +Although it should be pretty self-explanatory, these are the requirements for +this workflow: + +- **OS X** +- **Alfred 2** +- **Git** + +## Installation + +After installing the workflow, you need to download the templates. Enter the +following command into Alfred: + +``` +gitignore-update +``` + +Executing this will clone the [github/gitignore](https://github.com/github/gitignore) +repository, and make the templates in it available to you. + +## Usage + +To use this workflow, simply type in `gitignore`. You will now see a list of all +templates installed on your machine. You can search for specific templates by +typing in their name. Selecting a template will place add it to the command +line. + +If you've selected all templates that you want to combine, simply select the +first item in the list called "Build .gitignore file". This will start the +generation of the template, and open it in TextEdit once it has been created. + +Copy & paste the contents of the file and paste them into the `.gitignore` file +in your project. + +The temporary file is created in `/tmp/`, and will automatically be deleted +after three days. + +## License Copyright (c) 2015 Jan David Nose From 92c076d0c019aff3da74b8bb4e8f8a18fcb76b73 Mon Sep 17 00:00:00 2001 From: Jan David Date: Wed, 25 Nov 2015 17:34:44 +0100 Subject: [PATCH 10/12] Add workflow configuration The workflow configuration has been included. It consists of the images used by the workflow, as well as the info.plist file containing the workflow's settings. --- 487A8098-1037-4523-BCA6-B04539B70A80.png | Bin 0 -> 31068 bytes icon.png | Bin 0 -> 31068 bytes info.plist | 72 +++++++++++++++++++++++ version | 1 + 4 files changed, 73 insertions(+) create mode 100644 487A8098-1037-4523-BCA6-B04539B70A80.png create mode 100644 icon.png create mode 100644 info.plist create mode 100644 version diff --git a/487A8098-1037-4523-BCA6-B04539B70A80.png b/487A8098-1037-4523-BCA6-B04539B70A80.png new file mode 100644 index 0000000000000000000000000000000000000000..09ea45eca7565d68c27b671fe98ca52cfc5eac5f GIT binary patch literal 31068 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 l&t~Hq$6WRq5Z0 + + + + bundleid + de.jandavid.alfred.gitignore + category + Productivity + connections + + createdby + Jan David Nose + description + Build a .gitignore file with Alfred + disabled + + name + gitignore + objects + + + config + + argumenttype + 0 + escaping + 116 + keyword + gitignore + queuedelaycustom + 3 + queuedelayimmediatelyinitially + + queuedelaymode + 0 + queuemode + 1 + runningsubtext + Fetching templates from gitignore.io... + script + python gitignore.py + subtext + Combine multiple templates to a single .gitignore file + title + Build a .gitignore file from templates + type + 0 + withspace + + + type + alfred.workflow.input.scriptfilter + uid + 487A8098-1037-4523-BCA6-B04539B70A80 + version + 0 + + + readme + + uidata + + 487A8098-1037-4523-BCA6-B04539B70A80 + + ypos + 10 + + + webaddress + http://jandavid.de + + diff --git a/version b/version new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/version @@ -0,0 +1 @@ +0.1.0 From 61be212787ea4d1ca44570ceecee0957a705558e Mon Sep 17 00:00:00 2001 From: Jan David Date: Wed, 25 Nov 2015 17:35:19 +0100 Subject: [PATCH 11/12] Bump version number --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 6e8bf73..3eefcb9 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.1.0 +1.0.0 From 656bf3a8aa6e86dd66dcbfc3f1863614253db34b Mon Sep 17 00:00:00 2001 From: Jan David Date: Wed, 25 Nov 2015 17:40:12 +0100 Subject: [PATCH 12/12] Include exported workflow (v1.0.0) The workflow has been exported. To allow the workflow framework to automatically update the workflow, it is included in the Git repository. --- gitignore.alfredworkflow | Bin 0 -> 246964 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gitignore.alfredworkflow diff --git a/gitignore.alfredworkflow b/gitignore.alfredworkflow new file mode 100644 index 0000000000000000000000000000000000000000..e76ec15f5f0dce0f4c61875eacb72d624522648e GIT binary patch literal 246964 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 z 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

r8)$ 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�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%0Al{1osyGR{f4vDa# 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�K_TCy#nfzH$8ev}VN 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