From 7130ca9e0f84f847d4383aa36655c46ce6f385f1 Mon Sep 17 00:00:00 2001 From: Dean Jackson Date: Thu, 28 Jan 2016 16:11:46 +0100 Subject: [PATCH] Change delimiter to multiplication sign --- ...lfredworkflow => Fakeum-1.2.alfredworkflow | Bin 419687 -> 422251 bytes src/fakeum.py | 2 +- src/version | 2 +- src/workflow/__init__.py | 72 ++-- src/workflow/background.py | 47 ++- src/workflow/update.py | 126 +++--- src/workflow/version | 2 +- src/workflow/web.py | 21 +- src/workflow/workflow.py | 381 ++++++++++++++---- 9 files changed, 456 insertions(+), 197 deletions(-) rename Fakeum-1.1.alfredworkflow => Fakeum-1.2.alfredworkflow (82%) diff --git a/Fakeum-1.1.alfredworkflow b/Fakeum-1.2.alfredworkflow similarity index 82% rename from Fakeum-1.1.alfredworkflow rename to Fakeum-1.2.alfredworkflow index f2fc6229a2bb0d0cb19dc9a7aa9572b4e996d94a..aeb49eada725f11d7f8dc6322e154e340d9c4d08 100644 GIT binary patch delta 46026 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

jUht6OTAla~V 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 diff --git a/src/fakeum.py b/src/fakeum.py index df5f752..d278040 100644 --- a/src/fakeum.py +++ b/src/fakeum.py @@ -22,7 +22,7 @@ from workflow import Workflow, ICON_WARNING, MATCH_ALL, MATCH_ALLCHARS from faker import Factory -DELIMITER = '⟩' +DELIMITER = '×' HELP_URL = 'https://github.com/deanishe/alfred-fakeum' UPDATE_SETTINGS = {'github_slug': 'deanishe/alfred-fakeum'} diff --git a/src/version b/src/version index b123147..ea710ab 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.1 \ No newline at end of file +1.2 \ No newline at end of file diff --git a/src/workflow/__init__.py b/src/workflow/__init__.py index 01a9cb3..5de1a96 100644 --- a/src/workflow/__init__.py +++ b/src/workflow/__init__.py @@ -69,40 +69,40 @@ ) __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, + '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/src/workflow/background.py b/src/workflow/background.py index 7ed4df2..bcfa74d 100644 --- a/src/workflow/background.py +++ b/src/workflow/background.py @@ -23,8 +23,14 @@ __all__ = ['is_running', 'run_in_background'] -wf = Workflow() -log = wf.logger +_wf = None + + +def wf(): + global _wf + if _wf is None: + _wf = Workflow() + return _wf def _arg_cache(name): @@ -37,7 +43,7 @@ def _arg_cache(name): """ - return wf.cachefile('{}.argcache'.format(name)) + return wf().cachefile('{0}.argcache'.format(name)) def _pid_file(name): @@ -50,7 +56,7 @@ def _pid_file(name): """ - return wf.cachefile('{}.pid'.format(name)) + return wf().cachefile('{0}.pid'.format(name)) def _process_exists(pid): @@ -114,10 +120,11 @@ def _background(stdin='/dev/null', stdout='/dev/null', if pid > 0: sys.exit(0) # Exit first parent. except OSError as e: - log.critical("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror)) + 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.chdir(wf().workflowdir) os.umask(0) os.setsid() # Do second fork. @@ -126,7 +133,8 @@ def _background(stdin='/dev/null', stdout='/dev/null', if pid > 0: sys.exit(0) # Exit second parent. except OSError as e: - log.critical("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) + 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. @@ -169,7 +177,7 @@ def run_in_background(name, args, **kwargs): """ if is_running(name): - log.info('Task `{}` is already running'.format(name)) + wf().logger.info('Task `{0}` is already running'.format(name)) return argcache = _arg_cache(name) @@ -177,16 +185,16 @@ def run_in_background(name, args, **kwargs): # Cache arguments with open(argcache, 'wb') as file_obj: pickle.dump({'args': args, 'kwargs': kwargs}, file_obj) - log.debug('Command arguments cached to `{}`'.format(argcache)) + wf().logger.debug('Command arguments cached to `{0}`'.format(argcache)) # Call this script cmd = ['/usr/bin/python', __file__, name] - log.debug('Calling {!r} ...'.format(cmd)) + wf().logger.debug('Calling {0!r} ...'.format(cmd)) retcode = subprocess.call(cmd) if retcode: # pragma: no cover - log.error('Failed to call task in background') + wf().logger.error('Failed to call task in background') else: - log.debug('Executing task `{}` in background...'.format(name)) + wf().logger.debug('Executing task `{0}` in background...'.format(name)) return retcode @@ -200,7 +208,7 @@ def main(wf): # pragma: no cover name = wf.args[0] argcache = _arg_cache(name) if not os.path.exists(argcache): - log.critical('No arg cache found : {!r}'.format(argcache)) + wf.logger.critical('No arg cache found : {0!r}'.format(argcache)) return 1 # Load cached arguments @@ -221,23 +229,24 @@ def main(wf): # pragma: no cover # Write PID to file with open(pidfile, 'wb') as file_obj: - file_obj.write('{}'.format(os.getpid())) + file_obj.write('{0}'.format(os.getpid())) # Run the command try: - log.debug('Task `{}` running'.format(name)) - log.debug('cmd : {!r}'.format(args)) + wf.logger.debug('Task `{0}` running'.format(name)) + wf.logger.debug('cmd : {0!r}'.format(args)) retcode = subprocess.call(args, **kwargs) if retcode: - log.error('Command failed with [{}] : {!r}'.format(retcode, args)) + wf.logger.error('Command failed with [{0}] : {1!r}'.format( + retcode, args)) finally: if os.path.exists(pidfile): os.unlink(pidfile) - log.debug('Task `{}` finished'.format(name)) + wf.logger.debug('Task `{0}` finished'.format(name)) if __name__ == '__main__': # pragma: no cover - wf.run(main) + wf().run(main) diff --git a/src/workflow/update.py b/src/workflow/update.py index caa1125..b946e79 100644 --- a/src/workflow/update.py +++ b/src/workflow/update.py @@ -26,7 +26,6 @@ import os import tempfile -import argparse import re import subprocess @@ -35,10 +34,18 @@ # __all__ = [] -wf = workflow.Workflow() -log = wf.logger -RELEASES_BASE = 'https://api.github.com/repos/{}/releases' +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): @@ -66,7 +73,7 @@ def _parse(self, vstr): else: m = self.match_version(vstr) if not m: - raise ValueError('Invalid version number: {}'.format(vstr)) + raise ValueError('Invalid version number: {0}'.format(vstr)) version, suffix = m.groups() parts = self._parse_dotted_string(version) @@ -76,7 +83,7 @@ def _parse(self, vstr): if len(parts): self.patch = parts.pop(0) if not len(parts) == 0: - raise ValueError('Invalid version (too long) : {}'.format(vstr)) + raise ValueError('Invalid version (too long) : {0}'.format(vstr)) if suffix: # Build info @@ -87,11 +94,11 @@ def _parse(self, vstr): if suffix: if not suffix.startswith('-'): raise ValueError( - 'Invalid suffix : `{}`. Must start with `-`'.format( + 'Invalid suffix : `{0}`. Must start with `-`'.format( suffix)) self.suffix = suffix[1:] - # log.debug('version str `{}` -> {}'.format(vstr, repr(self))) + # wf().logger.debug('version str `{}` -> {}'.format(vstr, repr(self))) def _parse_dotted_string(self, s): """Parse string ``s`` into list of ints and strings""" @@ -112,7 +119,7 @@ def tuple(self): def __lt__(self, other): if not isinstance(other, Version): - raise ValueError('Not a Version instance: {!r}'.format(other)) + raise ValueError('Not a Version instance: {0!r}'.format(other)) t = self.tuple[:3] o = other.tuple[:3] if t < o: @@ -129,7 +136,7 @@ def __lt__(self, other): def __eq__(self, other): if not isinstance(other, Version): - raise ValueError('Not a Version instance: {!r}'.format(other)) + raise ValueError('Not a Version instance: {0!r}'.format(other)) return self.tuple == other.tuple def __ne__(self, other): @@ -137,27 +144,27 @@ def __ne__(self, other): def __gt__(self, other): if not isinstance(other, Version): - raise ValueError('Not a Version instance: {!r}'.format(other)) + 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: {!r}'.format(other)) + 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 = '{}.{}.{}'.format(self.major, self.minor, self.patch) + vstr = '{0}.{1}.{2}'.format(self.major, self.minor, self.patch) if self.suffix: - vstr += '-{}'.format(self.suffix) + vstr += '-{0}'.format(self.suffix) if self.build: - vstr += '+{}'.format(self.build) + vstr += '+{0}'.format(self.build) return vstr def __repr__(self): - return "Version('{}')".format(str(self)) + return "Version('{0}')".format(str(self)) def download_workflow(url): @@ -176,8 +183,9 @@ def download_workflow(url): local_path = os.path.join(tempfile.gettempdir(), filename) - log.debug('Downloading updated workflow from `{}` to `{}` ...'.format(url, - local_path)) + wf().logger.debug( + 'Downloading updated workflow from `{0}` to `{1}` ...'.format( + url, local_path)) response = web.get(url) @@ -196,7 +204,7 @@ def build_api_url(slug): """ if len(slug.split('/')) != 2: - raise ValueError('Invalid GitHub slug : {}'.format(slug)) + raise ValueError('Invalid GitHub slug : {0}'.format(slug)) return RELEASES_BASE.format(slug) @@ -219,15 +227,17 @@ def get_valid_releases(github_slug): api_url = build_api_url(github_slug) releases = [] - log.debug('Retrieving releases list from `{}` ...'.format(api_url)) + wf().logger.debug('Retrieving releases list from `{0}` ...'.format( + api_url)) def retrieve_releases(): - log.info('Retriving releases for `{}` ...'.format(github_slug)) + 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-{}'.format(slug), - retrieve_releases): + 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', []): @@ -238,19 +248,19 @@ def retrieve_releases(): # Validate release if release['prerelease']: - log.warning( - 'Invalid release {} : pre-release detected'.format(version)) + wf().logger.warning( + 'Invalid release {0} : pre-release detected'.format(version)) continue if not download_urls: - log.warning( - 'Invalid release {} : No workflow file'.format(version)) + wf().logger.warning( + 'Invalid release {0} : No workflow file'.format(version)) continue if len(download_urls) > 1: - log.warning( - 'Invalid release {} : multiple workflow files'.format(version)) + wf().logger.warning( + 'Invalid release {0} : multiple workflow files'.format(version)) continue - log.debug('Release `{}` : {}'.format(version, url)) + wf().logger.debug('Release `{0}` : {1}'.format(version, url)) releases.append({'version': version, 'download_url': download_urls[0]}) return releases @@ -272,10 +282,11 @@ def check_update(github_slug, current_version): releases = get_valid_releases(github_slug) - log.info('{} releases for {}'.format(len(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 {}'.format(github_slug)) + raise ValueError('No valid releases for {0}'.format(github_slug)) # GitHub returns releases newest-first latest_release = releases[0] @@ -283,10 +294,10 @@ def check_update(github_slug, current_version): # (latest_version, download_url) = get_latest_release(releases) vr = Version(latest_release['version']) vl = Version(current_version) - log.debug('Latest : {!r} Installed : {!r}'.format(vr, vl)) + wf().logger.debug('Latest : {0!r} Installed : {1!r}'.format(vr, vl)) if vr > vl: - wf.cache_data('__workflow_update_status', { + wf().cache_data('__workflow_update_status', { 'version': latest_release['version'], 'download_url': latest_release['download_url'], 'available': True @@ -294,7 +305,7 @@ def check_update(github_slug, current_version): return True - wf.cache_data('__workflow_update_status', { + wf().cache_data('__workflow_update_status', { 'available': False }) return False @@ -313,39 +324,40 @@ def install_update(github_slug, current_version): :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) + 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') + wf().logger.info('No update available') return False local_file = download_workflow(update_data['download_url']) - log.info('Installing updated workflow ...') + wf().logger.info('Installing updated workflow ...') subprocess.call(['open', local_file]) update_data['available'] = False - wf.cache_data('__workflow_update_status', update_data) + wf().cache_data('__workflow_update_status', update_data) return True if __name__ == '__main__': # pragma: nocover - parser = argparse.ArgumentParser( - description='Check for and install updates') - parser.add_argument( - 'action', - choices=['check', 'install'], - help='Check for new version or install new version?') - parser.add_argument( - 'github_slug', - help='GitHub repo name in format "username/repo"') - parser.add_argument( - 'version', - help='The version of the installed workflow') - - args = parser.parse_args() - if args.action == 'check': - check_update(args.github_slug, args.version) - elif args.action == 'install': - install_update(args.github_slug, args.version) + 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/src/workflow/version b/src/workflow/version index e33692a..9f76d37 100644 --- a/src/workflow/version +++ b/src/workflow/version @@ -1 +1 @@ -1.10.1 \ No newline at end of file +1.13 \ No newline at end of file diff --git a/src/workflow/web.py b/src/workflow/web.py index 8b8e421..153833b 100644 --- a/src/workflow/web.py +++ b/src/workflow/web.py @@ -24,10 +24,11 @@ import unicodedata import urllib import urllib2 +import urlparse import zlib -USER_AGENT = u'alfred-workflow-0.1' +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 @@ -215,7 +216,9 @@ def __init__(self, request): self.url = err.geturl() # sometimes (e.g. when authentication fails) # urllib can't get a URL from an HTTPError - except AttributeError: + # 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: @@ -468,6 +471,8 @@ def request(method, url, params=None, data=None, headers=None, cookies=None, """ + # TODO: cookies + # TODO: any way to force GET or POST? socket.setdefaulttimeout(timeout) # Default handlers @@ -518,7 +523,17 @@ def request(method, url, params=None, data=None, headers=None, cookies=None, url = url.encode('utf-8') if params: # GET args (POST args are handled in encode_multipart_formdata) - url = url + '?' + urllib.urlencode(str_dict(params)) + + 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) diff --git a/src/workflow/workflow.py b/src/workflow/workflow.py index ce6506d..fe65a81 100644 --- a/src/workflow/workflow.py +++ b/src/workflow/workflow.py @@ -17,20 +17,25 @@ 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 sys -import string -import re +import pickle import plistlib -import subprocess -import unicodedata +import re import shutil -import json -import cPickle -import pickle +import signal +import string +import subprocess +import sys import time -import logging -import logging.handlers +import unicodedata + try: import xml.etree.cElementTree as ET except ImportError: # pragma: no cover @@ -432,9 +437,13 @@ #################################################################### -# Keychain access errors +# 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` @@ -551,7 +560,7 @@ def unregister(self, name): """ if name not in self._serializers: - raise ValueError('No such serializer registered : {}'.format(name)) + raise ValueError('No such serializer registered : {0}'.format(name)) serializer = self._serializers[name] del self._serializers[name] @@ -734,13 +743,20 @@ def elem(self): """ + # 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', 'autocomplete'): + for name in ('uid', 'type'): value = getattr(self, name, None) if value: attr[name] = value @@ -748,14 +764,18 @@ def elem(self): 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: @@ -775,6 +795,153 @@ def elem(self): 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. @@ -826,9 +993,10 @@ def save(self): data = {} for key, value in self.items(): data[key] = value - with open(self._filepath, 'wb') as file_obj: - json.dump(data, file_obj, sort_keys=True, indent=2, - encoding='utf-8') + 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): @@ -940,9 +1108,6 @@ def __init__(self, default_settings=None, update_settings=None, if libraries: sys.path = libraries + sys.path - if update_settings: - self.check_update() - #################################################################### # API methods #################################################################### @@ -1036,7 +1201,7 @@ def info(self): @property def bundleid(self): - """Workflow bundle ID from Alfred's environmental vars or ``info.plist``. + """Workflow bundle ID from environmental vars or ``info.plist``. :returns: bundle ID :rtype: ``unicode`` @@ -1135,7 +1300,7 @@ def args(self): # Handle magic args if len(args) and self._capture_args: for name in self.magic_arguments: - key = '{}{}'.format(self.magic_prefix, name) + key = '{0}{1}'.format(self.magic_prefix, name) if key in args: msg = self.magic_arguments[name]() @@ -1374,6 +1539,8 @@ def settings(self): """ 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 @@ -1416,11 +1583,11 @@ def cache_serializer(self, serializer_name): if manager.serializer(serializer_name) is None: raise ValueError( - 'Unknown serializer : `{}`. Register your serializer ' + 'Unknown serializer : `{0}`. Register your serializer ' 'with `manager` first.'.format(serializer_name)) self.logger.debug( - 'default cache serializer set to `{}`'.format(serializer_name)) + 'default cache serializer set to `{0}`'.format(serializer_name)) self._cache_serializer = serializer_name @@ -1461,11 +1628,11 @@ def data_serializer(self, serializer_name): if manager.serializer(serializer_name) is None: raise ValueError( - 'Unknown serializer : `{}`. Register your serializer ' + 'Unknown serializer : `{0}`. Register your serializer ' 'with `manager` first.'.format(serializer_name)) self.logger.debug( - 'default data serializer set to `{}`'.format(serializer_name)) + 'default data serializer set to `{0}`'.format(serializer_name)) self._data_serializer = serializer_name @@ -1479,10 +1646,10 @@ def stored_data(self, name): """ - metadata_path = self.datafile('.{}.alfred-workflow'.format(name)) + metadata_path = self.datafile('.{0}.alfred-workflow'.format(name)) if not os.path.exists(metadata_path): - self.logger.debug('No data stored for `{}`'.format(name)) + self.logger.debug('No data stored for `{0}`'.format(name)) return None with open(metadata_path, 'rb') as file_obj: @@ -1492,18 +1659,18 @@ def stored_data(self, name): if serializer is None: raise ValueError( - 'Unknown serializer `{}`. Register a corresponding serializer ' - 'with `manager.register()` to load this data.'.format( - serializer_name)) + 'Unknown serializer `{0}`. Register a corresponding ' + 'serializer with `manager.register()` ' + 'to load this data.'.format(serializer_name)) - self.logger.debug('Data `{}` stored in `{}` format'.format( + self.logger.debug('Data `{0}` stored in `{1}` format'.format( name, serializer_name)) - filename = '{}.{}'.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 `{}`'.format(name)) + self.logger.debug('No data stored for `{0}`'.format(name)) if os.path.exists(metadata_path): os.unlink(metadata_path) @@ -1512,7 +1679,7 @@ def stored_data(self, name): with open(data_path, 'rb') as file_obj: data = serializer.load(file_obj) - self.logger.debug('Stored data loaded from : {}'.format(data_path)) + self.logger.debug('Stored data loaded from : {0}'.format(data_path)) return data @@ -1523,6 +1690,8 @@ def store_data(self, name, data, serializer=None): 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. @@ -1533,43 +1702,54 @@ def store_data(self, name, data, serializer=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 - if serializer_name == 'json' and name == 'settings': + # 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 `settings` with format `json`. ' + '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 `{}`. Register your serializer with ' + 'Invalid serializer `{0}`. Register your serializer with ' '`manager.register()` first.'.format(serializer_name)) - # 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('.{}.alfred-workflow'.format(name)) - filename = '{}.{}'.format(name, serializer_name) - data_path = self.datafile(filename) - if data is None: # Delete cached data - for path in (metadata_path, data_path): - if os.path.exists(path): - os.unlink(path) - self.logger.debug('Deleted data file : {}'.format(path)) - + delete_paths((metadata_path, data_path)) return - # Save file extension - with open(metadata_path, 'wb') as file_obj: - file_obj.write(serializer_name) + # 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 open(data_path, 'wb') as file_obj: - serializer.dump(data, file_obj) + with atomic_writer(data_path, 'wb') as file_obj: + serializer.dump(data, file_obj) + + _store() - self.logger.debug('Stored data saved at : {}'.format(data_path)) + 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 @@ -1628,7 +1808,7 @@ def cache_data(self, name, data): self.logger.debug('Deleted cache file : %s', cache_path) return - with open(cache_path, 'wb') as file_obj: + with atomic_writer(cache_path, 'wb') as file_obj: serializer.dump(data, file_obj) self.logger.debug('Cached data saved at : %s', cache_path) @@ -1717,19 +1897,30 @@ def filter(self, query, items, key=lambda x: x, ascending=False, 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. + 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). + 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. + :const:`MATCH_ALLCHARS` is considerably slower than the other + tests and provides much less accurate results. **Examples:** @@ -1804,12 +1995,12 @@ def filter(self, query, items, key=lambda x: x, ascending=False, results.sort(reverse=ascending) results = [t[1] for t in results] - if max_results and len(results) > max_results: - results = results[:max_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 @@ -1930,7 +2121,9 @@ def run(self, func): :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`` 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 @@ -1940,18 +2133,32 @@ def run(self, func): 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 : {}'.format(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: {}'.format(self.help_url)) + 'For assistance, see: {0}'.format(self.help_url)) if not sys.stdout.isatty(): # Show error in Alfred self._items = [] if self._name: @@ -1965,7 +2172,7 @@ def run(self, func): self.send_feedback() return 1 finally: - self.logger.debug('Workflow finished in {:0.3f} seconds.'.format( + self.logger.debug('Workflow finished in {0:0.3f} seconds.'.format( time.time() - start)) return 0 @@ -2090,7 +2297,7 @@ def last_version_run(self): self._last_version_run = version - self.logger.debug('Last run version : {}'.format( + self.logger.debug('Last run version : {0}'.format( self._last_version_run)) return self._last_version_run @@ -2121,7 +2328,7 @@ def set_last_version(self, version=None): self.settings['__workflow_last_version'] = str(version) - self.logger.debug('Set last run version : {}'.format(version)) + self.logger.debug('Set last run version : {0}'.format(version)) return True @@ -2139,7 +2346,7 @@ def update_available(self): """ update_data = self.cached_data('__workflow_update_status', max_age=0) - self.logger.debug('update_data : {}'.format(update_data)) + self.logger.debug('update_data : {0}'.format(update_data)) if not update_data or not update_data.get('available'): return False @@ -2291,8 +2498,23 @@ def get_password(self, account, service=None): if not service: service = self.bundleid - password = self._call_security('find-generic-password', service, - account, '-w') + 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 @@ -2323,6 +2545,7 @@ def delete_password(self, account, service=None): def _register_default_magic(self): """Register the built-in magic arguments""" + # TODO: refactor & simplify # Wrap callback and message with callable def callback(func, msg): @@ -2398,7 +2621,7 @@ def do_help(): def show_version(): if self.version: - return 'Version: {}'.format(self.version) + return 'Version: {0}'.format(self.version) else: return 'This workflow has no version number' @@ -2408,7 +2631,7 @@ def list_magic(): for name in sorted(self.magic_arguments.keys()): if name == 'magic': continue - arg = '{}{}'.format(self.magic_prefix, name) + arg = '{0}{1}'.format(self.magic_prefix, name) self.logger.debug(arg) if not isatty: