From ddbbcb36c608e7a1b1ef5e5a781cb82c23c4ac96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= Date: Wed, 28 Dec 2016 22:51:39 +0100 Subject: [PATCH] Import python-click-threading_0.4.2.orig.tar.gz --- .travis.yml | 3 +- MANIFEST.in | 5 + Makefile | 5 + README.rst | 6 +- click_threading/__init__.py | 59 +++++++- click_threading/monkey.py | 57 +++++++- docs/Makefile | 225 +++++++++++++++++++++++++++++ docs/conf.py | 112 ++++++++++++++ docs/index.rst | 6 + docs/make.bat | 281 ++++++++++++++++++++++++++++++++++++ docs/threading.rst | 7 + docs/uiworker.rst | 7 + setup.cfg | 3 + tests/test_basic.py | 41 +++++- 14 files changed, 798 insertions(+), 19 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/threading.rst create mode 100644 docs/uiworker.rst diff --git a/.travis.yml b/.travis.yml index dfb9017..eae7bcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,6 @@ python: - 3.3 - 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 diff --git a/MANIFEST.in b/MANIFEST.in index 1aba38f..b60c198 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,6 @@ include LICENSE +recursive-include docs * +recursive-include tests * + +prune docs/_build +global-exclude *.py[cdo] __pycache__ *.so *.pyd diff --git a/Makefile b/Makefile index d257e7b..902b6ed 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,7 @@ release: python setup.py sdist bdist_wheel upload + +docs: + cd docs && make html + +.PHONY: docs diff --git a/README.rst b/README.rst index b61d024..4bcb403 100644 --- a/README.rst +++ b/README.rst @@ -4,11 +4,13 @@ click-threading .. image:: https://travis-ci.org/click-contrib/click-threading.svg?branch=master :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 `_. -*This is rather experimental. See tests for usage for now.* - License ======= diff --git a/click_threading/__init__.py b/click_threading/__init__.py index 16cde06..7d9eab6 100644 --- a/click_threading/__init__.py +++ b/click_threading/__init__.py @@ -17,11 +17,11 @@ except ImportError: # Executors", but since I'm basically implementing my own executor here, I # think we're fine. try: - from concurrent.futures import Future + from concurrent.futures import Future as _Future 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' @@ -33,7 +33,21 @@ def _is_main_thread(thread=None): class Thread(threading.Thread): '''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): self._click_context = click.get_current_context() @@ -45,6 +59,41 @@ class Thread(threading.Thread): 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() def __init__(self): @@ -73,7 +122,7 @@ class UiWorker(object): if _is_main_thread(): return func() - future = Future() + future = _Future() self.tasks.put((func, future)) if not wait: return diff --git a/click_threading/monkey.py b/click_threading/monkey.py index 93f426f..4eb2662 100644 --- a/click_threading/monkey.py +++ b/click_threading/monkey.py @@ -1,11 +1,17 @@ # -*- coding: utf-8 -*- +import types import contextlib +import inspect + +from ._compat import PY2 + class FunctionInfo(object): def __init__(self, interactive): self.interactive = interactive + _ui_functions = { 'echo_via_pager': FunctionInfo(interactive=True), 'prompt': FunctionInfo(interactive=True), @@ -23,17 +29,52 @@ _ui_functions = { 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)) + saved = [] + + for name, info in sorted(_ui_functions.items()): + f = getattr(click, name, NONE) + 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: yield finally: - for name, orig in saved.items(): - setattr(click, name, orig) + for f, code in saved: + 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__) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..a49e6a4 --- /dev/null +++ b/docs/Makefile @@ -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 ' where 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." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..9e2d488 --- /dev/null +++ b/docs/conf.py @@ -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, +} diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..ddae66f --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,6 @@ +Utilities for multithreaded applications with Click +=================================================== + +.. toctree:: + threading + uiworker diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..711d86b --- /dev/null +++ b/docs/make.bat @@ -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 ^` where ^ 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 diff --git a/docs/threading.rst b/docs/threading.rst new file mode 100644 index 0000000..1c6b99d --- /dev/null +++ b/docs/threading.rst @@ -0,0 +1,7 @@ +================ +The Thread class +================ + +.. currentmodule:: click_threading + +.. autoclass:: click_threading.Thread diff --git a/docs/uiworker.rst b/docs/uiworker.rst new file mode 100644 index 0000000..a28d6f7 --- /dev/null +++ b/docs/uiworker.rst @@ -0,0 +1,7 @@ +======================================== +Output and prompts from multiple threads +======================================== + +.. currentmodule:: click_threading + +.. autoclass:: UiWorker diff --git a/setup.cfg b/setup.cfg index b090585..423f64a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,3 +4,6 @@ universal = 1 [flake8] # W503: Line break before operator ignore = W503 + +[tool:pytest] +addopts = --doctest-modules --ignore=setup.py diff --git a/tests/test_basic.py b/tests/test_basic.py index d705e52..99b69ac 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1,6 +1,7 @@ import pytest import click_threading +from click_threading._compat import PY2 import click from click.testing import CliRunner @@ -30,15 +31,12 @@ def test_context_pushing_thread(runner): 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() @@ -54,3 +52,40 @@ def test_ui_worker_basic(runner): result = runner.invoke(cli, catch_exceptions=False, input='y\n' * 3) 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' + ]