[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