3 from __future__
import absolute_import
9 Gtk
= pytest
.importorskip("gi.repository.Gtk")
10 GLib
= pytest
.importorskip("gi.repository.GLib")
11 GObject
= pytest
.importorskip("gi.repository.GObject")
12 Gio
= pytest
.importorskip("gi.repository.Gio")
15 from .helper
import capture_exceptions
18 def new_gtype_name(_count
=[0]):
20 return "GtkTemplateTest%d" % _count
[0]
23 def ensure_resource_registered():
24 resource_path
= "/org/gnome/pygobject/test/a.ui"
26 def is_registered(path
):
28 Gio
.resources_get_info(path
, Gio
.ResourceLookupFlags
.NONE
)
33 if is_registered(resource_path
):
37 b
'GVariant\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00'
38 b
'\xc8\x00\x00\x00\x00\x00\x00(\x06\x00\x00\x00\x00\x00\x00\x00'
39 b
'\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00'
40 b
'\x06\x00\x00\x00KP\x90\x0b\x03\x00\x00\x00\xc8\x00\x00\x00'
41 b
'\x04\x00L\x00\xcc\x00\x00\x00\xd0\x00\x00\x00\xb0\xb7$0'
42 b
'\x00\x00\x00\x00\xd0\x00\x00\x00\x06\x00L\x00\xd8\x00\x00\x00'
43 b
'\xdc\x00\x00\x00f\xc30\xd1\x01\x00\x00\x00\xdc\x00\x00\x00'
44 b
'\n\x00L\x00\xe8\x00\x00\x00\xec\x00\x00\x00\xd4\xb5\x02\x00'
45 b
'\xff\xff\xff\xff\xec\x00\x00\x00\x01\x00L\x00\xf0\x00\x00\x00'
46 b
'\xf4\x00\x00\x005H}\xe3\x02\x00\x00\x00\xf4\x00\x00\x00'
47 b
'\x05\x00L\x00\xfc\x00\x00\x00\x00\x01\x00\x00\xa2^\xd6t'
48 b
'\x04\x00\x00\x00\x00\x01\x00\x00\x04\x00v\x00\x08\x01\x00\x00'
49 b
'\xa5\x01\x00\x00org/\x01\x00\x00\x00gnome/\x00\x00\x02\x00\x00\x00'
50 b
'pygobject/\x00\x00\x04\x00\x00\x00/\x00\x00\x00\x00\x00\x00\x00'
51 b
'test/\x00\x00\x00\x05\x00\x00\x00a.ui\x00\x00\x00\x00'
52 b
'\x8d\x00\x00\x00\x00\x00\x00\x00<interface>\n <template class="G'
53 b
'tkTemplateTestResource" parent="GtkBox">\n <property name="spaci'
54 b
'ng">42</property>\n </template>\n</interface>\n\x00\x00(uuay)'
57 resource
= Gio
.Resource
.new_from_data(GLib
.Bytes
.new(gresource_data
))
58 Gio
.resources_register(resource
)
59 assert is_registered(resource_path
)
63 def test_allow_init_template_call():
65 type_name
= new_gtype_name()
69 <template class="{0}" parent="GtkBox">
74 @Gtk.Template
.from_string(xml
)
76 __gtype_name__
= type_name
79 super(Foo
, self
).__init
__()
82 # Stop current pygobject from handling the initialisation
83 del Foo
.__dontuse
_ginstance
_init
__
88 def test_init_template_second_instance():
89 type_name
= new_gtype_name()
93 <template class="{0}" parent="GtkBox">
95 <object class="GtkLabel" id="label">
100 """.format(type_name
)
102 @Gtk.Template
.from_string(xml
)
104 __gtype_name__
= type_name
106 label
= Gtk
.Template
.Child("label")
109 super(Foo
, self
).__init
__()
112 # Stop current pygobject from handling the initialisation
113 del Foo
.__dontuse
_ginstance
_init
__
116 assert isinstance(foo
.label
, Gtk
.Label
)
119 assert isinstance(foo2
.label
, Gtk
.Label
)
122 def test_main_example():
124 type_name
= new_gtype_name()
128 <template class="{0}" parent="GtkBox">
129 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
130 <property name="spacing">4</property>
132 <object class="GtkButton" id="hello_button">
133 <property name="label">Hello World</property>
134 <signal name="clicked" handler="hello_button_clicked"
135 object="{0}" swapped="no"/>
136 <signal name="clicked" handler="hello_button_clicked_after"
137 object="{0}" swapped="no" after="yes"/>
141 <object class="GtkButton" id="goodbye_button">
142 <property name="label">Goodbye World</property>
143 <signal name="clicked" handler="goodbye_button_clicked"/>
144 <signal name="clicked" handler="goodbye_button_clicked_after"
150 """.format(type_name
)
152 @Gtk.Template
.from_string(example_xml
)
154 __gtype_name__
= type_name
157 super(Foo
, self
).__init
__()
158 self
.callback_hello
= []
159 self
.callback_hello_after
= []
160 self
.callback_goodbye
= []
161 self
.callback_goodbye_after
= []
163 @Gtk.Template
.Callback("hello_button_clicked")
164 def _hello_button_clicked(self
, *args
):
165 self
.callback_hello
.append(args
)
167 @Gtk.Template
.Callback("hello_button_clicked_after")
168 def _hello_after(self
, *args
):
169 self
.callback_hello_after
.append(args
)
171 _hello_button
= Gtk
.Template
.Child("hello_button")
173 goodbye_button
= Gtk
.Template
.Child()
175 @Gtk.Template
.Callback("goodbye_button_clicked")
176 def _goodbye_button_clicked(self
, *args
):
177 self
.callback_goodbye
.append(args
)
179 @Gtk.Template
.Callback("goodbye_button_clicked_after")
180 def _goodbye_after(self
, *args
):
181 self
.callback_goodbye_after
.append(args
)
184 assert w
.__gtype
__.name
== type_name
185 assert w
.props
.orientation
== Gtk
.Orientation
.HORIZONTAL
186 assert w
.props
.spacing
== 4
187 assert isinstance(w
._hello
_button
, Gtk
.Button
)
188 assert w
._hello
_button
.props
.label
== "Hello World"
189 assert isinstance(w
.goodbye_button
, Gtk
.Button
)
190 assert w
.goodbye_button
.props
.label
== "Goodbye World"
192 assert w
.callback_hello
== []
193 w
._hello
_button
.clicked()
194 assert w
.callback_hello
== [(w
,)]
195 assert w
.callback_hello_after
== [(w
,)]
197 assert w
.callback_goodbye
== []
198 w
.goodbye_button
.clicked()
199 assert w
.callback_goodbye
== [(w
.goodbye_button
,)]
200 assert w
.callback_goodbye_after
== [(w
.goodbye_button
,)]
203 def test_duplicate_handler():
205 type_name
= new_gtype_name()
209 <template class="{0}" parent="GtkBox">
211 <object class="GtkButton" id="hello_button">
212 <signal name="clicked" handler="hello_button_clicked">
217 """.format(type_name
)
220 __gtype_name__
= type_name
222 @Gtk.Template
.Callback("hello_button_clicked")
223 def _hello_button_clicked(self
, *args
):
226 @Gtk.Template
.Callback()
227 def hello_button_clicked(self
, *args
):
230 with pytest
.raises(RuntimeError, match
=".*hello_button_clicked.*"):
231 Gtk
.Template
.from_string(xml
)(Foo
)
234 def test_duplicate_child():
235 type_name
= new_gtype_name()
239 <template class="{0}" parent="GtkBox">
241 <object class="GtkButton" id="hello_button" />
245 """.format(type_name
)
248 __gtype_name__
= type_name
250 foo
= Gtk
.Template
.Child("hello_button")
251 hello_button
= Gtk
.Template
.Child()
253 with pytest
.raises(RuntimeError, match
=".*hello_button.*"):
254 Gtk
.Template
.from_string(xml
)(Foo
)
257 def test_nonexist_handler():
258 type_name
= new_gtype_name()
262 <template class="{0}" parent="GtkBox">
265 """.format(type_name
)
267 @Gtk.Template
.from_string(xml
)
269 __gtype_name__
= type_name
271 @Gtk.Template
.Callback("nonexit")
272 def foo(self
, *args
):
275 with
capture_exceptions() as exc_info
:
277 assert "nonexit" in str(exc_info
[0].value
)
278 assert exc_info
[0].type is RuntimeError
281 def test_missing_handler_callback():
282 type_name
= new_gtype_name()
286 <template class="{0}" parent="GtkBox">
288 <object class="GtkButton" id="hello_button">
289 <signal name="clicked" handler="i_am_not_used_in_python" />
294 """.format(type_name
)
297 __gtype_name__
= type_name
299 Gtk
.Template
.from_string(xml
)(Foo
)()
302 def test_handler_swapped_not_supported():
304 type_name
= new_gtype_name()
308 <template class="{0}" parent="GtkBox">
310 <object class="GtkButton" id="hello_button">
311 <signal name="clicked" handler="hello_button_clicked"
312 object="{0}" swapped="yes" />
317 """.format(type_name
)
319 @Gtk.Template
.from_string(xml
)
321 __gtype_name__
= type_name
323 hello_button
= Gtk
.Template
.Child()
325 @Gtk.Template
.Callback("hello_button_clicked")
326 def foo(self
, *args
):
329 with
capture_exceptions() as exc_info
:
331 assert "G_CONNECT_SWAPPED" in str(exc_info
[0].value
)
334 def test_handler_class_staticmethod():
336 type_name
= new_gtype_name()
340 <template class="{0}" parent="GtkBox">
342 <object class="GtkButton" id="hello_button">
343 <signal name="clicked" handler="clicked_class" />
344 <signal name="clicked" handler="clicked_static" />
349 """.format(type_name
)
351 signal_args_class
= []
352 signal_args_static
= []
354 @Gtk.Template
.from_string(xml
)
356 __gtype_name__
= type_name
358 hello_button
= Gtk
.Template
.Child()
360 @Gtk.Template
.Callback("clicked_class")
363 signal_args_class
.append(args
)
365 @Gtk.Template
.Callback("clicked_static")
368 signal_args_static
.append(args
)
371 foo
.hello_button
.clicked()
372 assert signal_args_class
== [(Foo
, foo
.hello_button
)]
373 assert signal_args_static
== [(foo
.hello_button
,)]
376 def test_check_decorated_class():
378 NonWidget
= type("Foo", (object,), {})
379 with pytest
.raises(TypeError, match
=".*on Widgets.*"):
380 Gtk
.Template
.from_string("")(NonWidget
)
382 Widget
= type("Foo", (Gtk
.Widget
,), {"__gtype_name__": new_gtype_name()})
383 with pytest
.raises(TypeError, match
=".*Cannot nest.*"):
384 Gtk
.Template
.from_string("")(Gtk
.Template
.from_string("")(Widget
))
386 Widget
= type("Foo", (Gtk
.Widget
,), {})
387 with pytest
.raises(TypeError, match
=".*__gtype_name__.*"):
388 Gtk
.Template
.from_string("")(Widget
)
390 with pytest
.raises(TypeError, match
=".*on Widgets.*"):
391 Gtk
.Template
.from_string("")(object())
393 @Gtk.Template
.from_string("")
394 class Base(Gtk
.Widget
):
395 __gtype_name__
= new_gtype_name()
397 with
capture_exceptions() as exc_info
:
398 type("Sub", (Base
,), {})()
399 assert "not allowed at this time" in str(exc_info
[0].value
)
400 assert exc_info
[0].type is TypeError
403 def test_from_file():
404 fd
, name
= tempfile
.mkstemp()
408 type_name
= new_gtype_name()
410 with
open(name
, "wb") as h
:
413 <template class="{0}" parent="GtkBox">
414 <property name="spacing">42</property>
417 """.format(type_name
).encode())
419 @Gtk.Template
.from_file(name
)
421 __gtype_name__
= type_name
424 assert foo
.props
.spacing
== 42
429 def test_property_override():
430 type_name
= new_gtype_name()
434 <template class="{0}" parent="GtkBox">
435 <property name="spacing">42</property>
438 """.format(type_name
)
440 @Gtk.Template
.from_string(xml
)
442 __gtype_name__
= type_name
445 assert foo
.props
.spacing
== 42
447 foo
= Foo(spacing
=124)
448 assert foo
.props
.spacing
== 124
451 def test_from_file_non_exist():
452 dirname
= tempfile
.mkdtemp()
454 path
= os
.path
.join(dirname
, "noexist")
457 "Foo", (Gtk
.Widget
,), {"__gtype_name__": new_gtype_name()})
458 with pytest
.raises(GLib
.Error
, match
=".*No such file.*"):
459 Gtk
.Template
.from_file(path
)(Widget
)
464 def test_from_string_bytes():
465 type_name
= new_gtype_name()
469 <template class="{0}" parent="GtkBox">
470 <property name="spacing">42</property>
473 """.format(type_name
).encode()
475 @Gtk.Template
.from_string(xml
)
477 __gtype_name__
= type_name
480 assert foo
.props
.spacing
== 42
483 def test_from_resource():
484 resource_path
= ensure_resource_registered()
486 @Gtk.Template
.from_resource(resource_path
)
488 __gtype_name__
= "GtkTemplateTestResource"
491 assert foo
.props
.spacing
== 42
494 def test_from_resource_non_exit():
495 Widget
= type("Foo", (Gtk
.Widget
,), {"__gtype_name__": new_gtype_name()})
496 with pytest
.raises(GLib
.Error
, match
=".*/or/gnome/pygobject/noexit.*"):
497 Gtk
.Template
.from_resource("/or/gnome/pygobject/noexit")(Widget
)
500 def test_constructors():
501 with pytest
.raises(TypeError):
504 with pytest
.raises(TypeError):
507 Gtk
.Template(filename
="foo")
508 Gtk
.Template(resource_path
="foo")
509 Gtk
.Template(string
="foo")
511 with pytest
.raises(TypeError):
512 Gtk
.Template(filename
="foo", resource_path
="bar")
514 with pytest
.raises(TypeError):
515 Gtk
.Template(filename
="foo", nope
="bar")
517 Gtk
.Template
.from_string("bla")
518 Gtk
.Template
.from_resource("foo")
519 Gtk
.Template
.from_file("foo")
522 def test_child_construct():
524 Gtk
.Template
.Child("name")
525 with pytest
.raises(TypeError):
526 Gtk
.Template
.Child("name", True)
527 Gtk
.Template
.Child("name", internal
=True)
528 with pytest
.raises(TypeError):
529 Gtk
.Template
.Child("name", internal
=True, something
=False)
532 def test_internal_child():
534 main_type_name
= new_gtype_name()
538 <template class="{0}" parent="GtkBox">
540 <object class="GtkBox" id="somechild">
541 <property name="margin">42</property>
546 """.format(main_type_name
)
548 @Gtk.Template
.from_string(xml
)
549 class MainThing(Gtk
.Box
):
550 __gtype_name__
= main_type_name
552 somechild
= Gtk
.Template
.Child(internal
=True)
555 assert thing
.somechild
.props
.margin
== 42
557 other_type_name
= new_gtype_name()
561 <template class="{0}" parent="GtkBox">
564 <child internal-child="somechild">
565 <object class="GtkBox">
566 <property name="margin">24</property>
568 <object class="GtkLabel">
569 <property name="label">foo</property>
578 """.format(other_type_name
, main_type_name
)
580 @Gtk.Template
.from_string(xml
)
581 class OtherThing(Gtk
.Box
):
582 __gtype_name__
= other_type_name
585 child
= other
.get_children()[0]
586 assert isinstance(child
, MainThing
)
587 child
= child
.get_children()[0]
588 assert isinstance(child
, Gtk
.Box
)
589 assert child
.props
.margin
== 24
590 child
= child
.get_children()[0]
591 assert isinstance(child
, Gtk
.Label
)
592 assert child
.props
.label
== "foo"