merge patched into master
This commit is contained in:
commit
535f4cf17e
|
@ -6,5 +6,6 @@ python:
|
||||||
- 3.3
|
- 3.3
|
||||||
- 3.4
|
- 3.4
|
||||||
|
|
||||||
install: pip install tox
|
install:
|
||||||
|
- pip install -U tox setuptools wheel $(python -V |& grep -q 'Python 3.2' && echo 'pip<8.0 virtualenv<14.0')
|
||||||
script: tox
|
script: tox
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
include LICENSE
|
include LICENSE
|
||||||
|
recursive-include docs *
|
||||||
|
recursive-include tests *
|
||||||
|
|
||||||
|
prune docs/_build
|
||||||
|
global-exclude *.py[cdo] __pycache__ *.so *.pyd
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -1,2 +1,7 @@
|
||||||
release:
|
release:
|
||||||
python setup.py sdist bdist_wheel upload
|
python setup.py sdist bdist_wheel upload
|
||||||
|
|
||||||
|
docs:
|
||||||
|
cd docs && make html
|
||||||
|
|
||||||
|
.PHONY: docs
|
||||||
|
|
|
@ -4,11 +4,13 @@ click-threading
|
||||||
.. image:: https://travis-ci.org/click-contrib/click-threading.svg?branch=master
|
.. image:: https://travis-ci.org/click-contrib/click-threading.svg?branch=master
|
||||||
:target: https://travis-ci.org/click-contrib/click-threading
|
:target: https://travis-ci.org/click-contrib/click-threading
|
||||||
|
|
||||||
|
.. image:: https://readthedocs.org/projects/click-threading/badge/?version=latest
|
||||||
|
:target: http://click-threading.readthedocs.io/en/latest/?badge=latest
|
||||||
|
:alt: Documentation Status
|
||||||
|
|
||||||
|
|
||||||
Utilities for multithreading in `click <http://click.pocoo.org/>`_.
|
Utilities for multithreading in `click <http://click.pocoo.org/>`_.
|
||||||
|
|
||||||
*This is rather experimental. See tests for usage for now.*
|
|
||||||
|
|
||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,11 @@ except ImportError:
|
||||||
# Executors", but since I'm basically implementing my own executor here, I
|
# Executors", but since I'm basically implementing my own executor here, I
|
||||||
# think we're fine.
|
# think we're fine.
|
||||||
try:
|
try:
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future as _Future
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from futures import Future
|
from futures import Future as _Future
|
||||||
|
|
||||||
__version__ = '0.4.0'
|
__version__ = '0.4.2'
|
||||||
|
|
||||||
_CTX_WORKER_KEY = __name__ + '.uiworker'
|
_CTX_WORKER_KEY = __name__ + '.uiworker'
|
||||||
|
|
||||||
|
@ -33,7 +33,21 @@ def _is_main_thread(thread=None):
|
||||||
|
|
||||||
class Thread(threading.Thread):
|
class Thread(threading.Thread):
|
||||||
'''A thread that automatically pushes the parent thread's context in the
|
'''A thread that automatically pushes the parent thread's context in the
|
||||||
new thread.'''
|
new thread.
|
||||||
|
|
||||||
|
Since version 5.0, click maintains global stacks of context objects. The
|
||||||
|
topmost context on that stack can be accessed with
|
||||||
|
:py:func:`get_current_context`.
|
||||||
|
|
||||||
|
There is one stack for each Python thread. That means if you are in the
|
||||||
|
main thread (where you can use :py:func:`get_current_context` just fine)
|
||||||
|
and spawn a :py:class:`threading.Thread`, that thread won't be able to
|
||||||
|
access the same context using :py:func:`get_current_context`.
|
||||||
|
|
||||||
|
:py:class:`Thread` is a subclass of :py:class:`threading.Thread` that
|
||||||
|
preserves the current thread context when spawning a new one, by pushing it
|
||||||
|
on the stack of the new thread as well.
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._click_context = click.get_current_context()
|
self._click_context = click.get_current_context()
|
||||||
|
@ -45,6 +59,41 @@ class Thread(threading.Thread):
|
||||||
|
|
||||||
|
|
||||||
class UiWorker(object):
|
class UiWorker(object):
|
||||||
|
'''
|
||||||
|
A worker-queue system to manage and synchronize output and prompts from
|
||||||
|
other threads.
|
||||||
|
|
||||||
|
>>> import click
|
||||||
|
>>> from click_threading import UiWorker, Thread, get_ui_worker
|
||||||
|
>>> ui = UiWorker() # on main thread
|
||||||
|
>>> def target():
|
||||||
|
... click.echo("Hello world!")
|
||||||
|
... get_ui_worker().shutdown()
|
||||||
|
...
|
||||||
|
>>>
|
||||||
|
>>> @click.command()
|
||||||
|
... def cli():
|
||||||
|
... with ui.patch_click():
|
||||||
|
... t = Thread(target=target)
|
||||||
|
... t.start()
|
||||||
|
... ui.run()
|
||||||
|
>>> runner = click.testing.CliRunner()
|
||||||
|
>>> result = runner.invoke(cli, [])
|
||||||
|
>>> assert result.output.strip() == 'Hello world!'
|
||||||
|
|
||||||
|
Using this class instead of just spawning threads brings a few advantages:
|
||||||
|
|
||||||
|
- If one thread prompts for input, other output from other threads is
|
||||||
|
queued until the :py:func:`click.prompt` call returns.
|
||||||
|
- If you call echo with a multiline-string, it is guaranteed that this
|
||||||
|
string is not interleaved with other output.
|
||||||
|
|
||||||
|
Disadvantages:
|
||||||
|
|
||||||
|
- The main thread is used for the output (using any other thread produces
|
||||||
|
weird behavior with interrupts). ``ui.run()`` in the above example blocks
|
||||||
|
until ``ui.shutdown()`` is called.
|
||||||
|
'''
|
||||||
SHUTDOWN = object()
|
SHUTDOWN = object()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -73,7 +122,7 @@ class UiWorker(object):
|
||||||
if _is_main_thread():
|
if _is_main_thread():
|
||||||
return func()
|
return func()
|
||||||
|
|
||||||
future = Future()
|
future = _Future()
|
||||||
self.tasks.put((func, future))
|
self.tasks.put((func, future))
|
||||||
if not wait:
|
if not wait:
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import types
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from ._compat import PY2
|
||||||
|
|
||||||
|
|
||||||
class FunctionInfo(object):
|
class FunctionInfo(object):
|
||||||
def __init__(self, interactive):
|
def __init__(self, interactive):
|
||||||
self.interactive = interactive
|
self.interactive = interactive
|
||||||
|
|
||||||
|
|
||||||
_ui_functions = {
|
_ui_functions = {
|
||||||
'echo_via_pager': FunctionInfo(interactive=True),
|
'echo_via_pager': FunctionInfo(interactive=True),
|
||||||
'prompt': FunctionInfo(interactive=True),
|
'prompt': FunctionInfo(interactive=True),
|
||||||
|
@ -23,17 +29,52 @@ _ui_functions = {
|
||||||
def patch_ui_functions(wrapper):
|
def patch_ui_functions(wrapper):
|
||||||
'''Wrap all termui functions with a custom decorator.'''
|
'''Wrap all termui functions with a custom decorator.'''
|
||||||
NONE = object()
|
NONE = object()
|
||||||
saved = {}
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
for name, info in _ui_functions.items():
|
saved = []
|
||||||
orig = getattr(click, name, NONE)
|
|
||||||
if orig is not NONE:
|
for name, info in sorted(_ui_functions.items()):
|
||||||
saved[name] = orig
|
f = getattr(click, name, NONE)
|
||||||
setattr(click, name, wrapper(orig, info))
|
if f is NONE:
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_f = wrapper(_copy_fn(f), info)
|
||||||
|
|
||||||
|
argspec = inspect.getargspec(f)
|
||||||
|
signature = inspect.formatargspec(*argspec) \
|
||||||
|
.lstrip('(') \
|
||||||
|
.rstrip(')')
|
||||||
|
args = ', '.join(arg.split('=')[0].split(':')[0].strip()
|
||||||
|
for arg in signature.split(','))
|
||||||
|
|
||||||
|
stub_f = eval('lambda {s}: {n}._real_click_fn({a})'
|
||||||
|
.format(n=f.__name__, s=signature, a=args))
|
||||||
|
|
||||||
|
if PY2:
|
||||||
|
saved.append((f, f.func_code))
|
||||||
|
f.func_code = stub_f.func_code
|
||||||
|
else:
|
||||||
|
saved.append((f, f.__code__))
|
||||||
|
f.__code__ = stub_f.__code__
|
||||||
|
|
||||||
|
f._real_click_fn = new_f
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
for name, orig in saved.items():
|
for f, code in saved:
|
||||||
setattr(click, name, orig)
|
if PY2:
|
||||||
|
f.func_code = code
|
||||||
|
else:
|
||||||
|
f.__code__ = code
|
||||||
|
|
||||||
|
del f._real_click_fn
|
||||||
|
|
||||||
|
|
||||||
|
def _copy_fn(f):
|
||||||
|
if PY2:
|
||||||
|
return types.FunctionType(f.func_code, f.func_globals, f.func_name,
|
||||||
|
f.func_defaults, f.func_closure)
|
||||||
|
else:
|
||||||
|
return types.FunctionType(f.__code__, f.__globals__, f.__name__,
|
||||||
|
f.__defaults__, f.__closure__)
|
||||||
|
|
6
debian/.git-dpm
vendored
6
debian/.git-dpm
vendored
|
@ -1,7 +1,7 @@
|
||||||
# see git-dpm(1) from git-dpm package
|
# see git-dpm(1) from git-dpm package
|
||||||
6b4bd92e4caddbbf3f32c0d90ed0563c00732433
|
46f53aa96cd16257d8098f0cf20a1efb393aa4ab
|
||||||
6b4bd92e4caddbbf3f32c0d90ed0563c00732433
|
46f53aa96cd16257d8098f0cf20a1efb393aa4ab
|
||||||
577f318089c335e057efe6d5c72b9daedb1f6ce0
|
ddbbcb36c608e7a1b1ef5e5a781cb82c23c4ac96
|
||||||
ddbbcb36c608e7a1b1ef5e5a781cb82c23c4ac96
|
ddbbcb36c608e7a1b1ef5e5a781cb82c23c4ac96
|
||||||
python-click-threading_0.4.2.orig.tar.gz
|
python-click-threading_0.4.2.orig.tar.gz
|
||||||
50d98db867c0d63da8aa9fde154b16368b7128e7
|
50d98db867c0d63da8aa9fde154b16368b7128e7
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
From 6b4bd92e4caddbbf3f32c0d90ed0563c00732433 Mon Sep 17 00:00:00 2001
|
From 46f53aa96cd16257d8098f0cf20a1efb393aa4ab Mon Sep 17 00:00:00 2001
|
||||||
From: Filip Pytloun <filip@pytloun.cz>
|
From: Filip Pytloun <filip@pytloun.cz>
|
||||||
Date: Fri, 12 Aug 2016 11:23:17 +0200
|
Date: Fri, 12 Aug 2016 11:23:17 +0200
|
||||||
Subject: Fix conditional dependency on futures
|
Subject: Fix conditional dependency on futures
|
||||||
|
|
225
docs/Makefile
Normal file
225
docs/Makefile
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
# Makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
PAPER =
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Internal variables.
|
||||||
|
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||||
|
PAPEROPT_letter = -D latex_paper_size=letter
|
||||||
|
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
# the i18n builder cannot share the environment and doctrees with the others
|
||||||
|
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
|
||||||
|
.PHONY: help
|
||||||
|
help:
|
||||||
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
|
@echo " html to make standalone HTML files"
|
||||||
|
@echo " dirhtml to make HTML files named index.html in directories"
|
||||||
|
@echo " singlehtml to make a single large HTML file"
|
||||||
|
@echo " pickle to make pickle files"
|
||||||
|
@echo " json to make JSON files"
|
||||||
|
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||||
|
@echo " qthelp to make HTML files and a qthelp project"
|
||||||
|
@echo " applehelp to make an Apple Help Book"
|
||||||
|
@echo " devhelp to make HTML files and a Devhelp project"
|
||||||
|
@echo " epub to make an epub"
|
||||||
|
@echo " epub3 to make an epub3"
|
||||||
|
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||||
|
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||||
|
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||||
|
@echo " text to make text files"
|
||||||
|
@echo " man to make manual pages"
|
||||||
|
@echo " texinfo to make Texinfo files"
|
||||||
|
@echo " info to make Texinfo files and run them through makeinfo"
|
||||||
|
@echo " gettext to make PO message catalogs"
|
||||||
|
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||||
|
@echo " xml to make Docutils-native XML files"
|
||||||
|
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||||
|
@echo " linkcheck to check all external links for integrity"
|
||||||
|
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||||
|
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||||
|
@echo " dummy to check syntax errors of document sources"
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILDDIR)/*
|
||||||
|
|
||||||
|
.PHONY: html
|
||||||
|
html:
|
||||||
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||||
|
|
||||||
|
.PHONY: dirhtml
|
||||||
|
dirhtml:
|
||||||
|
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||||
|
|
||||||
|
.PHONY: singlehtml
|
||||||
|
singlehtml:
|
||||||
|
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||||
|
|
||||||
|
.PHONY: pickle
|
||||||
|
pickle:
|
||||||
|
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the pickle files."
|
||||||
|
|
||||||
|
.PHONY: json
|
||||||
|
json:
|
||||||
|
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the JSON files."
|
||||||
|
|
||||||
|
.PHONY: htmlhelp
|
||||||
|
htmlhelp:
|
||||||
|
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||||
|
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||||
|
|
||||||
|
.PHONY: qthelp
|
||||||
|
qthelp:
|
||||||
|
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||||
|
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||||
|
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/click-threading.qhcp"
|
||||||
|
@echo "To view the help file:"
|
||||||
|
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/click-threading.qhc"
|
||||||
|
|
||||||
|
.PHONY: applehelp
|
||||||
|
applehelp:
|
||||||
|
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||||
|
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||||
|
"~/Library/Documentation/Help or install it in your application" \
|
||||||
|
"bundle."
|
||||||
|
|
||||||
|
.PHONY: devhelp
|
||||||
|
devhelp:
|
||||||
|
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished."
|
||||||
|
@echo "To view the help file:"
|
||||||
|
@echo "# mkdir -p $$HOME/.local/share/devhelp/click-threading"
|
||||||
|
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/click-threading"
|
||||||
|
@echo "# devhelp"
|
||||||
|
|
||||||
|
.PHONY: epub
|
||||||
|
epub:
|
||||||
|
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||||
|
|
||||||
|
.PHONY: epub3
|
||||||
|
epub3:
|
||||||
|
$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
||||||
|
|
||||||
|
.PHONY: latex
|
||||||
|
latex:
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||||
|
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||||
|
"(use \`make latexpdf' here to do that automatically)."
|
||||||
|
|
||||||
|
.PHONY: latexpdf
|
||||||
|
latexpdf:
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||||
|
@echo "Running LaTeX files through pdflatex..."
|
||||||
|
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||||
|
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||||
|
|
||||||
|
.PHONY: latexpdfja
|
||||||
|
latexpdfja:
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||||
|
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||||
|
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||||
|
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||||
|
|
||||||
|
.PHONY: text
|
||||||
|
text:
|
||||||
|
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||||
|
|
||||||
|
.PHONY: man
|
||||||
|
man:
|
||||||
|
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||||
|
|
||||||
|
.PHONY: texinfo
|
||||||
|
texinfo:
|
||||||
|
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||||
|
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||||
|
"(use \`make info' here to do that automatically)."
|
||||||
|
|
||||||
|
.PHONY: info
|
||||||
|
info:
|
||||||
|
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||||
|
@echo "Running Texinfo files through makeinfo..."
|
||||||
|
make -C $(BUILDDIR)/texinfo info
|
||||||
|
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||||
|
|
||||||
|
.PHONY: gettext
|
||||||
|
gettext:
|
||||||
|
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||||
|
|
||||||
|
.PHONY: changes
|
||||||
|
changes:
|
||||||
|
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||||
|
@echo
|
||||||
|
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||||
|
|
||||||
|
.PHONY: linkcheck
|
||||||
|
linkcheck:
|
||||||
|
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||||
|
@echo
|
||||||
|
@echo "Link check complete; look for any errors in the above output " \
|
||||||
|
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||||
|
|
||||||
|
.PHONY: doctest
|
||||||
|
doctest:
|
||||||
|
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||||
|
@echo "Testing of doctests in the sources finished, look at the " \
|
||||||
|
"results in $(BUILDDIR)/doctest/output.txt."
|
||||||
|
|
||||||
|
.PHONY: coverage
|
||||||
|
coverage:
|
||||||
|
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||||
|
@echo "Testing of coverage in the sources finished, look at the " \
|
||||||
|
"results in $(BUILDDIR)/coverage/python.txt."
|
||||||
|
|
||||||
|
.PHONY: xml
|
||||||
|
xml:
|
||||||
|
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||||
|
|
||||||
|
.PHONY: pseudoxml
|
||||||
|
pseudoxml:
|
||||||
|
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||||
|
|
||||||
|
.PHONY: dummy
|
||||||
|
dummy:
|
||||||
|
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. Dummy builder generates no files."
|
112
docs/conf.py
Normal file
112
docs/conf.py
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.intersphinx',
|
||||||
|
'sphinx.ext.viewcode',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = 'click_threading'
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), '../LICENSE')) as f:
|
||||||
|
copyright = next(iter(f))[len('Copyright (c) '):]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = pkg_resources.require(project)[0].version
|
||||||
|
except pkg_resources.DistributionNotFound:
|
||||||
|
print('To build the documentation, the distribution information of '
|
||||||
|
'{} has to be available. Run "setup.py develop" to do '
|
||||||
|
'this.'.format(project))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
version = '.'.join(release.split('.')[:2]) # The short X.Y version.
|
||||||
|
|
||||||
|
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||||
|
|
||||||
|
try:
|
||||||
|
import sphinx_rtd_theme
|
||||||
|
html_theme = 'sphinx_rtd_theme'
|
||||||
|
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||||
|
except ImportError:
|
||||||
|
html_theme = 'default'
|
||||||
|
if not on_rtd:
|
||||||
|
print('-' * 74)
|
||||||
|
print('Warning: sphinx-rtd-theme not installed, building with default '
|
||||||
|
'theme.')
|
||||||
|
print('-' * 74)
|
||||||
|
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = '{}doc'.format(project)
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title,
|
||||||
|
# author, documentclass [howto, manual, or own class]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'atomicwrites.tex', '{} Documentation'.format(project),
|
||||||
|
'Markus Unterwaditzer', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
('index', 'atomicwrites', 'atomicwrites Documentation',
|
||||||
|
['Markus Unterwaditzer'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
('index', 'atomicwrites', 'atomicwrites Documentation',
|
||||||
|
'Markus Unterwaditzer', 'atomicwrites', 'One line description of project.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Bibliographic Dublin Core info.
|
||||||
|
epub_title = 'atomicwrites'
|
||||||
|
epub_author = 'Markus Unterwaditzer'
|
||||||
|
epub_publisher = 'Markus Unterwaditzer'
|
||||||
|
epub_copyright = copyright
|
||||||
|
|
||||||
|
# A list of files that should not be packed into the epub file.
|
||||||
|
epub_exclude_files = ['search.html']
|
||||||
|
|
||||||
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
|
intersphinx_mapping = {
|
||||||
|
'http://docs.python.org/': None,
|
||||||
|
'http://click.pocoo.org/': None,
|
||||||
|
}
|
6
docs/index.rst
Normal file
6
docs/index.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Utilities for multithreaded applications with Click
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
threading
|
||||||
|
uiworker
|
281
docs/make.bat
Normal file
281
docs/make.bat
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set BUILDDIR=_build
|
||||||
|
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||||
|
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||||
|
if NOT "%PAPER%" == "" (
|
||||||
|
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||||
|
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
if "%1" == "help" (
|
||||||
|
:help
|
||||||
|
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||||
|
echo. html to make standalone HTML files
|
||||||
|
echo. dirhtml to make HTML files named index.html in directories
|
||||||
|
echo. singlehtml to make a single large HTML file
|
||||||
|
echo. pickle to make pickle files
|
||||||
|
echo. json to make JSON files
|
||||||
|
echo. htmlhelp to make HTML files and a HTML help project
|
||||||
|
echo. qthelp to make HTML files and a qthelp project
|
||||||
|
echo. devhelp to make HTML files and a Devhelp project
|
||||||
|
echo. epub to make an epub
|
||||||
|
echo. epub3 to make an epub3
|
||||||
|
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||||
|
echo. text to make text files
|
||||||
|
echo. man to make manual pages
|
||||||
|
echo. texinfo to make Texinfo files
|
||||||
|
echo. gettext to make PO message catalogs
|
||||||
|
echo. changes to make an overview over all changed/added/deprecated items
|
||||||
|
echo. xml to make Docutils-native XML files
|
||||||
|
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||||
|
echo. linkcheck to check all external links for integrity
|
||||||
|
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||||
|
echo. coverage to run coverage check of the documentation if enabled
|
||||||
|
echo. dummy to check syntax errors of document sources
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "clean" (
|
||||||
|
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||||
|
del /q /s %BUILDDIR%\*
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
REM Check if sphinx-build is available and fallback to Python version if any
|
||||||
|
%SPHINXBUILD% 1>NUL 2>NUL
|
||||||
|
if errorlevel 9009 goto sphinx_python
|
||||||
|
goto sphinx_ok
|
||||||
|
|
||||||
|
:sphinx_python
|
||||||
|
|
||||||
|
set SPHINXBUILD=python -m sphinx.__init__
|
||||||
|
%SPHINXBUILD% 2> nul
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.http://sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:sphinx_ok
|
||||||
|
|
||||||
|
|
||||||
|
if "%1" == "html" (
|
||||||
|
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "dirhtml" (
|
||||||
|
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "singlehtml" (
|
||||||
|
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "pickle" (
|
||||||
|
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can process the pickle files.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "json" (
|
||||||
|
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can process the JSON files.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "htmlhelp" (
|
||||||
|
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||||
|
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "qthelp" (
|
||||||
|
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||||
|
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||||
|
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\click-threading.qhcp
|
||||||
|
echo.To view the help file:
|
||||||
|
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\click-threading.ghc
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "devhelp" (
|
||||||
|
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "epub" (
|
||||||
|
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "epub3" (
|
||||||
|
%SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "latex" (
|
||||||
|
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "latexpdf" (
|
||||||
|
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||||
|
cd %BUILDDIR%/latex
|
||||||
|
make all-pdf
|
||||||
|
cd %~dp0
|
||||||
|
echo.
|
||||||
|
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "latexpdfja" (
|
||||||
|
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||||
|
cd %BUILDDIR%/latex
|
||||||
|
make all-pdf-ja
|
||||||
|
cd %~dp0
|
||||||
|
echo.
|
||||||
|
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "text" (
|
||||||
|
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "man" (
|
||||||
|
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "texinfo" (
|
||||||
|
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "gettext" (
|
||||||
|
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "changes" (
|
||||||
|
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.The overview file is in %BUILDDIR%/changes.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "linkcheck" (
|
||||||
|
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Link check complete; look for any errors in the above output ^
|
||||||
|
or in %BUILDDIR%/linkcheck/output.txt.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "doctest" (
|
||||||
|
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Testing of doctests in the sources finished, look at the ^
|
||||||
|
results in %BUILDDIR%/doctest/output.txt.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "coverage" (
|
||||||
|
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Testing of coverage in the sources finished, look at the ^
|
||||||
|
results in %BUILDDIR%/coverage/python.txt.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "xml" (
|
||||||
|
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "pseudoxml" (
|
||||||
|
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "dummy" (
|
||||||
|
%SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
|
||||||
|
if errorlevel 1 exit /b 1
|
||||||
|
echo.
|
||||||
|
echo.Build finished. Dummy builder generates no files.
|
||||||
|
goto end
|
||||||
|
)
|
||||||
|
|
||||||
|
:end
|
7
docs/threading.rst
Normal file
7
docs/threading.rst
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
================
|
||||||
|
The Thread class
|
||||||
|
================
|
||||||
|
|
||||||
|
.. currentmodule:: click_threading
|
||||||
|
|
||||||
|
.. autoclass:: click_threading.Thread
|
7
docs/uiworker.rst
Normal file
7
docs/uiworker.rst
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
========================================
|
||||||
|
Output and prompts from multiple threads
|
||||||
|
========================================
|
||||||
|
|
||||||
|
.. currentmodule:: click_threading
|
||||||
|
|
||||||
|
.. autoclass:: UiWorker
|
|
@ -4,3 +4,6 @@ universal = 1
|
||||||
[flake8]
|
[flake8]
|
||||||
# W503: Line break before operator
|
# W503: Line break before operator
|
||||||
ignore = W503
|
ignore = W503
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
addopts = --doctest-modules --ignore=setup.py
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import click_threading
|
import click_threading
|
||||||
|
from click_threading._compat import PY2
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
@ -30,15 +31,12 @@ def test_context_pushing_thread(runner):
|
||||||
|
|
||||||
|
|
||||||
def test_ui_worker_basic(runner):
|
def test_ui_worker_basic(runner):
|
||||||
orig_click_prompt = click.prompt
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
def cli():
|
def cli():
|
||||||
|
|
||||||
ui = click_threading.UiWorker()
|
ui = click_threading.UiWorker()
|
||||||
|
|
||||||
def target():
|
def target():
|
||||||
assert click.prompt is not orig_click_prompt
|
|
||||||
click.prompt('two')
|
click.prompt('two')
|
||||||
ui.shutdown()
|
ui.shutdown()
|
||||||
|
|
||||||
|
@ -54,3 +52,40 @@ def test_ui_worker_basic(runner):
|
||||||
|
|
||||||
result = runner.invoke(cli, catch_exceptions=False, input='y\n' * 3)
|
result = runner.invoke(cli, catch_exceptions=False, input='y\n' * 3)
|
||||||
assert result.output.splitlines() == ['one: y', 'two: y', 'three: y']
|
assert result.output.splitlines() == ['one: y', 'two: y', 'three: y']
|
||||||
|
|
||||||
|
|
||||||
|
def test_monkey_patch(capsys):
|
||||||
|
old_echo = click.echo
|
||||||
|
if PY2:
|
||||||
|
old_code = old_echo.func_code
|
||||||
|
else:
|
||||||
|
old_code = old_echo.__code__
|
||||||
|
|
||||||
|
def wrapper(f, info):
|
||||||
|
def new_f(*a, **kw):
|
||||||
|
assert old_echo is not f
|
||||||
|
if PY2:
|
||||||
|
assert f.func_code is old_code
|
||||||
|
else:
|
||||||
|
assert f.__code__ is old_code
|
||||||
|
|
||||||
|
print("LOL")
|
||||||
|
rv = f(*a, **kw)
|
||||||
|
print("LOL")
|
||||||
|
return rv
|
||||||
|
return new_f
|
||||||
|
|
||||||
|
with click_threading.monkey.patch_ui_functions(wrapper):
|
||||||
|
assert click.echo is old_echo
|
||||||
|
click.echo('Hello world')
|
||||||
|
|
||||||
|
assert click.echo is old_echo
|
||||||
|
click.echo('Hello second world')
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
assert out.splitlines() == [
|
||||||
|
'LOL',
|
||||||
|
'Hello world',
|
||||||
|
'LOL',
|
||||||
|
'Hello second world'
|
||||||
|
]
|
||||||
|
|
Loading…
Reference in a new issue