Workflow variables¶
Alfred 3+ only
An extremely powerful new feature in Alfred 3 is its workflow variables.
Alfred exposes these to your workflow as environment variables, and you can set and manipulate these not only in Alfred’s own UI elements, but also via the output from a Run Script action or Script Filter results.
Any variables set via the JSON output of a Run Script or Script Filter only persist as long as the workflow is running. When the workflow is run again, the variables reset to the initial values set in the workflow configuration sheet.
Alfred 3.6 introduced a new API to update the values stored in the workflow configuration sheet, i.e. the values persist across workflow runs.
Important
You must use the correct mechanism for setting variables. Alfred requires different output formats from Script Filters and Run Script actions, and if you use the wrong one, it won’t work.
The two different mechanisms for setting workflow variables are:
The
Variables
class, which is for use in Run Script actions, andThe
Workflow
class provides an API for getting and setting workflow variables via Script Filter feedback.
Getting variables¶
Alfred-PyWorkflow does not automatically import any variables. All getters only consider variables you have set on the objects yourself, not those set by upstream workflow elements or the workflow configuration sheet.
The reason for this is that Alfred-PyWorkflow cannot distinguish between workflow variables and real environment variables.
For example, if you call os.getenv('HOME')
, you will get the user’s
home directory, but {var:HOME}
will not work in Alfred elements.
By restricting its scope to variables it has set itself, Alfred-PyWorkflow
can guarantee that if getvar('xyz')
works, {var:xyz}
will also
work in downstream Alfred elements.
Setting variables from Run Script actions¶
Variables
is a subclass of dict
that
serialises (prints) to an alfredworkflow
JSON object (or plain text if no
variables are set).
Set workflow variables using the standard dict
API or as keyword
arguments on instantiation.
Example usage¶
1from workflow import Variables
2
3# set arg on instantiation
4v = Variables(u'this is arg')
5
6# set workflow variables on instantiation
7v = Variables(var1=u'value 1', var2=u'value 2')
8
9# set arg via attribute
10v = Variables()
11v.arg = u'this is arg'
12
13# set workflow variables via dict API
14v = Variables()
15v['variable_name'] = u'variable value'
16
17# set config for downstream element
18v = Variables()
19v.config['key'] = u'value'
20
21# send to Alfred
22v = Variables(u'arg', var1='val1', var2='var2')
23print(v)
Setting variables in Script Filters¶
Variables can be set at the Workflow, Item or Modifier level using their
respective setvar(name, value)
methods. Variables set on the
Workflow
object are hereafter referred to as “top-level”
variables. setvar()
takes an additional, optional
persist
argument, which will also save the variable to info.plist
/
the workflow configuration sheet. As this requires calling Alfred via
AppleScript (which is slow), it’s generally a better option to save variables
further downstream in a non-interactive part of the workflow. See
Persisting variables for more information.
Note
Top-level variables are also passed back to your Script Filter when you’re using Alfred’s rerun feature. This is extremely handy for creating timers or progress bars.
Alfred-PyWorkflow imposes its own logic for setting variables that differs from the way Alfred handles them.
Item
and Modifier
objects inherit any variables
set on their parent object (Workflow
and Item
respectively) at the time of their creation via
Workflow.add_item()
and
Item.add_modifier()
.
This way, you can have some variables inherited and some not.
The validity and arg
of the modifier will be the same as the parent
item’s, so you only need to specify valid
or arg
if you wish to
override the parent item’s value.
Important
The way Alfred handles variables is somewhat arbitrary. Top-level variables are also passed downstream with any item-level variables, regardless of whether the item sets its own variables.
If a modifier sets any variables, however, Alfred ignores any top- and item-level variables completely.
The upshot for Alfred-PyWorkflow is that top-level variables will also be passed along with items created before the variables were set, but not with modifiers created before the variables were set.
Example usage¶
As Alfred passes workflow variables to scripts as environment variables,
combining var=1
style flags with a command-line library that can
map environment variables to command-line options (such as Click) is
a clean and powerful idiom.
Click allows you to set a prefix, e.g. WF_
, and it will then automatically
map matching environment variables to corresponding command-line options, e.g.
WF_USERNAME=deanishe
is equivalent to --username=deanishe
and
WF_DEBUG=1
is equivalent to --debug
.
Let’s say we’re using a client program for some imaginary social whatnot that works like this:
prog [--username=<name>] (profile|pages|friends) (--view|--edit|--share)
You could control this program from a Script Filter as follows. This assumes
you would connect the Script Filter to three Run Script Actions, one for
each of profile
, pages
and friends
, and with a Filter Utility
before each Run Script that checks for pages == 1
, profile == 1
etc.
The Run Script action behind the pages == 1
Filter Utility might then
read:
/usr/bin/env python3 myscript.py pages
The other options (--view
, --edit
, --share
) are set via the
corresponding environment variables (WF_VIEW
, WF_EDIT
and WF_SHARE
respectively).
The salient part of the Script Filter driving the workflow might look like this:
1from workflow import Workflow
2wf = Workflow()
3
4# Username will be needed in every case. Set at the workflow level
5# to ensure it is always passed to downstream workflow objects
6wf.setvar('WF_USERNAME', 'deanishe')
7
8# Some example actions. We've set username above as the main
9# identifier. We'll set flags on feedback items that subsequent workflow
10# Filter Utilities can use and WF_* variables to pass arguments
11# directly to the program
12
13# Profile
14it = wf.add_item('Profile', 'View profile', arg='profile', valid=True)
15# Inherited by all modifiers
16it.setvar('profile', '1')
17
18mod = it.add_modifier('cmd', 'Edit profile')
19# Set only on mod. Equivalent to option --edit
20mod.setvar('WF_EDIT', '1')
21
22mod = it.add_modifier('alt', 'Share profile')
23# Set only on mod. Equivalent to option --share
24mod.setvar('WF_SHARE', '1')
25
26# Set after modifier creation, so only set on item, and is thus the default
27# Equivalent to option --view
28it.setvar('WF_VIEW', '1')
29
30# Pages
31it = wf.add_item('Pages', 'View pages', arg='pages', valid=True)
32# Inherited by all modifiers
33it.setvar('pages', '1')
34
35mod = it.add_modifier('cmd', 'Edit pages')
36# Set only on mod. Equivalent to option --edit
37mod.setvar('WF_EDIT', '1')
38
39mod = it.add_modifier('alt', 'Share pages')
40# Set only on mod. Equivalent to option --share
41mod.setvar('WF_SHARE', '1')
42
43# Set after modifier creation, so only set on item, and is thus the default
44# Equivalent to option --view
45it.setvar('WF_VIEW', '1')
46
47# Repeat for Friends
48# ...
49# ...
Tip
While you could also replace the (view|edit|friends)
commands with
a --command (view|edit|friends)
option and drive the whole workflow
via environment/workflow variables, I’d advise against going too far in
that direction (e.g. having a single Script Filter connected to a single
Run Action containing an option-less command), as it could make your
workflow very hard to follow for people wanting to hack on it.
Persisting variables¶
As a convenience, the Workflow.setvar()
method takes an optional
persist
argument, which will also save the variable to
info.plist
/the workflow configuration sheet, but this method is
generally sub-optimal, as it immediately calls Alfred via AppleScript,
which is slow.
The better method for saving variables is the util.set_config()
function (and its util.unset_config()
counterpart).
Ideally, you should call these functions downstream of any interactive elements (e.g. Script Filters), as calling Alfred via AppleScript is slow.
1import os
2from workflow.util import set_config
3
4# Retrieve the current value from the environment
5current = int(os.getenv('SOME_COUNT') or '0')
6
7# Increment value and save back to Alfred
8set_config('SOME_COUNT', str(current + 1))
You can also save variables to other workflows by specifying its
bundle ID by passing bundleid="XYZ"
to util.set_config()
.
More information¶
Alfred’s own help has a few pages on workflow variables.
Also see this guide to getting, setting and saving workflow variables.