diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 0000000..1111705 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,17 @@ +Authors +======= + +This file contains the list of people involved in the development +of pytest-postgresql along its history. + +* Grzegorz Śliwiński +* Jonas Brunsgaard +* Tomasz Karbownicki +* Domen Kožar +* Przemysław Spodymek +* Michał Masłowski +* Karolina Blümke +* Paweł Wilczyński +* Georg Walther +* François Scala +* Donald Stufft diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..8ee3913 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,64 @@ +CHANGELOG +========= + +1.3.4 +------- + +- [bugfix] properly detect if executor running and clean after executor is being stopped + + .. note:: + + Previously if a test failed, there was a possibility of the executor being removed when python was closing, + causing it to print ignored errors on already unloaded modules. + +1.3.3 +------- + +- [enhancement] use executor's context manager to start/stop postrgesql server in a fixture + +1.3.2 +------- + +- [bugfix] version regexp to correctly catch postgresql 10 + +1.3.1 +------- + +- [enhancement] explicitly turn off logging_collector + +1.3.0 +------- + +- [feature] pypy compatibility + +1.2.0 +------- + +- [bugfix] - disallow connection to database before it gets dropped. + + .. note:: + + Otherwise it caused random test subprocess to connect again and this the drop was unsucessfull which resulted in many more test failes on setup. + +- [cleanup] - removed path.py dependency + +1.1.1 +------- + +- [bugfix] - Fixing the default pg_ctl path creation + +1.1.0 +------- + +- [feature] - migrate usage of getfuncargvalue to getfixturevalue. require at least pytest 3.0.0 + +1.0.0 +------- + +- create command line and pytest.ini configuration options for postgresql starting parameters +- create command line and pytest.ini configuration options for postgresql username +- make the port random by default +- create command line and pytest.ini configuration options for executable +- create command line and pytest.ini configuration options for host +- create command line and pytest.ini configuration options for port +- Extracted code from pytest-dbfixtures diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..95a474b --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,44 @@ +Contribute to pytest-postgresql +============== + +Thank you for taking time to contribute to pytest-postgresql! + +The following is a set of guidelines for contributing to pytest-postgresql. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. + +Bug Reports +----------- + +#. Use a clear and descriptive title for the issue - it'll be much easier to identify the problem. +#. Describe the steps to reproduce the problems in as many details as possible. +#. If possible, provide a code snippet to reproduce the issue. + +Feature requests/proposals +-------------------------- + +#. Use a clear and descriptive title for the proposal +#. Provide as detailed description as possible + * Use case is great to have +#. There'll be a bit of discussion for the feature. Don't worry, if it is to be accepted, we'd like to support it, so we need to understand it thoroughly. + + +Pull requests +------------- + +#. Start with a bug report or feature request +#. Use a clear and descriptive title +#. Provide a description - which issue does it refers to, and what part of the issue is being solved +#. Be ready for code review :) + +Commits +------- + +#. Make sure commits are atomic, and each atomic change is being followed by test. +#. If the commit solves part of the issue reported, include *refs #[Issue number]* in a commit message. +#. If the commit solves whole issue reported, please refer to `Closing issues via commit messages `_ for ways to close issues when commits will be merged. + + +Coding style +------------ + +#. All python coding style are being enforced by `Pylama `_ and configured in pylama.ini file. +#. Additional, not always mandatory checks are being performed by `QuantifiedCode `_ diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..2174f20 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include *.rst *.py +recursive-include src/pytest_postgresql *.py +recursive-include docs * +prune docs/build diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..d001f46 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,222 @@ +Metadata-Version: 2.1 +Name: pytest-postgresql +Version: 1.3.4 +Summary: Postgresql fixtures and fixture factories for Pytest. +Home-page: https://github.com/ClearcodeHQ/pytest-postgresql +Author: Clearcode - The A Room +Author-email: thearoom@clearcode.cc +License: LGPLv3+ +Description: pytest-postgresql + ================= + + .. image:: https://img.shields.io/pypi/v/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Latest PyPI version + + .. image:: https://img.shields.io/pypi/wheel/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Wheel Status + + .. image:: https://img.shields.io/pypi/pyversions/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Supported Python Versions + + .. image:: https://img.shields.io/pypi/l/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: License + + Package status + -------------- + + .. image:: https://travis-ci.org/ClearcodeHQ/pytest-postgresql.svg?branch=v1.3.4 + :target: https://travis-ci.org/ClearcodeHQ/pytest-postgresql + :alt: Tests + + .. image:: https://coveralls.io/repos/ClearcodeHQ/pytest-postgresql/badge.png?branch=v1.3.4 + :target: https://coveralls.io/r/ClearcodeHQ/pytest-postgresql?branch=v1.3.4 + :alt: Coverage Status + + .. image:: https://requires.io/github/ClearcodeHQ/pytest-postgresql/requirements.svg?tag=v1.3.4 + :target: https://requires.io/github/ClearcodeHQ/pytest-postgresql/requirements/?tag=v1.3.4 + :alt: Requirements Status + + What is this? + ============= + + This is a pytest plugin, that enables you to test your code that relies on a running PostgreSQL Database. + It allows you to specify fixtures for PostgreSQL process and client. + + How to use + ========== + + .. warning:: + + Tested on PostgreSQL versions > 9.x. See tests for more details. + + Plugin contains two fixtures + + * **postgresql** - it's a client fixture that has functional scope. After each test it ends all leftover connections, and drops test database from PostgreSQL ensuring repeatability. + * **postgresql_proc** - session scoped fixture, that starts PostgreSQL instance at it's first use and stops at the end of the tests. + + Simply include one of these fixtures into your tests fixture list. + + You can also create additional postgresql client and process fixtures if you'd need to: + + + .. code-block:: python + + from pytest_postgresql import factories + + postgresql_my_proc = factories.postgresql_proc( + port=None, logsdir='/tmp') + postgresql_my = factories.postgresql('postgresql_my_proc') + + .. note:: + + Each PostgreSQL process fixture can be configured in a different way than the others through the fixture factory arguments. + + Configuration + ============= + + You can define your settings in three ways, it's fixture factory argument, command line option and pytest.ini configuration option. + You can pick which you prefer, but remember that these settings are handled in the following order: + + * ``Fixture factory argument`` + * ``Command line option`` + * ``Configuration option in your pytest.ini file`` + + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | PostgreSQL option | Fixture factory argument | Command line option | pytest.ini option | Default | + +==========================+==========================+============================+==========================+====================================+ + | Path to executable | executable | --postgresql-exec | postgresql_exec | /usr/lib/postgresql/9.1/bin/pg_ctl | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | host | host | --postgresql-host | postgresql_host | 127.0.0.1 | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | port | port | --postgresql-port | postgresql_port | random | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | postgresql user | user | --postgresql-user | postgresql_user | postgres | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Starting parameters | startparams | --postgresql-startparams | postgresql_startparams | -w | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Log directory location | logsdir | --postgresql-logsdir | postgresql_logsdir | $TMPDIR | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Log filename's prefix | logsprefix | --postgresql-logsprefix | postgresql_logsprefix | | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Location for unixsockets | unixsocket | --postgresql-unixsocketdir | postgresql_unixsocketdir | $TMPDIR | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + + Example usage: + + * pass it as an argument in your own fixture + + .. code-block:: python + + postgresql_proc = factories.postgresql_proc( + port=8888) + + * use ``--postgresql-port`` command line option when you run your tests + + .. code-block:: + + py.test tests --postgresql-port=8888 + + + * specify your port as ``postgresql_port`` in your ``pytest.ini`` file. + + To do so, put a line like the following under the ``[pytest]`` section of your ``pytest.ini``: + + .. code-block:: ini + + [pytest] + postgresql_port = 8888 + + Package resources + ----------------- + + * Bug tracker: https://github.com/ClearcodeHQ/pytest-postgresql/issues + + + + CHANGELOG + ========= + + 1.3.4 + ------- + + - [bugfix] properly detect if executor running and clean after executor is being stopped + + .. note:: + + Previously if a test failed, there was a possibility of the executor being removed when python was closing, + causing it to print ignored errors on already unloaded modules. + + 1.3.3 + ------- + + - [enhancement] use executor's context manager to start/stop postrgesql server in a fixture + + 1.3.2 + ------- + + - [bugfix] version regexp to correctly catch postgresql 10 + + 1.3.1 + ------- + + - [enhancement] explicitly turn off logging_collector + + 1.3.0 + ------- + + - [feature] pypy compatibility + + 1.2.0 + ------- + + - [bugfix] - disallow connection to database before it gets dropped. + + .. note:: + + Otherwise it caused random test subprocess to connect again and this the drop was unsucessfull which resulted in many more test failes on setup. + + - [cleanup] - removed path.py dependency + + 1.1.1 + ------- + + - [bugfix] - Fixing the default pg_ctl path creation + + 1.1.0 + ------- + + - [feature] - migrate usage of getfuncargvalue to getfixturevalue. require at least pytest 3.0.0 + + 1.0.0 + ------- + + - create command line and pytest.ini configuration options for postgresql starting parameters + - create command line and pytest.ini configuration options for postgresql username + - make the port random by default + - create command line and pytest.ini configuration options for executable + - create command line and pytest.ini configuration options for host + - create command line and pytest.ini configuration options for port + - Extracted code from pytest-dbfixtures + +Keywords: tests py.test pytest fixture postgresql +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: docs +Provides-Extra: tests diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..3e12701 --- /dev/null +++ b/README.rst @@ -0,0 +1,129 @@ +pytest-postgresql +================= + +.. image:: https://img.shields.io/pypi/v/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Latest PyPI version + +.. image:: https://img.shields.io/pypi/wheel/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Wheel Status + +.. image:: https://img.shields.io/pypi/pyversions/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Supported Python Versions + +.. image:: https://img.shields.io/pypi/l/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: License + +Package status +-------------- + +.. image:: https://travis-ci.org/ClearcodeHQ/pytest-postgresql.svg?branch=v1.3.4 + :target: https://travis-ci.org/ClearcodeHQ/pytest-postgresql + :alt: Tests + +.. image:: https://coveralls.io/repos/ClearcodeHQ/pytest-postgresql/badge.png?branch=v1.3.4 + :target: https://coveralls.io/r/ClearcodeHQ/pytest-postgresql?branch=v1.3.4 + :alt: Coverage Status + +.. image:: https://requires.io/github/ClearcodeHQ/pytest-postgresql/requirements.svg?tag=v1.3.4 + :target: https://requires.io/github/ClearcodeHQ/pytest-postgresql/requirements/?tag=v1.3.4 + :alt: Requirements Status + +What is this? +============= + +This is a pytest plugin, that enables you to test your code that relies on a running PostgreSQL Database. +It allows you to specify fixtures for PostgreSQL process and client. + +How to use +========== + +.. warning:: + + Tested on PostgreSQL versions > 9.x. See tests for more details. + +Plugin contains two fixtures + +* **postgresql** - it's a client fixture that has functional scope. After each test it ends all leftover connections, and drops test database from PostgreSQL ensuring repeatability. +* **postgresql_proc** - session scoped fixture, that starts PostgreSQL instance at it's first use and stops at the end of the tests. + +Simply include one of these fixtures into your tests fixture list. + +You can also create additional postgresql client and process fixtures if you'd need to: + + +.. code-block:: python + + from pytest_postgresql import factories + + postgresql_my_proc = factories.postgresql_proc( + port=None, logsdir='/tmp') + postgresql_my = factories.postgresql('postgresql_my_proc') + +.. note:: + + Each PostgreSQL process fixture can be configured in a different way than the others through the fixture factory arguments. + +Configuration +============= + +You can define your settings in three ways, it's fixture factory argument, command line option and pytest.ini configuration option. +You can pick which you prefer, but remember that these settings are handled in the following order: + + * ``Fixture factory argument`` + * ``Command line option`` + * ``Configuration option in your pytest.ini file`` + ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| PostgreSQL option | Fixture factory argument | Command line option | pytest.ini option | Default | ++==========================+==========================+============================+==========================+====================================+ +| Path to executable | executable | --postgresql-exec | postgresql_exec | /usr/lib/postgresql/9.1/bin/pg_ctl | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| host | host | --postgresql-host | postgresql_host | 127.0.0.1 | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| port | port | --postgresql-port | postgresql_port | random | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| postgresql user | user | --postgresql-user | postgresql_user | postgres | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| Starting parameters | startparams | --postgresql-startparams | postgresql_startparams | -w | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| Log directory location | logsdir | --postgresql-logsdir | postgresql_logsdir | $TMPDIR | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| Log filename's prefix | logsprefix | --postgresql-logsprefix | postgresql_logsprefix | | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ +| Location for unixsockets | unixsocket | --postgresql-unixsocketdir | postgresql_unixsocketdir | $TMPDIR | ++--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + +Example usage: + +* pass it as an argument in your own fixture + + .. code-block:: python + + postgresql_proc = factories.postgresql_proc( + port=8888) + +* use ``--postgresql-port`` command line option when you run your tests + + .. code-block:: + + py.test tests --postgresql-port=8888 + + +* specify your port as ``postgresql_port`` in your ``pytest.ini`` file. + + To do so, put a line like the following under the ``[pytest]`` section of your ``pytest.ini``: + + .. code-block:: ini + + [pytest] + postgresql_port = 8888 + +Package resources +----------------- + +* Bug tracker: https://github.com/ClearcodeHQ/pytest-postgresql/issues + diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..fe20658 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,153 @@ +# 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) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html html_venv dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +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 " 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 " 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 " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +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." + +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/Subscribepyramidplugin.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Subscribepyramidplugin.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Subscribepyramidplugin" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Subscribepyramidplugin" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +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)." + +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." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +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)." + +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." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +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." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..0c5f8f6 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +set I18NSPHINXOPTS=%SPHINXOPTS% source +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. 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. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +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\Subscribepyramidplugin.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Subscribepyramidplugin.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" == "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" == "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 +) + +:end diff --git a/docs/source/authors.rst b/docs/source/authors.rst new file mode 100644 index 0000000..0181789 --- /dev/null +++ b/docs/source/authors.rst @@ -0,0 +1,3 @@ +.. _authors: + +.. include:: ../../AUTHORS.rst diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100644 index 0000000..cca3726 --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1,3 @@ +.. _changelog: + +.. include:: ../../CHANGES.rst diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..18ed60a --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,312 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016 by Clearcode +# and associates (see AUTHORS). + +# This file is part of pytest-postgresql. + +# pytest-postgresql is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pytest-postgresql is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with pytest-postgresql. If not, see . + + +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.intersphinx'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pytest-postgresql' +basename = ''.join(project.split('.')) +author = u'Clearcode - The A Room' +copyright = u'2016, ' + author + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. + +from pypt import __version__ + +# The full version, including alpha/beta/rc tags. +release = __version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# 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'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = basename + 'doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + #'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', basename + '.tex', project + ' Documentation', + author, 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', basename, project + u' Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# 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', basename, project + u' Documentation', + author, basename, 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + + +# -- Options for Epub output --------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = u'2016, ' + author + +# The language of the text. It defaults to the language option +# or en if the language is not set. +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# epub_identifier = '' + +# A unique identification for the text. +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# epub_cover = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +# epub_exclude_files = [] + +# The depth of the table of contents in toc.ncx. +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# epub_tocdup = True + +# Autodoc configuration: + +autoclass_content = 'both' +autodoc_default_flags = ['members', 'show-inheritance'] + +# Intersphinx configuration +intersphinx_mapping = {'python': ('http://docs.python.org/', None)} diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst new file mode 100644 index 0000000..2b6578f --- /dev/null +++ b/docs/source/contributing.rst @@ -0,0 +1,3 @@ +.. _contributing: + +.. include:: ../../CONTRIBUTING.rst diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..a27d625 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,19 @@ +.. include:: ../../README.rst + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + contributing + changelog + + +License +------- + +Copyright (c) 2016 by pypt authors and contributors. See :ref:`authors` + +This module is part of pytest-postgresql and is released under +the MIT License (MIT): http://opensource.org/licenses/MIT diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1e3eb36 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,7 @@ +[wheel] +universal = 1 + +[egg_info] +tag_build = +tag_date = 0 + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e4edcbe --- /dev/null +++ b/setup.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016 by Clearcode +# and associates (see AUTHORS). + +# This file is part of pytest-postgresql. + +# pytest-postgresql is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pytest-postgresql is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with pytest-postgresql. If not, see . +"""pytest-postgresql setup.py module.""" + +import os +from setuptools import setup, find_packages + +here = os.path.dirname(__file__) + + +def read(fname): + """ + Read given file's content. + + :param str fname: file name + :returns: file contents + :rtype: str + """ + return open(os.path.join(here, fname)).read() + + +requirements = [ + 'pytest>=3.0.0', + 'port-for', + 'mirakuru' +] + +test_requires = [ + 'pytest-cov==2.5.1', + 'pytest-xdist==1.22.2', +] + +extras_require = { + 'docs': ['sphinx'], + 'tests': test_requires, + ': platform_python_implementation != "PyPy"': ['psycopg2'], + ': platform_python_implementation == "PyPy"': ['psycopg2cffi'], +} + +setup_requires = [ + 'setuptools>=21', + 'pip>=9' +] + + +setup( + name='pytest-postgresql', + version='1.3.4', + description='Postgresql fixtures and fixture factories for Pytest.', + long_description=( + read('README.rst') + '\n\n' + read('CHANGES.rst') + ), + keywords='tests py.test pytest fixture postgresql', + author='Clearcode - The A Room', + author_email='thearoom@clearcode.cc', + url='https://github.com/ClearcodeHQ/pytest-postgresql', + license='LGPLv3+', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: ' + 'GNU Lesser General Public License v3 or later (LGPLv3+)', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Software Development :: Libraries :: Python Modules', + ], + package_dir={'': 'src'}, + packages=find_packages('src'), + install_requires=requirements, + tests_require=test_requires, + setup_requires=setup_requires, + test_suite='tests', + entry_points={ + 'pytest11': [ + 'pytest_postgresql = pytest_postgresql.plugin' + ]}, + include_package_data=True, + zip_safe=False, + extras_require=extras_require, +) diff --git a/src/pytest_postgresql.egg-info/PKG-INFO b/src/pytest_postgresql.egg-info/PKG-INFO new file mode 100644 index 0000000..d001f46 --- /dev/null +++ b/src/pytest_postgresql.egg-info/PKG-INFO @@ -0,0 +1,222 @@ +Metadata-Version: 2.1 +Name: pytest-postgresql +Version: 1.3.4 +Summary: Postgresql fixtures and fixture factories for Pytest. +Home-page: https://github.com/ClearcodeHQ/pytest-postgresql +Author: Clearcode - The A Room +Author-email: thearoom@clearcode.cc +License: LGPLv3+ +Description: pytest-postgresql + ================= + + .. image:: https://img.shields.io/pypi/v/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Latest PyPI version + + .. image:: https://img.shields.io/pypi/wheel/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Wheel Status + + .. image:: https://img.shields.io/pypi/pyversions/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: Supported Python Versions + + .. image:: https://img.shields.io/pypi/l/pytest-postgresql.svg + :target: https://pypi.python.org/pypi/pytest-postgresql/ + :alt: License + + Package status + -------------- + + .. image:: https://travis-ci.org/ClearcodeHQ/pytest-postgresql.svg?branch=v1.3.4 + :target: https://travis-ci.org/ClearcodeHQ/pytest-postgresql + :alt: Tests + + .. image:: https://coveralls.io/repos/ClearcodeHQ/pytest-postgresql/badge.png?branch=v1.3.4 + :target: https://coveralls.io/r/ClearcodeHQ/pytest-postgresql?branch=v1.3.4 + :alt: Coverage Status + + .. image:: https://requires.io/github/ClearcodeHQ/pytest-postgresql/requirements.svg?tag=v1.3.4 + :target: https://requires.io/github/ClearcodeHQ/pytest-postgresql/requirements/?tag=v1.3.4 + :alt: Requirements Status + + What is this? + ============= + + This is a pytest plugin, that enables you to test your code that relies on a running PostgreSQL Database. + It allows you to specify fixtures for PostgreSQL process and client. + + How to use + ========== + + .. warning:: + + Tested on PostgreSQL versions > 9.x. See tests for more details. + + Plugin contains two fixtures + + * **postgresql** - it's a client fixture that has functional scope. After each test it ends all leftover connections, and drops test database from PostgreSQL ensuring repeatability. + * **postgresql_proc** - session scoped fixture, that starts PostgreSQL instance at it's first use and stops at the end of the tests. + + Simply include one of these fixtures into your tests fixture list. + + You can also create additional postgresql client and process fixtures if you'd need to: + + + .. code-block:: python + + from pytest_postgresql import factories + + postgresql_my_proc = factories.postgresql_proc( + port=None, logsdir='/tmp') + postgresql_my = factories.postgresql('postgresql_my_proc') + + .. note:: + + Each PostgreSQL process fixture can be configured in a different way than the others through the fixture factory arguments. + + Configuration + ============= + + You can define your settings in three ways, it's fixture factory argument, command line option and pytest.ini configuration option. + You can pick which you prefer, but remember that these settings are handled in the following order: + + * ``Fixture factory argument`` + * ``Command line option`` + * ``Configuration option in your pytest.ini file`` + + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | PostgreSQL option | Fixture factory argument | Command line option | pytest.ini option | Default | + +==========================+==========================+============================+==========================+====================================+ + | Path to executable | executable | --postgresql-exec | postgresql_exec | /usr/lib/postgresql/9.1/bin/pg_ctl | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | host | host | --postgresql-host | postgresql_host | 127.0.0.1 | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | port | port | --postgresql-port | postgresql_port | random | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | postgresql user | user | --postgresql-user | postgresql_user | postgres | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Starting parameters | startparams | --postgresql-startparams | postgresql_startparams | -w | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Log directory location | logsdir | --postgresql-logsdir | postgresql_logsdir | $TMPDIR | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Log filename's prefix | logsprefix | --postgresql-logsprefix | postgresql_logsprefix | | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + | Location for unixsockets | unixsocket | --postgresql-unixsocketdir | postgresql_unixsocketdir | $TMPDIR | + +--------------------------+--------------------------+----------------------------+--------------------------+------------------------------------+ + + Example usage: + + * pass it as an argument in your own fixture + + .. code-block:: python + + postgresql_proc = factories.postgresql_proc( + port=8888) + + * use ``--postgresql-port`` command line option when you run your tests + + .. code-block:: + + py.test tests --postgresql-port=8888 + + + * specify your port as ``postgresql_port`` in your ``pytest.ini`` file. + + To do so, put a line like the following under the ``[pytest]`` section of your ``pytest.ini``: + + .. code-block:: ini + + [pytest] + postgresql_port = 8888 + + Package resources + ----------------- + + * Bug tracker: https://github.com/ClearcodeHQ/pytest-postgresql/issues + + + + CHANGELOG + ========= + + 1.3.4 + ------- + + - [bugfix] properly detect if executor running and clean after executor is being stopped + + .. note:: + + Previously if a test failed, there was a possibility of the executor being removed when python was closing, + causing it to print ignored errors on already unloaded modules. + + 1.3.3 + ------- + + - [enhancement] use executor's context manager to start/stop postrgesql server in a fixture + + 1.3.2 + ------- + + - [bugfix] version regexp to correctly catch postgresql 10 + + 1.3.1 + ------- + + - [enhancement] explicitly turn off logging_collector + + 1.3.0 + ------- + + - [feature] pypy compatibility + + 1.2.0 + ------- + + - [bugfix] - disallow connection to database before it gets dropped. + + .. note:: + + Otherwise it caused random test subprocess to connect again and this the drop was unsucessfull which resulted in many more test failes on setup. + + - [cleanup] - removed path.py dependency + + 1.1.1 + ------- + + - [bugfix] - Fixing the default pg_ctl path creation + + 1.1.0 + ------- + + - [feature] - migrate usage of getfuncargvalue to getfixturevalue. require at least pytest 3.0.0 + + 1.0.0 + ------- + + - create command line and pytest.ini configuration options for postgresql starting parameters + - create command line and pytest.ini configuration options for postgresql username + - make the port random by default + - create command line and pytest.ini configuration options for executable + - create command line and pytest.ini configuration options for host + - create command line and pytest.ini configuration options for port + - Extracted code from pytest-dbfixtures + +Keywords: tests py.test pytest fixture postgresql +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Provides-Extra: docs +Provides-Extra: tests diff --git a/src/pytest_postgresql.egg-info/SOURCES.txt b/src/pytest_postgresql.egg-info/SOURCES.txt new file mode 100644 index 0000000..a82af0a --- /dev/null +++ b/src/pytest_postgresql.egg-info/SOURCES.txt @@ -0,0 +1,26 @@ +AUTHORS.rst +CHANGES.rst +CONTRIBUTING.rst +MANIFEST.in +README.rst +setup.cfg +setup.py +docs/Makefile +docs/make.bat +docs/source/authors.rst +docs/source/changelog.rst +docs/source/conf.py +docs/source/contributing.rst +docs/source/index.rst +src/pytest_postgresql/__init__.py +src/pytest_postgresql/executor.py +src/pytest_postgresql/factories.py +src/pytest_postgresql/plugin.py +src/pytest_postgresql/port.py +src/pytest_postgresql.egg-info/PKG-INFO +src/pytest_postgresql.egg-info/SOURCES.txt +src/pytest_postgresql.egg-info/dependency_links.txt +src/pytest_postgresql.egg-info/entry_points.txt +src/pytest_postgresql.egg-info/not-zip-safe +src/pytest_postgresql.egg-info/requires.txt +src/pytest_postgresql.egg-info/top_level.txt \ No newline at end of file diff --git a/src/pytest_postgresql.egg-info/dependency_links.txt b/src/pytest_postgresql.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/pytest_postgresql.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/pytest_postgresql.egg-info/entry_points.txt b/src/pytest_postgresql.egg-info/entry_points.txt new file mode 100644 index 0000000..e83af0c --- /dev/null +++ b/src/pytest_postgresql.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[pytest11] +pytest_postgresql = pytest_postgresql.plugin + diff --git a/src/pytest_postgresql.egg-info/not-zip-safe b/src/pytest_postgresql.egg-info/not-zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/pytest_postgresql.egg-info/not-zip-safe @@ -0,0 +1 @@ + diff --git a/src/pytest_postgresql.egg-info/requires.txt b/src/pytest_postgresql.egg-info/requires.txt new file mode 100644 index 0000000..1f0f381 --- /dev/null +++ b/src/pytest_postgresql.egg-info/requires.txt @@ -0,0 +1,16 @@ +pytest>=3.0.0 +port-for +mirakuru + +[: platform_python_implementation != "PyPy"] +psycopg2 + +[: platform_python_implementation == "PyPy"] +psycopg2cffi + +[docs] +sphinx + +[tests] +pytest-cov==2.5.1 +pytest-xdist==1.22.2 diff --git a/src/pytest_postgresql.egg-info/top_level.txt b/src/pytest_postgresql.egg-info/top_level.txt new file mode 100644 index 0000000..906e435 --- /dev/null +++ b/src/pytest_postgresql.egg-info/top_level.txt @@ -0,0 +1 @@ +pytest_postgresql diff --git a/src/pytest_postgresql/__init__.py b/src/pytest_postgresql/__init__.py new file mode 100644 index 0000000..989cf8d --- /dev/null +++ b/src/pytest_postgresql/__init__.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2016 by Clearcode +# and associates (see AUTHORS). + +# This file is part of pytest-postgresql. + +# pytest-postgresql is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pytest-postgresql is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with pytest-postgresql. If not, see . +"""Main module for pytest-postgresql.""" + +import logging +import platform + +__version__ = '1.3.4' + + +logger = logging.getLogger(__name__) + +if platform.python_implementation() == "PyPy": + import psycopg2cffi.compat + psycopg2cffi.compat.register() diff --git a/src/pytest_postgresql/executor.py b/src/pytest_postgresql/executor.py new file mode 100644 index 0000000..75baf41 --- /dev/null +++ b/src/pytest_postgresql/executor.py @@ -0,0 +1,125 @@ +# Copyright (C) 2013 by Clearcode +# and associates (see AUTHORS). + +# This file is part of pytest-dbfixtures. + +# pytest-dbfixtures is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pytest-dbfixtures is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with pytest-dbfixtures. If not, see . +"""PostgreSQL executor crafter around pg_ctl.""" + +import os.path +import re +import subprocess + +from mirakuru import TCPExecutor + + +class PostgreSQLExecutor(TCPExecutor): + """ + PostgreSQL executor running on pg_ctl. + + Based over an `pg_ctl program + `_ + """ + + BASE_PROC_START_COMMAND = ' '.join(( + "{executable} start -D {datadir}", + "-o \"-F -p {port} -c log_destination='stderr'", + "-c logging_collector=off -c %s='{unixsocketdir}'\"", + "-l {logfile} {startparams}" + )) + + VERSION_RE = re.compile('.* (?P\d+\.\d)') + + def __init__(self, executable, host, port, + datadir, unixsocketdir, logfile, startparams, + shell=False, timeout=60, sleep=0.1, user='postgres'): + """ + Initialize PostgreSQLExecutor executor. + + :param str executable: pg_ctl location + :param str host: host under which process is accessible + :param int port: port under which process is accessible + :param str datadir: path to postgresql datadir + :param str unixsocketdir: path to socket directory + :param str logfile: path to logfile for postgresql + :param str startparams: additional start parameters + :param bool shell: see `subprocess.Popen` + :param int timeout: time to wait for process to start or stop. + if None, wait indefinitely. + :param float sleep: how often to check for start/stop condition + :param str user: [default] postgresql's username used to manage + and access PostgreSQL + """ + self.executable = executable + self.user = user + self.version = self.version() + self.datadir = datadir + self.unixsocketdir = unixsocketdir + command = self.proc_start_command().format( + executable=self.executable, + datadir=self.datadir, + port=port, + unixsocketdir=self.unixsocketdir, + logfile=logfile, + startparams=startparams, + ) + super(PostgreSQLExecutor, self).__init__( + command, host, port, shell=shell, timeout=timeout, sleep=sleep) + + def proc_start_command(self): + """Based on postgres version return proper start command.""" + if float(self.version) > 9.2: + unix_socket_dir_arg_name = 'unix_socket_directories' + else: + unix_socket_dir_arg_name = 'unix_socket_directory' + return self.BASE_PROC_START_COMMAND % unix_socket_dir_arg_name + + def version(self): + """Detect postgresql version.""" + version_string = subprocess.check_output( + [self.executable, '--version']).decode('utf-8') + matches = self.VERSION_RE.search(version_string) + return matches.groupdict()['version'] + + def running(self): + """Check if server is still running.""" + if not os.path.exists(self.datadir): + return False + + try: + output = subprocess.check_output( + '{pg_ctl} status -D {datadir}'.format( + pg_ctl=self.executable, + datadir=self.datadir + ), + shell=True + ).decode('utf-8') + except subprocess.CalledProcessError as e: + if b'pg_ctl: no server running' in e.output: + return False + raise + + return "pg_ctl: server is running" in output + + def stop(self): + """Issue a stop request to executable.""" + subprocess.check_output( + '{pg_ctl} stop -D {datadir} -m f'.format( + pg_ctl=self.executable, + datadir=self.datadir, + port=self.port, + unixsocketdir=self.unixsocketdir + ), + shell=True) + super(PostgreSQLExecutor, self).stop() diff --git a/src/pytest_postgresql/factories.py b/src/pytest_postgresql/factories.py new file mode 100644 index 0000000..dc09c29 --- /dev/null +++ b/src/pytest_postgresql/factories.py @@ -0,0 +1,291 @@ +# Copyright (C) 2013-2016 by Clearcode +# and associates (see AUTHORS). + +# This file is part of pytest-dbfixtures. + +# pytest-dbfixtures is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pytest-dbfixtures is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with pytest-dbfixtures. If not, see . +"""Fixture factories for postgresql fixtures.""" + +import os.path +import time +import shutil +import platform +import subprocess +from tempfile import gettempdir + +import pytest +import psycopg2 + +from pytest_postgresql.executor import PostgreSQLExecutor +from pytest_postgresql.port import get_port + + +def get_config(request): + """Return a dictionary with config options.""" + config = {} + options = [ + 'exec', 'host', 'port', 'user', 'startparams', 'logsdir', + 'logsprefix', 'unixsocketdir' + ] + for option in options: + option_name = 'postgresql_' + option + conf = request.config.getoption(option_name) or \ + request.config.getini(option_name) + config[option] = conf + return config + + +START_INFO = 'database system is ready to accept connections' + + +def wait_for_postgres(logfile, awaited_msg): + """ + Wait for postgresql being started. + + :param str logfile: logfile path + :param str awaited_msg: awaited message + """ + # wait until logfile is created + while not os.path.isfile(logfile): + time.sleep(1) + + # wait for expected message. + while 1: + with open(logfile, 'r') as content_file: + content = content_file.read() + if awaited_msg in content: + break + time.sleep(1) + + +def remove_postgresql_directory(datadir): + """ + Remove directory created for postgresql run. + + :param str datadir: datadir path + """ + if os.path.isdir(datadir): + shutil.rmtree(datadir) + + +def init_postgresql_directory(postgresql_ctl, user, datadir): + """ + Initialize postgresql data directory. + + See `Initialize postgresql data directory + `_ + + :param str postgresql_ctl: ctl path + :param str user: postgresql username + :param str datadir: datadir path + + """ + # remove old one if exists first. + remove_postgresql_directory(datadir) + init_directory = ( + postgresql_ctl, 'initdb', + '-o "--auth=trust --username=%s"' % user, + '-D %s' % datadir, + ) + subprocess.check_output(' '.join(init_directory), shell=True) + + +def init_postgresql_database(user, host, port, db): + """ + Create database in postgresql. + + :param str user: postgresql username + :param str host: postgresql host + :param str port: postgresql port + :param str db: database name + """ + conn = psycopg2.connect(user=user, host=host, port=port) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cur = conn.cursor() + cur.execute('CREATE DATABASE {0};'.format(db)) + cur.close() + conn.close() + + +def drop_postgresql_database(user, host, port, db, version): + """ + Drop databse in postgresql. + + :param str user: postgresql username + :param str host: postgresql host + :param str port: postgresql port + :param str db: database name + :param str version: postgresql version number + """ + conn = psycopg2.connect(user=user, host=host, port=port) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cur = conn.cursor() + # We cannot drop the database while there are connections to it, so we + # terminate all connections first while not allowing new connections. + if float(version) >= 9.2: + pid_column = 'pid' + else: + pid_column = 'procpid' + cur.execute( + 'UPDATE pg_database SET datallowconn=false WHERE datname = %s;', + (db,)) + cur.execute( + 'SELECT pg_terminate_backend(pg_stat_activity.{0})' + 'FROM pg_stat_activity WHERE pg_stat_activity.datname = %s;'.format( + pid_column), + (db,)) + cur.execute('DROP DATABASE IF EXISTS {0};'.format(db)) + cur.close() + conn.close() + + +def postgresql_proc( + executable=None, host=None, port=-1, user=None, + startparams=None, unixsocketdir=None, logsdir=None, logs_prefix='', +): + """ + Postgresql process factory. + + :param str executable: path to postgresql_ctl + :param str host: hostname + :param str|int|tuple|set|list port: + exact port (e.g. '8000', 8000) + randomly selected port (None) - any random available port + -1 - command line or pytest.ini configured port + [(2000,3000)] or (2000,3000) - random available port from a given range + [{4002,4003}] or {4002,4003} - random of 4002 or 4003 ports + [(2000,3000), {4002,4003}] - random of given range and set + :param str user: postgresql username + :param str startparams: postgresql starting parameters + :param str unixsocketdir: directory to create postgresql's unixsockets + :param str logsdir: location for logs + :param str logs_prefix: prefix for log filename + :rtype: func + :returns: function which makes a postgresql process + """ + @pytest.fixture(scope='session') + def postgresql_proc_fixture(request): + """ + Process fixture for PostgreSQL. + + :param FixtureRequest request: fixture request object + :rtype: pytest_dbfixtures.executors.TCPExecutor + :returns: tcp executor + """ + config = get_config(request) + postgresql_ctl = executable or config['exec'] + # check if that executable exists, as it's no on system PATH + # only replace if executable isn't passed manually + if not os.path.exists(postgresql_ctl) and executable is None: + pg_bindir = subprocess.check_output( + ['pg_config', '--bindir'], universal_newlines=True + ).strip() + postgresql_ctl = os.path.join(pg_bindir, 'pg_ctl') + + pg_host = host or config['host'] + pg_port = get_port(port) or get_port(config['port']) + datadir = os.path.join( + gettempdir(), 'postgresqldata.{0}'.format(pg_port)) + pg_user = user or config['user'] + pg_unixsocketdir = unixsocketdir or config['unixsocketdir'] + pg_startparams = startparams or config['startparams'] + pg_logsdir = logsdir or config['logsdir'] + logfile_path = os.path.join( + pg_logsdir, '{prefix}postgresql.{port}.log'.format( + prefix=logs_prefix, + port=pg_port + )) + + init_postgresql_directory( + postgresql_ctl, pg_user, datadir + ) + + if 'FreeBSD' == platform.system(): + with (datadir / 'pg_hba.conf').open(mode='a') as f: + f.write('host all all 0.0.0.0/0 trust\n') + + postgresql_executor = PostgreSQLExecutor( + executable=postgresql_ctl, + host=pg_host, + port=pg_port, + user=pg_user, + datadir=datadir, + unixsocketdir=pg_unixsocketdir, + logfile=logfile_path, + startparams=pg_startparams, + ) + + # start server + with postgresql_executor: + if '-w' in pg_startparams: + wait_for_postgres(logfile_path, START_INFO) + + yield postgresql_executor + + remove_postgresql_directory(datadir) + + return postgresql_proc_fixture + + +def postgresql(process_fixture_name, db='tests'): + """ + Return connection fixture factory for PostgreSQL. + + :param str process_fixture_name: name of the process fixture + :param str db: database name + :rtype: func + :returns: function which makes a connection to postgresql + """ + @pytest.fixture + def postgresql_factory(request): + """ + Fixture factory for PostgreSQL. + + :param FixtureRequest request: fixture request object + :rtype: psycopg2.connection + :returns: postgresql client + """ + proc_fixture = request.getfixturevalue(process_fixture_name) + + # _, config = try_import('psycopg2', request) + pg_host = proc_fixture.host + pg_port = proc_fixture.port + pg_user = proc_fixture.user + pg_db = db + + init_postgresql_database( + pg_user, pg_host, pg_port, pg_db + ) + connection = psycopg2.connect( + dbname=pg_db, + user=pg_user, + host=pg_host, + port=pg_port + ) + + def drop_database(): + connection.close() + drop_postgresql_database( + pg_user, pg_host, pg_port, pg_db, + proc_fixture.version + ) + + request.addfinalizer(drop_database) + + return connection + + return postgresql_factory + + +__all__ = ('postgresql', 'postgresql_proc') diff --git a/src/pytest_postgresql/plugin.py b/src/pytest_postgresql/plugin.py new file mode 100644 index 0000000..32b04cd --- /dev/null +++ b/src/pytest_postgresql/plugin.py @@ -0,0 +1,143 @@ +# Copyright (C) 2016 by Clearcode +# and associates (see AUTHORS). + +# This file is part of pytest-postgresql. + +# pytest-dbfixtures is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pytest-postgresql is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with pytest-postgresql. If not, see . +"""Plugin module of pytest-postgresql.""" +from tempfile import gettempdir + +from pytest_postgresql import factories + + +_help_executable = 'Path to PostgreSQL executable' +_help_host = 'Host at which PostgreSQL will accept connections' +_help_port = 'Port at which PostgreSQL will accept connections' +_help_user = "PostgreSQL username" +_help_startparams = "Starting parameters for the PostgreSQL" +_help_logsdir = "Logs directory location" +_help_logsprefix = "Prefix for the log files" +_help_unixsocketdir = "Location of the socket directory" + + +def pytest_addoption(parser): + """Configure options for pytest-postgresql.""" + parser.addini( + name='postgresql_exec', + help=_help_executable, + default='/usr/lib/postgresql/9.1/bin/pg_ctl' + ) + + parser.addini( + name='postgresql_host', + help=_help_host, + default='127.0.0.1' + ) + + parser.addini( + name='postgresql_port', + help=_help_port, + default=None, + ) + + parser.addini( + name='postgresql_user', + help=_help_user, + default='postgres' + ) + + parser.addini( + name='postgresql_startparams', + help=_help_startparams, + default='-w' + ) + + parser.addini( + name='postgresql_logsdir', + help=_help_logsdir, + default=gettempdir() + ) + + parser.addini( + name='postgresql_logsprefix', + help=_help_logsprefix, + default='' + ) + + parser.addini( + name='postgresql_unixsocketdir', + help=_help_unixsocketdir, + default=gettempdir() + ) + + parser.addoption( + '--postgresql-exec', + action='store', + metavar='path', + dest='postgresql_exec', + help=_help_executable + ) + + parser.addoption( + '--postgresql-host', + action='store', + dest='postgresql_host', + help=_help_host, + ) + + parser.addoption( + '--postgresql-port', + action='store', + dest='postgresql_port', + help=_help_port + ) + + parser.addoption( + '--postgresql-user', + action='store', + dest='postgresql_user', + help=_help_user + ) + + parser.addoption( + '--postgresql-startparams', + action='store', + dest='postgresql_startparams', + help=_help_startparams + ) + + parser.addoption( + '--postgresql-logsdir', + action='store', + dest='postgresql_logsdir', + help=_help_logsdir + ) + + parser.addoption( + '--postgresql-logsprefix', + action='store', + dest='postgresql_logsprefix', + help=_help_logsprefix + ) + + parser.addoption( + '--postgresql-unixsocketdir', + action='store', + dest='postgresql_unixsocketdir', + help=_help_unixsocketdir + ) + + +postgresql_proc = factories.postgresql_proc() +postgresql = factories.postgresql('postgresql_proc') diff --git a/src/pytest_postgresql/port.py b/src/pytest_postgresql/port.py new file mode 100644 index 0000000..e8a43db --- /dev/null +++ b/src/pytest_postgresql/port.py @@ -0,0 +1,88 @@ +# Copyright (C) 2016 by Clearcode +# and associates (see AUTHORS). + +# This file is part of pytest-postgresql. + +# pytest-postgresql is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# pytest-postgresql is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with pytest-postgresql. If not, see . +"""Helpers for port-for package.""" +from itertools import chain + +import port_for + + +class InvalidPortsDefinition(ValueError): + """Exception raised if ports definition is not a valid string.""" + + def __init__(self, ports): + """Construct InvalidPortsDefinition exception.""" + self.ports = ports + + def __str__(self): + """Return string representation.""" + return ( + 'Unknown format of ports: {0}.\n' + 'You should provide a ports range "[(4000,5000)]"' + 'or "(4000,5000)" or a comma-separated ports set' + '"[{4000,5000,6000}]" or list of ints "[400,5000,6000,8000]"' + 'or all of them "[(20000, 30000), {48889, 50121}, 4000, 4004]"' + ).format(self.ports) + + +def get_port(ports): + """ + Retun a random available port. + + If there's only one port passed (e.g. 5000 or '5000') function + does not check if port is available. + it there's -1 passed as an argument, function returns None. + When a range or list of ports is passed `port_for` external package + is used in order to find a free port. + + :param str|int|tuple|set|list port: + exact port (e.g. '8000', 8000) + randomly selected port (None) - any random available port + [(2000,3000)] or (2000,3000) - random available port from a given range + [{4002,4003}] or {4002,4003} - random of 4002 or 4003 ports + [(2000,3000), {4002,4003}] -random of given orange and set + :returns: a random free port + :raises: ValueError + """ + if ports == -1: + return None + elif not ports: + return port_for.select_random(None) + + try: + return int(ports) + except TypeError: + pass + + ports_set = set() + + try: + if not isinstance(ports, list): + ports = [ports] + ranges = port_for.utils.ranges_to_set(filter_by_type(ports, tuple)) + nums = set(filter_by_type(ports, int)) + sets = set(chain(*filter_by_type(ports, (set, frozenset)))) + ports_set = ports_set.union(ranges, sets, nums) + except ValueError: + raise InvalidPortsDefinition + + return port_for.select_random(ports_set) + + +def filter_by_type(lst, type_of): + """Return a list of elements with given type.""" + return [e for e in lst if isinstance(e, type_of)]