summaryrefslogtreecommitdiff
blob: 31250e3561c8e074af247bf1da68e1f67790ab1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
From c60ef6a2bcc05010a89328d5fc2704d1aa505e2a Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Sat, 10 Mar 2018 18:04:14 +0100
Subject: [PATCH 1/4] tests_gi: Use capture_output() context manager instead of
 manually mocking stdout

---
 tests/helper.py  |  2 +-
 tests/test_gi.py | 14 +++-----------
 2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/tests/helper.py b/tests/helper.py
index c4308fee..892cb740 100644
--- a/tests/helper.py
+++ b/tests/helper.py
@@ -113,7 +113,7 @@ def capture_glib_deprecation_warnings():
 @contextlib.contextmanager
 def capture_output():
     """
-    with capture_output as (stdout, stderr):
+    with capture_output() as (stdout, stderr):
         some_action()
     print(stdout.getvalue(), stderr.getvalue())
     """
diff --git a/tests/test_gi.py b/tests/test_gi.py
index d0c72b64..3b77ff2d 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -13,7 +13,6 @@ import subprocess
 import gc
 import weakref
 import warnings
-from io import StringIO, BytesIO
 
 import gi
 import gi.overrides
@@ -24,7 +23,7 @@ from gi.repository import GObject, GLib, Gio
 from gi.repository import GIMarshallingTests
 
 from compathelper import _bytes, _unicode
-from helper import capture_exceptions
+from helper import capture_exceptions, capture_output
 
 if sys.version_info < (3, 0):
     CONSTANT_UTF8 = "const \xe2\x99\xa5 utf8"
@@ -2836,16 +2835,9 @@ class TestModule(unittest.TestCase):
             self.assertTrue(hasattr(item, '__class__'))
 
     def test_help(self):
-        orig_stdout = sys.stdout
-        try:
-            if sys.version_info < (3, 0):
-                sys.stdout = BytesIO()
-            else:
-                sys.stdout = StringIO()
+        with capture_output() as (stdout, stderr):
             help(GIMarshallingTests)
-            output = sys.stdout.getvalue()
-        finally:
-            sys.stdout = orig_stdout
+        output = stdout.getvalue()
 
         self.assertTrue('SimpleStruct' in output, output)
         self.assertTrue('Interface2' in output, output)
-- 
2.18.0

From 1826e41cd317ba3c19cf8767b1ef8752f1865aac Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Sat, 10 Mar 2018 18:54:28 +0100
Subject: [PATCH 2/4] IntrospectionModule: __path__ should be List[str] and not
 str

This fixes a crash when calling help() on the module which got stricter with
Python 3.7.

It's a bit questionable why the type has __path__ in the first place as
that's only meant for packages. But let's leave that for now.
---
 gi/module.py     | 5 +++--
 tests/test_gi.py | 6 ++++--
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/gi/module.py b/gi/module.py
index fd8f5080..9e8b5256 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -124,10 +124,11 @@ class IntrospectionModule(object):
         self._version = version
         self.__name__ = 'gi.repository.' + namespace
 
-        self.__path__ = repository.get_typelib_path(self._namespace)
+        path = repository.get_typelib_path(self._namespace)
+        self.__path__ = [path]
         if _have_py3:
             # get_typelib_path() delivers bytes, not a string
-            self.__path__ = self.__path__.decode('UTF-8')
+            self.__path__ = [path.decode('UTF-8')]
 
         if self._version is None:
             self._version = repository.get_version(self._namespace)
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 3b77ff2d..2fa0423d 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -2813,8 +2813,10 @@ class TestKeywords(unittest.TestCase):
 
 class TestModule(unittest.TestCase):
     def test_path(self):
-        self.assertTrue(GIMarshallingTests.__path__.endswith('GIMarshallingTests-1.0.typelib'),
-                        GIMarshallingTests.__path__)
+        path = GIMarshallingTests.__path__
+        assert isinstance(path, list)
+        assert len(path) == 1
+        assert path[0].endswith('GIMarshallingTests-1.0.typelib')
 
     def test_str(self):
         self.assertTrue("'GIMarshallingTests' from '" in str(GIMarshallingTests),
-- 
2.18.0

From 8adc8be0a2ab17e5215a4bc6f63a9ee391237596 Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Sun, 11 Mar 2018 13:13:30 +0100
Subject: [PATCH 3/4] _struct_dealloc: handle being called with an error set

With Python 3.7 it gets called with an error set and tp_dealloc
implementations need to handle that.
Fix by saving and restoring the error.
---
 gi/pygi-struct.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/gi/pygi-struct.c b/gi/pygi-struct.c
index 4d5b5411..e2906e0a 100644
--- a/gi/pygi-struct.c
+++ b/gi/pygi-struct.c
@@ -61,7 +61,14 @@ out:
 static void
 _struct_dealloc (PyGIStruct *self)
 {
-    GIBaseInfo *info = _struct_get_info ( (PyObject *) self );
+    GIBaseInfo *info;
+    PyObject *error_type, *error_value, *error_traceback;
+    gboolean have_error = !!PyErr_Occurred ();
+
+    if (have_error)
+        PyErr_Fetch (&error_type, &error_value, &error_traceback);
+
+    info = _struct_get_info ( (PyObject *) self );
 
     if (info != NULL && g_struct_info_is_foreign ( (GIStructInfo *) info)) {
         pygi_struct_foreign_release (info, pyg_pointer_get_ptr (self));
@@ -73,6 +80,9 @@ _struct_dealloc (PyGIStruct *self)
         g_base_info_unref (info);
     }
 
+    if (have_error)
+        PyErr_Restore (error_type, error_value, error_traceback);
+
     Py_TYPE (self)->tp_free ((PyObject *)self);
 }
 
-- 
2.18.0

From 2299d46e7a57a74f734844ae28bfd536e89e5254 Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Sat, 10 Mar 2018 20:03:33 +0100
Subject: [PATCH 4/4] marshal-cleanup: save and restore exception around
 cleanup

With Python 3.7 some Python API in the cleanup path clears exceptions
which makes us return NULL in the end without an error set.

Make if safe to call the cleanup functions with an error set by
saving and restoring exceptions.
---
 gi/pygi-marshal-cleanup.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/gi/pygi-marshal-cleanup.c b/gi/pygi-marshal-cleanup.c
index b4d04bc5..0e4eda3f 100644
--- a/gi/pygi-marshal-cleanup.c
+++ b/gi/pygi-marshal-cleanup.c
@@ -93,6 +93,11 @@ pygi_marshal_cleanup_args_from_py_marshal_success (PyGIInvokeState   *state,
                                                    PyGICallableCache *cache)
 {
     gssize i;
+    PyObject *error_type, *error_value, *error_traceback;
+    gboolean have_error = !!PyErr_Occurred ();
+
+    if (have_error)
+        PyErr_Fetch (&error_type, &error_value, &error_traceback);
 
     for (i = 0; i < _pygi_callable_cache_args_len (cache); i++) {
         PyGIArgCache *arg_cache = _pygi_callable_cache_get_arg (cache, i);
@@ -112,6 +117,9 @@ pygi_marshal_cleanup_args_from_py_marshal_success (PyGIInvokeState   *state,
             state->args[i].arg_cleanup_data = NULL;
         }
     }
+
+    if (have_error)
+        PyErr_Restore (error_type, error_value, error_traceback);
 }
 
 void
@@ -119,6 +127,12 @@ pygi_marshal_cleanup_args_to_py_marshal_success (PyGIInvokeState   *state,
                                                  PyGICallableCache *cache)
 {
     GSList *cache_item;
+    PyObject *error_type, *error_value, *error_traceback;
+    gboolean have_error = !!PyErr_Occurred ();
+
+    if (have_error)
+        PyErr_Fetch (&error_type, &error_value, &error_traceback);
+
     /* clean up the return if available */
     if (cache->return_cache != NULL) {
         PyGIMarshalCleanupFunc cleanup_func = cache->return_cache->to_py_cleanup;
@@ -153,6 +167,9 @@ pygi_marshal_cleanup_args_to_py_marshal_success (PyGIInvokeState   *state,
 
         cache_item = cache_item->next;
     }
+
+    if (have_error)
+        PyErr_Restore (error_type, error_value, error_traceback);
 }
 
 void
@@ -161,6 +178,11 @@ pygi_marshal_cleanup_args_from_py_parameter_fail (PyGIInvokeState   *state,
                                                   gssize failed_arg_index)
 {
     gssize i;
+    PyObject *error_type, *error_value, *error_traceback;
+    gboolean have_error = !!PyErr_Occurred ();
+
+    if (have_error)
+        PyErr_Fetch (&error_type, &error_value, &error_traceback);
 
     state->failed = TRUE;
 
@@ -192,6 +214,9 @@ pygi_marshal_cleanup_args_from_py_parameter_fail (PyGIInvokeState   *state,
         }
         state->args[i].arg_cleanup_data = NULL;
     }
+
+    if (have_error)
+        PyErr_Restore (error_type, error_value, error_traceback);
 }
 
 void
-- 
2.18.0