Imported Upstream version 1.17.0 upstream/1.17.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 31 Oct 2018 02:17:16 +0000 (11:17 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 31 Oct 2018 02:17:16 +0000 (11:17 +0900)
27 files changed:
MANIFEST.in
NEWS
PKG-INFO
README.rst
cairo/__init__.pyi
cairo/compat.h
cairo/device.c
cairo/error.c
cairo/py.typed [new file with mode: 0644]
cairo/pycairo.h
cairo/surface.c
docs/pycairo_c_api.rst
docs/reference/devices.rst
docs/reference/surfaces.rst
pycairo.egg-info/PKG-INFO [new file with mode: 0644]
pycairo.egg-info/SOURCES.txt [new file with mode: 0644]
pycairo.egg-info/dependency_links.txt [new file with mode: 0644]
pycairo.egg-info/top_level.txt [new file with mode: 0644]
setup.cfg
setup.py
tests/cmodule/cmodule.c [new file with mode: 0644]
tests/cmodule/cmodulelib.c [new file with mode: 0644]
tests/cmodule/cmodulelib.h [new file with mode: 0644]
tests/test_cmod.py [new file with mode: 0644]
tests/test_device.py
tests/test_surface.py
tests/test_typing.py

index 8dd9b50..680c2a5 100644 (file)
@@ -5,6 +5,6 @@ include MANIFEST.in
 include setup.cfg
 recursive-include docs *.py Makefile *.rst *.css *.svg
 prune docs/_build
-recursive-include tests *.py README
+recursive-include tests *.py README *.c *.h
 recursive-include examples *.py README
 include cairo/*.h
diff --git a/NEWS b/NEWS
index 77078cc..809d597 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,20 @@
 Since version 1.11.0 Pycairo uses `Semantic Versioning
 <http://semver.org/>`__ i.e. the newest version is the latest stable one.
 
+.. _v1.17.0:
+
+1.17.0 - 2018-04-15
+-------------------
+
+* :class:`cairo.Surface` and :class:`cairo.Device` can now be used as context
+  managers. :bug:`103`
+* Fix a leak when a cairo error was raised.
+* Fix a leak when a mapped surface was GCed instead of unmapped.
+* Make it possible to use the C API with Python 3 outside of the compilation
+  unit doing the import by defining ``PYCAIRO_NO_IMPORT``. :bug:`110`
+* Implement PEP 561 (added a py.typed marker)
+
+
 .. _v1.16.3:
 
 1.16.3 - 2018-02-27
index 1361934..caf0963 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,10 +1,10 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: pycairo
-Version: 1.16.3
+Version: 1.17.0
 Summary: Python interface for cairo
 Home-page: https://pycairo.readthedocs.io
-Author: Christoph Reiter
-Author-email: reiter.christoph@gmail.com
+Maintainer: Christoph Reiter
+Maintainer-email: reiter.christoph@gmail.com
 License: UNKNOWN
 Description: .. image:: https://cdn.rawgit.com/pygobject/pycairo/master/docs/images/pycairo.svg
            :align: center
@@ -37,23 +37,22 @@ Description: .. image:: https://cdn.rawgit.com/pygobject/pycairo/master/docs/ima
         
             import cairo
         
-            surface = cairo.SVGSurface("example.svg", 200, 200)
-            context = cairo.Context(surface)
-            x, y, x1, y1 = 0.1, 0.5, 0.4, 0.9
-            x2, y2, x3, y3 = 0.6, 0.1, 0.9, 0.5
-            context.scale(200, 200)
-            context.set_line_width(0.04)
-            context.move_to(x, y)
-            context.curve_to(x1, y1, x2, y2, x3, y3)
-            context.stroke()
-            context.set_source_rgba(1, 0.2, 0.2, 0.6)
-            context.set_line_width(0.02)
-            context.move_to(x, y)
-            context.line_to(x1, y1)
-            context.move_to(x2, y2)
-            context.line_to(x3, y3)
-            context.stroke()
-            surface.finish()
+            with cairo.SVGSurface("example.svg", 200, 200) as surface:
+                context = cairo.Context(surface)
+                x, y, x1, y1 = 0.1, 0.5, 0.4, 0.9
+                x2, y2, x3, y3 = 0.6, 0.1, 0.9, 0.5
+                context.scale(200, 200)
+                context.set_line_width(0.04)
+                context.move_to(x, y)
+                context.curve_to(x1, y1, x2, y2, x3, y3)
+                context.stroke()
+                context.set_source_rgba(1, 0.2, 0.2, 0.6)
+                context.set_line_width(0.02)
+                context.move_to(x, y)
+                context.line_to(x1, y1)
+                context.move_to(x2, y2)
+                context.line_to(x3, y3)
+                context.stroke()
         
         ----
         
index 53c40cd..aa2632e 100644 (file)
@@ -29,23 +29,22 @@ Features of the Pycairo bindings:
 
     import cairo
 
-    surface = cairo.SVGSurface("example.svg", 200, 200)
-    context = cairo.Context(surface)
-    x, y, x1, y1 = 0.1, 0.5, 0.4, 0.9
-    x2, y2, x3, y3 = 0.6, 0.1, 0.9, 0.5
-    context.scale(200, 200)
-    context.set_line_width(0.04)
-    context.move_to(x, y)
-    context.curve_to(x1, y1, x2, y2, x3, y3)
-    context.stroke()
-    context.set_source_rgba(1, 0.2, 0.2, 0.6)
-    context.set_line_width(0.02)
-    context.move_to(x, y)
-    context.line_to(x1, y1)
-    context.move_to(x2, y2)
-    context.line_to(x3, y3)
-    context.stroke()
-    surface.finish()
+    with cairo.SVGSurface("example.svg", 200, 200) as surface:
+        context = cairo.Context(surface)
+        x, y, x1, y1 = 0.1, 0.5, 0.4, 0.9
+        x2, y2, x3, y3 = 0.6, 0.1, 0.9, 0.5
+        context.scale(200, 200)
+        context.set_line_width(0.04)
+        context.move_to(x, y)
+        context.curve_to(x1, y1, x2, y2, x3, y3)
+        context.stroke()
+        context.set_source_rgba(1, 0.2, 0.2, 0.6)
+        context.set_line_width(0.02)
+        context.move_to(x, y)
+        context.line_to(x1, y1)
+        context.move_to(x2, y2)
+        context.line_to(x3, y3)
+        context.stroke()
 
 ----
 
index 63c6455..fde6a65 100644 (file)
@@ -563,6 +563,12 @@ class ScriptSurface(Surface):
     @classmethod
     def create_for_target(cls, script: ScriptDevice, target: Surface) -> "ScriptSurface": ...
 
+class Win32Surface(Surface):
+    def __init__(self, hdc: int) -> None: ...
+
+class Win32PrintingSurface(Surface):
+    def __init__(self, hdc: int) -> None: ...
+
 class SolidPattern(Pattern):
     def __init__(self, red: float, green: float, blue: float, alpha: float=1.0) -> None: ...
     def get_rgba(self) -> Tuple[float, float, float, float]: ...
index 7f877d0..ae84cca 100644 (file)
         PyObject_HEAD_INIT(type) size,
 #endif
 
+#ifdef __GNUC__
+#define PYCAIRO_MODINIT_FUNC __attribute__((visibility("default"))) PyMODINIT_FUNC
+#else
+#define PYCAIRO_MODINIT_FUNC PyMODINIT_FUNC
+#endif
+
 #if PY_MAJOR_VERSION < 3
 
 #define PYCAIRO_MOD_ERROR_VAL
 #define PYCAIRO_MOD_SUCCESS_VAL(val)
-#define PYCAIRO_MOD_INIT(name) void init##name(void)
+#define PYCAIRO_MOD_INIT(name) PYCAIRO_MODINIT_FUNC init##name(void)
 
 #define PYCAIRO_PyUnicode_FromString PyString_FromString
 #define PYCAIRO_PyUnicode_Format PyString_Format
@@ -67,7 +73,7 @@
 
 #define PYCAIRO_MOD_ERROR_VAL NULL
 #define PYCAIRO_MOD_SUCCESS_VAL(val) val
-#define PYCAIRO_MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
+#define PYCAIRO_MOD_INIT(name) PYCAIRO_MODINIT_FUNC PyInit_##name(void)
 
 #define PYCAIRO_PyUnicode_FromString PyUnicode_FromString
 #define PYCAIRO_PyUnicode_Format PyUnicode_Format
index 1b3933d..c2d5882 100644 (file)
@@ -72,7 +72,23 @@ device_release (PycairoDevice *obj) {
     Py_RETURN_NONE;
 }
 
+static PyObject *
+device_ctx_enter (PyObject *obj) {
+    Py_INCREF (obj);
+    return obj;
+}
+
+static PyObject *
+device_ctx_exit (PycairoDevice *obj, PyObject *args) {
+    Py_BEGIN_ALLOW_THREADS;
+    cairo_device_finish (obj->device);
+    Py_END_ALLOW_THREADS;
+    Py_RETURN_NONE;
+}
+
 static PyMethodDef device_methods[] = {
+    {"__enter__",      (PyCFunction)device_ctx_enter,          METH_NOARGS},
+    {"__exit__",       (PyCFunction)device_ctx_exit,           METH_VARARGS},
     {"finish",         (PyCFunction)device_finish,             METH_NOARGS},
     {"flush",          (PyCFunction)device_flush,              METH_NOARGS},
     {"acquire",        (PyCFunction)device_acquire,            METH_NOARGS},
index 275e79a..df79217 100644 (file)
@@ -54,10 +54,13 @@ status_to_string(cairo_status_t status)
 static void
 set_error (PyObject *error_type, cairo_status_t status)
 {
-    PyObject *args, *v;
+    PyObject *args, *v, *int_enum;
 
-    args = Py_BuildValue("(sO)", status_to_string(status),
-                         CREATE_INT_ENUM(Status, status));
+    int_enum = CREATE_INT_ENUM(Status, status);
+    if (int_enum == NULL)
+        return;
+    args = Py_BuildValue("(sO)", status_to_string(status), int_enum);
+    Py_DECREF (int_enum);
     v = PyObject_Call(error_type, args, NULL);
     Py_DECREF(args);
     if (v != NULL) {
diff --git a/cairo/py.typed b/cairo/py.typed
new file mode 100644 (file)
index 0000000..e69de29
index ac08be2..c965268 100644 (file)
@@ -249,11 +249,17 @@ typedef struct {
 
 #else
 
+#ifdef PYCAIRO_NO_IMPORT
+
+extern Pycairo_CAPI_t *Pycairo_CAPI;
+
+#else
+
 /* To access the Pycairo C API, the client module should call 'import_cairo()'
  * from the init<module> function, and check the return value, < 0 means the
  * import failed.
  */
-static Pycairo_CAPI_t *Pycairo_CAPI;
+Pycairo_CAPI_t *Pycairo_CAPI;
 
 /* Return -1 on error, 0 on success.
  * PyCapsule_Import will set an exception if there's an error.
@@ -267,6 +273,8 @@ import_cairo(void)
 
 #endif
 
+#endif
+
 #endif /* ifndef _INSIDE_PYCAIRO_ */
 
 #endif /* ifndef _PYCAIRO_H_ */
index 9fbe8fe..3743261 100644 (file)
@@ -644,7 +644,7 @@ surface_unmap_image (PycairoSurface *self, PyObject *args) {
   /* Replace the mapped image surface with a fake one and finish it so
    * that any operation on it fails.
    */
-  fake_surface = cairo_image_surface_create (CAIRO_FORMAT_INVALID, 0, 0);
+  fake_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
   cairo_surface_finish (fake_surface);
   pymapped->surface = fake_surface;
   /* We no longer need the base surface */
@@ -655,6 +655,20 @@ surface_unmap_image (PycairoSurface *self, PyObject *args) {
 
 #endif /* CAIRO_HAS_IMAGE_SURFACE */
 
+static PyObject *
+surface_ctx_enter (PyObject *obj) {
+  Py_INCREF (obj);
+  return obj;
+}
+
+static PyObject *
+surface_ctx_exit (PycairoSurface *obj, PyObject *args) {
+  Py_BEGIN_ALLOW_THREADS;
+  cairo_surface_finish (obj->surface);
+  Py_END_ALLOW_THREADS;
+  Py_RETURN_NONE;
+}
+
 static PyMethodDef surface_methods[] = {
   /* methods never exposed in a language binding:
    * cairo_surface_destroy()
@@ -663,6 +677,8 @@ static PyMethodDef surface_methods[] = {
    * cairo_surface_reference()
    * cairo_surface_set_user_data()
    */
+  {"__enter__",      (PyCFunction)surface_ctx_enter,          METH_NOARGS},
+  {"__exit__",       (PyCFunction)surface_ctx_exit,           METH_VARARGS},
   {"copy_page",      (PyCFunction)surface_copy_page,          METH_NOARGS},
   {"create_similar", (PyCFunction)surface_create_similar,     METH_VARARGS},
   {"create_similar_image", (PyCFunction)surface_create_similar_image,
@@ -1124,8 +1140,13 @@ mapped_image_surface_dealloc (PycairoImageSurface *self) {
   if (cairo_surface_get_user_data (
       self->surface, &surface_is_mapped_image) != NULL) {
     cairo_surface_unmap_image (pybasesurface->surface, self->surface);
+    self->surface = NULL;
+  } else {
+    cairo_surface_destroy(self->surface);
+    self->surface = NULL;
   }
 
+  Py_CLEAR (self->base);
   Py_TYPE (self)->tp_free (self);
 }
 
@@ -1137,7 +1158,21 @@ mapped_image_surface_finish (PycairoSurface *self) {
   return NULL;
 }
 
+static PyObject *
+mapped_image_surface_ctx_exit (PycairoImageSurface *self, PyObject *args) {
+  PycairoSurface *pybasesurface = (PycairoSurface *)(self->base);
+  PyObject *subargs, *result;
+
+  subargs = Py_BuildValue("(O)", self);
+  if (subargs == NULL)
+    return NULL;
+  result = surface_unmap_image (pybasesurface, subargs);
+  Py_DECREF (subargs);
+  return result;
+}
+
 static PyMethodDef mapped_image_surface_methods[] = {
+  {"__exit__",     (PyCFunction)mapped_image_surface_ctx_exit,   METH_VARARGS},
   {"finish",       (PyCFunction)mapped_image_surface_finish,     METH_NOARGS},
   {NULL, NULL, 0, NULL},
 };
index 818b4b6..f3c41e8 100644 (file)
@@ -59,11 +59,18 @@ Edit the client module file to add the following lines::
   #include "pycairo.h"
 
   /* define a variable for the C API */
-  static Pycairo_CAPI_t *Pycairo_CAPI;
+  Pycairo_CAPI_t *Pycairo_CAPI;
 
   /* import pycairo - add to the init<module> function */
   Pycairo_IMPORT;
 
+In case you want to use the API from another compilation unit::
+
+  #include <pycairo.h>
+
+  extern Pycairo_CAPI_t *Pycairo_CAPI;
+
+  ...
 
 To access the Pycairo C API under Python 3
 ==========================================
@@ -86,6 +93,17 @@ Example showing how to import the pycairo API::
     return m;
   }
 
+In case you want to use the API from another compilation unit::
+
+  #define PYCAIRO_NO_IMPORT
+  #include <py3cairo.h>
+
+  ...
+
+.. versionadded:: 1.17.0
+
+    The ``PYCAIRO_NO_IMPORT`` macro is used since 1.17.0
+
 
 Misc Functions
 ==============
index fe9fe0f..a4caae2 100644 (file)
@@ -17,6 +17,18 @@ class Device()
 
     .. versionadded:: 1.14
 
+    .. note::
+
+        .. versionadded:: 1.17.0
+
+            :class:`cairo.Device` can be used as a context manager:
+
+        .. code:: python
+
+            # device.finish() will be called on __exit__
+            with cairo.ScriptDevice(f) as device:
+                pass
+
     .. method:: finish()
 
         This function finishes the device and drops all references to external
index a9b7baf..1c27871 100644 (file)
@@ -48,6 +48,22 @@ class Surface()
    *Surface* is the abstract base class from which all the other surface
    classes derive. It cannot be instantiated directly.
 
+   .. note::
+
+      .. versionadded:: 1.17.0
+
+         :class:`cairo.Surface` can be used as a context manager:
+
+      .. code:: python
+
+         # surface.finish() will be called on __exit__
+         with cairo.SVGSurface("example.svg", 200, 200) as surface:
+             pass
+
+         # surface.unmap_image(image_surface) will be called on __exit__
+         with surface.map_to_image(None) as image_surface:
+             pass
+
    .. method:: copy_page()
 
       Emits the current page for backends that support multiple pages, but
diff --git a/pycairo.egg-info/PKG-INFO b/pycairo.egg-info/PKG-INFO
new file mode 100644 (file)
index 0000000..caf0963
--- /dev/null
@@ -0,0 +1,88 @@
+Metadata-Version: 1.2
+Name: pycairo
+Version: 1.17.0
+Summary: Python interface for cairo
+Home-page: https://pycairo.readthedocs.io
+Maintainer: Christoph Reiter
+Maintainer-email: reiter.christoph@gmail.com
+License: UNKNOWN
+Description: .. image:: https://cdn.rawgit.com/pygobject/pycairo/master/docs/images/pycairo.svg
+           :align: center
+           :width: 370px
+        
+        |
+        
+        Pycairo is a Python module providing bindings for the `cairo graphics library
+        <https://cairographics.org/>`__. It depends on **cairo >= 1.13.1** and
+        works with **Python 2.7+** as well as **Python 3.3+**. Pycairo, including this
+        documentation, is licensed under the **LGPLv2.1** as well as the **MPLv1.1**.
+        
+        The Pycairo bindings are designed to match the cairo C API as closely as
+        possible, and to deviate only in cases which are clearly better implemented in
+        a more â€˜Pythonic’ way.
+        
+        Features of the Pycairo bindings:
+        
+        * Provides an object oriented interface to cairo.
+        * Queries the error status of objects and translates them to exceptions.
+        * Provides a C API that can be used by other Python extensions.
+        
+        ----
+        
+        .. image:: https://cdn.rawgit.com/pygobject/pycairo/master/docs/images/example.svg
+           :align: right
+           :width: 200px
+        
+        .. code:: python
+        
+            import cairo
+        
+            with cairo.SVGSurface("example.svg", 200, 200) as surface:
+                context = cairo.Context(surface)
+                x, y, x1, y1 = 0.1, 0.5, 0.4, 0.9
+                x2, y2, x3, y3 = 0.6, 0.1, 0.9, 0.5
+                context.scale(200, 200)
+                context.set_line_width(0.04)
+                context.move_to(x, y)
+                context.curve_to(x1, y1, x2, y2, x3, y3)
+                context.stroke()
+                context.set_source_rgba(1, 0.2, 0.2, 0.6)
+                context.set_line_width(0.02)
+                context.move_to(x, y)
+                context.line_to(x1, y1)
+                context.move_to(x2, y2)
+                context.line_to(x3, y3)
+                context.stroke()
+        
+        ----
+        
+        If Pycairo is not what you need, have a look at `cairocffi
+        <https://cairocffi.readthedocs.io>`__, which is an API compatible package
+        using `cffi <https://cffi.readthedocs.io/>`__ or `Qahirah
+        <https://github.com/ldo/qahirah>`__, which is using `ctypes
+        <https://docs.python.org/3/library/ctypes.html>`__ and provides a more
+        "pythonic" API with less focus on matching the cairo C API.
+        
+        For more information visit https://pycairo.readthedocs.io
+        
+        .. image:: https://travis-ci.org/pygobject/pycairo.svg?branch=master
+            :target: https://travis-ci.org/pygobject/pycairo
+        
+        .. image:: https://ci.appveyor.com/api/projects/status/9hurdbb19lg2i9xm/branch/master?svg=true
+            :target: https://ci.appveyor.com/project/lazka/pycairo/branch/master
+        
+        .. image:: https://codecov.io/gh/pygobject/pycairo/branch/master/graph/badge.svg
+          :target: https://codecov.io/gh/pygobject/pycairo
+        
+Platform: UNKNOWN
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)
+Classifier: License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)
diff --git a/pycairo.egg-info/SOURCES.txt b/pycairo.egg-info/SOURCES.txt
new file mode 100644 (file)
index 0000000..71011cc
--- /dev/null
@@ -0,0 +1,123 @@
+COPYING
+COPYING-LGPL-2.1
+COPYING-MPL-1.1
+MANIFEST.in
+NEWS
+README.rst
+setup.cfg
+setup.py
+cairo/__init__.py
+cairo/__init__.pyi
+cairo/bufferproxy.c
+cairo/cairomodule.c
+cairo/compat.h
+cairo/context.c
+cairo/device.c
+cairo/enums.c
+cairo/error.c
+cairo/font.c
+cairo/glyph.c
+cairo/matrix.c
+cairo/misc.c
+cairo/path.c
+cairo/pattern.c
+cairo/private.h
+cairo/py.typed
+cairo/pycairo.h
+cairo/rectangle.c
+cairo/region.c
+cairo/surface.c
+cairo/textcluster.c
+cairo/textextents.c
+docs/Makefile
+docs/changelog.rst
+docs/conf.py
+docs/examples.rst
+docs/extra.css
+docs/faq.rst
+docs/index.rst
+docs/integration.rst
+docs/pycairo_c_api.rst
+docs/resources.rst
+docs/tutorial.rst
+docs/images/example.svg
+docs/images/pycairo.svg
+docs/reference/constants.rst
+docs/reference/context.rst
+docs/reference/devices.rst
+docs/reference/enums.rst
+docs/reference/exceptions.rst
+docs/reference/glyph.rst
+docs/reference/index.rst
+docs/reference/legacy_constants.rst
+docs/reference/matrix.rst
+docs/reference/paths.rst
+docs/reference/patterns.rst
+docs/reference/rectangle.rst
+docs/reference/region.rst
+docs/reference/surfaces.rst
+docs/reference/text.rst
+docs/reference/textcluster.rst
+docs/reference/textextents.rst
+examples/pygame-demo.py
+examples/cairo_snippets/snippets_gtk.py
+examples/cairo_snippets/snippets_pdf.py
+examples/cairo_snippets/snippets_png.py
+examples/cairo_snippets/snippets_ps.py
+examples/cairo_snippets/snippets_svg.py
+examples/cairo_snippets/snippets/__init__.py
+examples/cairo_snippets/snippets/arc.py
+examples/cairo_snippets/snippets/arc_negative.py
+examples/cairo_snippets/snippets/clip.py
+examples/cairo_snippets/snippets/curve_rectangle.py
+examples/cairo_snippets/snippets/curve_to.py
+examples/cairo_snippets/snippets/ellipse.py
+examples/cairo_snippets/snippets/fill_and_stroke.py
+examples/cairo_snippets/snippets/fill_and_stroke2.py
+examples/cairo_snippets/snippets/glyph_path.py
+examples/cairo_snippets/snippets/gradient.py
+examples/cairo_snippets/snippets/gradient_mask.py
+examples/cairo_snippets/snippets/group.py
+examples/cairo_snippets/snippets/hering.py
+examples/cairo_snippets/snippets/path.py
+examples/cairo_snippets/snippets/set_line_cap.py
+examples/cairo_snippets/snippets/set_line_join.py
+examples/cairo_snippets/snippets/show_glyphs.py
+examples/cairo_snippets/snippets/spiral.py
+examples/cairo_snippets/snippets/text.py
+examples/cairo_snippets/snippets/text_align_center.py
+examples/cairo_snippets/snippets/text_extents.py
+examples/cairo_snippets/snippets/warpedtext.py
+examples/gtk/cairo-demo.py
+examples/gtk/cairo-knockout.py
+examples/gtk/png_view.py
+examples/gtk/text.py
+pycairo.egg-info/PKG-INFO
+pycairo.egg-info/SOURCES.txt
+pycairo.egg-info/dependency_links.txt
+pycairo.egg-info/top_level.txt
+tests/__init__.py
+tests/hypothesis_fspaths.py
+tests/test_api.py
+tests/test_cmod.py
+tests/test_context.py
+tests/test_device.py
+tests/test_enums.py
+tests/test_error.py
+tests/test_font.py
+tests/test_glyph.py
+tests/test_hypothesis.py
+tests/test_matrix.py
+tests/test_path.py
+tests/test_pattern.py
+tests/test_rectangle.py
+tests/test_region.py
+tests/test_surface.py
+tests/test_surface_numpy.py
+tests/test_surface_pygame.py
+tests/test_textcluster.py
+tests/test_textextents.py
+tests/test_typing.py
+tests/cmodule/cmodule.c
+tests/cmodule/cmodulelib.c
+tests/cmodule/cmodulelib.h
\ No newline at end of file
diff --git a/pycairo.egg-info/dependency_links.txt b/pycairo.egg-info/dependency_links.txt
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/pycairo.egg-info/top_level.txt b/pycairo.egg-info/top_level.txt
new file mode 100644 (file)
index 0000000..129fe16
--- /dev/null
@@ -0,0 +1 @@
+cairo
index 642c317..69ae391 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,11 +1,11 @@
 [flake8]
-ignore=E402,E741
-builtins=buffer,unichr
+ignore = E402,E741
+builtins = buffer,unichr
 
 [coverage:run]
-include=
-    cairo/*
-    tests/*
+include = 
+       cairo/*
+       tests/*
 
 [mypy-setup]
 ignore_errors = True
@@ -25,3 +25,9 @@ warn_return_any = True
 no_implicit_optional = True
 disallow_any_decorated = True
 disallow_any_explicit = False
+strict_optional = True
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
index 8cb12d6..472fe4e 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -6,18 +6,19 @@ import sys
 import os
 import errno
 
-if os.environ.get("PYCAIRO_SETUPTOOLS"):
-    # for testing
-    import setuptools
-    setuptools
+try:
+    from setuptools import setup
+except ImportError:
+    from distutils.core import setup
 
-from distutils.core import Extension, setup, Command, Distribution
+from distutils.core import Extension, Command, Distribution
 from distutils.ccompiler import new_compiler
+from distutils.sysconfig import customize_compiler
 from distutils import log
 from distutils import sysconfig
 
 
-PYCAIRO_VERSION = '1.16.3'
+PYCAIRO_VERSION = '1.17.0'
 CAIRO_VERSION_REQUIRED = '1.13.1'
 XPYB_VERSION_REQUIRED = '1.3'
 
@@ -58,6 +59,184 @@ def pkg_config_parse(opt, pkg):
     return [x.lstrip(opt) for x in output.split()]
 
 
+def filter_compiler_arguments(compiler, args):
+    """Given a compiler instance and a list of compiler warning flags
+    returns the list of supported flags.
+    """
+
+    if compiler.compiler_type == "msvc":
+        # TODO
+        return []
+
+    extra = []
+
+    def check_arguments(compiler, args):
+        p = subprocess.Popen(
+            [compiler.compiler[0]] + args + extra + ["-x", "c", "-E", "-"],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate(b"int i;\n")
+        if p.returncode != 0:
+            text = stderr.decode("ascii", "replace")
+            return False, [a for a in args if a in text]
+        else:
+            return True, []
+
+    def check_argument(compiler, arg):
+        return check_arguments(compiler, [arg])[0]
+
+    # clang doesn't error out for unknown options, force it to
+    if check_argument(compiler, '-Werror=unknown-warning-option'):
+        extra += ['-Werror=unknown-warning-option']
+    if check_argument(compiler, '-Werror=unused-command-line-argument'):
+        extra += ['-Werror=unused-command-line-argument']
+
+    # first try to remove all arguments contained in the error message
+    supported = list(args)
+    while 1:
+        ok, maybe_unknown = check_arguments(compiler, supported)
+        if ok:
+            return supported
+        elif not maybe_unknown:
+            break
+        for unknown in maybe_unknown:
+            if not check_argument(compiler, unknown):
+                supported.remove(unknown)
+
+    # hm, didn't work, try each argument one by one
+    supported = []
+    for arg in args:
+        if check_argument(compiler, arg):
+            supported.append(arg)
+    return supported
+
+
+def add_ext_cflags(ext, compiler):
+    args = [
+        "-Wall",
+        "-Warray-bounds",
+        "-Wcast-align",
+        "-Wconversion",
+        "-Wdeclaration-after-statement",
+        "-Wextra",
+        "-Wformat=2",
+        "-Wformat-nonliteral",
+        "-Wformat-security",
+        "-Wimplicit-function-declaration",
+        "-Winit-self",
+        "-Winline",
+        "-Wmissing-format-attribute",
+        "-Wmissing-noreturn",
+        "-Wnested-externs",
+        "-Wold-style-definition",
+        "-Wpacked",
+        "-Wpointer-arith",
+        "-Wreturn-type",
+        "-Wshadow",
+        "-Wsign-compare",
+        "-Wstrict-aliasing",
+        "-Wundef",
+        "-Wunused-but-set-variable",
+    ]
+
+    if sys.version_info[:2] not in [(3, 3), (3, 4)]:
+        args += [
+            "-Wswitch-default",
+        ]
+
+    args += [
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-parameter",
+    ]
+
+    # silence clang for unused gcc CFLAGS added by Debian
+    args += [
+        "-Wno-unused-command-line-argument",
+    ]
+
+    args += [
+        "-fno-strict-aliasing",
+        "-fvisibility=hidden",
+    ]
+
+    ext.extra_compile_args += filter_compiler_arguments(compiler, args)
+
+
+class build_tests(Command):
+    description = "build test libraries and extensions"
+    user_options = [
+        ("force", "f", "force a rebuild"),
+        ("enable-xpyb", None, "Build with xpyb support (default=disabled)"),
+    ]
+
+    def initialize_options(self):
+        self.force = False
+        self.build_base = None
+        self.enable_xpyb = False
+
+    def finalize_options(self):
+        self.set_undefined_options(
+            'build',
+            ('build_base', 'build_base'))
+        self.enable_xpyb = bool(self.enable_xpyb)
+        self.force = bool(self.force)
+
+    def run(self):
+        cmd = self.reinitialize_command("build_ext")
+        cmd.inplace = True
+        cmd.enable_xpyb = self.enable_xpyb
+        cmd.force = self.force
+        cmd.ensure_finalized()
+        cmd.run()
+
+        import cairo
+
+        tests_dir = os.path.join("tests", "cmodule")
+
+        ext = Extension(
+            name='tests.cmod',
+            sources=[
+                os.path.join(tests_dir, "cmodule.c"),
+                os.path.join(tests_dir, "cmodulelib.c"),
+            ],
+            include_dirs=[
+                tests_dir,
+                cairo.get_include(),
+            ],
+            depends=[
+                os.path.join(tests_dir, "cmodulelib.h"),
+                os.path.join(cairo.get_include(), "pycairo.h"),
+            ],
+            define_macros=[("PY_SSIZE_T_CLEAN", None)],
+        )
+
+        compiler = new_compiler()
+        customize_compiler(compiler)
+
+        add_ext_cflags(ext, compiler)
+
+        if compiler.compiler_type == "msvc":
+            ext.libraries += ['cairo']
+        else:
+            pkg_config_version_check('cairo', CAIRO_VERSION_REQUIRED)
+            ext.include_dirs += pkg_config_parse('--cflags-only-I', 'cairo')
+            ext.library_dirs += pkg_config_parse('--libs-only-L', 'cairo')
+            ext.libraries += pkg_config_parse('--libs-only-l', 'cairo')
+
+        dist = Distribution({"ext_modules": [ext]})
+
+        build_cmd = dist.get_command_obj("build")
+        build_cmd.build_base = os.path.join(self.build_base, "pycairo_tests")
+        build_cmd.ensure_finalized()
+
+        cmd = dist.get_command_obj("build_ext")
+        cmd.inplace = True
+        cmd.force = self.force
+        cmd.ensure_finalized()
+        cmd.run()
+
+
 class test_cmd(Command):
     description = "run tests"
     user_options = [
@@ -68,16 +247,14 @@ class test_cmd(Command):
         self.enable_xpyb = None
 
     def finalize_options(self):
-        pass
+        self.enable_xpyb = bool(self.enable_xpyb)
 
     def run(self):
         import pytest
 
         # ensure the C extension is build inplace
-        cmd = self.reinitialize_command("build_ext")
-        if self.enable_xpyb is not None:
-            cmd.enable_xpyb = self.enable_xpyb
-        cmd.inplace = True
+        cmd = self.reinitialize_command("build_tests")
+        cmd.enable_xpyb = self.enable_xpyb
         cmd.ensure_finalized()
         cmd.run()
 
@@ -286,47 +463,10 @@ class build_ext(du_build_ext):
             ext.include_dirs += pkg_config_parse('--cflags-only-I', 'cairo')
             ext.library_dirs += pkg_config_parse('--libs-only-L', 'cairo')
             ext.libraries += pkg_config_parse('--libs-only-l', 'cairo')
-            if sys.version_info[0] == 2:
-                # Some python setups don't pass -fno-strict-aliasing,
-                # while MACROS like Py_RETURN_TRUE require it.
-                ext.extra_compile_args += ["-fno-strict-aliasing"]
-
-            if os.environ.get("PYCAIRO_WARN"):
-                ext.extra_compile_args += [
-                    "-Wall",
-                    "-Wundef",
-                    "-Wextra",
-                    "-Wno-missing-field-initializers",
-                    "-Wno-unused-parameter",
-                    "-Wnested-externs",
-                    "-Wpointer-arith",
-                    "-Wno-missing-field-initializers",
-                    "-Wdeclaration-after-statement",
-                    "-Wformat=2",
-                    "-Wold-style-definition",
-                    "-Wcast-align",
-                    "-Wformat-nonliteral",
-                    "-Wformat-security",
-                    "-Wsign-compare",
-                    "-Wstrict-aliasing",
-                    "-Wshadow",
-                    "-Winline",
-                    "-Wpacked",
-                    "-Wmissing-format-attribute",
-                    "-Wmissing-noreturn",
-                    "-Winit-self",
-                    "-Wunused-but-set-variable",
-                    "-Warray-bounds",
-                    "-Wimplicit-function-declaration",
-                    "-Wreturn-type",
-                    "-Wconversion",
-                    "-Wno-unknown-warning-option",
-                ]
-
-                if sys.version_info[:2] not in [(3, 3), (3, 4)]:
-                    ext.extra_compile_args += [
-                        "-Wswitch-default",
-                    ]
+
+            compiler = new_compiler(compiler=self.compiler)
+            customize_compiler(compiler)
+            add_ext_cflags(ext, compiler)
 
         if self.enable_xpyb:
             if sys.version_info[0] != 2:
@@ -404,6 +544,7 @@ def main():
         "install_pkgconfig": install_pkgconfig,
         "install_pycairo_header": install_pycairo_header,
         "test": test_cmd,
+        "build_tests": build_tests,
     }
 
     setup(
@@ -416,7 +557,12 @@ def main():
         maintainer_email="reiter.christoph@gmail.com",
         ext_modules=[cairo_ext],
         packages=["cairo"],
-        package_data={"cairo": ["__init__.pyi"]},
+        package_data={
+            "cairo": [
+                "__init__.pyi",
+                "py.typed",
+            ],
+        },
         classifiers=[
             'Operating System :: OS Independent',
             'Programming Language :: Python :: 2',
diff --git a/tests/cmodule/cmodule.c b/tests/cmodule/cmodule.c
new file mode 100644 (file)
index 0000000..95d5fd6
--- /dev/null
@@ -0,0 +1,53 @@
+#include <Python.h>
+/* not pycairo3.h because we use the one from the source directory */
+#include <pycairo.h>
+#include "cmodulelib.h"
+
+static PyMethodDef CModMethods[] = {
+    {"create_image_surface", create_image_surface, METH_NOARGS, NULL},
+    {NULL, NULL, 0, NULL}
+};
+
+#ifdef __GNUC__
+#define PYCAIRO_MODINIT_FUNC __attribute__((visibility("default"))) PyMODINIT_FUNC
+#else
+#define PYCAIRO_MODINIT_FUNC PyMODINIT_FUNC
+#endif
+
+#if PY_MAJOR_VERSION < 3
+
+Pycairo_CAPI_t *Pycairo_CAPI;
+
+PYCAIRO_MODINIT_FUNC
+initcmod (void)
+{
+    Py_InitModule ("cmod", CModMethods);
+
+    Pycairo_IMPORT;
+}
+#else
+
+static struct PyModuleDef cmod_module = {
+    PyModuleDef_HEAD_INIT,
+    "cmod",
+    NULL,
+    -1,
+    CModMethods,
+};
+
+PYCAIRO_MODINIT_FUNC
+PyInit_cmod (void)
+{
+    PyObject *m;
+
+    if (import_cairo () < 0)
+        return NULL;
+
+    m = PyModule_Create (&cmod_module);
+    if (m == NULL)
+        return NULL;
+
+    return m;
+}
+
+#endif
diff --git a/tests/cmodule/cmodulelib.c b/tests/cmodule/cmodulelib.c
new file mode 100644 (file)
index 0000000..c93b14b
--- /dev/null
@@ -0,0 +1,18 @@
+#include <Python.h>
+#define PYCAIRO_NO_IMPORT
+#include <pycairo.h>
+#include "cmodulelib.h"
+
+#if PY_MAJOR_VERSION < 3
+extern Pycairo_CAPI_t *Pycairo_CAPI;
+#endif
+
+PyObject *
+create_image_surface (PyObject *self, PyObject *args)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 10, 10);
+
+    return PycairoSurface_FromSurface (surface, NULL);
+}
diff --git a/tests/cmodule/cmodulelib.h b/tests/cmodule/cmodulelib.h
new file mode 100644 (file)
index 0000000..a374fe9
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _PYCAIRO_CMODULELIB_H_
+#define _PYCAIRO_CMODULELIB_H_
+
+PyObject *create_image_surface (PyObject *self, PyObject *args);
+
+#endif
diff --git a/tests/test_cmod.py b/tests/test_cmod.py
new file mode 100644 (file)
index 0000000..8353dd8
--- /dev/null
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+import cairo
+
+from . import cmod
+
+
+def test_foo():
+    surface = cmod.create_image_surface()
+    assert isinstance(surface, cairo.ImageSurface)
index 97381f0..40ae782 100644 (file)
@@ -8,6 +8,17 @@ import cairo
 import pytest
 
 
+def test_context_manager():
+    f = io.BytesIO()
+    with cairo.ScriptDevice(f) as dev:
+        dev.acquire()
+        dev.release()
+
+    with pytest.raises(cairo.Error) as excinfo:
+        dev.acquire()
+    assert excinfo.value.status == cairo.Status.DEVICE_FINISHED
+
+
 def test_cmp_hash():
     f = io.BytesIO()
     dev = cairo.ScriptDevice(f)
index 6018f04..78f8068 100644 (file)
@@ -13,6 +13,14 @@ import cairo
 import pytest
 
 
+def test_context_manager():
+    with cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10) as surface:
+        surface.show_page()
+    with pytest.raises(cairo.Error) as excinfo:
+        surface.show_page()
+    assert excinfo.value.status == cairo.Status.SURFACE_FINISHED
+
+
 def test_surface_cmp_hash():
     main = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
     ctx = cairo.Context(main)
@@ -66,6 +74,23 @@ def test_surface_map_to_image():
     del gced
 
 
+def test_surface_map_to_image_context_manager():
+    main = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
+    with main.map_to_image(None) as image:
+        pass
+
+    with pytest.raises(RuntimeError):
+        main.unmap_image(image)
+
+    with pytest.raises(cairo.Error) as excinfo:
+        image.show_page()
+    assert excinfo.value.status == cairo.Status.SURFACE_FINISHED
+
+    with pytest.raises(RuntimeError):
+        with image:
+            pass
+
+
 def test_surface_map_to_image_data():
     main = cairo.ImageSurface(cairo.Format.RGB24, 2, 1)
 
index 0fe36af..8001939 100644 (file)
@@ -28,7 +28,8 @@ def test_typing():
     def collect_names(t):
         names = set()
         for key, value in vars(t).items():
-            if key in ["XlibSurface", "XCBSurface"]:
+            if key in ["XlibSurface", "XCBSurface", "Win32PrintingSurface",
+                       "Win32Surface"]:
                 continue
             if key.startswith("_"):
                 continue