METADATA 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. Metadata-Version: 2.1
  2. Name: pytest-localserver
  3. Version: 0.8.1
  4. Summary: pytest plugin to test server connections locally.
  5. Home-page: https://github.com/pytest-dev/pytest-localserver
  6. Author: Sebastian Rahlf
  7. Author-email: basti@redtoad.de
  8. Maintainer: David Zaslavsky
  9. Maintainer-email: diazona@ellipsix.net
  10. License: MIT License
  11. Keywords: pytest server localhost http smtp
  12. Classifier: Framework :: Pytest
  13. Classifier: Operating System :: OS Independent
  14. Classifier: Development Status :: 4 - Beta
  15. Classifier: Intended Audience :: Developers
  16. Classifier: License :: OSI Approved :: MIT License
  17. Classifier: Programming Language :: Python :: 3
  18. Classifier: Programming Language :: Python :: 3 :: Only
  19. Classifier: Programming Language :: Python :: 3.5
  20. Classifier: Programming Language :: Python :: 3.6
  21. Classifier: Programming Language :: Python :: 3.7
  22. Classifier: Programming Language :: Python :: 3.8
  23. Classifier: Programming Language :: Python :: 3.9
  24. Classifier: Programming Language :: Python :: 3.10
  25. Classifier: Programming Language :: Python :: 3.11
  26. Classifier: Programming Language :: Python :: 3.12
  27. Classifier: Topic :: Software Development :: Testing
  28. Requires-Python: >=3.5
  29. License-File: LICENSE
  30. License-File: AUTHORS
  31. Requires-Dist: werkzeug >=0.10
  32. Provides-Extra: smtp
  33. Requires-Dist: aiosmtpd ; extra == 'smtp'
  34. .. image:: https://img.shields.io/pypi/v/pytest-localserver.svg?style=flat
  35. :alt: PyPI Version
  36. :target: https://pypi.python.org/pypi/pytest-localserver
  37. .. image:: https://img.shields.io/pypi/pyversions/pytest-localserver.svg
  38. :alt: Supported Python versions
  39. :target: https://pypi.python.org/pypi/pytest-localserver
  40. .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
  41. :target: https://github.com/psf/black
  42. .. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest-localserver/master.svg
  43. :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest-localserver/master
  44. :alt: pre-commit.ci status
  45. ==================
  46. pytest-localserver
  47. ==================
  48. pytest-localserver is a plugin for the `pytest`_ testing framework which enables
  49. you to test server connections locally.
  50. Sometimes `monkeypatching`_ ``urllib2.urlopen()`` just does not cut it, for
  51. instance if you work with ``urllib2.Request``, define your own openers/handlers
  52. or work with ``httplib``. In these cases it may come in handy to have an HTTP
  53. server running locally which behaves just like the real thing [1]_. Well, look
  54. no further!
  55. Quickstart
  56. ==========
  57. Let's say you have a function to scrape HTML which only required to be pointed
  58. at a URL ::
  59. import requests
  60. def scrape(url):
  61. html = requests.get(url).text
  62. # some parsing happens here
  63. # ...
  64. return result
  65. You want to test this function in its entirety without having to rely on a
  66. remote server whose content you cannot control, neither do you want to waste
  67. time setting up a complex mechanism to mock or patch the underlying Python
  68. modules dealing with the actual HTTP request (of which there are more than one
  69. BTW). So what do you do?
  70. You simply use pytest's `funcargs feature`_ and simulate an entire server
  71. locally! ::
  72. def test_retrieve_some_content(httpserver):
  73. httpserver.serve_content(open('cached-content.html').read())
  74. assert scrape(httpserver.url) == 'Found it!'
  75. What happened here is that for the duration of your tests an HTTP server is
  76. started on a random port on localhost which will serve the content you tell it
  77. to and behaves just like the real thing.
  78. The added bonus is that you can test whether your code behaves gracefully if
  79. there is a network problem::
  80. def test_content_retrieval_fails_graciously(httpserver):
  81. httpserver.serve_content('File not found!', 404)
  82. pytest.raises(ContentNotFoundException, scrape, httpserver.url)
  83. The same thing works for SMTP servers, too::
  84. def test_sending_some_message(smtpserver):
  85. mailer = MyMailer(host=smtpserver.addr[0], port=smtpserver.addr[1])
  86. mailer.send(to='bob@example.com', from_='alice@example.com',
  87. subject='MyMailer v1.0', body='Check out my mailer!')
  88. assert len(smtpserver.outbox)==1
  89. Here an SMTP server is started which accepts e-mails being sent to it. The
  90. nice feature here is that you can actually check if the message was received
  91. and what was sent by looking into the smtpserver's ``outbox``.
  92. It is really that easy!
  93. Available funcargs
  94. ==================
  95. Here is a short overview of the available funcargs. For more details I suggest
  96. poking around in the code itself.
  97. ``httpserver``
  98. provides a threaded HTTP server instance running on localhost. It has the
  99. following attributes:
  100. * ``code`` - HTTP response code (int)
  101. * ``content`` - content of next response (str, bytes, or iterable of either)
  102. * ``headers`` - response headers (dict)
  103. * ``chunked`` - whether to chunk-encode the response (enumeration)
  104. Once these attributes are set, all subsequent requests will be answered with
  105. these values until they are changed or the server is stopped. A more
  106. convenient way to change these is ::
  107. httpserver.serve_content(content=None, code=200, headers=None, chunked=pytest_localserver.http.Chunked.NO)
  108. The ``chunked`` attribute or parameter can be set to
  109. * ``Chunked.YES``, telling the server to always apply chunk encoding
  110. * ``Chunked.NO``, telling the server to never apply chunk encoding
  111. * ``Chunked.AUTO``, telling the server to apply chunk encoding only if
  112. the ``Transfer-Encoding`` header includes ``chunked``
  113. If chunk encoding is applied, each str or bytes in ``content`` becomes one
  114. chunk in the response.
  115. The server address can be found in property
  116. * ``url``
  117. which is the string representation of tuple ``server_address`` (host as str,
  118. port as int).
  119. If you want to check which form fields have been POSTed, Try ::
  120. httpserver.serve_content(..., show_post_vars=True)
  121. which will display them as parsable text.
  122. If you need to inspect the requests sent to the server, a list of all
  123. received requests can be found in property
  124. * ``requests``
  125. which is a list of ``werkzeug.wrappers.Request`` objects.
  126. ``httpsserver``
  127. is the same as ``httpserver`` only with SSL encryption.
  128. ``smtpserver``
  129. provides a threaded SMTP server, with an API similar to ``smtpd.SMTPServer``,
  130. (the deprecated class from the Python standard library) running on localhost.
  131. It has the following attributes:
  132. * ``addr`` - server address as tuple (host as str, port as int)
  133. * ``outbox`` - list of ``email.message.Message`` instances received.
  134. Using your a WSGI application as test server
  135. ============================================
  136. As of version 0.3 you can now use a `WSGI application`_ to run on the test
  137. server ::
  138. from pytest_localserver.http import WSGIServer
  139. def simple_app(environ, start_response):
  140. """Simplest possible WSGI application"""
  141. status = '200 OK'
  142. response_headers = [('Content-type', 'text/plain')]
  143. start_response(status, response_headers)
  144. return ['Hello world!\n']
  145. @pytest.fixture
  146. def testserver(request):
  147. """Defines the testserver funcarg"""
  148. server = WSGIServer(application=simple_app)
  149. server.start()
  150. request.addfinalizer(server.stop)
  151. return server
  152. def test_retrieve_some_content(testserver):
  153. assert scrape(testserver.url) == 'Hello world!\n'
  154. Have a look at the following page for more information on WSGI:
  155. http://wsgi.readthedocs.org/en/latest/learn.html
  156. Download and Installation
  157. =========================
  158. You can install the plugin by running ::
  159. pip install pytest-localserver
  160. Alternatively, get the latest stable version from `PyPI`_ or the latest
  161. `bleeding-edge`_ from Github.
  162. License and Credits
  163. ===================
  164. This plugin is released under the MIT license. You can find the full text of
  165. the license in the LICENSE file.
  166. Copyright (C) 2010-2022 Sebastian Rahlf and others (see AUTHORS).
  167. Some parts of this package is based on ideas or code from other people:
  168. - I borrowed some implementation ideas for the httpserver from `linkchecker`_.
  169. - The implementation for the SMTP server is based on the `Mailsink recipe`_ by
  170. Adam Feuer, Matt Branthwaite and Troy Frever.
  171. - The HTTPS implementation is based on work by `Sebastien Martini`_.
  172. Thanks guys!
  173. Development and future plans
  174. ============================
  175. Feel free to clone the repository and add your own changes. Pull requests are
  176. always welcome!::
  177. git clone https://github.com/pytest-dev/pytest-localserver
  178. If you find any bugs, please file a `report`_.
  179. Test can be run with tox.
  180. I already have a couple of ideas for future versions:
  181. * support for FTP, SSH (maybe base all on twisted?)
  182. * making the SMTP outbox as convenient to use as ``django.core.mail.outbox``
  183. * add your own here!
  184. Preparing a release
  185. -------------------
  186. For package maintainers, here is how we release a new version:
  187. #. Ensure that the ``CHANGES`` file is up to date with the latest changes.
  188. #. Make sure that all tests pass on the version you want to release.
  189. #. Use the `new release form on Github`_ (or some other equivalent method) to
  190. create a new release, following the pattern of previous releases.
  191. * Each release has to be based on a tag. You can either create the tag first
  192. (e.g. using ``git tag``) and then make a release from that tag, or you can
  193. have Github create the tag as part of the process of making a release;
  194. either way works.
  195. * The tag name **must** be the `PEP 440`_-compliant version number prefixed
  196. by ``v``, making sure to include at least three version number components
  197. (e.g. ``v0.6.0``).
  198. * The "Auto-generate release notes" button will be useful in summarizing
  199. the changes since the last release.
  200. #. Using either the `release workflows page`_ or the link in the email you
  201. received about a "Deployment review", go to the workflow run created for
  202. the new release and click "Review deployments", then either approve or reject
  203. the two deployments, one to Test PyPI and one to real PyPI. (It should not be
  204. necessary to reject a deployment unless something really weird happens.)
  205. Once the deployment is approved, Github will automatically upload the files.
  206. ----
  207. .. [1] The idea for this project was born when I needed to check that `a piece
  208. of software`_ behaved itself when receiving HTTP error codes 404 and 500.
  209. Having unsuccessfully tried to mock a server, I stumbled across
  210. `linkchecker`_ which uses a the same idea to test its internals.
  211. .. _monkeypatching: http://pytest.org/latest/monkeypatch.html
  212. .. _pytest: http://pytest.org/
  213. .. _funcargs feature: http://pytest.org/latest/funcargs.html
  214. .. _linkchecker: http://linkchecker.sourceforge.net/
  215. .. _WSGI application: http://www.python.org/dev/peps/pep-0333/
  216. .. _PyPI: http://pypi.python.org/pypi/pytest-localserver/
  217. .. _bleeding-edge: https://github.com/pytest-dev/pytest-localserver
  218. .. _report: https://github.com/pytest-dev/pytest-localserver/issues/
  219. .. _tox: http://testrun.org/tox/
  220. .. _a piece of software: http://pypi.python.org/pypi/python-amazon-product-api/
  221. .. _Mailsink recipe: http://code.activestate.com/recipes/440690/
  222. .. _Sebastien Martini: http://code.activestate.com/recipes/442473/
  223. .. _PEP 440: https://peps.python.org/pep-0440/
  224. .. _build: https://pypa-build.readthedocs.io/en/latest/
  225. .. _twine: https://twine.readthedocs.io/en/stable/
  226. .. _new release form on Github: https://github.com/pytest-dev/pytest-localserver/releases/new
  227. .. _release workflows page: https://github.com/pytest-dev/pytest-localserver/actions/workflows/release.yml