Skip to content

Commit d783fd1

Browse files
committed
Update to Alfred-Workflow 1.29
1 parent 25ed139 commit d783fd1

File tree

6 files changed

+129
-86
lines changed

6 files changed

+129
-86
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Session.vim
6060
# Packages
6161
*.egg
6262
*.egg-info/
63+
*.dist-info/
6364
dist/
6465
build/
6566
eggs/
Binary file not shown.

src/icon.png

-2.25 KB
Loading

src/info.plist

+46-46
Original file line numberDiff line numberDiff line change
@@ -48,44 +48,23 @@
4848
<dict>
4949
<key>config</key>
5050
<dict>
51-
<key>concurrently</key>
51+
<key>lastpathcomponent</key>
5252
<false/>
53-
<key>escaping</key>
54-
<integer>102</integer>
55-
<key>script</key>
56-
<string>mode=$1
57-
datadir="$alfred_workflow_data"
58-
cachedir="$alfred_workflow_cache"
59-
blacklist="${datadir}/blacklist.txt"
60-
logfile="${cachedir}/net.deanishe.alfred.fixum.log"
61-
62-
# create data &amp; cache directories, logfile and blacklist
63-
test -d "$cachedir" || mkdir -p "$cachedir"
64-
test -f "$logfile" || touch "$logfile"
65-
66-
test -d "$datadir" || mkdir -p "$datadir"
67-
test -f "$blacklist" || cp blacklist.default.txt "$blacklist"
68-
69-
# script actions
70-
[[ "$mode" = dryrun ]] &amp;&amp; /usr/bin/python fixum.py --nothing
71-
[[ "$mode" = fix ]] &amp;&amp; /usr/bin/python fixum.py
72-
[[ "$mode" = blacklist ]] &amp;&amp; open "$blacklist"
73-
[[ "$mode" = log ]] &amp;&amp; open -a Console "$logfile"
74-
75-
exit 0</string>
76-
<key>scriptargtype</key>
77-
<integer>1</integer>
78-
<key>scriptfile</key>
79-
<string></string>
80-
<key>type</key>
81-
<integer>5</integer>
53+
<key>onlyshowifquerypopulated</key>
54+
<true/>
55+
<key>removeextension</key>
56+
<false/>
57+
<key>text</key>
58+
<string>{query}</string>
59+
<key>title</key>
60+
<string>Fixum</string>
8261
</dict>
8362
<key>type</key>
84-
<string>alfred.workflow.action.script</string>
63+
<string>alfred.workflow.output.notification</string>
8564
<key>uid</key>
86-
<string>97033D94-9B6F-446C-94E5-AB677B5ABB4F</string>
65+
<string>90302262-60E4-4C1C-AAEA-2A5C3F4C025A</string>
8766
<key>version</key>
88-
<integer>2</integer>
67+
<integer>1</integer>
8968
</dict>
9069
<dict>
9170
<key>config</key>
@@ -137,23 +116,44 @@ exit 0</string>
137116
<dict>
138117
<key>config</key>
139118
<dict>
140-
<key>lastpathcomponent</key>
141-
<false/>
142-
<key>onlyshowifquerypopulated</key>
143-
<true/>
144-
<key>removeextension</key>
119+
<key>concurrently</key>
145120
<false/>
146-
<key>text</key>
147-
<string>{query}</string>
148-
<key>title</key>
149-
<string>Fixum</string>
121+
<key>escaping</key>
122+
<integer>102</integer>
123+
<key>script</key>
124+
<string>mode=$1
125+
datadir="$alfred_workflow_data"
126+
cachedir="$alfred_workflow_cache"
127+
blacklist="${datadir}/blacklist.txt"
128+
logfile="${cachedir}/net.deanishe.alfred.fixum.log"
129+
130+
# create data &amp; cache directories, logfile and blacklist
131+
test -d "$cachedir" || mkdir -p "$cachedir"
132+
test -f "$logfile" || touch "$logfile"
133+
134+
test -d "$datadir" || mkdir -p "$datadir"
135+
test -f "$blacklist" || cp blacklist.default.txt "$blacklist"
136+
137+
# script actions
138+
[[ "$mode" = dryrun ]] &amp;&amp; /usr/bin/python fixum.py --nothing
139+
[[ "$mode" = fix ]] &amp;&amp; /usr/bin/python fixum.py
140+
[[ "$mode" = blacklist ]] &amp;&amp; open "$blacklist"
141+
[[ "$mode" = log ]] &amp;&amp; open -a Console "$logfile"
142+
143+
exit 0</string>
144+
<key>scriptargtype</key>
145+
<integer>1</integer>
146+
<key>scriptfile</key>
147+
<string></string>
148+
<key>type</key>
149+
<integer>5</integer>
150150
</dict>
151151
<key>type</key>
152-
<string>alfred.workflow.output.notification</string>
152+
<string>alfred.workflow.action.script</string>
153153
<key>uid</key>
154-
<string>90302262-60E4-4C1C-AAEA-2A5C3F4C025A</string>
154+
<string>97033D94-9B6F-446C-94E5-AB677B5ABB4F</string>
155155
<key>version</key>
156-
<integer>1</integer>
156+
<integer>2</integer>
157157
</dict>
158158
</array>
159159
<key>readme</key>
@@ -185,7 +185,7 @@ It is primarily a workaround to fix bugs that are preventing the workflows from
185185
</dict>
186186
</dict>
187187
<key>version</key>
188-
<string>0.6</string>
188+
<string>0.7</string>
189189
<key>webaddress</key>
190190
<string></string>
191191
</dict>

src/workflow/background.py

+81-39
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from __future__ import print_function, unicode_literals
2121

22+
import signal
2223
import sys
2324
import os
2425
import subprocess
@@ -82,6 +83,31 @@ def _process_exists(pid):
8283
return True
8384

8485

86+
def _job_pid(name):
87+
"""Get PID of job or `None` if job does not exist.
88+
89+
Args:
90+
name (str): Name of job.
91+
92+
Returns:
93+
int: PID of job process (or `None` if job doesn't exist).
94+
"""
95+
pidfile = _pid_file(name)
96+
if not os.path.exists(pidfile):
97+
return
98+
99+
with open(pidfile, 'rb') as fp:
100+
pid = int(fp.read())
101+
102+
if _process_exists(pid):
103+
return pid
104+
105+
try:
106+
os.unlink(pidfile)
107+
except Exception: # pragma: no cover
108+
pass
109+
110+
85111
def is_running(name):
86112
"""Test whether task ``name`` is currently running.
87113
@@ -91,26 +117,18 @@ def is_running(name):
91117
:rtype: bool
92118
93119
"""
94-
pidfile = _pid_file(name)
95-
if not os.path.exists(pidfile):
96-
return False
97-
98-
with open(pidfile, 'rb') as file_obj:
99-
pid = int(file_obj.read().strip())
100-
101-
if _process_exists(pid):
120+
if _job_pid(name) is not None:
102121
return True
103122

104-
elif os.path.exists(pidfile):
105-
os.unlink(pidfile)
106-
107123
return False
108124

109125

110-
def _background(stdin='/dev/null', stdout='/dev/null',
126+
def _background(pidfile, stdin='/dev/null', stdout='/dev/null',
111127
stderr='/dev/null'): # pragma: no cover
112128
"""Fork the current process into a background daemon.
113129
130+
:param pidfile: file to write PID of daemon process to.
131+
:type pidfile: filepath
114132
:param stdin: where to read input
115133
:type stdin: filepath
116134
:param stdout: where to write stdout output
@@ -119,24 +137,31 @@ def _background(stdin='/dev/null', stdout='/dev/null',
119137
:type stderr: filepath
120138
121139
"""
122-
def _fork_and_exit_parent(errmsg):
140+
def _fork_and_exit_parent(errmsg, wait=False, write=False):
123141
try:
124142
pid = os.fork()
125143
if pid > 0:
144+
if write: # write PID of child process to `pidfile`
145+
tmp = pidfile + '.tmp'
146+
with open(tmp, 'wb') as fp:
147+
fp.write(str(pid))
148+
os.rename(tmp, pidfile)
149+
if wait: # wait for child process to exit
150+
os.waitpid(pid, 0)
126151
os._exit(0)
127152
except OSError as err:
128153
_log().critical('%s: (%d) %s', errmsg, err.errno, err.strerror)
129154
raise err
130155

131-
# Do first fork.
132-
_fork_and_exit_parent('fork #1 failed')
156+
# Do first fork and wait for second fork to finish.
157+
_fork_and_exit_parent('fork #1 failed', wait=True)
133158

134159
# Decouple from parent environment.
135160
os.chdir(wf().workflowdir)
136161
os.setsid()
137162

138-
# Do second fork.
139-
_fork_and_exit_parent('fork #2 failed')
163+
# Do second fork and write PID to pidfile.
164+
_fork_and_exit_parent('fork #2 failed', write=True)
140165

141166
# Now I am a daemon!
142167
# Redirect standard file descriptors.
@@ -151,10 +176,30 @@ def _fork_and_exit_parent(errmsg):
151176
os.dup2(se.fileno(), sys.stderr.fileno())
152177

153178

179+
def kill(name, sig=signal.SIGTERM):
180+
"""Send a signal to job ``name`` via :func:`os.kill`.
181+
182+
.. versionadded:: 1.29
183+
184+
Args:
185+
name (str): Name of the job
186+
sig (int, optional): Signal to send (default: SIGTERM)
187+
188+
Returns:
189+
bool: `False` if job isn't running, `True` if signal was sent.
190+
"""
191+
pid = _job_pid(name)
192+
if pid is None:
193+
return False
194+
195+
os.kill(pid, sig)
196+
return True
197+
198+
154199
def run_in_background(name, args, **kwargs):
155200
r"""Cache arguments then call this script again via :func:`subprocess.call`.
156201
157-
:param name: name of task
202+
:param name: name of job
158203
:type name: unicode
159204
:param args: arguments passed as first argument to :func:`subprocess.call`
160205
:param \**kwargs: keyword arguments to :func:`subprocess.call`
@@ -183,18 +228,20 @@ def run_in_background(name, args, **kwargs):
183228
argcache = _arg_cache(name)
184229

185230
# Cache arguments
186-
with open(argcache, 'wb') as file_obj:
187-
pickle.dump({'args': args, 'kwargs': kwargs}, file_obj)
231+
with open(argcache, 'wb') as fp:
232+
pickle.dump({'args': args, 'kwargs': kwargs}, fp)
188233
_log().debug('[%s] command cached: %s', name, argcache)
189234

190235
# Call this script
191236
cmd = ['/usr/bin/python', __file__, name]
192237
_log().debug('[%s] passing job to background runner: %r', name, cmd)
193238
retcode = subprocess.call(cmd)
239+
194240
if retcode: # pragma: no cover
195-
_log().error('[%s] background runner failed with %d', retcode)
241+
_log().error('[%s] background runner failed with %d', name, retcode)
196242
else:
197243
_log().debug('[%s] background job started', name)
244+
198245
return retcode
199246

200247

@@ -209,12 +256,17 @@ def main(wf): # pragma: no cover
209256
name = wf.args[0]
210257
argcache = _arg_cache(name)
211258
if not os.path.exists(argcache):
212-
log.critical('[%s] command cache not found: %r', name, argcache)
213-
return 1
259+
msg = '[{0}] command cache not found: {1}'.format(name, argcache)
260+
log.critical(msg)
261+
raise IOError(msg)
262+
263+
# Fork to background and run command
264+
pidfile = _pid_file(name)
265+
_background(pidfile)
214266

215267
# Load cached arguments
216-
with open(argcache, 'rb') as file_obj:
217-
data = pickle.load(file_obj)
268+
with open(argcache, 'rb') as fp:
269+
data = pickle.load(fp)
218270

219271
# Cached arguments
220272
args = data['args']
@@ -223,28 +275,18 @@ def main(wf): # pragma: no cover
223275
# Delete argument cache file
224276
os.unlink(argcache)
225277

226-
pidfile = _pid_file(name)
227-
228-
# Fork to background
229-
_background()
230-
231-
# Write PID to file
232-
with open(pidfile, 'wb') as file_obj:
233-
file_obj.write(str(os.getpid()))
234-
235-
# Run the command
236278
try:
279+
# Run the command
237280
log.debug('[%s] running command: %r', name, args)
238281

239282
retcode = subprocess.call(args, **kwargs)
240283

241284
if retcode:
242285
log.error('[%s] command failed with status %d', name, retcode)
243-
244286
finally:
245-
if os.path.exists(pidfile):
246-
os.unlink(pidfile)
247-
log.debug('[%s] job complete', name)
287+
os.unlink(pidfile)
288+
289+
log.debug('[%s] job complete', name)
248290

249291

250292
if __name__ == '__main__': # pragma: no cover

src/workflow/version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.28.1
1+
1.29

0 commit comments

Comments
 (0)