[Python-modules-commits] [python-click-threading] 01/03: Import python-click-threading_0.4.0.orig.tar.gz

Filip Pytloun fpytloun-guest at moszumanska.debian.org
Thu Aug 11 09:48:27 UTC 2016


This is an automated email from the git hooks/post-receive script.

fpytloun-guest pushed a commit to branch master
in repository python-click-threading.

commit 577f318089c335e057efe6d5c72b9daedb1f6ce0
Author: Filip Pytloun <filip at pytloun.cz>
Date:   Thu Aug 11 11:30:06 2016 +0200

    Import python-click-threading_0.4.0.orig.tar.gz
---
 .gitignore                  |  11 +++++
 .travis.yml                 |  10 ++++
 LICENSE                     |  19 ++++++++
 MANIFEST.in                 |   1 +
 Makefile                    |   2 +
 README.rst                  |  15 ++++++
 click_threading/__init__.py | 108 ++++++++++++++++++++++++++++++++++++++++++++
 click_threading/_compat.py  |  13 ++++++
 click_threading/monkey.py   |  39 ++++++++++++++++
 setup.cfg                   |   6 +++
 setup.py                    |  29 ++++++++++++
 tests/test_basic.py         |  56 +++++++++++++++++++++++
 tox.ini                     |   6 +++
 13 files changed, 315 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ce68b0a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+.DS_Store
+*.pyc
+*.pyo
+*.egg-ignore
+*.egg-info
+dist
+build/
+docs/_build
+click.egg-info
+.tox
+.cache
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..dfb9017
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: python
+
+python:
+    - 2.7
+    - pypy
+    - 3.3
+    - 3.4
+
+install: pip install tox
+script: tox
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ab35c0e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014-2015 Markus Unterwaditzer & contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..1aba38f
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include LICENSE
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d257e7b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+release:
+	python setup.py sdist bdist_wheel upload
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..b61d024
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,15 @@
+click-threading
+===============
+
+.. image:: https://travis-ci.org/click-contrib/click-threading.svg?branch=master
+    :target: https://travis-ci.org/click-contrib/click-threading
+
+
+Utilities for multithreading in `click <http://click.pocoo.org/>`_.
+
+*This is rather experimental.  See tests for usage for now.*
+
+License
+=======
+
+Licensed under the MIT, see ``LICENSE``.
diff --git a/click_threading/__init__.py b/click_threading/__init__.py
new file mode 100644
index 0000000..16cde06
--- /dev/null
+++ b/click_threading/__init__.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+
+import sys
+import threading
+import functools
+import contextlib
+import click
+
+from ._compat import reraise
+
+try:
+    import queue
+except ImportError:
+    import Queue as queue
+
+# The docs state that "Future should not be instantiated directly, only by
+# Executors", but since I'm basically implementing my own executor here, I
+# think we're fine.
+try:
+    from concurrent.futures import Future
+except ImportError:
+    from futures import Future
+
+__version__ = '0.4.0'
+
+_CTX_WORKER_KEY = __name__ + '.uiworker'
+
+
+def _is_main_thread(thread=None):
+    thread = thread or threading.current_thread()
+    return type(thread).__name__ == '_MainThread'
+
+
+class Thread(threading.Thread):
+    '''A thread that automatically pushes the parent thread's context in the
+    new thread.'''
+
+    def __init__(self, *args, **kwargs):
+        self._click_context = click.get_current_context()
+        super(Thread, self).__init__(*args, **kwargs)
+
+    def run(self):
+        with self._click_context:
+            return super(Thread, self).run()
+
+
+class UiWorker(object):
+    SHUTDOWN = object()
+
+    def __init__(self):
+        if not _is_main_thread():
+            raise RuntimeError('The UiWorker can only run on the main thread.')
+
+        self.tasks = queue.Queue()
+
+    def shutdown(self):
+        self.put(self.SHUTDOWN, wait=False)
+
+    def run(self):
+        while True:
+            func, future = self.tasks.get()
+            if func is self.SHUTDOWN:
+                return
+
+            try:
+                result = func()
+            except BaseException as e:
+                future.set_exception(e)
+            else:
+                future.set_result(result)
+
+    def put(self, func, wait=True):
+        if _is_main_thread():
+            return func()
+
+        future = Future()
+        self.tasks.put((func, future))
+        if not wait:
+            return
+
+        return future.result()
+
+    @contextlib.contextmanager
+    def patch_click(self):
+        from .monkey import patch_ui_functions
+
+        def wrapper(f, info):
+            @functools.wraps(f)
+            def inner(*a, **kw):
+                return get_ui_worker() \
+                    .put(lambda: f(*a, **kw), wait=info.interactive)
+            return inner
+
+        ctx = click.get_current_context()
+        with patch_ui_functions(wrapper):
+            ctx.meta[_CTX_WORKER_KEY] = self
+            try:
+                yield
+            finally:
+                assert ctx.meta.pop(_CTX_WORKER_KEY) is self
+
+
+def get_ui_worker():
+    try:
+        ctx = click.get_current_context()
+        return ctx.meta[_CTX_WORKER_KEY]
+    except (RuntimeError, KeyError):
+        raise RuntimeError('UI worker not found.')
diff --git a/click_threading/_compat.py b/click_threading/_compat.py
new file mode 100644
index 0000000..9f1e82d
--- /dev/null
+++ b/click_threading/_compat.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+import sys
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+    exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
+else:
+    def reraise(tp, value, tb=None):
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
diff --git a/click_threading/monkey.py b/click_threading/monkey.py
new file mode 100644
index 0000000..93f426f
--- /dev/null
+++ b/click_threading/monkey.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+
+import contextlib
+
+class FunctionInfo(object):
+    def __init__(self, interactive):
+        self.interactive = interactive
+
+_ui_functions = {
+    'echo_via_pager': FunctionInfo(interactive=True),
+    'prompt': FunctionInfo(interactive=True),
+    'confirm': FunctionInfo(interactive=True),
+    'clear': FunctionInfo(interactive=False),
+    'echo': FunctionInfo(interactive=False),
+    'edit': FunctionInfo(interactive=True),
+    'launch': FunctionInfo(interactive=True),
+    'getchar': FunctionInfo(interactive=True),
+    'pause': FunctionInfo(interactive=True),
+}
+
+
+ at contextlib.contextmanager
+def patch_ui_functions(wrapper):
+    '''Wrap all termui functions with a custom decorator.'''
+    NONE = object()
+    saved = {}
+    import click
+
+    for name, info in _ui_functions.items():
+        orig = getattr(click, name, NONE)
+        if orig is not NONE:
+            saved[name] = orig
+            setattr(click, name, wrapper(orig, info))
+
+    try:
+        yield
+    finally:
+        for name, orig in saved.items():
+            setattr(click, name, orig)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..b090585
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,6 @@
+[wheel]
+universal = 1
+
+[flake8]
+# W503: Line break before operator
+ignore = W503
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..1b3d84f
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+import ast
+import re
+
+from setuptools import setup
+
+_version_re = re.compile(r'__version__\s+=\s+(.*)')
+
+with open('click_threading/__init__.py', 'rb') as f:
+    version = str(ast.literal_eval(_version_re.search(
+        f.read().decode('utf-8')).group(1)))
+
+setup(
+    name='click-threading',
+    version=version,
+    description='Multithreaded Click apps made easy',
+    author='Markus Unterwaditzer',
+    author_email='markus at unterwaditzer.net',
+    url='https://github.com/click-contrib/click-threading',
+    license='MIT',
+    packages=['click_threading'],
+    install_requires=[
+        'click>=5.0',
+    ],
+    extras_require={
+        ':python_version < "3.2"': 'futures'
+    }
+)
diff --git a/tests/test_basic.py b/tests/test_basic.py
new file mode 100644
index 0000000..d705e52
--- /dev/null
+++ b/tests/test_basic.py
@@ -0,0 +1,56 @@
+import pytest
+
+import click_threading
+
+import click
+from click.testing import CliRunner
+
+
+ at pytest.fixture
+def runner():
+    return CliRunner()
+
+
+def test_context_pushing_thread(runner):
+    @click.command()
+    @click.pass_context
+    def cli(ctx):
+        contexts = []
+
+        def check_ctx():
+            contexts.append(click.get_current_context())
+
+        t = click_threading.Thread(target=check_ctx)
+        t.start()
+        t.join()
+
+        assert contexts == [ctx]
+
+    runner.invoke(cli, catch_exceptions=False)
+
+
+def test_ui_worker_basic(runner):
+    orig_click_prompt = click.prompt
+
+    @click.command()
+    def cli():
+
+        ui = click_threading.UiWorker()
+
+        def target():
+            assert click.prompt is not orig_click_prompt
+            click.prompt('two')
+            ui.shutdown()
+
+        click.prompt('one')
+
+        with ui.patch_click():
+            t = click_threading.Thread(target=target)
+            t.start()
+            ui.run()
+
+        click.prompt('three')
+        t.join()
+
+    result = runner.invoke(cli, catch_exceptions=False, input='y\n' * 3)
+    assert result.output.splitlines() == ['one: y', 'two: y', 'three: y']
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..131e0fe
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,6 @@
+[testenv]
+passenv = LANG
+deps =
+    pytest
+    git+https://github.com/mitsuhiko/click
+commands = py.test

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-click-threading.git



More information about the Python-modules-commits mailing list