diff --git a/README.rst b/README.rst index 285df44..d6e5a42 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ and cffi_ (optional). API compatible with PyGObject_. **Requirements:** -- CPython_ 2.7 or PyPy_ 1.9 +- CPython_ 2.7 or Python 3.3 or PyPy_ 1.9 - libgirepository_ 1.0 - cffi_ 0.6+ (optional) - cairocffi_ 0.4+ (optional, for cairo support) diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py index 1614360..f9338c7 100644 --- a/benchmarks/__init__.py +++ b/benchmarks/__init__.py @@ -1,14 +1,19 @@ # -*- coding: utf-8 -*- -# Copyright 2012 Christoph Reiter +# Copyright 2012-2014 Christoph Reiter # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. +import sys import time +if sys.version_info[0] == 3: + xrange = range + + def run(load_gi, backend=None): if not load_gi: import pgi diff --git a/pgi/codegen/cffi_backend.py b/pgi/codegen/cffi_backend.py index 3b916de..e75bba4 100644 --- a/pgi/codegen/cffi_backend.py +++ b/pgi/codegen/cffi_backend.py @@ -5,8 +5,6 @@ # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -import __builtin__ - from cffi import FFI from pgi.clib.gir import GIRepository, GITypeTag, GIInfoType @@ -135,14 +133,15 @@ def get_reference(self, value): def free(self, name): raise NotImplementedError - -class BasicType(BaseType): - - def pack_in(self, name): - return name + def __getattr__(self, attr): + if attr.endswith(("_py2", "_py3")): + raise AttributeError(attr) + if _compat.PY3: + return getattr(self, attr + "_py3") + return getattr(self, attr + "_py2") -class Boolean(BasicType): +class Boolean(BaseType): GI_TYPE_TAG = GITypeTag.BOOLEAN def check(self, name): @@ -153,6 +152,9 @@ def check(self, name): def pack(self, name): return name + def pack_in(self, name): + return name + def unpack(self, name): return self.parse(""" $bool = $_.bool($value) @@ -164,7 +166,7 @@ def new(self): """)["value"] -class Int32(BasicType): +class Int32(BaseType): GI_TYPE_TAG = GITypeTag.INT32 def check(self, name): @@ -185,6 +187,9 @@ def pack(self, valid): $c_value = $ffi.cast("gint32", $value) """, value=valid)["c_value"] + def pack_in(self, name): + return name + def unpack(self, name): raise NotImplementedError @@ -195,13 +200,34 @@ def new(self): """)["value"] -class Utf8(BasicType): +class Utf8(BaseType): GI_TYPE_TAG = GITypeTag.UTF8 - def check(self, name): + def check_py3(self, name): + if self.may_be_null: + return self.parse(""" +if $value is not None: + if isinstance($value, $_.str): + $string = $value.encode("utf-8") + else: + $string = $value +else: + $string = None +""", value=name)["string"] + + return self.parse(""" +if isinstance($value, $_.str): + $string = $value.encode("utf-8") +elif not isinstance($value, $_.bytes): + raise TypeError +else: + $string = $value +""", value=name)["string"] + + def check_py2(self, name): if self.may_be_null: return self.parse(""" -if $value is not $_.None: +if $value is not $none: if isinstance($value, $_.unicode): $string = $value.encode("utf-8") elif not isinstance($value, $_.str): @@ -209,8 +235,8 @@ def check(self, name): else: $string = $value else: - $string = $_.None -""", value=name)["string"] + $string = $none +""", value=name, none=None)["string"] return self.parse(""" if $_.isinstance($value, $_.unicode): @@ -221,7 +247,7 @@ def check(self, name): $string = $value """, value=name)["string"] - def pack(self, name): + def pack_py2(self, name): return self.parse(""" if $value: $c_value = $value @@ -229,6 +255,14 @@ def pack(self, name): $c_value = $ffi.cast("char*", 0) """, value=name)["c_value"] + def pack_py3(self, name): + return self.parse(""" +if $value is not None: + $c_value = $value +else: + $c_value = $ffi.cast("char*", 0) +""", value=name)["c_value"] + def dup(self, name): raise NotImplementedError @@ -252,7 +286,7 @@ def parse(self, code, **kwargs): kwargs["ffi"] = self._ffi assert "_" not in kwargs - kwargs["_"] = __builtin__ + kwargs["_"] = _compat.builtins block, var = parse_with_objects(code, self.var, **kwargs) return block, var diff --git a/pgi/codegen/ctypes_backend.py b/pgi/codegen/ctypes_backend.py index 557cc1f..370af2d 100644 --- a/pgi/codegen/ctypes_backend.py +++ b/pgi/codegen/ctypes_backend.py @@ -699,14 +699,21 @@ def unpack(self, name): param_type = self.type.get_param_type(0) ctypes_type = typeinfo_to_ctypes(param_type) + p = self.get_type(param_type) + item_in = self.var() + item_out = p.unpack(item_in) + return self.parse(""" $out = [] $elm = $in_ while $elm: $entry = $elm.contents - $out.append($ctypes_type($entry.data or 0).value) + $item_in = $ctypes_type($entry.data or 0).value + $item_unpack + $out.append($item_out) $elm = $entry.next -""", in_=name, ctypes_type=ctypes_type)["out"] +""", in_=name, ctypes_type=ctypes_type, item_in=item_in, +item_out=item_out, item_unpack=p.block)["out"] def free(self, name): return self.parse(""" @@ -1227,24 +1234,41 @@ def unpack(self, name, length): # fixme: do unpack with param type + p = self.get_type(param_type) + item_in = self.var() + item_out = p.unpack(item_in) + if self.type.array_length != -1: return self.parse(""" -$out = $array[:$length.value] -""", array=data, length=length)["out"] +$out = [] +for $item_in in $array[:$length.value]: + $unpack_item + $out.append($item_out) +#$out = $array[:$length.value] +""", array=data, length=length, unpack_item=p.block, +item_in=item_in, item_out=item_out)["out"] + elif self.type.array_fixed_size != -1: return self.parse(""" -$out = $array[:$length] -""", array=data, length=self.type.array_fixed_size)["out"] +$out = [] +for $item_in in $array[:$length]: + $unpack_item + $out.append($item_out) +""", array=data, length=self.type.array_fixed_size, unpack_item=p.block, +item_in=item_in, item_out=item_out)["out"] else: return self.parse(""" $list = [] $i = 0 $current = $array and $array[$i] while $current: - $list.append($current) + $item_in = $current + $unpack_item + $list.append($item_out) $i += 1 $current = $array[$i] -""", array=data)["list"] +""", array=data, unpack_item=p.block, +item_in=item_in, item_out=item_out)["list"] def new(self, length_type): if self.type.array_length != -1: diff --git a/pgi/enum.py b/pgi/enum.py index 2d866e2..0be7794 100644 --- a/pgi/enum.py +++ b/pgi/enum.py @@ -5,6 +5,7 @@ # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. +import sys import ctypes from .clib.gobject import GEnumClassPtr, GFlagsClassPtr @@ -47,6 +48,8 @@ def value_name(self): def __new__(cls, value): if not isinstance(value, integer_types): raise TypeError("int expected, got %r instead" % type(value)) + if value > sys.maxsize: + raise OverflowError instance = EnumBase.__new__(cls, value) if value in cls._allowed: return instance @@ -114,6 +117,8 @@ def first_value_name(self): def __new__(cls, value): if not isinstance(value, integer_types): raise TypeError("int expected, got %r instead" % type(value)) + if value > sys.maxsize: + raise OverflowError return FlagsBase.__new__(cls, value) def __repr__(self): diff --git a/setup.py b/setup.py index 2ba2a68..8ed040e 100755 --- a/setup.py +++ b/setup.py @@ -233,6 +233,8 @@ def run(self): 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], diff --git a/tests/tests_mixed/test_funcs.py b/tests/tests_mixed/test_funcs.py index 75deada..34d12cc 100644 --- a/tests/tests_mixed/test_funcs.py +++ b/tests/tests_mixed/test_funcs.py @@ -32,6 +32,11 @@ def test_basic_argument(self): self.assertEqual(GLib.basename("/omg/foo/test"), "test") self.assertEqual(GLib.basename(u"/omg/foo/test"), "test") + def test_basic_utf8_arg(self): + b = Gtk.Button() + b.set_name("foobar") + self.assertEqual(b.get_name(), "foobar") + def test_return_guint(self): self.assertTrue(isinstance(Gtk.get_binary_age(), _compat.integer_types)) diff --git a/tests/tests_mixed/test_objects.py b/tests/tests_mixed/test_objects.py index 99a305f..cb622b2 100644 --- a/tests/tests_mixed/test_objects.py +++ b/tests/tests_mixed/test_objects.py @@ -18,10 +18,6 @@ except ImportError: Clutter = None else: - # FIXME: marshalling of string arrays is missing the decoding in PY3 - # and the previous Gtk.init call changed sys.argv to bytes - if sys.version_info[0] == 3: - sys.argv = [] status, argv = Clutter.init(sys.argv) if status == Clutter.InitError.SUCCESS: sys.argv = argv diff --git a/tests/tests_pgi/test_pgi_codegen_backends.py b/tests/tests_pgi/test_pgi_codegen_backends.py new file mode 100644 index 0000000..d0ccab6 --- /dev/null +++ b/tests/tests_pgi/test_pgi_codegen_backends.py @@ -0,0 +1,29 @@ +# Copyright 2014 Christoph Reiter +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +import unittest + +from pgi.codegen.cffi_backend import CFFIBackend +from pgi.codegen.ctypes_backend import CTypesBackend + + +class _TBackend(unittest.TestCase): + + Backend = None + + def test_misc(self): + backend = self.Backend() + lib = backend.get_library("GObject") + self.assertTrue(lib) + + +class TBackendCFFI(_TBackend): + Backend = CFFIBackend + + +class TBackendCTypes(_TBackend): + Backend = CTypesBackend