Skip to content

Commit

Permalink
update workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
whyliam committed Jul 3, 2017
1 parent 00aae03 commit e820b14
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 79 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# whyliam.workflows.youdao

## 有道翻译 workflow v2.0.0
## 有道翻译 workflow v2.0.1

默认快捷键 `yd`,查看翻译结果。

Expand All @@ -20,7 +20,7 @@

### 下载

[点击下载](https://github.com/liszd/whyliam.workflows.youdao/releases/download/2.0.0/whyliam.workflows.youdao.alfredworkflow)
[点击下载](https://github.com/liszd/whyliam.workflows.youdao/releases/download/2.0.1/whyliam.workflows.youdao.alfredworkflow)

### 安装

Expand Down
2 changes: 1 addition & 1 deletion info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@
</dict>
</array>
<key>readme</key>
<string>有道翻译 Workflow v2.0.0
<string>有道翻译 Workflow v2.0.1
默认快捷键 yd, 查看翻译结果。
Expand Down
2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0
2.0.1
3 changes: 2 additions & 1 deletion workflow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# Workflow objects
from .workflow import Workflow, manager
from .workflow3 import Workflow3
from .workflow3 import Variables, Workflow3

# Exceptions
from .workflow import PasswordNotFound, KeychainError
Expand Down Expand Up @@ -67,6 +67,7 @@
__copyright__ = 'Copyright 2014 Dean Jackson'

__all__ = [
'Variables',
'Workflow',
'Workflow3',
'manager',
Expand Down
38 changes: 18 additions & 20 deletions workflow/background.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,33 +108,31 @@ def _background(stdin='/dev/null', stdout='/dev/null',
:type stderr: filepath
"""
def _fork_and_exit_parent(errmsg):
try:
pid = os.fork()
if pid > 0:
os._exit(0)
except OSError as err:
wf().logger.critical('%s: (%d) %s', errmsg, err.errno,
err.strerror)
raise err

# Do first fork.
try:
pid = os.fork()
if pid > 0:
sys.exit(0) # Exit first parent.
except OSError as e:
wf().logger.critical("fork #1 failed: ({0:d}) {1}".format(
e.errno, e.strerror))
sys.exit(1)
_fork_and_exit_parent('fork #1 failed')

# Decouple from parent environment.
os.chdir(wf().workflowdir)
os.umask(0)
os.setsid()

# Do second fork.
try:
pid = os.fork()
if pid > 0:
sys.exit(0) # Exit second parent.
except OSError as e:
wf().logger.critical("fork #2 failed: ({0:d}) {1}".format(
e.errno, e.strerror))
sys.exit(1)
_fork_and_exit_parent('fork #2 failed')

# Now I am a daemon!
# Redirect standard file descriptors.
si = file(stdin, 'r', 0)
so = file(stdout, 'a+', 0)
se = file(stderr, 'a+', 0)
si = open(stdin, 'r', 0)
so = open(stdout, 'a+', 0)
se = open(stderr, 'a+', 0)
if hasattr(sys.stdin, 'fileno'):
os.dup2(si.fileno(), sys.stdin.fileno())
if hasattr(sys.stdout, 'fileno'):
Expand Down
4 changes: 2 additions & 2 deletions workflow/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ def download_workflow(url):
"""
filename = url.split("/")[-1]

if (not url.endswith('.alfredworkflow') or
not filename.endswith('.alfredworkflow')):
if (not filename.endswith('.alfredworkflow') and
not filename.endswith('.alfred3workflow')):
raise ValueError('Attachment `{0}` not a workflow'.format(filename))

local_path = os.path.join(tempfile.gettempdir(), filename)
Expand Down
2 changes: 1 addition & 1 deletion workflow/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.24
1.26
84 changes: 62 additions & 22 deletions workflow/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

from __future__ import print_function, unicode_literals

import atexit
import binascii
from contextlib import contextmanager
import cPickle
Expand Down Expand Up @@ -804,6 +805,7 @@ def __init__(self, protected_path, timeout=0, delay=0.05):
self.timeout = timeout
self.delay = delay
self._locked = False
atexit.register(self.release)

@property
def locked(self):
Expand All @@ -817,11 +819,14 @@ def acquire(self, blocking=True):
``False``.
Otherwise, check every `self.delay` seconds until it acquires
lock or exceeds `self.timeout` and raises an exception.
lock or exceeds `self.timeout` and raises an `~AcquisitionError`.
"""
start = time.time()
while True:

self._validate_lockfile()

try:
fd = os.open(self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR)
with os.fdopen(fd, 'w') as fd:
Expand All @@ -830,6 +835,7 @@ def acquire(self, blocking=True):
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:
Expand All @@ -839,10 +845,36 @@ def acquire(self, blocking=True):
self._locked = True
return True

def _validate_lockfile(self):
"""Check existence and validity of lockfile.
If the lockfile exists, but contains an invalid PID
or the PID of a non-existant process, it is removed.
"""
try:
with open(self.lockfile) as fp:
s = fp.read()
except Exception:
return

try:
pid = int(s)
except ValueError:
return self.release()

from background import _process_exists
if not _process_exists(pid):
self.release()

def release(self):
"""Release the lock by deleting `self.lockfile`."""
self._locked = False
os.unlink(self.lockfile)
try:
os.unlink(self.lockfile)
except (OSError, IOError) as err: # pragma: no cover
if err.errno != 2:
raise err

def __enter__(self):
"""Acquire lock."""
Expand Down Expand Up @@ -1942,26 +1974,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.
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
1. :const:`MATCH_STARTSWITH`
Item search key starts with ``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).
9. :const:`MATCH_ALL` : Combination of all the above.
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`
``query`` is a substring of item search key (case-insensitive).
8. :const:`MATCH_ALLCHARS`
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
Expand Down Expand Up @@ -2400,7 +2436,11 @@ def update_available(self):
:returns: ``True`` if an update is available, else ``False``
"""
update_data = self.cached_data('__workflow_update_status', max_age=0)
# Create a new workflow object to ensure standard serialiser
# is used (update.py is called without the user's settings)
update_data = Workflow().cached_data('__workflow_update_status',
max_age=0)

self.logger.debug('update_data : {0}'.format(update_data))

if not update_data or not update_data.get('available'):
Expand Down
Loading

0 comments on commit e820b14

Please sign in to comment.