Compare commits

..

36 Commits

Author SHA1 Message Date
Bruno Oliveira
3652e21f3b Prepare release 1.18.1 2017-07-05 21:25:10 -03:00
Bruno Oliveira
a84b106a3b Add issue links to changelog template 2017-07-05 21:17:16 -03:00
Bruno Oliveira
5d757f9db3 Merge pull request #178 from timyhou/locals_d_issue
Unserialize reprlocals to a ReprLocal instance
2017-07-05 12:46:41 -03:00
Bruno Oliveira
674c623ffa Merge pull request #180 from luisincrespo/patch-1
Update CHANGELOG.rst
2017-07-05 12:36:00 -03:00
Luis Crespo
1fca8ee16d Update CHANGELOG.rst 2017-07-05 11:17:54 -04:00
Luis Crespo
185595f596 Update CHANGELOG.rst 2017-07-05 11:17:36 -04:00
Luis Crespo
55bc8c49d6 Update CHANGELOG.rst
Update `1.18.0` entry in CHANGELOG to reflect that `pytest-xdist` now requires `pytest>=3.0.0`.
2017-07-05 11:14:52 -04:00
thou
530c9b234a Unserialize reprlocals to a ReprLocal instance 2017-06-30 15:00:50 -05:00
Bruno Oliveira
1ca9eff35d Fixed changelog wording to be more user friendly 2017-06-28 18:32:59 -03:00
Bruno Oliveira
5320d25e47 Merge pull request #171 from timyhou/longrepr.sections_issue
Serialization/Deserialization was dropping longrepr.sections
2017-06-28 18:31:19 -03:00
thou
96ad4d1cfd Added news fragment 2017-06-28 10:58:37 -05:00
thou
5c1a35fee4 Serialization / Deserialization was dropping longrepr.sections 2017-06-27 16:30:49 -05:00
Bruno Oliveira
665b8eda8d Merge pull request #170 from nicoddemus/release-1.18.0
release 1.18.0
2017-06-26 21:28:30 -03:00
Bruno Oliveira
cee78e9b20 CHANGELOG for 1.18.0 2017-06-26 15:16:48 -03:00
Bruno Oliveira
6e1154881d Merge pull request #164 from timyhou/master
Issue #130
2017-06-23 19:39:27 -03:00
Ronny Pfannschmidt
a96f6c9038 Merge pull request #168 from kain88-de/numprocesses-option
add '--numprocesses' as long option for '-n'
2017-06-23 09:45:46 +02:00
Bruno Oliveira
47b65716a3 Fix test for --numprocess flag 2017-06-22 21:47:34 -03:00
Max Linke
dbaf49cd69 add '--numprocesses' as long option for '-n'
This is more verbose in scripts that run pytest-xdist. It can also help
in some ci-helper scripts like to auto detect if  pytest-xdist should be
installed.
2017-06-22 21:53:16 +02:00
Bruno Oliveira
41c00ca47e Clarify PR template
Clarify that a new "file" should be added to "changelog" folder.
2017-06-22 16:41:27 -03:00
thou
f0dee10a0d Fix failing test since spawn.out does not contain "LOOPONFAILING" in pytest >= 3 2017-06-21 13:37:03 -05:00
Tim Hou
6256608669 Update tox and appveyor for CI tests to exclude pytest < 3
Update the install requires versions for pytest
2017-06-21 13:37:02 -05:00
thou
4a1f1d879b Updated serialize_report and unserialize_report to pass entire exception info using ReprExceptionInfo.
This addresses issue #130.
2017-06-21 13:36:58 -05:00
Ronny Pfannschmidt
2094157950 Merge pull request #162 from nicoddemus/let-setuptools-scm-manage-manifest
Let setuptools_scm manage the files that go into the package
2017-06-14 13:43:46 +02:00
Bruno Oliveira
7473683cdb Let setuptools_scm manage the files that go into the package
As discussed in #161, setuptools_scm will automatically
add all version controlled files to the package, so
MANIFEST.in is no longer needed

Fix #161
2017-06-14 07:58:57 -03:00
Bruno Oliveira
c48834aa69 Merge pull request #159 from nicoddemus/release-1.17.1
Release 1.17.1
2017-06-10 10:59:42 -03:00
Bruno Oliveira
6d9d6c6a34 Update HOWTORELEASE docs 2017-06-10 10:03:10 -03:00
Bruno Oliveira
1542aff326 Update CHANGELOG 2017-06-10 10:02:55 -03:00
Bruno Oliveira
cc95cf26e1 Skip uploading docs when publishing to PyPI
This should also solve the problem of not publishing wheels
2017-06-10 10:02:43 -03:00
Bruno Oliveira
eb90a7c82c Merge pull request #158 from nicoddemus/revert-pr-125
Revert PR 125 due to 1.17.0 breakage
2017-06-10 09:55:51 -03:00
Bruno Oliveira
afe8bad9ff Add news fragment
Fix #157
2017-06-10 09:44:55 -03:00
Bruno Oliveira
95a62460aa Revert "Mark test as complete at teardown."
This reverts commit 82b992a5fd.
2017-06-10 09:43:59 -03:00
Bruno Oliveira
8e2fdeee3e Revert "- Reinforce test_crashing_item to match test name instead of test"
This reverts commit 99f667380f.
2017-06-10 09:43:59 -03:00
Bruno Oliveira
fae1e02c1d Revert "Fix missing blank line."
This reverts commit 602694b817.
2017-06-10 09:43:59 -03:00
Ronny Pfannschmidt
3440f08a40 Merge pull request #156 from nicoddemus/release-howto-towncrier
Add towncrier and HOWTORELEASE docs
2017-06-10 09:00:47 +02:00
Bruno Oliveira
a9b064d992 Add HOWTORELEASE document
Fix #155
2017-06-09 19:50:54 -03:00
Bruno Oliveira
47875cbea9 Add towncrier for CHANGELOG management
Fix #154
2017-06-09 19:49:06 -03:00
19 changed files with 343 additions and 62 deletions

17
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,17 @@
Thanks for submitting a PR, your contribution is really appreciated!
Here's a quick checklist that should be present in PRs:
- [ ] Make sure to include reasonable tests for your change if necessary
- [ ] Add a *news* file into the `changelog` folder, following these guidelines:
* Name it `$issue_id.$type` for example `588.bug`
* If you don't have an issue_id change it to the PR id after creating it
* Ensure type is one of `removal`, `feature`, `bugfix`, `vendor`, `doc` or `trivial`
* Make sure to use full sentences with correct case and punctuation, for example:
```
Fix issue with non-ascii contents in doctest text files.
```

View File

@@ -28,6 +28,7 @@ notifications:
deploy:
provider: pypi
user: ronny
skip_upload_docs: true
password:
secure: cxmSDho5d+PYKEM4ZCg8ms1P4lzhYkrw6fEOm2HtTcsuCyY6aZMSgImWAnEYbJHSkdzgcxlXK9UKJ9B0YenXmBCkAr7UjdnpNXNmkySr0sYzlH/sfqt/dDATCHFaRKxnkOSOVywaDYhT9n8YudbXI77pXwD12i/CeSSJDbHhsu0JYUfAcb+D6YjRYoA2SEGCnzSzg+gDDfwXZx4ZiODCGLVwieNp1klCg88YROUE1BaYYNuUOONvfXX8+TWowbCF6ChH1WL/bZ49OStEYQNuYxZQZr4yClIqu9VJbchrU8j860K9ott2kkGTgfB/dDrQB/XncBubyIX9ikzCQAmmBXWAI3eyvWLPDk2Jz7kW2l2RT7syct80tCq3JhvQ1qdwr5ap7siocTLgnBW0tF4tkHSTFN3510fkc43npnp6FThebESQpnI24vqpwJ9hI/kW5mYi014Og2E/cpCXnz2XO8iZPDbqAMQpDsqEQoyhfGNgPTGp4K30TxRtwZBI5hHhDKnnR16fXtRgt1gYPvz/peUQvvpOm4JzIzGXPzluuutpnCBy75v5+oiwT3YRrLL/Meims9FtDDXL3qQubAE/ezIOOpm0N5XXV8DxIom8EN71yq5ab1tqhM+tBX7owRjy4FR4If2Q8feBdmTuh26DIQt/y+qSG8VkB9Sw/JCjc7c=
on:

View File

@@ -1,3 +1,63 @@
pytest-xdist 1.18.1 (2017-07-05)
================================
Bug Fixes
---------
- Fixed serialization of ``longrepr.sections`` during error reporting from
workers. (`#171 <https://github.com/pytest-dev/pytest-xdist/issues/171>`_)
- Fix ``ReprLocal`` not being unserialized breaking --showlocals usages. (`#176
<https://github.com/pytest-dev/pytest-xdist/issues/176>`_)
pytest-xdist 1.18.0 (2017-06-26)
================================
- ``pytest-xdist`` now requires ``pytest>=3.0.0``.
Features
--------
- Add long option `--numprocesses` as alternative for `-n`. (#168)
Bug Fixes
---------
- Fix serialization and deserialization dropping longrepr details. (#133)
pytest-xdist 1.17.1 (2017-06-10)
================================
Bug Fixes
---------
- Hot fix release reverting the change introduced by #124, unfortunately it
broke a number of test suites so we are reversing this change while we
investigate the problem. (#157)
Improved Documentation
----------------------
- Introduced ``towncrier`` for ``CHANGELOG`` management. (#154)
- Added ``HOWTORELEASE`` documentation. (#155)
..
You should *NOT* be adding new change log entries to this file, this
file is managed by towncrier. You *may* edit previous change logs to
fix problems like typo corrections or such.
To add a new change log entry, please see
https://pip.pypa.io/en/latest/development/#adding-a-news-entry
We named the news folder ``changelog``
.. towncrier release notes start
1.17.0
------

45
HOWTORELEASE.rst Normal file
View File

@@ -0,0 +1,45 @@
======================
Releasing pytest-xdist
======================
This document describes the steps to make a new ``pytest-xdist`` release.
Version
-------
``master`` should always be green and a potential release candidate. ``pytest-xdist`` follows
semantic versioning, so given that the current version is ``X.Y.Z``, to find the next version number
one needs to look at the ``changelog`` folder:
- If there is any file named ``*.feature``, then we must make a new **minor** release: next
release will be ``X.Y+1.0``.
- Otherwise it is just a **bug fix** release: ``X.Y.Z+1``.
Steps
-----
To publish a new release ``X.Y.Z``, the steps are as follows:
#. Create a new branch named ``release-X.Y.Z`` from the latest ``master``.
#. Install ``pytest-xdist`` and dev requirements in a virtualenv::
$ pip install -e . -r dev-requirements.txt
#. Update ``CHANGELOG.rst`` file by running::
$ towncrier --version X.Y.Z
It might ask for confirmation to remove news fragments; answer yes.
#. Commit and push the branch for review.
#. Once PR is **green** and **approved**, create and push a tag::
$ export VERSION=X.Y.Z
$ git tag v$VERSION release-$VERSION
$ git push git@github.com:pytest-dev/pytest-xdist.git v$VERSION
That will build the package and publish it on ``PyPI`` automatically.

View File

@@ -1,7 +0,0 @@
include CHANGELOG
include LICENSE
include README.txt
include setup.py
include tox.ini
graft testing
prune .git

View File

@@ -1,28 +1,13 @@
environment:
matrix:
# note: please use "tox --listenvs" to populate the build matrix
- TOXENV: "py26-pytest27"
- TOXENV: "py26-pytest28"
- TOXENV: "py26-pytest29"
- TOXENV: "py26-pytest30"
- TOXENV: "py27-pytest27"
- TOXENV: "py27-pytest28"
- TOXENV: "py27-pytest29"
- TOXENV: "py27-pytest30"
- TOXENV: "py34-pytest27"
- TOXENV: "py34-pytest28"
- TOXENV: "py34-pytest29"
- TOXENV: "py34-pytest30"
- TOXENV: "py35-pytest27"
- TOXENV: "py35-pytest28"
- TOXENV: "py35-pytest29"
- TOXENV: "py35-pytest30"
- TOXENV: "py36-pytest27"
- TOXENV: "py36-pytest28"
- TOXENV: "py36-pytest29"
- TOXENV: "py36-pytest30"
- TOXENV: "py27-pytest28-pexpect"
- TOXENV: "py35-pytest28-pexpect"
- TOXENV: "py27-pytest30-pexpect"
- TOXENV: "py35-pytest30-pexpect"
- TOXENV: "flakes"
- TOXENV: "readme"

39
changelog/_template.rst Normal file
View File

@@ -0,0 +1,39 @@
{% for section in sections %}
{% set underline = "-" %}
{% if section %}
{{section}}
{{ underline * section|length }}{% set underline = "~" %}
{% endif %}
{% if sections[section] %}
{% for category, val in definitions.items() if category in sections[section] %}
{{ definitions[category]['name'] }}
{{ underline * definitions[category]['name']|length }}
{% if definitions[category]['showcontent'] %}
{% for text, values in sections[section][category]|dictsort(by='value') %}
- {{ text }}{% if category != 'vendor' %} (`{{ values[0] }} <https://github.com/pytest-dev/pytest-xdist/issues/{{ values[0][1:] }}>`_){% endif %}
{% endfor %}
{% else %}
- {{ sections[section][category]['']|sort|join(', ') }}
{% endif %}
{% if sections[section][category]|length == 0 %}
No significant changes.
{% else %}
{% endif %}
{% endfor %}
{% else %}
No significant changes.
{% endif %}
{% endfor %}

1
dev-requirements.txt Normal file
View File

@@ -0,0 +1 @@
towncrier

36
pyproject.toml Normal file
View File

@@ -0,0 +1,36 @@
[tool.towncrier]
package = "xdist"
filename = "CHANGELOG.rst"
directory = "changelog/"
title_format = "pytest-xdist {version} ({project_date})"
template = "changelog/_template.rst"
[[tool.towncrier.type]]
directory = "removal"
name = "Deprecations and Removals"
showcontent = true
[[tool.towncrier.type]]
directory = "feature"
name = "Features"
showcontent = true
[[tool.towncrier.type]]
directory = "bugfix"
name = "Bug Fixes"
showcontent = true
[[tool.towncrier.type]]
directory = "vendor"
name = "Vendored Libraries"
showcontent = true
[[tool.towncrier.type]]
directory = "doc"
name = "Improved Documentation"
showcontent = true
[[tool.towncrier.type]]
directory = "trivial"
name = "Trivial Changes"
showcontent = false

View File

@@ -20,7 +20,7 @@ setup(
],
},
zip_safe=False,
install_requires=['execnet>=1.1', 'pytest>=2.7.0', 'py>=1.4.22'],
install_requires=['execnet>=1.1', 'pytest>=3.0.0', 'py>=1.4.22'],
setup_requires=['setuptools_scm'],
classifiers=[
'Development Status :: 5 - Production/Stable',

View File

@@ -454,31 +454,9 @@ def test_crashing_item(testdir):
pass
""")
result = testdir.runpytest("-n2", p)
result.stdout.fnmatch_lines(
["*crashed*::test_crash*", "*1 failed*1 passed*"])
def test_crashing_item_teardown(testdir):
p = testdir.makepyfile("""
import py
import pytest
import os
import time
@pytest.fixture
def crash_fixture(request):
def kill_me():
py.process.kill(os.getpid())
request.addfinalizer(kill_me)
def test_a(crash_fixture):
pass
def test_b():
pass
""")
result = testdir.runpytest("-n1", p)
result.stdout.fnmatch_lines(["*crashed*::test_a*", "*1 failed*2 passed*"])
result.stdout.fnmatch_lines([
"*crashed*test_crash*", "*1 failed*1 passed*"
])
def test_skipping(testdir):

View File

@@ -268,7 +268,7 @@ class TestFunctional:
""")
child = testdir.spawn_pytest("-f %s" % p)
child.expect("1 xpass")
child.expect("### LOOPONFAILING ####")
# child.expect("### LOOPONFAILING ####")
child.expect("waiting for changes")
child.kill(15)

View File

@@ -20,6 +20,10 @@ def test_dist_options(testdir):
check_options(config)
assert config.option.dist == "load"
assert config.option.tx == ['popen'] * 2
config = testdir.parseconfigure("--numprocesses", "2")
check_options(config)
assert config.option.dist == "load"
assert config.option.tx == ['popen'] * 2
config = testdir.parseconfigure("-d")
check_options(config)
assert config.option.dist == "load"

View File

@@ -79,6 +79,62 @@ def test_remoteinitconfig(testdir):
class TestReportSerialization:
def test_xdist_report_longrepr_reprcrash_130(self, testdir):
reprec = testdir.inline_runsource("""
import py
def test_fail(): assert False, 'Expected Message'
""")
reports = reprec.getreports("pytest_runtest_logreport")
assert len(reports) == 3
rep = reports[1]
added_section = ('Failure Metadata', str("metadata metadata"), "*")
rep.longrepr.sections.append(added_section)
d = serialize_report(rep)
check_marshallable(d)
a = unserialize_report("testreport", d)
# Check assembled == rep
assert a.__dict__.keys() == rep.__dict__.keys()
for key in rep.__dict__.keys():
if key != 'longrepr':
assert getattr(a, key) == getattr(rep, key)
assert rep.longrepr.reprcrash.lineno == a.longrepr.reprcrash.lineno
assert rep.longrepr.reprcrash.message == a.longrepr.reprcrash.message
assert rep.longrepr.reprcrash.path == a.longrepr.reprcrash.path
assert rep.longrepr.reprtraceback.entrysep \
== a.longrepr.reprtraceback.entrysep
assert rep.longrepr.reprtraceback.extraline \
== a.longrepr.reprtraceback.extraline
assert rep.longrepr.reprtraceback.style \
== a.longrepr.reprtraceback.style
assert rep.longrepr.sections == a.longrepr.sections
assert rep.longrepr.reprtraceback.reprentries \
== a.longrepr.reprtraceback.reprentries
# Missing section attribute PR171
assert added_section in a.longrepr.sections
def test_reprentries_serialization_170(self, testdir):
reprec = testdir.inline_runsource("""
def test_fail():
x = 0
assert x
""", '--showlocals', '-n1')
reports = reprec.getreports("pytest_runtest_logreport")
assert len(reports) == 3
rep = reports[1]
d = serialize_report(rep)
a = unserialize_report("testreport", d)
rep_entries = rep.longrepr.reprtraceback.reprentries
a_entries = a.longrepr.reprtraceback.reprentries
assert rep_entries == a_entries
for i in range(len(a_entries)):
assert rep_entries[i].lines == a_entries[i].lines
assert rep_entries[i].localssep == a_entries[i].localssep
assert rep_entries[i].reprfileloc == a_entries[i].reprfileloc
assert rep_entries[i].reprfuncargs == a_entries[i].reprfuncargs
assert rep_entries[i].reprlocals == a_entries[i].reprlocals
assert rep_entries[i].style == a_entries[i].style
def test_itemreport_outcomes(self, testdir):
reprec = testdir.inline_runsource("""
import py
@@ -108,7 +164,7 @@ class TestReportSerialization:
assert newrep.when == rep.when
assert newrep.keywords == rep.keywords
if rep.failed:
assert newrep.longrepr == str(rep.longrepr)
assert newrep.longreprtext == rep.longreprtext
def test_collectreport_passed(self, testdir):
reprec = testdir.inline_runsource("def test_func(): pass")

View File

@@ -1,8 +1,8 @@
[tox]
# if you change the envlist, please update .travis.yml file as well
envlist=
py{26,27,34,35,36}-pytest{27,28,29,30}
py{27,35}-pytest28-pexpect
py{26,27,34,35,36}-pytest{30}
py{27,35}-pytest{30}-pexpect
flakes
readme
@@ -14,9 +14,6 @@ deps =
pycmd
# to avoid .eggs
setuptools_scm
pytest27: pytest~=2.7.2
pytest28: pytest~=2.8.7
pytest29: pytest~=2.9.2
pytest30: pytest~=3.0.5
pexpect: pexpect
platform=

View File

@@ -660,7 +660,7 @@ class DSession:
If the node indicates it is finished with a test item, remove
the item from the pending list in the scheduler.
"""
if rep.when == "teardown" or (rep.when == "setup" and not rep.passed):
if rep.when == "call" or (rep.when == "setup" and not rep.passed):
self.sched.mark_test_complete(node, rep.item_index, rep.duration)
# self.report_line("testreport %s: %s" %(rep.id, rep.status))
rep.node = node

View File

@@ -20,7 +20,7 @@ def parse_numprocesses(s):
def pytest_addoption(parser):
group = parser.getgroup("xdist", "distributed and subprocess testing")
group._addoption(
'-n', dest="numprocesses", metavar="numprocesses",
'-n', '--numprocesses', dest="numprocesses", metavar="numprocesses",
action="store",
type=parse_numprocesses,
help="shortcut for '--dist=load --tx=NUM*popen', "

View File

@@ -98,10 +98,34 @@ class SlaveInteractor:
def serialize_report(rep):
def disassembled_report(rep):
reprtraceback = rep.longrepr.reprtraceback.__dict__
reprcrash = rep.longrepr.reprcrash.__dict__
new_entries = []
for entry in reprtraceback['reprentries']:
new_entry = entry.__dict__
for key, value in new_entry.items():
if hasattr(value, '__dict__'):
new_entry[key] = value.__dict__
new_entries.append(new_entry)
reprtraceback['reprentries'] = new_entries
return {
'reprcrash': reprcrash,
'reprtraceback': reprtraceback,
'sections': rep.longrepr.sections
}
import py
d = rep.__dict__.copy()
if hasattr(rep.longrepr, 'toterminal'):
d['longrepr'] = str(rep.longrepr)
if hasattr(rep.longrepr, 'reprtraceback') \
and hasattr(rep.longrepr, 'reprcrash'):
d['longrepr'] = disassembled_report(rep)
else:
d['longrepr'] = str(rep.longrepr)
else:
d['longrepr'] = rep.longrepr
for name in d:

View File

@@ -327,7 +327,52 @@ class SlaveController(object):
def unserialize_report(name, reportdict):
def assembled_report(reportdict):
from _pytest._code.code import (
ReprEntry,
ReprExceptionInfo,
ReprFileLocation,
ReprFuncArgs,
ReprLocals,
ReprTraceback
)
if reportdict['longrepr']:
if 'reprcrash' and 'reprtraceback' in reportdict['longrepr']:
reprtraceback = reportdict['longrepr']['reprtraceback']
reprcrash = reportdict['longrepr']['reprcrash']
unserialized_entries = []
for entry in reprtraceback['reprentries']:
reprfuncargs, reprfileloc, reprlocals = None, None, None
if entry['reprfuncargs']:
reprfuncargs = ReprFuncArgs(**entry['reprfuncargs'])
if entry['reprfileloc']:
reprfileloc = ReprFileLocation(**entry['reprfileloc'])
if entry['reprlocals']:
reprlocals = ReprLocals(entry['reprlocals']['lines'])
reprentry = ReprEntry(
lines=entry['lines'],
reprfuncargs=reprfuncargs,
reprlocals=reprlocals,
filelocrepr=reprfileloc,
style=entry['style']
)
unserialized_entries.append(reprentry)
reprtraceback['reprentries'] = unserialized_entries
exception_info = ReprExceptionInfo(
reprtraceback=ReprTraceback(**reprtraceback),
reprcrash=ReprFileLocation(**reprcrash),
)
for section in reportdict['longrepr']['sections']:
exception_info.addsection(*section)
reportdict['longrepr'] = exception_info
return reportdict
if name == "testreport":
return runner.TestReport(**reportdict)
return runner.TestReport(**assembled_report(reportdict))
elif name == "collectreport":
return runner.CollectReport(**reportdict)
return runner.CollectReport(**assembled_report(reportdict))