Skip to content

Commit

Permalink
Merge pull request #219 from python-greenlet/issue218
Browse files Browse the repository at this point in the history
Use PyString_FromString in tp_repr on Py2.
  • Loading branch information
jamadden committed Nov 23, 2020
2 parents d4bd918 + 9ed87d5 commit f005b37
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 34 deletions.
5 changes: 4 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
1.0a2 (unreleased)
==================

- Nothing changed yet.
- Fix %s and %r formatting of a greenlet on Python 2. Previously it
would result in a Unicode string instead of a native string. See
`issue 218
<https://github.com/python-greenlet/greenlet/issues/218>`_.


1.0a1 (2020-11-20)
Expand Down
12 changes: 10 additions & 2 deletions src/greenlet/greenlet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1537,8 +1537,14 @@ green_repr(PyGreenlet* self)
return NULL;
}

#if PY_MAJOR_VERSION >= 3
# define GNative_FromFormat PyUnicode_FromFormat
#else
# define GNative_FromFormat PyString_FromFormat
#endif

if (_green_not_dead(self)) {
result = PyUnicode_FromFormat(
result = GNative_FromFormat(
"<%s object at %p (otid=%p)%s%s%s%s>",
Py_TYPE(self)->tp_name,
self,
Expand All @@ -1553,13 +1559,15 @@ green_repr(PyGreenlet* self)
}
else {
/* main greenlets never really appear dead. */
result = PyUnicode_FromFormat(
result = GNative_FromFormat(
"<%s object at %p (otid=%p) dead>",
Py_TYPE(self)->tp_name,
self,
self->run_info
);
}
#undef GNative_FromFormat

return result;
}

Expand Down
94 changes: 63 additions & 31 deletions src/greenlet/tests/test_greenlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ def f():

def test_parent_equals_None(self):
g = greenlet(parent=None)
self.assertIsNotNone(g)
self.assertIs(g.parent, greenlet.getcurrent())

def test_run_equals_None(self):
g = greenlet(run=None)
self.assertIsNotNone(g)
self.assertIsNone(g.run)

def test_two_children(self):
lst = []
Expand Down Expand Up @@ -172,17 +176,18 @@ def f():

def test_frame(self):
def f1():
f = sys._getframe(0)
f = sys._getframe(0) # pylint:disable=protected-access
self.assertEqual(f.f_back, None)
greenlet.getcurrent().parent.switch(f)
return "meaning of life"
g = greenlet(f1)
frame = g.switch()
self.assertTrue(frame is g.gr_frame)
self.assertTrue(g)
next = g.switch()

from_g = g.switch()
self.assertFalse(g)
self.assertEqual(next, 'meaning of life')
self.assertEqual(from_g, 'meaning of life')
self.assertEqual(g.gr_frame, None)

def test_thread_bug(self):
Expand All @@ -197,17 +202,19 @@ def runner(x):
t2.join()

def test_switch_kwargs(self):
def foo(a, b):
def run(a, b):
self.assertEqual(a, 4)
self.assertEqual(b, 2)
greenlet(foo).switch(a=4, b=2)
return 42
x = greenlet(run).switch(a=4, b=2)
self.assertEqual(x, 42)

def test_switch_kwargs_to_parent(self):
def foo(x):
def run(x):
greenlet.getcurrent().parent.switch(x=x)
greenlet.getcurrent().parent.switch(2, x=3)
return x, x ** 2
g = greenlet(foo)
g = greenlet(run)
self.assertEqual({'x': 3}, g.switch(3))
self.assertEqual(((2,), {'x': 3}), g.switch())
self.assertEqual((3, 9), g.switch())
Expand All @@ -218,26 +225,26 @@ def test_switch_to_another_thread(self):
created_event = threading.Event()
done_event = threading.Event()

def foo():
def run():
data['g'] = greenlet(lambda: None)
created_event.set()
done_event.wait()
thread = threading.Thread(target=foo)
thread = threading.Thread(target=run)
thread.start()
created_event.wait()
try:
data['g'].switch()
except greenlet.error:
error = sys.exc_info()[1]
self.assertTrue(error != None, "greenlet.error was not raised!")
self.assertIsNotNone(error, "greenlet.error was not raised!")
done_event.set()
thread.join()

def test_exc_state(self):
def f():
try:
raise ValueError('fun')
except:
except: # pylint:disable=bare-except
exc_info = sys.exc_info()
greenlet(h).switch()
self.assertEqual(exc_info, sys.exc_info())
Expand Down Expand Up @@ -269,7 +276,7 @@ def test_threaded_reparent(self):
created_event = threading.Event()
done_event = threading.Event()

def foo():
def run():
data['g'] = greenlet(lambda: None)
created_event.set()
done_event.wait()
Expand All @@ -280,7 +287,7 @@ def blank():
def setparent(g, value):
g.parent = value

thread = threading.Thread(target=foo)
thread = threading.Thread(target=run)
thread.start()
created_event.wait()
g = greenlet(blank)
Expand Down Expand Up @@ -327,7 +334,7 @@ class mygreenlet(greenlet):
def __getattribute__(self, name):
try:
raise Exception()
except:
except: # pylint:disable=bare-except
pass
return greenlet.__getattribute__(self, name)
g = mygreenlet(lambda: None)
Expand Down Expand Up @@ -374,7 +381,7 @@ def worker():
class convoluted(greenlet):
def __getattribute__(self, name):
if name == 'run':
self.parent = another[0]
self.parent = another[0] # pylint:disable=attribute-defined-outside-init
return greenlet.__getattribute__(self, name)
g = convoluted(lambda: None)
self.assertRaises(greenlet.error, g.switch)
Expand Down Expand Up @@ -450,21 +457,27 @@ def initiator():
self.assertTrue(seen)
self.assertEqual(value, 42)

if sys.version_info[0] == 2:
# There's no apply in Python 3.x
def test_tuple_subclass(self):
class mytuple(tuple):
def __len__(self):
greenlet.getcurrent().switch()
return tuple.__len__(self)
args = mytuple()
kwargs = dict(a=42)
def switchapply():
apply(greenlet.getcurrent().parent.switch, args, kwargs)
g = greenlet(switchapply)
self.assertEqual(g.switch(), kwargs)


def test_tuple_subclass(self):
if sys.version_info[0] > 2:
# There's no apply in Python 3.x
def _apply(func, a, k):
func(*a, **k)
else:
_apply = apply # pylint:disable=undefined-variable

class mytuple(tuple):
def __len__(self):
greenlet.getcurrent().switch()
return tuple.__len__(self)
args = mytuple()
kwargs = dict(a=42)
def switchapply():
_apply(greenlet.getcurrent().parent.switch, args, kwargs)
g = greenlet(switchapply)
self.assertEqual(g.switch(), kwargs)

def test_abstract_subclasses(self):
AbstractSubclass = ABCMeta(
'AbstractSubclass',
Expand Down Expand Up @@ -512,7 +525,7 @@ def __init__(self):
x = range(N*2)
current = greenlet.getcurrent()
g = garbage()
for i in x:
for _ in x:
g = None # lose reference to garbage
if recycled[0]:
# gc callback called prematurely
Expand All @@ -532,7 +545,7 @@ def __init__(self):
for g in l:
self.assertEqual(g.parent, current)
return True
for i in range(5):
for _ in range(5):
if attempt():
break

Expand Down Expand Up @@ -582,7 +595,6 @@ def run(self):
assert not t.main_glet.dead
self.assertEndsWith(r, ' suspended active started main>')


def test_dead(self):
g = greenlet(lambda: None)
g.switch()
Expand All @@ -591,5 +603,25 @@ def test_dead(self):
self.assertNotIn('started', repr(g))
self.assertNotIn('active', repr(g))

def test_formatting_produces_native_str(self):
# https://github.com/python-greenlet/greenlet/issues/218
# %s formatting on Python 2 was producing unicode, not str.

g_dead = greenlet(lambda: None)
g_not_started = greenlet(lambda: None)
g_cur = greenlet.getcurrent()

for g in g_dead, g_not_started, g_cur:

self.assertIsInstance(
'%s' % (g,),
str
)
self.assertIsInstance(
'%r' % (g,),
str,
)


if __name__ == '__main__':
unittest.main()

0 comments on commit f005b37

Please sign in to comment.