Skip to content

Commit

Permalink
Add help_url; Allow user to turn off automatic updates
Browse files Browse the repository at this point in the history
Developer can specify a `help_url` when instantiating `Workflow`.

If the workflow throws an error, this URL will be displayed in the log/Alfred debugger.

It can also be opened directly by the user with `workflow:help`

-----------------------------------

Users can toggle automatic update checks with `workflow:noautoupdate` and workflow:autoupdate`
  • Loading branch information
deanishe committed Dec 20, 2014
1 parent a636e6f commit 7de8094
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 10 deletions.
13 changes: 8 additions & 5 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ Features to Add:


update.py:
- Allow users to turn off automatic update checks @done(14-12-20 11:20)
- Generalise update functionality @v2
Use subclass-based plugin feature to automatically register updaters?


web.py:


workflow.py:
- Add `first_run()` to determine if the workflow has been run on this computer before. @priority(1)
Save a "flag" in the data directory upon first run.
Should the file only be created the first time `first_run()` is called instead?
How about being able to pass a version argument to `first_run()`?
- `filter()` should warn when comparing Unicode and `str` objects @priority(3)
- move "magic" args to class-level dictionary, so authors can add their own. @priority(2) @v2
- `filter()` should replace "smart" punctuation with dumb equivalents @priority(1)
Expand Down Expand Up @@ -64,10 +68,6 @@ Features to Consider:


workflow.py:
- Add `first_run()` to determine if the workflow has been run on this computer before.
Save a "flag" in the data directory upon first run.
Should the file only be created the first time `first_run()` is called instead?
How about being able to pass a version argument to `first_run()`?
- automatically add `lib` and/or `packages` subdirectory to `sys.path`
- Add `TechnicolorLogger` formatter

Expand All @@ -79,6 +79,9 @@ General:

___________________
Archive:
- Add `save_to_path()` method to save HTTP data directly to disk @done(14-12-20 10:34) @project(Features to Add / web.py)
- Handle gzip-encoded HTTP data @done(14-12-20 10:34) @project(Features to Add / web.py)
- Add `help_url`. Will be shown in log when an error occurs and can be opened via `workflow:help` magic arg. @done(14-12-20 10:39) @project(Features to Add / workflow.py)
- Integrate Fabio's update feature @done(14-09-14 19:50) @project(Features to Add / update.py)
- Allow omission of `v` in version specified in `update_settings` @done(14-09-14 22:14) @project(Features to Add / update.py)
- Perform download & installation in the background, so Workflow can inform user that an update is being performed @priority(1) @done(14-09-25 11:57) @project(Features to Add / update.py)
Expand Down
4 changes: 4 additions & 0 deletions docs/user-manual/magic-args.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ for debugging.

:class:`~workflow.workflow.Workflow` supports the following magic arguments:

- ``workflow:help`` — Open workflow's help URL in default web browser. This URL is specified in the ``help_url`` argument to :class:`~workflow.workflow.Workflow`.
- ``workflow:delcache`` — Delete the Workflow's cache.
- ``workflow:deldata`` — Delete the Workflow's saved data.
- ``workflow:delsettings`` — Delete the Workflow's settings file (which contains the data stored using :attr:`Workflow.settings <workflow.workflow.Workflow.settings>`).
Expand All @@ -44,6 +45,8 @@ for debugging.
- ``workflow:openworkflow`` — Open the Workflow's root directory (where ``info.plist`` is).
- ``workflow:reset`` — Delete the Workflow's settings, cache and saved data.
- ``workflow:update`` — Check for a newer version of the workflow using GitHub releases and install the newer version if one is available.
- ``workflow:noautoupdate`` — Turn off automatic checks for updates.
- ``workflow:autoupdate`` — Turn automatic checks for updates on.

The three ``workflow:folding…`` settings allow users to override the diacritic
folding set by a workflow's author. This may be useful if the author's choice
Expand All @@ -54,6 +57,7 @@ You can turn off magic arguments by passing ``capture_args=False`` to
methods of :class:`~workflow.workflow.Workflow` directly, perhaps assigning your
own keywords within your Workflow:

- :meth:`~workflow.workflow.Workflow.open_help`
- :meth:`~workflow.workflow.Workflow.open_log`
- :meth:`~workflow.workflow.Workflow.open_cachedir`
- :meth:`~workflow.workflow.Workflow.open_datadir`
Expand Down
2 changes: 2 additions & 0 deletions docs/user-manual/update.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ It caches information on the latest available release under the cache key
``__workflow_update_status``, which you can access via
:meth:`Workflow.cached_data() <workflow.workflow.Workflow.cached_data>`.

Users can turn off automatic checks for updates with the ``workflow:noautoupdate``
:ref:`magic argument <magic-arguments>` and back on again with ``workflow:autoupdate``.


.. _version-numbers:
Expand Down
30 changes: 26 additions & 4 deletions tests/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ def test_equal_versions(self):
('v1.1.1', 'v1.1.1'),
('dave', 'dave'),
('bob', 'bob'),
('vernon', 'vernon'),
]:
('vernon', 'vernon')]:
self.assertFalse(update.is_newer_version(l, r))

# Unequal versions
Expand All @@ -137,8 +136,7 @@ def test_equal_versions(self):
('v1.1.1', 'v1.2.1'),
('dave', 'bob'),
('bob', 'dave'),
('vernon', 'vvernon'),
]:
('vernon', 'vvernon')]:
self.assertTrue(update.is_newer_version(l, r))

def test_check_update(self):
Expand Down Expand Up @@ -177,6 +175,30 @@ def test_install_update(self):
self.assertFalse(wf.cached_data(
'__workflow_update_status')['available'])

def test_no_auto_update(self):
"""Update: no update check"""

# Make sure there's no cached update data
wf = Workflow()
wf.reset()

self.assertIsNone(self.wf.cached_data('__workflow_update_status'))

wf = Workflow()
c = WorkflowMock(['script', 'workflow:noautoupdate'])
with c:
wf.args
self.assertFalse(wf.settings.get('__workflow_autoupdate'))

self.assertIsNone(self.wf.cached_data('__workflow_update_status'))

c = WorkflowMock()
with c:
wf = Workflow(update_settings={'github_slug': TEST_REPO_SLUG,
'version': RELEASE_CURRENT})

self.assertIsNone(self.wf.cached_data('__workflow_update_status'))


if __name__ == '__main__': # pragma: no cover
unittest.main()
37 changes: 37 additions & 0 deletions tests/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ def cb(wf):
self.assertEqual(wf, self.wf)
raise ValueError('Have an error')
self.wf.name # cause info.plist to be parsed
self.wf.help_url = 'http://www.deanishe.net/alfred-workflow/'
ret = self.wf.run(cb)
self.assertEqual(ret, 1)
# named after bundleid
Expand Down Expand Up @@ -1040,6 +1041,26 @@ def setUp(self):
def tearDown(self):
pass

def test_openhelp(self):
"""Magic: open help URL"""
url = 'http://www.deanishe.net/alfred-workflow/'
c = WorkflowMock(['script', 'workflow:help'])
with c:
wf = Workflow(help_url=url)
# Process magic arguments
wf.args
self.assertEquals(c.cmd[0], 'open')
self.assertEquals(c.cmd[1], url)

def test_openhelp_no_url(self):
"""Magic: no help URL"""
c = WorkflowMock(['script', 'workflow:help'])
with c:
wf = Workflow()
# Process magic arguments
wf.args
self.assertEquals(len(c.cmd), 0)

def test_openlog(self):
"""Magic: open logfile"""
c = WorkflowMock(['script', 'workflow:openlog'])
Expand Down Expand Up @@ -1179,6 +1200,22 @@ def test_folding(self):
wf.args
self.assertFalse(wf.settings.get('__workflow_diacritic_folding'))

def test_auto_update(self):
"""Magic: auto-update"""
wf = Workflow()
c = WorkflowMock(['script', 'workflow:autoupdate'])
with c:
wf.args
self.assertTrue(wf.settings.get('__workflow_autoupdate'))

wf = Workflow()
c = WorkflowMock(['script', 'workflow:noautoupdate'])
with c:
wf.args
self.assertFalse(wf.settings.get('__workflow_autoupdate'))

del wf.settings['__workflow_autoupdate']

def test_update(self):
"""Magic: update"""
update_settings = {
Expand Down
34 changes: 33 additions & 1 deletion workflow/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,13 @@ class Workflow(object):
: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 <magic-arguments>`.
:type help_url: :class:`unicode` or :class:`str`
"""

Expand All @@ -864,13 +871,15 @@ class Workflow(object):

def __init__(self, default_settings=None, update_settings=None,
input_encoding='utf-8', normalization='NFC',
capture_args=True, libraries=None):
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
Expand Down Expand Up @@ -1042,6 +1051,12 @@ def args(self):
msg = None
args = [self.decode(arg) for arg in sys.argv[1:]]
if len(args) and self._capture_args:
if 'workflow:help' in args:
if self.help_url:
msg = 'Opening workflow help URL in browser'
self.open_help()
else:
msg = 'Workflow has no help URL'
if 'workflow:openlog' in args:
msg = 'Opening workflow log file'
self.open_log()
Expand Down Expand Up @@ -1079,6 +1094,12 @@ def args(self):
msg = 'Diacritics folding reset'
if '__workflow_diacritic_folding' in self.settings:
del self.settings['__workflow_diacritic_folding']
elif 'workflow:noautoupdate' in args:
msg = 'Auto update turned off'
self.settings['__workflow_autoupdate'] = False
elif 'workflow:autoupdate' in args:
msg = 'Auto update turned on'
self.settings['__workflow_autoupdate'] = True
elif 'workflow:update' in args:
if self.start_update():
msg = 'Downloading and installing update ...'
Expand Down Expand Up @@ -1890,6 +1911,9 @@ def run(self, func):
func(self)
except Exception as err:
self.logger.exception(err)
if self.help_url:
self.logger.info(
'For assistance, see: {}'.format(self.help_url))
if not sys.stdout.isatty(): # Show error in Alfred
self._items = []
if self._name:
Expand Down Expand Up @@ -2029,6 +2053,10 @@ def check_update(self, force=False):
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)):
Expand Down Expand Up @@ -2244,6 +2272,10 @@ def open_terminal(self):
subprocess.call(['open', '-a', 'Terminal',
self.workflowdir])

def open_help(self):
"""Open :attr:`help_url` in default browser"""
subprocess.call(['open', self.help_url])

####################################################################
# Helper methods
####################################################################
Expand Down

0 comments on commit 7de8094

Please sign in to comment.