<commit_before># coding: utf8
"""
    cairocffi.surface
    ~~~~~~~~~~~~~~~~~

    Bindings for the various types of surface objects.

    :copyright: Copyright 2013 by <NAME>
    :license: BSD, see LICENSE for details.

"""

import io
import sys
import ctypes

from . import ffi, cairo, _check_status, constants
from .fonts import FontOptions, _encode_string


SURFACE_TARGET_KEY = ffi.new('cairo_user_data_key_t *')


def _make_read_func(file_obj):
    """Return a CFFI callback that reads from a file-like object."""
    @ffi.callback("cairo_read_func_t", error=constants.STATUS_READ_ERROR)
    def read_func(closure, data, length):
        string = file_obj.read(length)
        if len(string) < length:  # EOF too early
            return constants.STATUS_READ_ERROR
        ffi.buffer(data, length)[:len(string)] = string
        return constants.STATUS_SUCCESS
    return read_func


def _make_write_func(file_obj):
    """Return a CFFI callback that writes to a file-like object."""
    if file_obj is None:
        return ffi.NULL

    @ffi.callback("cairo_write_func_t", error=constants.STATUS_WRITE_ERROR)
    def read_func(_closure, data, length):
        file_obj.write(ffi.buffer(data, length))
        return constants.STATUS_SUCCESS
    return read_func


def _encode_filename(filename):
    """Return a byte string, encoding Unicode with the filesystem encoding."""
    if not isinstance(filename, bytes):
        filename = filename.encode(sys.getfilesystemencoding())
    return ffi.new('char[]', filename)


def from_buffer(obj):
    """Return ``(pointer_address, length_in_bytes)`` for a buffer object."""
    if hasattr(obj, 'buffer_info'):
        # Looks like a array.array object.
        address, length = obj.buffer_info()
        return address, length * obj.itemsize
    else:
        # Other buffers.
        # XXX Unfortunately ctypes.c_char.from_buffer
        # does not have length information,
        # and we’re not sure that len(obj) is measured in bytes.
        # (It’s not for array.array, though that is taken care of.)
        return ctypes.addressof(ctypes.c_char.from_buffer(obj)), len(obj)


class KeepAlive(object):
    """
    Keep some objects alive until a callback is called.
    :attr:`closure` is a tuple of cairo_destroy_func_t and void* cdata objects,
    as expected by cairo_surface_set_mime_data().

    Either :meth:`save` must be called before the callback,
    or none of them must be called.

    """
    instances = set()

    def __init__(self, *objects):
        self.objects = objects
        callback = ffi.callback(
            'cairo_destroy_func_t', lambda _: self.instances.remove(self))
        # cairo wants a non-NULL closure pointer.
        self.closure = (callback, callback)

    def save(self):
        """Start keeping a reference to the passed objects."""<commit_msg>Resolve circular dependency in KeepAlive object.<commit_after># coding: utf8
"""
    cairocffi.surface
    ~~~~~~~~~~~~~~~~~

    Bindings for the various types of surface objects.

    :copyright: Copyright 2013 by <NAME>
    :license: BSD, see LICENSE for details.

"""

import io
import sys
import ctypes
import weakref

from . import ffi, cairo, _check_status, constants
from .fonts import FontOptions, _encode_string


SURFACE_TARGET_KEY = ffi.new('cairo_user_data_key_t *')


def _make_read_func(file_obj):
    """Return a CFFI callback that reads from a file-like object."""
    @ffi.callback("cairo_read_func_t", error=constants.STATUS_READ_ERROR)
    def read_func(closure, data, length):
        string = file_obj.read(length)
        if len(string) < length:  # EOF too early
            return constants.STATUS_READ_ERROR
        ffi.buffer(data, length)[:len(string)] = string
        return constants.STATUS_SUCCESS
    return read_func


def _make_write_func(file_obj):
    """Return a CFFI callback that writes to a file-like object."""
    if file_obj is None:
        return ffi.NULL

    @ffi.callback("cairo_write_func_t", error=constants.STATUS_WRITE_ERROR)
    def read_func(_closure, data, length):
        file_obj.write(ffi.buffer(data, length))
        return constants.STATUS_SUCCESS
    return read_func


def _encode_filename(filename):
    """Return a byte string, encoding Unicode with the filesystem encoding."""
    if not isinstance(filename, bytes):
        filename = filename.encode(sys.getfilesystemencoding())
    return ffi.new('char[]', filename)


def from_buffer(obj):
    """Return ``(pointer_address, length_in_bytes)`` for a buffer object."""
    if hasattr(obj, 'buffer_info'):
        # Looks like a array.array object.
        address, length = obj.buffer_info()
        return address, length * obj.itemsize
    else:
        # Other buffers.
        # XXX Unfortunately ctypes.c_char.from_buffer
        # does not have length information,
        # and we’re not sure that len(obj) is measured in bytes.
        # (It’s not for array.array, though that is taken care of.)
        return ctypes.addressof(ctypes.c_char.from_buffer(obj)), len(obj)


class KeepAlive(object):
    """
    Keep some objects alive until a callback is called.
    :attr:`closure` is a tuple of cairo_destroy_func_t and void* cdata objects,
    as expected by cairo_surface_set_mime_data().

    Either :meth:`save` must be called before the callback,
    or none of them must be called.

    """
    instances = set()

    def __init__(self, *objects):
        self.objects = objects
        weakself = weakref.ref(self)

        def closure(_):
            value = weakself()
            if value is not None:
                value.instances.remove(value)

        callback = ffi.callback(
            'cairo_destroy_func_t', closure)
        # cairo wants a non-NULL closure pointer.
        self.closure = (callback, callback)

    def save(self):
        """Start keeping a reference to the passed objects."""
{'fraction_non_alphanumeric': 0.10067911714770798, 'fraction_numerical': 0.001697792869269949, 'mean_word_length': 3.108089260808926, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5963820', 'n_tokens_mistral': 1753, 'n_tokens_neox': 1714, 'n_words': 531}