question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

pytest assumes __repr__ is safe on an object with unsuccessful constructor - causing Segmentation Fault

See original GitHub issue

When presenting a failed stack trace, pytest assumes that it is safe to repr an object even if the failure occurred in that object’s constructor. Take the following case:

import ctypes
import pytest

class SegRepr:
    """The most horrific class... raises a SegFault if you try to repr."""
    def __init__(self):
        raise ValueError("You really don't want me.")

    def __repr__(self):
        print("You can't get here, which is lucky because I'm "
              "about to SegFault.")
        import ctypes
        ctypes.string_at(0)


def test_bad():
    # The test case (it fails because of a ValueError being raised).
    assert SegRepr()

In everyday python, if you try to print(SegRepr()) you will get a ValueError because you can’t construct the object (though if you really try, you could do print(SegRepr.__new__(SegRepr))).

However with pytest, we end up calling through to the repr:

pytest seg.py -sv
======================================================================== test session starts ========================================================================
platform darwin -- Python 3.7.2, pytest-4.1.1, py-1.7.0, pluggy-0.8.1 -- /Users/pelson/dev/scitools/cf-units/seg_env/bin/python
cachedir: .pytest_cache
rootdir: /Users/pelson/dev/scitools/cf-units, inifile:
collected 1 item                                                                                                                                                    

seg.py::test_bad You can't get here, which is lucky because I'm about to SegFault.
Segmentation fault: 11

This might sound like an esoteric case, but this is a boiled down version of a case that was genuinely seen in https://github.com/SciTools/cf-units/issues/133#issuecomment-456051865. In my comment on that issue I do state that I think it could be a challenge to address this issue without losing some of the functionality that pytest already provides (like giving a really helpful stack trace with information about the objects at each level). I therefore think this issue should be addressed in one of two ways:

  • Recognise that it was the constructor that failed, and only do a repr if the exception was caused elsewhere (potentially hard)
  • Document the pytest feature that it will call __repr__ on potentially incomplete classes, and that objects that make use of state to call out to external interfaces run the real risk of Segmentation Faulting.

To be honest, I consider this issue as the beginning of addressing bullet-point 2 - hopefully others will see this and be able to recognise why their own libraries are failing with such ugly seg faults.

For my own next course of action, I intend to make my __repr__ more tolerant of incomplete initialisation within the library that this turned up for.

Apologies if this is a duplicate, however I went through each of the issues relating to Seg Faulting and could not find such a simple case that reproduced the issue.

Full environment definition

Using a basic conda environment on OSX Mojave, created with:

$ conda install pip python
$ pip install pytest

Spec:

# Name                    Version                   Build  Channel
atomicwrites              1.2.1                     <pip>
attrs                     18.2.0                    <pip>
ca-certificates           2018.12.5                     0    defaults
certifi                   2018.11.29               py37_0    defaults
libcxx                    4.0.1                hcfea43d_1    defaults
libcxxabi                 4.0.1                hcfea43d_1    defaults
libedit                   3.1.20181209         hb402a30_0    defaults
libffi                    3.2.1                h475c297_4    defaults
more-itertools            5.0.0                     <pip>
ncurses                   6.1                  h0a44026_1    defaults
openssl                   1.1.1a               h1de35cc_0    defaults
pip                       18.1                     py37_0    defaults
pluggy                    0.8.1                     <pip>
py                        1.7.0                     <pip>
pytest                    4.1.1                     <pip>
python                    3.7.2                haf84260_0    defaults
readline                  7.0                  h1de35cc_5    defaults
setuptools                40.6.3                   py37_0    defaults
six                       1.12.0                    <pip>
sqlite                    3.26.0               ha441bb4_0    defaults
tk                        8.6.8                ha441bb4_0    defaults
wheel                     0.32.3                   py37_0    defaults
xz                        5.2.4                h1de35cc_4    defaults
zlib                      1.2.11               h1de35cc_3    defaults

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:42 (41 by maintainers)

github_iconTop GitHub Comments

1reaction
asottilecommented, Oct 22, 2019

the object is constructed (that is, it has been created through __new__, it’s just potentially not initialized.

it’s pretty easy to repr such an object through debugger or (in this case) traceback. the fix should be in the faulty __repr__ implementation

1reaction
nicoddemuscommented, Jan 21, 2019

Do you agree that this should be documented in pytest?

I think @pelson’s suggestion is reasonable, in the sense that we might warn that pytest on failure will call repr() on the object, and depending on the library this might cause a segfault. This might seem “obvious”, but at least will appear on a search on the docs and might save someone some time.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Boost Python, propagate C++ callbacks to ... - Stack Overflow
What is happening is I start the program from the Python main method, if then the C++ listener tries to callback the Python...
Read more >
JPype User Guide — JPype 1.4.2_dev0 documentation
JPype only works with Python 3, thus all examples will be using Python version 3 syntax and assume the use of the Python...
Read more >
Bug listing with status RESOLVED with resolution OBSOLETE ...
Bug:1523 - "[IDEA] Offload work by distributing trivial ebuild maintenance to users, introduce a simple stability voting system and have a core team...
Read more >
I'm an Expert in Memory Management & Segfaults, Ask Me ...
I am assuming it is a segfault of some variant when I receive a response of double free or corruption (out) and Aborted...
Read more >
Changelog — pytest documentation
obj to return unbound rather than bound method. Fixes a crash during a failed teardown in unittest TestCases with non-default __init__ . Regressed...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found