Skip to content

Commit

Permalink
Work from within non-ASCII directories
Browse files Browse the repository at this point in the history
  • Loading branch information
deanishe committed Aug 29, 2014
1 parent 9ca860e commit b1f4ae6
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 63 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,12 @@ Currently, there is Travis-CI integration, but also a `run-tests.sh` script in t

- [Dean Jackson](https://github.com/deanishe)
- [Stephen Margheim](https://github.com/smargh)
- [Fabio Niephaus](https://github.com/fniephaus)

## Tests ##

**Alfred-Workflow** includes a full suite of unit tests. Please use the `run-tests.sh` script in the root directory of the repo to run the unit tests: it creates the necessary test environment to run the unit tests. `test_workflow.py` *will* fail if not run via `run-scripts.sh`, but the test suites for the other modules may also be run directly.

Moreover, `run-tests.sh` checks the coverage of the unit tests and will fail if it is below 100%.

## Workflows using Alfred-Workflow ##

Expand Down
4 changes: 4 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
Docs:
- Improve description of how to use `Workflow(libraries=)`
Make paths to specify to `pip` clearer.
See https://github.com/deanishe/alfred-workflow/issues/28
Possible Features:
- `handlers.py` module to provide system-default apps for protocols like `http`, `mailto` etc.
- Enable `background.py` to be run from within a ZIP file
Expand Down
Binary file modified alfred-workflow.zip
Binary file not shown.
107 changes: 82 additions & 25 deletions doc/howto.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,40 @@ in the OS X system Python. **Alfred-Workflow** makes it easy to include them in
your Workflow.

Simply create a ``lib`` subdirectory under your Workflow's root directory
(or call it whatever you want), install your dependencies there with
and install your dependencies there. You can call the directory whatever you
want, but in the following explanation, I'll assume you used ``lib``.

To install libraries in your dependencies directory, use:

.. code-block:: bash
:linenos:
pip install --target=path/to/my/workflow/lib python-lib-name
The path you pass as the ``--target`` argument should be the path to
the directory under your Workflow's root directory in which you want to install
your libraries. ``python-lib-name`` should be the "pip name" (i.e. the name the
library has on `PyPI <https://pypi.python.org/pypi>`_) of the library you want
to install, e.g. ``requests`` or ``feedparser``.

This name is usually, but not always, the same as the name you use with ``import``.

For example, to install **Alfred-Workflow**, you would run
``pip install Alfred-Workflow`` but use ``import workflow`` to import it.

**An example:** You're in a shell in Terminal.app in the Workflow's root directory
and you're using ``lib`` as the directory for your Python libraries. You want to
install `requests <http://docs.python-requests.org/en/latest/>`_. You would run:

.. code-block:: bash
:linenos:
pip install --target=my-workflow-root-dir/lib python-lib-name
pip install --target=lib requests
and instantiate :class:`Workflow <workflow.workflow.Workflow>`
This will install the ``requests`` library into the ``lib`` subdirectory of the
current working directory.

Then you instantiate :class:`Workflow <workflow.workflow.Workflow>`
with the ``libraries`` argument:

.. code-block:: python
Expand All @@ -103,12 +129,34 @@ with the ``libraries`` argument:
from workflow import Workflow
def main(wf):
import module_from_lib_subdirectory_here
import requests # Imported from ./lib
if __name__ == '__main__':
wf = Workflow(libraries=['./lib'])
sys.exit(wf.run(main))
When using this feature you **do not** need to create an ``__init__.py`` file in
the ``lib`` subdirectory. ``Workflow(…, libraries=['./lib'])`` and creating
``./lib/__init__.py`` are effectively equal alternatives.

Instead of using ``Workflow(…, libraries=['./lib'])``, you can add an empty
``__init__.py`` file to your ``lib`` subdirectory and import the libraries
installed therein using:

.. code-block:: python
:linenos:
from lib import requests
instead of simply:


.. code-block:: python
:linenos:
import requests
.. _persistent-data:

Expand Down Expand Up @@ -151,7 +199,7 @@ may help you with development/debugging.
In addition, :class:`Workflow <workflow.workflow.Workflow>` also provides a
convenient interface for storing persistent settings with
:attr:`Workflow.settings <workflow.workflow.Workflow.settings>`.
See :ref:`Settings <settings>` and :ref:`Keychain access <keychain>` for more
See :ref:`Settings <howto-settings>` and :ref:`Keychain access <keychain>` for more
information on storing settings and sensitive data.

.. _caching-data:
Expand Down Expand Up @@ -261,8 +309,8 @@ explicitly call :meth:`Workflow.settings.save() <workflow.workflow.Settings.save
If you need to store arbitrary data, you can use the :ref:`cached data API <caching-data>`.

If you need to store data securely (such as passwords and API keys),
:class:`Workflow <workflow.workflow.Workflow>` also provides simple access to
the OS X Keychain.
:class:`Workflow <workflow.workflow.Workflow>` also provides :ref:`simple access to
the OS X Keychain <keychain>`.


.. _keychain:
Expand All @@ -274,13 +322,12 @@ Methods :meth:`Workflow.save_password(account, password) <workflow.workflow.Work
:meth:`Workflow.get_password(account) <workflow.workflow.Workflow.get_password>`
and :meth:`Workflow.delete_password(account) <workflow.workflow.Workflow.delete_password>`
allow access to the Keychain. They may raise
:class:`PasswordNotFound <workflow.workflow.Workflow.PasswordNotFound>` if
no password is set for the given ``account`` or
:class:`KeychainError <workflow.workflow.Workflow.KeychainError>` if there is
a problem accessing the Keychain. Passwords are stored in the user's default
Keychain. By default, the Workflow's Bundle ID will be used as the service name,
but this can be overridden by setting the ``service`` argument to the above
methods.
:class:`~workflow.workflow.Workflow.PasswordNotFound` if no password is set for
the given ``account`` or :class:`~workflow.workflow.Workflow.KeychainError` if
there is a problem accessing the Keychain. Passwords are stored in the user's
default Keychain. By default, the Workflow's Bundle ID will be used as the
service name, but this can be overridden by passing the ``service`` argument
to the above methods.

Example usage:

Expand Down Expand Up @@ -563,12 +610,13 @@ which is a bit smarter about showing the user update information.
Serialization
=============

By default, both cache and data files are cached using :mod:`cPickle`. This
provides a great compromise in terms of speed and ability to store arbitrary
objects.
By default, both cache and data files (created using the
:ref:`APIs described above <caching-data>`) are cached using :mod:`cPickle`.
This provides a great compromise in terms of speed and the ability to store
arbitrary objects.

When it comes to cache data, it is strongly recommended to stick with
the default. :mod:`cPickle` is very fast and fully supports standard Python
When it comes to cache data, it is *strongly recommended* to stick with
the default. :mod:`cPickle` is *very* fast and fully supports standard Python
data structures (``dict``, ``list``, ``tuple``, ``set`` etc.).

If you need the ability to customise caching, you can change the default
Expand All @@ -581,7 +629,7 @@ cache serialization format to :mod:`pickle` thus:
wf.cache_serializer = 'pickle'
In the case of stored data, you are free to specify either a global default
serializer of one for each individual datastore:
serializer or one for each individual datastore:

.. code-block:: python
:linenos:
Expand Down Expand Up @@ -628,7 +676,7 @@ of the stored file.

The :meth:`stored_data() <workflow.workflow.Workflow.stored_data>` method can
automatically determine the serialization of the stored data, provided the
corresponding serializer is registered. If it isn't, an exception will be raised.
corresponding serializer is registered. If it isn't, a ``ValueError`` will be raised.


Built-in icons
Expand Down Expand Up @@ -725,7 +773,7 @@ for debugging.
use the :attr:`~workflow.workflow.Workflow.args` property (where magic arguments
are parsed).

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

- ``workflow:openlog`` — Open the Workflow's log file in the default app.
- ``workflow:opencache`` — Open the Workflow's cache directory.
Expand All @@ -746,6 +794,15 @@ does not correspond with a user's usage pattern.

You can turn off magic arguments by passing ``capture_args=False`` to
:class:`~workflow.workflow.Workflow` on instantiation, or call the corresponding
:meth:`~workflow.workflow.Workflow.open_log`, :meth:`~workflow.workflow.Workflow.clear_cache`
and :meth:`~workflow.workflow.Workflow.clear_settings` methods directly, perhaps
assigning them to your own Keywords.
methods of :class:`~workflow.workflow.Workflow` directly, perhaps assigning your
own keywords within your Workflow:

- :meth:`~workflow.workflow.Workflow.open_log`
- :meth:`~workflow.workflow.Workflow.open_cachedir`
- :meth:`~workflow.workflow.Workflow.open_datadir`
- :meth:`~workflow.workflow.Workflow.open_workflowdir`
- :meth:`~workflow.workflow.Workflow.open_terminal`
- :meth:`~workflow.workflow.Workflow.clear_cache`
- :meth:`~workflow.workflow.Workflow.clear_data`
- :meth:`~workflow.workflow.Workflow.clear_settings`
- :meth:`~workflow.workflow.Workflow.reset` (a shortcut to call the three previous ``clear_*`` methods)
50 changes: 33 additions & 17 deletions run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,40 @@

# OUTPUT_PATH=$(pwd)/tests_output

mydir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

LOGPATH="${mydir}/test.log"
rootdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
workflow="${rootdir}/workflow"
logpath="${rootdir}/test.log"
testroot="/tmp/alfred-wörkflöw-$$"
testdir="${testroot}/tests"
curdir=$(pwd)

function log() {
echo "$@" | tee -a $LOGPATH
echo "$@" | tee -a $logpath
}

rm -rf $LOGPATH
# Delete old log file if it exists
if [[ -f "${logpath}" ]]; then
rm -rf "${logpath}"
fi

curdir=$(pwd)
wdir="${mydir}/tests"
info_linked=0
###############################################################################
# Set up test environment
###############################################################################
if [[ -d "${testroot}" ]]; then
rm -rf "${testroot}"
fi

mkdir -p "${testroot}"
cp -R "${rootdir}/tests" "${testdir}"
ln -s "${rootdir}/workflow" "${testdir}/workflow"
ln -s "${rootdir}/tests/info.plist.test" "${testroot}/info.plist"

if [[ ! -f "info.plist" ]]; then
# link info.plist to parent directory so `background.py` can find it
ln -s "${wdir}/info.plist.test" "${mydir}/info.plist"
info_linked=1
fi
cd "${testdir}"

cd "$wdir"

###############################################################################
# Set test options and run tests
###############################################################################

NOSETEST_OPTIONS="-d"

Expand All @@ -49,7 +61,7 @@ fi

log "Running tests..."

nosetests $NOSETEST_OPTIONS 2>&1 | tee -a $LOGPATH
nosetests $NOSETEST_OPTIONS 2>&1 | tee -a $logpath
ret=${PIPESTATUS[0]}

echo
Expand All @@ -59,10 +71,14 @@ case "$ret" in
*) log -e "FAILURE" ;;
esac

###############################################################################
# Delete test environment
###############################################################################

cd "$curdir"

if [[ $info_linked -eq 1 ]]; then
rm -f "${mydir}/info.plist"
if [[ -d "${testroot}" ]]; then
rm -rf "${testroot}"
fi

exit $ret
2 changes: 1 addition & 1 deletion tests/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def setUp(self):
self.data = {'name': 'My name is Jürgen!',
'address': 'Hürterstr. 42\nEssen'}
self.test_file = os.path.join(os.path.dirname(__file__),
'cönfüsed.gif')
b'cönfüsed.gif')
self.fubar_url = 'http://deanishe.net/fubar.txt'
self.fubar_bytes = b'fübar'
self.fubar_unicode = 'fübar'
Expand Down
Loading

0 comments on commit b1f4ae6

Please sign in to comment.