Skip to content

Commit

Permalink
Merge pull request #6 from Crunch-io/fix-tests-for-python3
Browse files Browse the repository at this point in the history
Porting tests to Python 3
  • Loading branch information
szymonlipinski authored Jan 7, 2021
2 parents 187f110 + eeb52a6 commit 18ccedc
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 45 deletions.
12 changes: 10 additions & 2 deletions src/diagnose/probes.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ def probe_wrapper(*args, **kwargs):
for parent in gc.get_referrers(ref):
if parent is _resolved_target or parent is primary_patch:
continue
if parent is probe_wrapper:
# In Python 3.2+, `@functools.wraps(base)` above sets
# `wrapper.__wrapped__ = wrapped`. We don't want to
# patch that with itself!
continue

if getattr(parent, "__dict__", None) is ref:
# An attribute of a module or class or instance.
Expand Down Expand Up @@ -451,7 +456,7 @@ class WeakMethodPatch(object):
then replaces the given attribute of that object with the new value.
On stop/__exit__, replaces the same attribute with the previous value.
Used by FunctionPatch to replace references to functions which appear in
Used by FunctionProbe to replace references to functions which appear in
modules, classes, or other objects. Weak references are used internally
so that, if the object is removed from that module etc (has no more strong
references), then the patch is automatically abandoned.
Expand All @@ -462,6 +467,9 @@ def __init__(self, getter, attribute, new):
self.attribute = attribute
self.new = new

def __repr__(self):
return "%s(%s, %s, %s)" % (self.__class__.__name__, self.getter, self.attribute, self.new)

def get_original(self):
target = self.getter()
name = self.attribute
Expand Down Expand Up @@ -529,7 +537,7 @@ class DictPatch(object):
identified by the given key with a new object. On stop/__exit__,
replaces the same key with the previous object.
Used by FunctionPatch to replace references to functions which appear
Used by FunctionProbe to replace references to functions which appear
in any dictionary, such as a function registry.
"""

Expand Down
1 change: 1 addition & 0 deletions src/diagnose/test_fixtures.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import functools
from six.moves import xrange

import diagnose

Expand Down
7 changes: 2 additions & 5 deletions tests/test_instruments.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import datetime
from cStringIO import StringIO
import sys

from mock import call, patch
from six.moves import cStringIO, StringIO

import diagnose
from diagnose import probes
from diagnose.test_fixtures import a_func, Thing

from . import ProbeTestCase


registry = {}


Expand Down Expand Up @@ -211,7 +208,7 @@ def test_replace_instrument(self):
spec["target"] = target2
mgr.apply()
assert (
probes.active_probes[target2].instruments.values()[0].name
next(iter(probes.active_probes[target2].instruments.values())).name
== "a_func"
)
# The old target MUST be removed from the probes
Expand Down
98 changes: 60 additions & 38 deletions tests/test_probes.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from collections import defaultdict
import datetime
import gc
import sys
import time
import types
from collections import defaultdict

import six
from mock import patch
from six.moves import xrange

try:
from mock import _patch as MockPatch
Expand Down Expand Up @@ -42,7 +44,7 @@ def test_return_event_result(self):
assert result == "<ok>"

# The probe MUST have logged an entry
assert p.instruments.values()[0].results == ["<ok>"]
assert list(p.instruments.values())[0].results == ["<ok>"]

def test_return_event_elapsed(self):
with self.probe(
Expand All @@ -55,7 +57,7 @@ def test_return_event_elapsed(self):
assert result == "<ok>"

# The probe MUST have logged an entry
assert p.instruments.values()[0].results[0] < elapsed
assert list(p.instruments.values())[0].results[0] < elapsed

def test_return_event_locals(self):
with self.probe(
Expand All @@ -66,7 +68,7 @@ def test_return_event_locals(self):
assert result == "<ok>"

# The probe MUST have logged an entry
assert p.instruments.values()[0].results == [
assert list(p.instruments.values())[0].results == [
[
"arg",
"args",
Expand Down Expand Up @@ -132,22 +134,22 @@ def test_call_event_args(self):
assert result == "<ok>"

# The probe MUST have logged an entry
assert p.instruments.values()[0].results == [(t, "ok")]
assert list(p.instruments.values())[0].results == [(t, "ok")]

def test_call_event_elapsed(self):
with self.probe(
"test", "do", "diagnose.test_fixtures.Thing.do", "elapsed", event="call"
) as p:
errs = []
p.instruments.values()[0].handle_error = lambda probe: errs.append(
list(p.instruments.values())[0].handle_error = lambda probe: errs.append(
sys.exc_info()[1].args[0] if sys.exc_info()[1].args else ""
)
result = Thing().do("ok")

assert result == "<ok>"

# The probe MUST NOT have logged an entry...
assert p.instruments.values()[0].results == []
assert list(p.instruments.values())[0].results == []
# ...but the instrument MUST have handled the error:
assert errs == ["name 'elapsed' is not defined"]

Expand All @@ -164,7 +166,7 @@ def test_call_event_locals(self):
assert result == "<ok>"

# The probe MUST have logged an entry
assert p.instruments.values()[0].results == [
assert list(p.instruments.values())[0].results == [
["arg", "args", "frame", "kwargs", "now", "self", "start"]
]

Expand Down Expand Up @@ -261,7 +263,7 @@ def test_slowest_line(self):
)
assert hard_work(0, 10000) == 1000
assert [tags for tags, value in i.log] == [
["source:34: summary = len([x for x in output if x % 10 == 0])\n"]
["source:35: summary = len([x for x in output if x % 10 == 0])\n"]
]
assert [type(value) for tags, value in i.log] == [float]
finally:
Expand Down Expand Up @@ -307,17 +309,23 @@ def owner_types(obj):
if getattr(parent, "__dict__", None) is ref:
num_instances[type(parent)] += 1
break
return num_instances
return dict(num_instances)


class TestTargets(ProbeTestCase):
def test_probe_bad_mock(self):
p = probes.attach_to("diagnose.test_fixtures.Thing.notamethod")
with self.assertRaises(AttributeError) as exc:
p.start()

if six.PY2:
expected_message = "diagnose.test_fixtures.Thing does not have the attribute 'notamethod'"
else:
expected_message = "<class 'diagnose.test_fixtures.Thing'> does not have the attribute 'notamethod'"

assert (
exc.exception.args[0]
== "diagnose.test_fixtures.Thing does not have the attribute 'notamethod'"
== expected_message
)

def test_target_copies(self):
Expand All @@ -342,6 +350,17 @@ class Entity(object):
registry["in_a_dict"] = func_2
self.assertTrue(registry["in_a_dict"] is old_local_func_2)

# Before attaching the probe, there should be some references to func_2,
# but not our patch objects.
expected_result = {
types.ModuleType: 2,
Entity: 2
}
self.assertEqual(
owner_types(func_2),
expected_result,
)

probe = probes.attach_to("diagnose.test_fixtures.func_2")
try:
probe.start()
Expand Down Expand Up @@ -377,29 +396,32 @@ class Entity(object):
# The next problem is that, while our patch is live,
# if t2 goes out of its original scope, we've still got
# a reference to it in our mock patch.
self.assertEqual(
owner_types(func_2),
{
types.ModuleType: 2,
Entity: 2,
probes.WeakMethodPatch: 3,
MockPatch: 1,
probes.DictPatch: 1,
},
)
expected_result = {
# These referred to func_2 before our probe was attached...
types.ModuleType: 2,
Entity: 2,
# ...and these are added by attaching the probe:
# a) the target that we passed to probes.attach_to()
MockPatch: 1,
# b) 3 "methods": t.add13, t2.add13, and test_probes.func_2
probes.WeakMethodPatch: 3,
# c) the registry dict.
probes.DictPatch: 1,
}
assert owner_types(func_2) == expected_result

# Delete one of our references.
del t2
self.assertEqual(
owner_types(func_2),
{
types.ModuleType: 2,
# The number of Entity references MUST decrease by 1.
Entity: 1,
# The number of WeakMethodPatch references MUST decrease by 1.
probes.WeakMethodPatch: 2,
MockPatch: 1,
probes.DictPatch: 1,
},
)
expected_result = {
types.ModuleType: 2,
# The number of Entity references MUST decrease by 1.
Entity: 1,
MockPatch: 1,
# The number of WeakMethodPatch references MUST decrease by 1.
probes.WeakMethodPatch: 2,
probes.DictPatch: 1,
}
assert owner_types(func_2) == expected_result
finally:
probe.stop()

Expand All @@ -418,7 +440,7 @@ def test_function_registries(self):
assert funcs["orig"]("ahem") == "aha!"

# The probe MUST have logged an entry
i = p.instruments.values()[0]
i = list(p.instruments.values())[0]
assert i.results == ["aha!"]

i.log = []
Expand All @@ -440,7 +462,7 @@ def test_patch_staticmethod(self):
"test", "quantile", "diagnose.test_fixtures.Thing.static", "result"
) as p:
assert Thing().static() == 15
assert p.instruments.values()[0].results == [15]
assert list(p.instruments.values())[0].results == [15]

def test_patch_wrapped_function_end_event(self):
probe = probes.attach_to("diagnose.test_fixtures.Thing.add5")
Expand Down Expand Up @@ -471,7 +493,7 @@ def test_patch_property(self):
"test", "quantile", "diagnose.test_fixtures.Thing.exists", "result"
) as p:
assert Thing().exists is True
assert p.instruments.values()[0].results == [True]
assert list(p.instruments.values())[0].results == [True]
assert Thing.exists is not old_prop

assert Thing().exists is True
Expand All @@ -493,10 +515,10 @@ def only_some_users(probe, instrument, *args, **kwargs):
custom={"valid_ids": [1, 2, 3]},
) as p:
assert Thing().do("ok", user_id=2) == "<ok>"
assert p.instruments.values()[0].results == ["<ok>"]
assert list(p.instruments.values())[0].results == ["<ok>"]

assert Thing().do("not ok", user_id=10004) == "<not ok>"
assert p.instruments.values()[0].results == ["<ok>"]
assert list(p.instruments.values())[0].results == ["<ok>"]


class TestHardcodedProbes(ProbeTestCase):
Expand Down

0 comments on commit 18ccedc

Please sign in to comment.