[Pytest-commit] commit/pytest: 2 new changesets
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Fri Sep 27 10:22:05 CEST 2013
2 new commits in pytest:
https://bitbucket.org/hpk42/pytest/commits/a3261d045144/
Changeset: a3261d045144
User: hpk42
Date: 2013-09-27 09:49:39
Summary: don't manipulate FDs at all if output capturing is turned off.
Affected #: 2 files
diff -r e4ca07d8ab69b50c6a13d04cbff8417d5a2ad0b0 -r a3261d045144baf409b00ca4a260727361d7f403 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -34,9 +34,10 @@
def pytest_configure(config):
config.option.verbose -= config.option.quiet
# we try hard to make printing resilient against
- # later changes on FD level.
+ # later changes on FD level. (unless capturing is turned off)
stdout = py.std.sys.stdout
- if hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
+ capture = config.option.capture != "no"
+ if capture and hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
try:
newstdout = py.io.dupfile(stdout, buffering=1,
encoding=stdout.encoding)
diff -r e4ca07d8ab69b50c6a13d04cbff8417d5a2ad0b0 -r a3261d045144baf409b00ca4a260727361d7f403 testing/test_terminal.py
--- a/testing/test_terminal.py
+++ b/testing/test_terminal.py
@@ -677,6 +677,13 @@
"*2 passed*"
])
+def test_nofd_manipulation_with_capture_disabled(testdir):
+ from _pytest.terminal import pytest_configure
+ config = testdir.parseconfig("--capture=no")
+ stdout = sys.stdout
+ pytest_configure(config)
+ reporter = config.pluginmanager.getplugin('terminalreporter')
+ assert reporter._tw._file == stdout
def test_tbstyle_native_setup_error(testdir):
p = testdir.makepyfile("""
@@ -684,7 +691,7 @@
@pytest.fixture
def setup_error_fixture():
raise Exception("error in exception")
-
+
def test_error_fixture(setup_error_fixture):
pass
""")
https://bitbucket.org/hpk42/pytest/commits/c1b01e0df4b3/
Changeset: c1b01e0df4b3
User: hpk42
Date: 2013-09-27 10:21:23
Summary: rework docs to demonstrate and discuss current yield syntax in more depth.
Affected #: 3 files
diff -r a3261d045144baf409b00ca4a260727361d7f403 -r c1b01e0df4b3bb4bd865af33bf0134e1ef6d519e doc/en/apiref.txt
--- a/doc/en/apiref.txt
+++ b/doc/en/apiref.txt
@@ -11,6 +11,7 @@
customize.txt
assert.txt
fixture.txt
+ yieldfixture.txt
parametrize.txt
xunit_setup.txt
capture.txt
diff -r a3261d045144baf409b00ca4a260727361d7f403 -r c1b01e0df4b3bb4bd865af33bf0134e1ef6d519e doc/en/fixture.txt
--- a/doc/en/fixture.txt
+++ b/doc/en/fixture.txt
@@ -79,7 +79,7 @@
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
- smtp = <smtplib.SMTP instance at 0x1ac66c8>
+ smtp = <smtplib.SMTP instance at 0x22530e0>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
@@ -198,7 +198,7 @@
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
- smtp = <smtplib.SMTP instance at 0x15b2d88>
+ smtp = <smtplib.SMTP instance at 0x165fa28>
def test_ehlo(smtp):
response = smtp.ehlo()
@@ -210,7 +210,7 @@
test_module.py:6: AssertionError
________________________________ test_noop _________________________________
- smtp = <smtplib.SMTP instance at 0x15b2d88>
+ smtp = <smtplib.SMTP instance at 0x165fa28>
def test_noop(smtp):
response = smtp.noop()
@@ -219,7 +219,7 @@
E assert 0
test_module.py:11: AssertionError
- ========================= 2 failed in 0.16 seconds =========================
+ ========================= 2 failed in 0.18 seconds =========================
You see the two ``assert 0`` failing and more importantly you can also see
that the same (module-scoped) ``smtp`` object was passed into the two
@@ -266,8 +266,7 @@
$ py.test -s -q --tb=no
FF
- 2 failed in 0.16 seconds
- teardown smtp
+ 2 failed in 0.20 seconds
We see that the ``smtp`` instance is finalized after the two
tests finished execution. Note that if we decorated our fixture
@@ -276,8 +275,9 @@
module itself does not need to change or know about these details
of fixture setup.
-Note that pytest-2.4 introduced an alternative `yield-context <yieldctx>`_
-mechanism which allows to interact nicely with context managers.
+Note that pytest-2.4 introduced an experimental alternative
+:ref:`yield fixture mechanism <yieldctx>` for easier context manager integration
+and more linear writing of teardown code.
.. _`request-context`:
@@ -310,8 +310,7 @@
$ py.test -s -q --tb=no
FF
- 2 failed in 0.17 seconds
- teardown smtp
+ 2 failed in 0.18 seconds
Let's quickly create another test module that actually sets the
server URL in its module namespace::
@@ -331,7 +330,7 @@
______________________________ test_showhelo _______________________________
test_anothersmtp.py:5: in test_showhelo
> assert 0, smtp.helo()
- E AssertionError: (250, 'hq.merlinux.eu')
+ E AssertionError: (250, 'mail.python.org')
voila! The ``smtp`` fixture function picked up our mail server name
from the module namespace.
@@ -378,7 +377,7 @@
================================= FAILURES =================================
__________________________ test_ehlo[merlinux.eu] __________________________
- smtp = <smtplib.SMTP instance at 0x1d1b680>
+ smtp = <smtplib.SMTP instance at 0x28d13b0>
def test_ehlo(smtp):
response = smtp.ehlo()
@@ -390,7 +389,7 @@
test_module.py:6: AssertionError
__________________________ test_noop[merlinux.eu] __________________________
- smtp = <smtplib.SMTP instance at 0x1d1b680>
+ smtp = <smtplib.SMTP instance at 0x28d13b0>
def test_noop(smtp):
response = smtp.noop()
@@ -401,7 +400,7 @@
test_module.py:11: AssertionError
________________________ test_ehlo[mail.python.org] ________________________
- smtp = <smtplib.SMTP instance at 0x1d237e8>
+ smtp = <smtplib.SMTP instance at 0x28d8440>
def test_ehlo(smtp):
response = smtp.ehlo()
@@ -412,7 +411,7 @@
test_module.py:5: AssertionError
________________________ test_noop[mail.python.org] ________________________
- smtp = <smtplib.SMTP instance at 0x1d237e8>
+ smtp = <smtplib.SMTP instance at 0x28d8440>
def test_noop(smtp):
response = smtp.noop()
@@ -421,7 +420,7 @@
E assert 0
test_module.py:11: AssertionError
- 4 failed in 6.04 seconds
+ 4 failed in 6.47 seconds
We see that our two test functions each ran twice, against the different
``smtp`` instances. Note also, that with the ``mail.python.org``
@@ -462,14 +461,14 @@
$ py.test -v test_appsetup.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev12 -- /home/hpk/venv/0/bin/python
- cachedir: /tmp/doc-exec-120/.cache
+ cachedir: /tmp/doc-exec-127/.cache
plugins: xdist, pep8, cov, cache, capturelog, instafail
collecting ... collected 2 items
test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED
test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED
- ========================= 2 passed in 6.98 seconds =========================
+ ========================= 2 passed in 6.07 seconds =========================
Due to the parametrization of ``smtp`` the test will run twice with two
different ``App`` instances and respective smtp servers. There is no
@@ -528,30 +527,30 @@
$ py.test -v -s test_module.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev12 -- /home/hpk/venv/0/bin/python
- cachedir: /tmp/doc-exec-120/.cache
+ cachedir: /tmp/doc-exec-127/.cache
plugins: xdist, pep8, cov, cache, capturelog, instafail
collecting ... collected 8 items
- test_module.py:15: test_0[1] PASSED
- test_module.py:15: test_0[2] PASSED
- test_module.py:17: test_1[mod1] PASSED
- test_module.py:19: test_2[1-mod1] PASSED
- test_module.py:19: test_2[2-mod1] PASSED
- test_module.py:17: test_1[mod2] PASSED
- test_module.py:19: test_2[1-mod2] PASSED
- test_module.py:19: test_2[2-mod2] PASSED
+ test_module.py:15: test_0[1] test0 1
+ PASSED
+ test_module.py:15: test_0[2] test0 2
+ PASSED
+ test_module.py:17: test_1[mod1] create mod1
+ test1 mod1
+ PASSED
+ test_module.py:19: test_2[1-mod1] test2 1 mod1
+ PASSED
+ test_module.py:19: test_2[2-mod1] test2 2 mod1
+ PASSED
+ test_module.py:17: test_1[mod2] create mod2
+ test1 mod2
+ PASSED
+ test_module.py:19: test_2[1-mod2] test2 1 mod2
+ PASSED
+ test_module.py:19: test_2[2-mod2] test2 2 mod2
+ PASSED
========================= 8 passed in 0.02 seconds =========================
- test0 1
- test0 2
- create mod1
- test1 mod1
- test2 1 mod1
- test2 2 mod1
- create mod2
- test1 mod2
- test2 1 mod2
- test2 2 mod2
You can see that the parametrized module-scoped ``modarg`` resource caused
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
@@ -728,61 +727,3 @@
``conftest.py`` files and finally builtin and third party plugins.
-.. _yieldctx:
-
-Fixture functions using "yield" / context manager integration
----------------------------------------------------------------
-
-.. versionadded:: 2.4
-
-pytest-2.4 allows fixture functions to use a ``yield`` instead
-of a ``return`` statement to provide a fixture value. Let's
-look at a quick example before discussing advantages::
-
- # content of conftest.py
-
- import smtplib
- import pytest
-
- @pytest.fixture(scope="module", yieldctx=True)
- def smtp():
- smtp = smtplib.SMTP("merlinux.eu")
- yield smtp # provide the fixture value
- print ("teardown smtp after a yield")
- smtp.close()
-
-In contrast to the `finalization`_ example, our fixture
-function uses a single ``yield`` to provide the ``smtp`` fixture
-value. The code after the ``yield`` statement serves as the
-teardown code, avoiding the indirection of registering a
-teardown function. More importantly, it also allows to
-seemlessly re-use existing context managers, for example::
-
- @pytest.fixture(yieldctx=True)
- def somefixture():
- with open("somefile") as f:
- yield f.readlines()
-
-The file ``f`` will be closed once ``somefixture`` goes out of scope.
-It is possible to achieve the same result by using a ``request.addfinalizer``
-call but it is more boilerplate and not very obvious unless
-you know about the exact ``__enter__|__exit__`` protocol of with-style
-context managers.
-
-For some background, here is the protocol pytest follows for when
-``yieldctx=True`` is specified in the fixture decorator:
-
-a) iterate once into the generator for producing the value
-b) iterate a second time for tearing the fixture down, expecting
- a StopIteration (which is produced automatically from the Python
- runtime when the generator returns).
-
-The teardown will always execute, independently of the outcome of
-test functions. You do **not need** to write the teardown code into a
-``try-finally`` clause like you would usually do with
-:py:func:`contextlib.contextmanager` decorated functions.
-
-If the fixture generator yields a second value pytest will report
-an error. Yielding cannot be used for parametrization, rather
-see `fixture-parametrize`_.
-
diff -r a3261d045144baf409b00ca4a260727361d7f403 -r c1b01e0df4b3bb4bd865af33bf0134e1ef6d519e doc/en/yieldfixture.txt
--- /dev/null
+++ b/doc/en/yieldfixture.txt
@@ -0,0 +1,132 @@
+
+.. _yieldctx:
+
+Fixture functions using "yield" / context manager integration
+---------------------------------------------------------------
+
+.. versionadded:: 2.4
+
+.. regendoc:wipe
+
+pytest-2.4 allows fixture functions to seemlessly use a ``yield`` instead
+of a ``return`` statement to provide a fixture value while otherwise
+fully supporting all other fixture features.
+
+.. note::
+
+ "yielding" fixture values is an experimental feature and its exact
+ declaration may change later but earliest in a 2.5 release. You can thus
+ safely use this feature in the 2.4 series but may need to adapt your
+ fixtures later. Test functions themselves will not need to change
+ (they can be completely ignorant of the return/yield modes of
+ fixture functions).
+
+Let's look at a simple standalone-example using the new ``yield`` syntax::
+
+ # content of test_yield.py
+
+ import pytest
+
+ @pytest.fixture(yieldctx=True)
+ def passwd():
+ print ("\nsetup before yield")
+ f = open("/etc/passwd")
+ yield f.readlines()
+ print ("teardown after yield")
+ f.close()
+
+ def test_has_lines(passwd):
+ print ("test called")
+ assert passwd
+
+In contrast to :ref:`finalization through registering callbacks
+<finalization>`, our fixture function used a ``yield``
+statement to provide the lines of the ``/etc/passwd`` file.
+The code after the ``yield`` statement serves as the teardown code,
+avoiding the indirection of registering a teardown callback function.
+
+Let's run it with output capturing disabled::
+
+ $ py.test -q -s test_yield.py
+
+ setup before yield
+ test called
+ .teardown after yield
+
+ 1 passed in 0.01 seconds
+
+We can also seemlessly use the new syntax with ``with`` statements.
+Let's simplify the above ``passwd`` fixture::
+
+ # content of test_yield2.py
+
+ import pytest
+
+ @pytest.fixture(yieldctx=True)
+ def passwd():
+ with open("/etc/passwd") as f:
+ yield f.readlines()
+
+ def test_has_lines(passwd):
+ assert len(passwd) >= 1
+
+The file ``f`` will be closed after the test finished execution
+because the Python ``file`` object supports finalization when
+the ``with`` statement ends.
+
+Note that the new syntax is fully integrated with using ``scope``,
+``params`` and other fixture features. Changing existing
+fixture functions to use ``yield`` is thus straight forward.
+
+Discussion and future considerations / feedback
+++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+The yield-syntax has been discussed by pytest users extensively.
+In general, the advantages of the using a ``yield`` fixture syntax are:
+
+- easy provision of fixtures in conjunction with context managers.
+
+- no need to register a callback, providing for more synchronous
+ control flow in the fixture function. Also there is no need to accept
+ the ``request`` object into the fixture function just for providing
+ finalization code.
+
+However, there are also limitations or foreseeable irritations:
+
+- usually ``yield`` is typically used for producing multiple values.
+ But fixture functions can only yield exactly one value.
+ Yielding a second fixture value will get you an error.
+ It's possible we can evolve pytest to allow for producing
+ multiple values as an alternative to current parametrization.
+ For now, you can just use the normal
+ :ref:`fixture parametrization <fixture-parametrize>`
+ mechanisms together with ``yield``-style fixtures.
+
+- the ``yield`` syntax is similar to what
+ :py:func:`contextlib.contextmanager` decorated functions
+ provide. With pytest fixture functions, the "after yield" part will
+ always be invoked, independently from the exception status
+ of the test function which uses the fixture. The pytest
+ behaviour makes sense if you consider that many different
+ test functions might use a module or session scoped fixture.
+ Some test functions might raise exceptions and others not,
+ so how could pytest re-raise a single exception at the
+ ``yield`` point in the fixture function?
+
+- lastly ``yield`` introduces more than one way to write
+ fixture functions, so what's the obvious way to a newcomer?
+ Newcomers reading the docs will see feature examples using the
+ ``return`` style so should use that, if in doubt.
+ Others can start experimenting with writing yield-style fixtures
+ and possibly help evolving them further.
+
+Some developers also expressed their preference for
+rather introduce a new ``@pytest.yieldfixture`` decorator
+instead of a keyword argument, or for assuming the above
+yield-semantics automatically by introspecting if a fixture
+function is a generator. Depending on more experiences and
+feedback during the 2.4 cycle, we revisit theses issues.
+
+If you want to feedback or participate in the ongoing
+discussion, please join our :ref:`contact channels`.
+you are most welcome.
Repository URL: https://bitbucket.org/hpk42/pytest/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
More information about the pytest-commit
mailing list