[Python-modules-commits] [python-djvulibre] 01/05: Imported Upstream version 0.5

Daniel Stender danstender-guest at moszumanska.debian.org
Tue Aug 11 00:00:47 UTC 2015


This is an automated email from the git hooks/post-receive script.

danstender-guest pushed a commit to branch master
in repository python-djvulibre.

commit 5bce0f4bfa6f8400d43fc8de25c595280f0564e0
Author: Daniel Stender <debian at danielstender.com>
Date:   Tue Aug 11 01:48:33 2015 +0200

    Imported Upstream version 0.5
---
 MANIFEST.in                           |   2 +-
 PKG-INFO                              |   2 +-
 djvu/common.pxi                       | 153 +++++++++--------
 djvu/config.pxi                       |   3 -
 djvu/decode.pyx                       |  28 ++-
 djvu/sexpr.pyx                        | 315 ++++++++++++++++++++++------------
 doc/api/conf.py                       |   2 +-
 doc/changelog                         |  26 +++
 python_djvulibre.egg-info/PKG-INFO    |   2 +-
 python_djvulibre.egg-info/SOURCES.txt |   1 -
 setup.py                              |  45 ++++-
 tests/common.py                       |   8 +-
 tests/test_decode.py                  |   3 +-
 tests/test_sexpr.py                   | 256 +++++++++++++++------------
 14 files changed, 520 insertions(+), 326 deletions(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index e035ff7..ff7776d 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -8,7 +8,7 @@ include doc/api/conf.py
 
 include examples/*
 
-recursive-exclude djvu config.pxi
 recursive-include djvu *.py *.pxi *.pxd *.pyx
+recursive-exclude djvu config.pxi
 
 recursive-include tests *.py Makefile *.jpeg *.tex *.djvu
diff --git a/PKG-INFO b/PKG-INFO
index f202e05..62cb069 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: python-djvulibre
-Version: 0.4
+Version: 0.5
 Summary: Python support for the DjVu image format
 Home-page: http://jwilk.net/software/python-djvulibre
 Author: Jakub Wilk
diff --git a/djvu/common.pxi b/djvu/common.pxi
index b93f349..936ae67 100644
--- a/djvu/common.pxi
+++ b/djvu/common.pxi
@@ -11,96 +11,106 @@
 
 include 'config.pxi'
 
-# python-distutils preprocessor macros
+# C library:
 
-cdef extern from *:
-    char* PYTHON_DJVULIBRE_VERSION
+from libc.stdlib cimport free
+from libc.string cimport strlen
 
-# C library
+# Python memory handling:
 
-ctypedef int size_t
+from cpython.mem cimport PyMem_Malloc as py_malloc
+from cpython.mem cimport PyMem_Free as py_free
 
-cdef extern from 'stdio.h':
-    ctypedef struct FILE
+# Python numbers:
 
-cdef extern from 'stdlib.h':
-    void libc_free 'free'(void* ptr) nogil
+from cpython cimport (
+    PyInt_Check as is_short_int,
+    PyLong_Check as is_long_int,
+)
+cdef int is_int(object o):
+    return is_short_int(o) or is_long_int(o)
 
-cdef extern from 'string.h':
-    int strcmp(char *s1, char *s2) nogil
-    size_t strlen(char *s) nogil
+from cpython cimport (
+    PyNumber_Check as is_number,
+    PyFloat_Check as is_float,
+)
 
-# Python library
+IF PY3K:
+    from cpython cimport PyNumber_Long as int
+ELSE:
+    from cpython cimport PyNumber_Int as int
+
+# Python strings:
+
+from cpython cimport (
+    PyUnicode_Check as is_unicode,
+    PyString_Check as is_string,
+    PyBytes_Check as is_bytes,
+)
+
+from cpython cimport (
+    PyUnicode_AsUTF8String as encode_utf8,
+    PyUnicode_DecodeUTF8 as decode_utf8_ex,
+    PyBytes_AsStringAndSize as bytes_to_charp,
+    PyBytes_FromStringAndSize as charp_to_bytes,
+)
+IF PY3K:
+    cdef extern from 'Python.h':
+        object charp_to_string 'PyUnicode_FromString'(char *v)
+ELSE:
+    from cpython cimport PyString_FromString as charp_to_string
+
+cdef object decode_utf8(char* s):
+    return decode_utf8_ex(s, strlen(s), NULL)
 
 cdef extern from 'Python.h':
+    int buffer_to_writable_memory 'PyObject_AsWriteBuffer'(object, void **, Py_ssize_t *)
 
-    void* py_malloc 'PyMem_Malloc'(size_t)
-    void py_free 'PyMem_Free'(void*)
+# Python booleans:
 
-    int is_short_int 'PyInt_Check'(object)
-    int is_long_int 'PyLong_Check'(object)
-    int is_number 'PyNumber_Check'(object)
-    int is_float 'PyFloat_Check'(object)
-    int is_slice 'PySlice_Check'(object)
+from cpython cimport PyBool_FromLong as bool
 
-    int is_unicode 'PyUnicode_Check'(object)
-    int is_string 'PyString_Check'(object)
-    IF PY3K:
-        int is_bytes 'PyBytes_Check'(object)
-    ELSE:
-        int is_bytes 'PyString_Check'(object)
-
-    object encode_utf8 'PyUnicode_AsUTF8String'(object)
-    object decode_utf8_ex 'PyUnicode_DecodeUTF8'(char *, Py_ssize_t, char *)
-    IF PY3K:
-        int bytes_to_charp 'PyBytes_AsStringAndSize'(object, char**, Py_ssize_t*) except -1
-        object charp_to_bytes 'PyBytes_FromStringAndSize'(char *, Py_ssize_t)
-    ELSE:
-        int bytes_to_charp 'PyString_AsStringAndSize'(object, char**, Py_ssize_t*) except -1
-        object charp_to_bytes 'PyString_FromStringAndSize'(char *, Py_ssize_t)
-    IF PY3K:
-        object charp_to_string 'PyUnicode_FromString'(char *)
-    ELSE:
-        object charp_to_string 'PyString_FromString'(char *)
-    int buffer_to_writable_memory 'PyObject_AsWriteBuffer'(object, void **, Py_ssize_t *)
+# Python pointer->integer conversion:
+
+from cpython cimport PyLong_FromVoidPtr as voidp_to_int
 
-    IF PY3K:
-        object int 'PyNumber_Long'(object)
-    ELSE:
-        object int 'PyNumber_Int'(object)
-    object bool 'PyBool_FromLong'(long)
-    object voidp_to_int 'PyLong_FromVoidPtr'(void *)
+# Python files:
 
-    IF PY3K:
-        object posix_error 'PyErr_SetFromErrno'(object)
-        int file_to_fd 'PyObject_AsFileDescriptor'(object)
-    ELSE:
-        FILE* file_to_cfile 'PyFile_AsFile'(object)
+from libc.stdio cimport FILE
 
-    int list_append 'PyList_Append'(object, object) except -1
+# Python lists:
 
-    cdef object richcmp 'PyObject_RichCompare'(object, object, int)
+from cpython cimport PyList_Append as list_append
 
-cdef extern from 'pythread.h':
-    ctypedef void* Lock 'PyThread_type_lock'
-    cdef Lock allocate_lock 'PyThread_allocate_lock'()
-    cdef void free_lock 'PyThread_free_lock'(Lock lock)
-    cdef int acquire_lock 'PyThread_acquire_lock'(Lock lock, int mode) nogil
-    cdef void release_lock 'PyThread_release_lock'(Lock lock)
+# Python rich comparison:
 
-    ctypedef enum:
-        WAIT_LOCK
-        NOWAIT_LOCK
+from cpython cimport PyObject_RichCompare as richcmp
+
+# Python slices:
+
+cdef extern from 'Python.h':
+    int is_slice 'PySlice_Check'(object)
+
+# Python threads:
+
+from cpython cimport (
+    PyThread_type_lock as Lock,
+    PyThread_allocate_lock as allocate_lock,
+    PyThread_free_lock as free_lock,
+    PyThread_acquire_lock as acquire_lock,
+    PyThread_release_lock as release_lock,
+    WAIT_LOCK,
+    NOWAIT_LOCK,
+)
+
+# Python type checks:
 
 cdef extern from 'object.h':
     ctypedef struct PyTypeObject:
         char *tp_name
-    ctypedef struct PyObject:
-        PyTypeObject *ob_type
-    int _typecheck 'PyObject_TypeCheck'(object o, PyTypeObject* type)
 
-cdef int is_int(object o):
-    return is_short_int(o) or is_long_int(o)
+from cpython cimport PyObject
+from cpython cimport PyObject_TypeCheck as _typecheck
 
 cdef object type(object o):
     return <object>((<PyObject*>o).ob_type)
@@ -115,18 +125,11 @@ ELSE:
 cdef int typecheck(object o, object type):
     return _typecheck(o, <PyTypeObject*> type)
 
-cdef int is_file(object o):
-    IF PY3K:
-        return not is_number(o) and file_to_fd(o) != -1
-    ELSE:
-        return typecheck(o, file)
+# Python exceptions:
 
 cdef void raise_instantiation_error(object cls) except *:
     raise TypeError, 'cannot create \'{tp}\' instances'.format(tp=get_type_name(cls))
 
-cdef object decode_utf8(char* s):
-    return decode_utf8_ex(s, strlen(s), NULL)
-
 cdef extern from 'pyerrors.h':
     ctypedef class __builtin__.Exception [object PyBaseExceptionObject]:
         pass
diff --git a/djvu/config.pxi b/djvu/config.pxi
deleted file mode 100644
index 9ac1fdd..0000000
--- a/djvu/config.pxi
+++ /dev/null
@@ -1,3 +0,0 @@
-DEF PY3K = False
-DEF PYTHON_DJVULIBRE_VERSION = "0.4"
-DEF HAVE_LANGINFO_H = True
diff --git a/djvu/decode.pyx b/djvu/decode.pyx
index 0ba53ca..bfe56e0 100644
--- a/djvu/decode.pyx
+++ b/djvu/decode.pyx
@@ -197,10 +197,23 @@ cdef extern from 'libdjvu/ddjvuapi.h':
     cexpr_t* ddjvu_anno_get_metadata_keys(cexpr_t annotations) nogil
     char* ddjvu_anno_get_metadata(cexpr_t annotations, cexpr_t key) nogil
 
+# Python files:
+
+IF PY3K:
+    from cpython cimport (
+        PyErr_SetFromErrno as posix_error,
+        PyObject_AsFileDescriptor as file_to_fd,
+    )
+    cdef int is_file(object o):
+        return not is_number(o) and file_to_fd(o) != -1
+ELSE:
+    cdef extern from 'Python.h':
+        FILE* file_to_cfile 'PyFile_AsFile'(object)
+        int is_file 'PyFile_Check'(object)
+from posix.unistd cimport dup
+from libc.stdio cimport fclose
 cdef extern from 'unistd.h':
-    int dup(int)
     FILE *fdopen(int, char*)
-    int fclose(FILE *)
 
 IF HAVE_LANGINFO_H:
     cdef extern from 'langinfo.h':
@@ -516,7 +529,7 @@ cdef class Page:
             try:
                 return decode_utf8(s)
             finally:
-                libc_free(s)
+                free(s)
 
     def decode(self, wait=1):
         '''
@@ -897,7 +910,7 @@ cdef class File:
             try:
                 return decode_utf8(s)
             finally:
-                libc_free(s)
+                free(s)
 
 cdef object pages_to_opt(object pages, int sort_uniq):
     if sort_uniq:
@@ -3317,7 +3330,7 @@ cdef class Hyperlinks:
                 list_append(self._sexpr, wrap_sexpr(annotations._document, current[0]))
                 current = current + 1
         finally:
-            libc_free(all)
+            free(all)
 
     def __len__(self):
         return len(self._sexpr)
@@ -3349,7 +3362,7 @@ cdef class Metadata:
                 current = current + 1
             self._keys = frozenset(keys)
         finally:
-            libc_free(all)
+            free(all)
 
     def __len__(self):
         return len(self._keys)
@@ -3419,7 +3432,6 @@ __author__ = 'Jakub Wilk <jwilk at jwilk.net>'
 IF PY3K:
     __version__ = decode_utf8(PYTHON_DJVULIBRE_VERSION)
 ELSE:
-    __version__ = PYTHON_DJVULIBRE_VERSION
-__version__ = '{0}/{1}'.format(__version__, DDJVU_VERSION)
+    __version__ = str(PYTHON_DJVULIBRE_VERSION)
 
 # vim:ts=4 sts=4 sw=4 et ft=pyrex
diff --git a/djvu/sexpr.pyx b/djvu/sexpr.pyx
index 060bc54..d5b7bd5 100644
--- a/djvu/sexpr.pyx
+++ b/djvu/sexpr.pyx
@@ -52,15 +52,28 @@ cdef extern from 'libdjvu/miniexp.h':
     void cvar_free 'minivar_free'(cvar_t* v) nogil
     cexpr_t* cvar_ptr 'minivar_pointer'(cvar_t* v) nogil
 
-    int io_7bit 'minilisp_print_7bits'
-    int (*io_puts 'minilisp_puts')(char *s)
-    int (*io_getc 'minilisp_getc')()
-    int (*io_ungetc 'minilisp_ungetc')(int c)
-    void io_set_output 'minilisp_set_output'(FILE *f)
-    void io_set_input 'minilisp_set_input'(FILE *f)
-    cexpr_t cexpr_read 'miniexp_read'()
-    cexpr_t cexpr_print 'miniexp_prin'(cexpr_t cexpr)
-    cexpr_t cexpr_printw 'miniexp_pprin'(cexpr_t cexpr, int width)
+    IF HAVE_MINIEXP_IO_T:
+        ctypedef cexpr_io_s cexpr_io_t 'miniexp_io_t'
+        struct cexpr_io_s 'miniexp_io_s':
+            int (*puts 'fputs')(cexpr_io_t*, char*)
+            int (*getc 'fgetc')(cexpr_io_t*)
+            int (*ungetc)(cexpr_io_t*, int)
+            void *data[4]
+            int *p_flags
+        void cexpr_io_init 'miniexp_io_init'(cexpr_io_t *cio)
+        enum:
+            cexpr_io_print7bits 'miniexp_io_print7bits'
+        cexpr_t cexpr_read 'miniexp_read_r'(cexpr_io_t *cio)
+        cexpr_t cexpr_print 'miniexp_prin_r'(cexpr_io_t *cio, cexpr_t cexpr)
+        cexpr_t cexpr_printw 'miniexp_pprin_r'(cexpr_io_t *cio, cexpr_t cexpr, int width)
+    ELSE:
+        int io_7bit 'minilisp_print_7bits'
+        int (*io_puts 'minilisp_puts')(char *s)
+        int (*io_getc 'minilisp_getc')()
+        int (*io_ungetc 'minilisp_ungetc')(int c)
+        cexpr_t cexpr_read 'miniexp_read'()
+        cexpr_t cexpr_print 'miniexp_prin'(cexpr_t cexpr)
+        cexpr_t cexpr_printw 'miniexp_pprin'(cexpr_t cexpr, int width)
 
 cdef extern from 'stdio.h':
     int EOF
@@ -83,112 +96,186 @@ import weakref
 cdef object symbol_dict
 symbol_dict = weakref.WeakValueDictionary()
 
-cdef Lock _myio_lock
-_myio_lock = allocate_lock()
-cdef object _myio_stdin
-cdef object _myio_stdout
-cdef int _myio_stdout_binary
-cdef object _myio_buffer
-_myio_buffer = []
-cdef int _backup_io_7bit
-cdef int (*_backup_io_puts)(char *s)
-cdef int (*_backup_io_getc)()
-cdef int (*_backup_io_ungetc)(int c)
-
-cdef object write_unraisable_exception(object cause):
-    message = format_exc()
-    sys.stderr.write(
-        'Unhandled exception ({obj!r})\n{msg}\n'.format(obj=cause, msg=message)
-    )
-
-cdef void myio_set(object stdin, object stdout, int escape_unicode=True):
-    global _myio_stdin, _myio_stdout, _myio_stdout_binary, _myio_buffer
-    global _backup_io_7bit, _backup_io_puts, _backup_io_getc, _backup_io_ungetc
-    global io_7bit, io_puts, io_getc, io_ungetc
-    cdef int opt_stdin, opt_stdout
-    with nogil: acquire_lock(_myio_lock, WAIT_LOCK)
-    _backup_io_7bit = io_7bit
-    _backup_io_puts = io_puts
-    _backup_io_getc = io_getc
-    _backup_io_ungetc = io_ungetc
-    _myio_stdin = stdin
-    IF PY3K:
-        # TODO
-        opt_stdin = opt_stdout = 0
+cdef object codecs
+import codecs
+
+IF not HAVE_MINIEXP_IO_T:
+    cdef Lock _myio_lock
+    _myio_lock = allocate_lock()
+
+cdef class _ExpressionIO:
+    IF HAVE_MINIEXP_IO_T:
+        cdef cexpr_io_t cio
+        cdef int flags
     ELSE:
-        opt_stdin = is_file(stdin)
-        opt_stdout = is_file(stdout)
-    if opt_stdin:
-        IF not PY3K:
-            io_set_input(file_to_cfile(stdin))
+        cdef int (*backup_io_puts)(char *s)
+        cdef int (*backup_io_getc)()
+        cdef int (*backup_io_ungetc)(int c)
+        cdef int backup_io_7bit
+    cdef object stdin
+    cdef object stdout
+    cdef int stdout_binary
+    cdef object buffer
+    cdef object exc
+
+    _reentrant = HAVE_MINIEXP_IO_T
+
+    def __init__(self, object stdin=None, object stdout=None, int escape_unicode=True):
+        IF not HAVE_MINIEXP_IO_T:
+            global io_7bit, io_puts, io_getc, io_ungetc
+            global _myio
+            with nogil: acquire_lock(_myio_lock, WAIT_LOCK)
+            self.backup_io_7bit = io_7bit
+            self.backup_io_puts = io_puts
+            self.backup_io_getc = io_getc
+            self.backup_io_ungetc = io_ungetc
+        self.stdin = stdin
+        self.stdout = stdout
+        IF PY3K:
+            self.stdout_binary = not hasattr(stdout, 'encoding')
         ELSE:
-            pass  # TODO
-    else:
-        io_getc = _myio_getc
-        io_ungetc = _myio_ungetc
-    _myio_stdout = stdout
-    IF PY3K:
-        _myio_stdout_binary = not hasattr(stdout, 'encoding')
-    ELSE:
-        _myio_stdout_binary = 1
-    if opt_stdout:
-        IF not PY3K:
-            io_set_output(file_to_cfile(stdout))
+            # In Python 2, sys.stdout has the encoding attribute,
+            # even though it accepts byte strings.
+            # Let's only make a special-case for codecs.
+            self.stdout_binary = not isinstance(stdout, codecs.StreamReaderWriter)
+        self.buffer = []
+        self.exc = None
+        IF HAVE_MINIEXP_IO_T:
+            cexpr_io_init(&self.cio)
+            self.cio.data[0] = <void*>self
+            self.cio.getc = _myio_getc
+            self.cio.ungetc = _myio_ungetc
+            self.cio.puts = _myio_puts
+            if escape_unicode:
+                self.flags = cexpr_io_print7bits
+            else:
+                self.flags = 0
+            self.cio.p_flags = &self.flags
         ELSE:
-            pass  # TODO
-    else:
-        io_puts = _myio_puts
-    io_7bit = escape_unicode
-    _myio_buffer = []
-
-cdef void myio_reset():
-    global _myio_stdin, _myio_stdout, _myio_stdout_binary, _myio_buffer
-    global io_7bit, io_puts, io_getc, io_ungetc
-    _myio_stdin = None
-    _myio_stdout = None
-    _myio_stdout_binary = 0
-    _myio_buffer = None
-    io_7bit = _backup_io_7bit
-    io_puts = _backup_io_puts
-    io_getc = _backup_io_getc
-    io_ungetc = _backup_io_ungetc
-    release_lock(_myio_lock)
-
-cdef int _myio_puts(char *s):
-    try:
-        if _myio_stdout_binary:
-            _myio_stdout.write(s)
-        else:
-            _myio_stdout.write(decode_utf8(s))
-    except:
-        write_unraisable_exception(_myio_stdout)
-        return EOF
-
-cdef int _myio_getc():
-    global _myio_buffer
-    cdef int result
-    if _myio_buffer:
-        return _myio_buffer.pop()
-    else:
+            io_getc = _myio_getc
+            io_ungetc = _myio_ungetc
+            io_puts = _myio_puts
+            io_7bit = escape_unicode
+            _myio = self
+
+    cdef close(self):
+        IF not HAVE_MINIEXP_IO_T:
+            global io_7bit, io_puts, io_getc, io_ungetc
+            global _myio
+            _myio = None
+        self.stdin = None
+        self.stdout = None
+        self.buffer = None
+        IF not HAVE_MINIEXP_IO_T:
+            io_7bit = self.backup_io_7bit
+            io_puts = self.backup_io_puts
+            io_getc = self.backup_io_getc
+            io_ungetc = self.backup_io_ungetc
+        try:
+            if self.exc is not None:
+                raise self.exc[0], self.exc[1], self.exc[2]
+        finally:
+            IF not HAVE_MINIEXP_IO_T:
+                release_lock(_myio_lock)
+            self.exc = None
+
+    IF HAVE_MINIEXP_IO_T:
+
+        cdef cexpr_t read(self):
+            return cexpr_read(&self.cio)
+
+        cdef cexpr_t print_(self, cexpr_t cexpr):
+            return cexpr_print(&self.cio, cexpr)
+
+        cdef cexpr_t printw(self, cexpr_t cexpr, int width):
+            return cexpr_printw(&self.cio, cexpr, width)
+
+    ELSE:
+
+        cdef cexpr_t read(self):
+            return cexpr_read()
+
+        cdef cexpr_t print_(self, cexpr_t cexpr):
+            return cexpr_print(cexpr)
+
+        cdef cexpr_t printw(self, cexpr_t cexpr, int width):
+            return cexpr_printw(cexpr, width)
+
+IF HAVE_MINIEXP_IO_T:
+
+    cdef int _myio_puts(cexpr_io_t* cio, char *s):
+        cdef _ExpressionIO io
+        xio = <_ExpressionIO> cio.data[0]
         try:
-            s = _myio_stdin.read(1)
+            if xio.stdout_binary:
+                xio.stdout.write(s)
+            else:
+                xio.stdout.write(decode_utf8(s))
         except:
-            write_unraisable_exception(_myio_stdin)
+            xio.exc = sys.exc_info()
             return EOF
-        if s:
+
+    cdef int _myio_getc(cexpr_io_t* cio):
+        cdef _ExpressionIO xio
+        cdef int result
+        xio = <_ExpressionIO> cio.data[0]
+        if xio.buffer:
+            return xio.buffer.pop()
+        try:
+            s = xio.stdin.read(1)
+            if not s:
+                return EOF
             if is_unicode(s):
-                s = s.encode('UTF-8')
+                s = encode_utf8(s)
             IF PY3K:
-                _myio_buffer += reversed(s)
+                xio.buffer += reversed(s)
             ELSE:
-                _myio_buffer += map(ord, reversed(s))
-            return _myio_buffer.pop()
-        else:
+                xio.buffer += map(ord, reversed(s))
+            return xio.buffer.pop()
+        except:
+            xio.exc = sys.exc_info()
+            return EOF
+
+    cdef int _myio_ungetc(cexpr_io_t* cio, int c):
+        cdef _ExpressionIO io
+        xio = <_ExpressionIO> cio.data[0]
+        list_append(xio.buffer, c)
+
+ELSE:
+
+    cdef _ExpressionIO _myio
+
+    cdef int _myio_puts(char *s):
+        try:
+            if _myio.stdout_binary:
+                _myio.stdout.write(s)
+            else:
+                _myio.stdout.write(decode_utf8(s))
+        except:
+            _myio.exc = sys.exc_info()
+            return EOF
+
+    cdef int _myio_getc():
+        cdef int result
+        if _myio.buffer:
+            return _myio.buffer.pop()
+        try:
+            s = _myio.stdin.read(1)
+            if not s:
+                return EOF
+            if is_unicode(s):
+                s = encode_utf8(s)
+            IF PY3K:
+                _myio.buffer += reversed(s)
+            ELSE:
+                _myio.buffer += map(ord, reversed(s))
+            return _myio.buffer.pop()
+        except:
+            _myio.exc = sys.exc_info()
             return EOF
 
-cdef int _myio_ungetc(int c):
-    global _myio_buffer
-    _myio_buffer += (c,)
+    cdef int _myio_ungetc(int c):
+        list_append(_myio.buffer, c)
 
 cdef object the_sentinel
 the_sentinel = object()
@@ -205,6 +292,7 @@ cdef class _WrappedCExpr:
 
     cdef object print_into(self, object stdout, object width, int escape_unicode):
         cdef cexpr_t cexpr
+        cdef _ExpressionIO xio
         if width is None:
             pass
         elif not is_int(width):
@@ -212,14 +300,14 @@ cdef class _WrappedCExpr:
         elif width <= 0:
             raise ValueError('width <= 0')
         cexpr = self.cexpr()
-        myio_set(None, stdout, escape_unicode)
+        xio = _ExpressionIO(stdout=stdout, escape_unicode=escape_unicode)
         try:
             if width is None:
-                cexpr_print(cexpr)
+                xio.print_(cexpr)
             else:
-                cexpr_printw(cexpr, width)
+                xio.printw(cexpr, width)
         finally:
-            myio_reset()
+            xio.close()
 
     cdef object as_string(self, object width, int escape_unicode):
         stdout = StringIO()
@@ -355,14 +443,15 @@ def _expression_from_stream(stdin):
 
     Read an expression from a stream.
     '''
+    cdef _ExpressionIO xio
     try:
-        myio_set(stdin, None)
+        xio = _ExpressionIO(stdin=stdin)
         try:
-            return _c2py(cexpr_read())
+            return _c2py(xio.read())
         except InvalidExpression:
             raise ExpressionSyntaxError
     finally:
-        myio_reset()
+        xio.close()
 
 def _expression_from_string(str):
     '''
@@ -663,7 +752,7 @@ cdef BaseExpression _c2py(cexpr_t cexpr):
     elif cexpr_is_str(cexpr):
         result = StringExpression(_wexpr)
     else:
-        raise ValueError
+        raise InvalidExpression
     return result
 
 cdef _WrappedCExpr _build_list_cexpr(object items):
@@ -987,6 +1076,6 @@ __author__ = 'Jakub Wilk <jwilk at jwilk.net>'
 IF PY3K:
     __version__ = decode_utf8(PYTHON_DJVULIBRE_VERSION)
 ELSE:
-    __version__ = PYTHON_DJVULIBRE_VERSION
+    __version__ = str(PYTHON_DJVULIBRE_VERSION)
 
 # vim:ts=4 sts=4 sw=4 et ft=pyrex
diff --git a/doc/api/conf.py b/doc/api/conf.py
index 77c6174..eed4890 100644
--- a/doc/api/conf.py
+++ b/doc/api/conf.py
@@ -19,7 +19,7 @@ master_doc = 'index'
 
 import setup as _setup
 project = _setup.setup_params['name']
-version = release = _setup.__version__
+version = release = _setup.py_version
 _setup_file = codecs.open(
     os.path.splitext(_setup.__file__)[0] + '.py',
     'r', encoding='UTF-8'
diff --git a/doc/changelog b/doc/changelog
index fb0ec09..9e02054 100644
--- a/doc/changelog
+++ b/doc/changelog
@@ -1,3 +1,28 @@
+python-djvulibre (0.5) unstable; urgency=low
+
+  * Make print_into() raise exceptions properly instead of just printing them
+    on stderr.
+  * Make it possible to print S-expressions into file-like objects created by
+    codecs.open().
+  * Make djvu.sexpr.__version__ a byte string in Python 2.X.
+  * Don't include DjVu file format version in djvu.decode.__version__.
+    This information is more conveniently available in
+    djvu.decode.DDJVU_VERSION.
+  * Make S-expression input/output methods reentrant.
+    (DjVuLibre >= 3.5.26 is required for this feature.)
+  * Improve error handling.
+  * Improve the test suite.
+
+ -- Jakub Wilk <jwilk at jwilk.net>  Sun, 02 Aug 2015 17:45:08 +0200
+
+python-djvulibre (0.4.1) unstable; urgency=low
+
+  * Exclude djvu/config.pxi from the source tarball.
+    Thanks to Daniel Stender for the bug report.
+    https://bitbucket.org/jwilk/python-djvulibre/issues/4
+
+ -- Jakub Wilk <jwilk at jwilk.net>  Mon, 27 Jul 2015 10:28:21 +0200
+
 python-djvulibre (0.4) unstable; urgency=low
 
   * Drop support for Python 2.5.
@@ -12,6 +37,7 @@ python-djvulibre (0.4) unstable; urgency=low
     Making it work offline and consistently across different Python versions
     is too much of a hassle, and it was used only to cross-reference one
     well-known exception (IOError).
+  * Make djvu.decode.__version__ a byte string in Python 2.X.
   * Improve the test suite.
 
  -- Jakub Wilk <jwilk at jwilk.net>  Wed, 22 Jul 2015 21:05:11 +0200
diff --git a/python_djvulibre.egg-info/PKG-INFO b/python_djvulibre.egg-info/PKG-INFO
index f202e05..62cb069 100644
--- a/python_djvulibre.egg-info/PKG-INFO
+++ b/python_djvulibre.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: python-djvulibre
-Version: 0.4
+Version: 0.5
 Summary: Python support for the DjVu image format
 Home-page: http://jwilk.net/software/python-djvulibre
 Author: Jakub Wilk
diff --git a/python_djvulibre.egg-info/SOURCES.txt b/python_djvulibre.egg-info/SOURCES.txt
index 4f08d38..377fa18 100644
--- a/python_djvulibre.egg-info/SOURCES.txt
+++ b/python_djvulibre.egg-info/SOURCES.txt
@@ -2,7 +2,6 @@ MANIFEST.in
 setup.py
 djvu/__init__.py
 djvu/common.pxi
-djvu/config.pxi
 djvu/const.py
 djvu/decode.pxd
 djvu/decode.pyx
diff --git a/setup.py b/setup.py
index 46e4df8..6bce5b9 100644
--- a/setup.py
+++ b/setup.py
@@ -63,6 +63,7 @@ import distutils.ccompiler
 import distutils.command.clean
 import distutils.command.build_ext
 import distutils.dep_util
+import distutils.version
 
 try:
     import sphinx.setup_command as sphinx_setup_command
@@ -89,9 +90,12 @@ def get_version():
     finally:
         changelog.close()
 
-PKG_CONFIG_FLAG_MAP = {'-I': 'include_dirs', '-L': 'library_dirs', '-l': 'libraries'}
-
-def pkg_config(*packages, **kwargs):
+def pkgconfig_build_flags(*packages, **kwargs):
+    flag_map = {
+        '-I': 'include_dirs',
+        '-L': 'library_dirs',
+        '-l': 'libraries',
+    }
     fallback = dict(
         libraries=['djvulibre'],
     )
@@ -123,13 +127,34 @@ def pkg_config(*packages, **kwargs):
         key = argument[:2]
         try:
             value = argument[2:]
-            kwargs.setdefault(PKG_CONFIG_FLAG_MAP[key], []).append(value)
+            kwargs.setdefault(flag_map[key], []).append(value)
         except KeyError:
             kwargs['extra_link_args'].append(argument)
             kwargs['extra_compile_args'].append(argument)
     return kwargs
 
-__version__ = get_version()
+def pkgconfig_version(package):
+    V = distutils.version.LooseVersion
+    try:
+        pkgconfig = ipc.Popen(
+            ['pkg-config', '--modversion', package],
+            stdout=ipc.PIPE, stderr=ipc.PIPE
+        )
+    except OSError:
+        _, ex, _ = sys.exc_info()
+        distutils.log.warn('cannot execute pkg-config: ' + str(ex))
+        return V('0')
+    stdout, stderr = pkgconfig.communicate()
+    stdout = stdout.decode('ASCII', 'replace')
+    stderr = stderr.decode('ASCII', 'replace')
+    if pkgconfig.returncode:
+        distutils.log.warn('pkg-config failed: ' + stderr.strip())
+        return V('0')
+    version = stdout.strip()
+    return V(version)
+
+djvulibre_version = pkgconfig_version('ddjvuapi')
+py_version = get_version()
 
 # Work-around for <https://bugs.python.org/issue969718>:
 try:
@@ -144,11 +169,13 @@ class build_ext(distutils.command.build_ext.build_ext):
     def run(self):
         new_config = [
             'DEF PY3K = {0}'.format(sys.version_info >= (3, 0)),
-            'DEF PYTHON_DJVULIBRE_VERSION = "{0}"'.format(__version__),
+            'DEF PYTHON_DJVULIBRE_VERSION = "{0}"'.format(py_version),
+            'DEF HAVE_MINIEXP_IO_T = {0}'.format(djvulibre_version >= '3.5.26'),
             'DEF HAVE_LANGINFO_H = {0}'.format(os.name == 'posix' and not mingw32cross),
         ]
         try:
-            old_config = open(self.config_filename, 'rt').read()
+            with open(self.config_filename, 'rt') as fp:
+                old_config = fp.read()
         except IOError:
             old_config = ''
         if '\n'.join(new_config).strip() != old_config.strip():
@@ -223,11 +250,11 @@ if sphinx_setup_command:
 else:
     build_sphinx = None
 
-compiler_flags = pkg_config('ddjvuapi')
+compiler_flags = pkgconfig_build_flags('ddjvuapi')
 
 setup_params = dict(
     name='python-djvulibre',
-    version=__version__,
+    version=py_version,
     author='Jakub Wilk',
     author_email='jwilk at jwilk.net',
     license='GNU GPL 2',
diff --git a/tests/common.py b/tests/common.py
index afec209..72cb41a 100644
--- a/tests/common.py
+++ b/tests/common.py
@@ -228,9 +228,11 @@ def skip_unless_translation_exists(lang):
         raise SkipTest('libc translation not found: ' + lang)
 
 def skip_unless_command_exists(command):
-    child = ipc.Popen('command -v ' + command, shell=True, stdout=ipc.PIPE, stderr=ipc.PIPE)
-    if child.wait() == 0:
-        return
+    directories = os.environ['PATH'].split(os.pathsep)
+    for directory in directories:
+        path = os.path.join(directory, command)
+        if os.access(path, os.X_OK):
+            return
     raise SkipTest('command not found: ' + command)
 
 __all__ = [
diff --git a/tests/test_decode.py b/tests/test_decode.py
index 6b8c4bc..e0c2d79 100644
--- a/tests/test_decode.py
+++ b/tests/test_decode.py
@@ -568,7 +568,8 @@ class test_streams:
             document.pages[0].annotations.sexpr
 
         try:
-            message.stream.write(open(images + 'test1.djvu', 'rb').read())
+            with open(images + 'test1.djvu', 'rb') as fp:
+                message.stream.write(fp.read())
         finally:
             message.stream.close()
         with assert_raises_str(IOError, 'I/O operation on closed file'):
diff --git a/tests/test_sexpr.py b/tests/test_sexpr.py
index 2724904..2d4f1c8 100644
--- a/tests/test_sexpr.py
+++ b/tests/test_sexpr.py
@@ -12,11 +12,10 @@
 # General Public License for more details.
 
 import collections
+import codecs
 import copy
+import errno
 import io
-import os
-import re
-import sys
 import tempfile
 
 import pickle
@@ -26,6 +25,8 @@ except ImportError:
     cpickle = None
 
 from djvu.sexpr import *
+from djvu.sexpr import __version__
+from djvu.sexpr import _ExpressionIO
 
 from common import *
 
@@ -78,6 +79,16 @@ class test_int_expressions():
         x = Expression(42)
         assert_pickle_equal(x)
 
+class test_float_expressions():
+
+    # TODO: float expressions are not implemented yet
+
+    def test_parse(self):
+        with assert_raises(ExpressionSyntaxError):
+            x = Expression.from_string('3.14')
+            if isinstance(x.value, Symbol):
+                raise ExpressionSyntaxError
+
 class test_symbols():
 
     def t(self, name):
@@ -405,11 +416,6 @@ class test_list_expressions():
             x = Expression(lst)
             assert_pickle_equal(x)
 
-def strip_line_numbers_from_traceback(s):
-    s = re.sub('(?<=[.]c):[0-9]+(?=[)])', '', s)
-    s = re.sub(', line [0-9]+(?=, )', '', s)
-    return s
-
 class test_expression_parser():
 
     def test_badstring(self):
@@ -420,91 +426,113 @@ class test_expression_parser():
         assert_is(getattr(Expression, 'from_file', None), None)
 
     def test_bad_io(self):
-        stderr = StringIO()
-        with interim(sys, stderr=stderr):
-            with assert_raises(ExpressionSyntaxError):
-                Expression.from_stream(42)
-        stderr = strip_line_numbers_from_traceback(stderr.getvalue())
-        stderr = stderr.replace(
-            '"sexpr.pyx"',  # Cython < 0.21
-            '"djvu/sexpr.pyx"'  # Cython ≥ 0.21
-        )
-        stderr = stderr.replace('\n    s = _myio_stdin.read(1)\n', '\n')
-        assert_multi_line_equal(stderr, '''\
-Unhandled exception (42)
-Traceback (most recent call last):
-  File "djvu/sexpr.pyx", in djvu.sexpr._myio_getc (djvu/sexpr.c)
-AttributeError: 'int' object has no attribute 'read'
+        with assert_raises_str(AttributeError, "'int' object has no attribute 'read'"):
+            Expression.from_stream(42)
 
... 255 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-djvulibre.git



More information about the Python-modules-commits mailing list