Jump to content

Recommended Posts

IMPORTANT NOTE ABOUT HANGING PROCESSES ON SIERRA (2017-04-03)

 

Versions of Alfred-Workflow older than 1.25 cannot launch background processes properly on Sierra. In fact, they regularly hang quite dramatically and consume 100% CPU :( 

 

Users (in particular) affected by this bug should install and run this workflow, which can find and fix any workflows with broken versions of Alfred-Workflow.

 


 

The workflow library for Python

 

GitHub | Documentation


A feature-rich library for developing Alfred workflows in Python

The library is simple to install, has no external dependencies, is very well-documented and maintained, and boasts an eye-wateringly high feature-to-size ratio at under 400 KB. It is the only Python library that is always up-to-date with Alfred's features.


Main features

  • Supports all Alfred features from 2.0 to 3.6.
  • Catches, logs and notifies users (and developers) of errors in Workflows. No more confusing, silent failure.
  • Super-simple, yet powerful data caching (e.g. from a web service) and storage, including session-scoped data.
  • Easy-to-use Workflow settings API.
  • Supports Alfred's AppleScript API, including saving settings to info.plist and calling External Triggers.
  • Keychain access for secure storage (and cross-machine syncing) of sensitive data, like passwords and API keys.
  • Tunable and understandable Alfred-like fuzzy search (e.g. got matches Game of Thrones as well as Baby Got Back. Or not: that's up to you.)
  • Extremely lightweight, but full-featured, HTTP library with Requests-like interface, but just 12 KB instead of > 2 MB.
  • Convenient access to standard macOS icons, for high-quality, familiar icons without adding size to the library. Also available via proper English.
  • Pre-configured, built-in logging to enable simpler Workflow debugging.
  • Painlessly run (update) scripts in the background without blocking your workflow, so you can still show "old" results while fetching new ones.
  • API for running AppleScript/JXA scripts.
  • Simple support for 3rd-party libraries your Workflow relies on.
  • "Magic" arguments to make developing/debugging Workflows so much easier, especially when helping less technically-inclined users. With "magic" arguments, you and your Workflow's users can open the Workflow's log file in Console.app, its cache and data directories in Finder, and its root directory in Finder or Terminal from the comfort of Alfred's query box. You can also delete the cache/data/settings if something is corrupted.
  • Your workflow can update itself via GitHub releases.
  • Smart handling of non-ASCII. Query sale will match result salé, but query salé will not match result sale.
  • Functions to support migrating settings/data from older versions of your workflow.

Alfred 3-only features

  • Workflow variables
  • Advanced modifiers
  • Alfred 3-only updates
  • Re-run Script Filters

 

And as you can see from the above links, there is extensive documentation, including a two-part tutorial on building a Workflow from scratch.

Examples

Here are a few examples of how you can do some pretty cools stuff in just a few lines of code.

Remember, each of these Workflows also has—for free—full error-catching and -logging support, and the ability to open its log file (which contains all errors) via Alfred's query box. No need to ask users to grub around in ~/Library or flounder in Terminal here. This is not the Workflow library 2014 deserves, but it's the one it needs  ;) 

Feedback

If you have any bug reports/feature requests, add them either here or on GitHub.

More info

The documentation is the definitive source of information on the Alfred-Workflow library. The User Guide and Tutorial provide fairly extensive information both on how to use Alfred-Workflow and write Workflows in general (if you're new to this lark).

 

Edited by deanishe
Add new features
Link to comment
As Dean said, I've moved my ZotQuery workflow to this library. For the many workflow authors out there who have used the wonderful, but unfortunately abandonware alp Python library, you will want to move over. Not only is this an actively developed, but Dean isn't joking about the debugging/error handling functionality. It is extremely useful. As someone who's dealt with a good bit of user debugging, these features are exactly what workflow authors need to take care of issue quickly, even with user's who aren't knowledgable about the funky code stuff. 

 

I hope at some point soon(ish) to write something short comparing Alfred-Workflow to alp and describing some of the specific issues I encountered when moving from one to the other. But, it was pretty simple (and also offered me an opportunity to clean some code up). All that to say, I would recommend Python workflow authors to seriously check this out. I feel confident that at some point it will make your life easier. And isn't that what Alfred is all about :)

Edited by smarg19
Link to comment

Thanks for the endorsement :)

BTW, I've added a new "Open Workflow directory in Terminal" magic arg (workflow:openterm).

What do you think about automatically adding a lib or packages subdirectory to sys.path if one exists? I'm not sure if that's perhaps a bit too much "magic", but I don't think it'd break anything. Perhaps only if there's no __init__.py inside it.

Link to comment

Depends. Me personally, I'm a bit anal about only importing what I need when I need it. But if it were "invisible", then it wouldn't really affect that part of my brain. I do like the idea tho, insofar as it would help workflows move towards standardization, since that folder would need to named something fixed, for Alfred-Workflows to find it. 

Link to comment

To clarify: I'm not talking about importing anything. I'm just considering automatically adding certain subdirectories to sys.path, so you can import from them if and when needed.

The library is quite careful about paths, so it should still do its thing correctly if the workflow root isn't the working directory. To get that behaviour in your scripts while using the Workflow(libraries=...) argument, you need to do os.path.join(os.path.dirname(__file__), 'mysubdirectory') which is a bit more complex that I was aiming for.

Link to comment
  • 1 month later...

A few new features:

  • Supports Alfred 2.3 modifier-specific subtitles
  • Multi-word queries
  • Running background scripts, so you can still show (cached) results while your workflow fetches new data.
  • Basic accent-folding to search non-ASCII text with ASCII queries (e.g. munchen will match München)
Edited by deanishe
Link to comment
  • 2 months later...

Hey deanische!

Your workflow sounds awesome! I've been using an equivalent workflow fetching results from Wolfram Alpha live in Alfred, but it's pretty slow...

I tried your workflow, but I cannot get past "conv {input}". As soon as I type an input, I get an alert requiring me to install XCode... Any idea of what's going on?

Thanks for your help!

Link to comment
  • 3 weeks later...

Hi! I've got a problem with another workflow, based on this helper.

Can you help me?

 

I've moved (for test) workflow from /Users/pavel/Dropbox/Приложения/ to /Users/pavel/Dropbox

and the workflow started working.

 

There is the error log:

Starting debug for 'Homebrew for Alfred'

[ERROR: alfred.workflow.input.scriptfilter] Code 1: Traceback (most recent call last):

  File "brew.py", line 148, in <module>

    wf = Workflow()

  File "/Users/pavel/Dropbox/Приложения/Alfred/Alfred.alfredpreferences/workflows/user.workflow.08964E06-AFFB-45F2-AA38-CFEC96D9534C/workflow/workflow.py", line 825, in __init__

    self._info_plist = self.workflowfile('info.plist')

  File "/Users/pavel/Dropbox/Приложения/Alfred/Alfred.alfredpreferences/workflows/user.workflow.08964E06-AFFB-45F2-AA38-CFEC96D9534C/workflow/workflow.py", line 1125, in workflowfile

    return os.path.join(self.workflowdir, filename)

  File "/Users/pavel/Dropbox/Приложения/Alfred/Alfred.alfredpreferences/workflows/user.workflow.08964E06-AFFB-45F2-AA38-CFEC96D9534C/workflow/workflow.py", line 1081, in workflowdir

    if os.path.exists(os.path.join(dirpath, 'info.plist')):

  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.py", line 80, in join

    path += '/' + b

UnicodeDecodeError: 'ascii' codec can't decode byte 0xd0 in position 21: ordinal not in range(128)

Edited by facetheheat
Link to comment

Important Message

 

There is a pretty serious bug in all versions of the library previous to 1.8.6 (the current version). If a user has non-ASCII characters anywhere in the path to where his/her workflows are installed, the library will fail.

 

If you've released a workflow based on Alfred-Workflow, please update it to use at least version 1.8.6 of the library.

 

My sincere apologies to the users and developers this has affected.

Edited by deanishe
Link to comment

This library really helped me re-jump into trying to learn programming . I've written three workflows now based around using this, each one building on the other and it's been super helpful.

 

Much appreciated :)

 

They're some pretty cool workflows you've written there.

 

May I suggest you consider including the library (and info.plist, icon.png etc.) in your repos. That way, if someone downloads or forks the repo, they have a working workflow.

Edited by deanishe
Link to comment

Much appreciated :)

 

They're some pretty cool workflows you've written there.

 

May I suggest you consider including the library (and info.plist, icon.png etc.) in your repos. That way, if someone downloads or forks the repo, they have a working workflow.

 

I'm planning to redo my github repo for my Alfred workflows, so I'll note to make sure these are included.

Link to comment

Important Message

 

There is a pretty serious bug in all versions of the library previous to 1.8.6 (the current version). If a user has non-ASCII characters anywhere in the path to where his/her workflows are installed, the library will fail.

 

If you've released a workflow based on Alfred-Workflow, please update it to use at least version 1.8.6 of the library.

 

My sincere apologies to the users and developers this has affected.

 

Thanks for the heads up. I'll update as soon as poss.

Link to comment
  • 4 weeks later...

Hi!

 

Thanks for all your work getting this together.

 

I'm rather new to the workflow scene. Little experience in python.. enough to be dangerous but lots to learn in terms of making more application based stuff.

 

I'm getting this error when trying to use the workflow library in my script:

_init__.py", line 18, in <module>
    __version__ = open(os.path.join(os.path.dirname(__file__), 'version')).read()
IOError: [Errno 2] No such file or directory: '/Users/carsonjones

Any idea on how to fix this?

 

Thanks!

Edited by carson
Link to comment
  • 3 weeks later...

I've used this library a bunch but this is the first time I've tried to cache data. I've looked through the documentation but I'm hitting a point where I can't troubleshoot further.

     boards = wf.cached_data('trello_boards', getBoards(api_key), max_age = 60)

     for board in boards:
        wf.add_item(title = board['name'])

When I run the workflow I get: TypeError: 'list' object is not callable

 

I doublechecked my function and it works so I'm at a bit of a loss.

Link to comment

The idea of cached_data() is to avoid generating/fetching data if the cache is up-to-date. So, instead of passing it the data it should cache, you pass it a function it can call to get fresh data if the cached data is too old/non-existent.
 
You're passing the results of a call to getBoards(). You need to pass the function itself, which cached_data() will then call if necessary.
 
Normally, that looks something like this:
 

def fetch_new_data():
    # grab data from a web service/app here
    # ...
    return new_data

data = wf.cached_data('cache-name', fetch_new_data, max_age=600)

 
 Note that I'm passing fetch_new_data not fetch_new_data() to cached_data(), i.e. the function itself, not the data returned from calling the function.
 
You can't just pass the function in this case, however, as getBoards() takes an argument.
 
Check out the example script here. Look at the wrapper function and the call to cached_data() (lines 82–89). Note that I pass wrapper to cached_data without calling it.
 
You could also pass lambda api_key: getBoards(api_key) or functools.partial(getBoards, api_key). I wrote a separate wrapper() function in the tutorial to make it clearer and more explicit (lambda is ugly).
 
Does that help?

Edited by deanishe
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...