TurboGears 1.5 test failing with coverage

Issue #51 wontfix
Christoph Zwerschke created an issue

When running the test suite of TurboGears 1.5 with coverage, two tests are failing for me which do not fail without coverage.

You can reproduce the problem with the following test file:

{{{ class Foo:

def __init__(self, name):
    self.name = name

def __repr__(self):
    return "<Foo %r>" % self.name

class MetaFooList(type):

def __new__(mcs, cls_name, bases, cls_dict):
    declared = []
    for base in bases:
        declared.extend(getattr(base, 'declared', []))
    for name, value in cls_dict.items():
        if isinstance(value, Foo):
    declared.sort(key=lambda w: w.name)
    cls = type.__new__(mcs, cls_name, bases, cls_dict)
    cls.declared = declared
    return cls

class FooList(list):

__metaclass__ = MetaFooList

def __init__(self, *args):
    super(FooList, self).__init__(self.declared)
    if args:
        if len(args) == 1:
            args = args[0]
            if isinstance(args, Foo):
                args = [args]

def test_foolist():

w = Foo(name="foo")

class W(FooList):
    foo = w

w2 = Foo(name="bar")

class W2(W):
    bar = w2

foolist = W2()

assert len(foolist) == 2

if name == 'main': test_foolist() }}}

If you run this with coverage (3.2 or 3.3), the assert statement will be failing because the len of the foolist is 4 instead of 2.

Comments (13)

  1. Ned Batchelder repo owner

    I tried this on Python 2.5 and 2.6, and did not see the problem as you report it. The assert passed both with Python and with "coverage run".

  2. Christoph Zwerschke reporter

    I had tried this only on Py 2.5. Checked it on Py 2.6 now, and there it works. Maye it happens only with the py2.5-win-64 package, which I compiled with MinGW instead of Visual C++. Will test some more and report back.

  3. Christoph Zwerschke reporter

    Ok, I think I found the real cause. MinGW is not the problem.

    What I did was to "easy_install coverage" on Win Py 2.5 64bit. I naively believed easy_install would pick and install the proper 64bit exe file from PyPI, but alas, it did not - it downloaded the 32bit exe file instead, and installed it into the 64bit Python installation! These are obviously two bugs in easy_install: First, it picked the wrong file, and second, it mercylessly installed it into the unfitting environment without giving notice (if you run the exe file manually, it compains).

    Just out of curiosity, I tried the same on Win Py 2.6 64bit. There, easy_install downloaded the source code instead of the exe file (neither 32bit nor 64bit). Which is also bad, because Windows cannot build it, but not as bad as installing the wrong binary.

    Is this a known problem? Maybe we need to upload Windows binaries as eggs, too, so that easy_install can pick them up properly?

  4. Christoph Zwerschke reporter

    We're experiencing another strange issue with the TG 1.5 test suite: nearly all modules show 0% coverage. My suspicion is that it has something to do with TG 1.5 using peak.rules - the tracing seems to stop immediately after peak.rules is imported. Is this a known problem? What makes this really weird is that it does not work (i.e. shows 0% coverage) with any properly installed coverage on 32 or 64 bit Win or Linux, but it worked when I mistakenly installed the 32 bit coverage package for Win on a 64 bit Python (i.e. it shows proper coverage, except for the problem originally posted above). Do you have any explanation for this strange behavior?

  5. Ned Batchelder repo owner

    This is a known issue: TurboGears uses Peak, which includes some very strange manipulations using sys.settrace, which interferes with coverage.py. The "coverage run --timid" switch is made for precisely this problem. Try it and see if it helps.

  6. Christoph Zwerschke reporter

    Thanks - that explains a lot. In fact it works with the --timid option - but it also shows exactly the same problem as mentioned in the original ticket. So I guess what happens when a 32bit binary is installed on a 64bit system is simply that it falls back to the --timid method.

    Can you run the code above again with --timid and see if you can reproduce the problem (i.e. the assert fails for you as well)?

    Btw, I do not find a way to set --timid via the nose plugin except using a .coveragerc file in the current directory. Maybe the plugin should get an option to pass arbitrary parameters to coverage, or even a --cover-timid option. Or am I overlooking something?

  7. Ned Batchelder repo owner

    Now things are starting to make sense. The --timid switch means to use a native Python tracer instead of the one written in C. When you install the wrong kit, it can't compile the C extension, and so the native Python tracer is used, so it makes sense that --timid and "wrong kit" give the same results.

    And when I run --timid, your code fails, as you describe. I'll have to dig into why that is.

  8. Ned Batchelder repo owner


    Installing the wrong-architecture kit doesn't have a problem compiling the C extension, it comes with a compiled extension, which can't run because it is the wrong architecture.

    On the nose plugin: I'm not interested in adding every possible option to the nose plugin. One of the motivations for the .coveragerc support was so I could add features to coverage.py without also adding switches to the nose plugin. Is there a reason you don't want to write a .coveragerc file?

    Lastly, this bug has all the markings of a difficult one to debug, with an end-result of a bug in Python. Let's hope that isn't what it is, but I wouldn't hold out too much hope...

  9. Christoph Zwerschke reporter

    Right. If the tracer shared lib was compiled for the wrong architecture, importing it probably throws an ImportError, i.e. the behavior is the same as if it is not there or as if the --timid switch is set. Maybe there should be a warning printed when this happens, unless --timid is set explicitly? Then the behavior would not have appeared so puzzling at first.

    If .coveragerc should be used for configuring nose, then at least its location should be settable with an option of the noseplugin. The existence of that option would also give users a hint that there are more options they may want to consider.

  10. Log in to comment