From 965caf5fc74fbbf2516eb1d098fac07cfbdb6820 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 7 Oct 2019 02:18:29 +0100 Subject: [PATCH] @@@ cython and python 3 wip --- codec.pyx | 243 +++++++++++++++++++++++++++++++---------------------------- codec.pyx.in | 77 +++++++++---------- defs.pxi | 48 ++++++++++-- fdutils.pyx | 16 ++-- fwatch.pyx | 42 +++++++---- grim.h | 2 + mLib.pyx | 20 ++--- mdup.pyx | 19 +++-- report.pyx | 19 +++-- setup.py | 16 ++-- str.pyx | 60 +++++++-------- test.py | 175 ++++++++++++++++++++++++++++++++++++++++++ url.pyx | 106 +++++++++++++------------- utils.pyx | 22 +++--- 14 files changed, 555 insertions(+), 310 deletions(-) diff --git a/codec.pyx b/codec.pyx index 1274afd..0bef296 100644 --- a/codec.pyx +++ b/codec.pyx @@ -24,126 +24,138 @@ ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ###-------------------------------------------------------------------------- -### Base classes. - -cdef extern from 'mLib/codec.h': - - ctypedef struct codec - - ctypedef struct codec_class: - char *name - codec *(*encoder)(unsigned f, char *ind, unsigned max) - codec *(*decoder)(unsigned f) - - ctypedef struct codec_ops: - codec_class *c - int (*code)(codec *c, void *p, size_t, dstr *d) - void (*destroy)(codec *c) - ctypedef struct codec: - codec_ops *ops - - enum: - CDCF_LOWERC - CDCF_IGNCASE - CDCF_NOEQPAD - CDCF_IGNEQPAD - CDCF_IGNEQMID - CDCF_IGNZPAD - CDCF_IGNNEWL - CDCF_IGNSPC - CDCF_IGNINVCH - CDCF_IGNJUNK - - enum: - CDCERR_OK - CDCERR_INVCH - CDCERR_INVEQPAD - CDCERR_INVZPAD - - char *_codec_strerror "codec_strerror"(int err) +### Constants. + +CDCF_LOWERC = _CDCF_LOWERC +CDCF_IGNCASE = _CDCF_IGNCASE +CDCF_NOEQPAD = _CDCF_NOEQPAD +CDCF_IGNEQPAD = _CDCF_IGNEQPAD +CDCF_IGNEQMID = _CDCF_IGNEQMID +CDCF_IGNZPAD = _CDCF_IGNZPAD +CDCF_IGNNEWL = _CDCF_IGNNEWL +CDCF_IGNSPC = _CDCF_IGNSPC +CDCF_IGNINVCH = _CDCF_IGNINVCH +CDCF_IGNJUNK = _CDCF_IGNJUNK class CDCF: - LOWERC = CDCF_LOWERC - IGNCASE = CDCF_IGNCASE - NOEQPAD = CDCF_NOEQPAD - IGNEQPAD = CDCF_IGNEQPAD - IGNEQMID = CDCF_IGNEQMID - IGNZPAD = CDCF_IGNZPAD - IGNNEWL = CDCF_IGNNEWL - IGNSPC = CDCF_IGNSPC - IGNINVCH = CDCF_IGNINVCH - IGNJUNK = CDCF_IGNJUNK + ## DEPRECATED. Use the original names at module level. + LOWERC = _CDCF_LOWERC + IGNCASE = _CDCF_IGNCASE + NOEQPAD = _CDCF_NOEQPAD + IGNEQPAD = _CDCF_IGNEQPAD + IGNEQMID = _CDCF_IGNEQMID + IGNZPAD = _CDCF_IGNZPAD + IGNNEWL = _CDCF_IGNNEWL + IGNSPC = _CDCF_IGNSPC + IGNINVCH = _CDCF_IGNINVCH + IGNJUNK = _CDCF_IGNJUNK + +CDCERR_OK = _CDCERR_OK +CDCERR_INVCH = _CDCERR_INVCH +CDCERR_INVEQPAD = _CDCERR_INVEQPAD +CDCERR_INVZPAD = _CDCERR_INVZPAD class CDCERR: - OK = CDCERR_OK - INVCH = CDCERR_INVCH - INVEQPAD = CDCERR_INVEQPAD - INVZPAD = CDCERR_INVZPAD + ## DEPRECATED. Use the original names at module level. + OK = _CDCERR_OK + INVCH = _CDCERR_INVCH + INVEQPAD = _CDCERR_INVEQPAD + INVZPAD = _CDCERR_INVZPAD + +###-------------------------------------------------------------------------- +### Base classes. class CodecError (Exception): """ Exception from decoding operation. - Attributes: err = CDCERR.* code, msg = message string + Attributes: `err' = `CDCERR_*' code, `msg' = message string """ def __init__(me, err): me.err = err - me.msg = _codec_strerror(err) + me.msg = _codec_strerror(err) def __str__(me): return me.msg def codec_strerror(err): - """codec_strerror(ERR) -> STR: message for CDCERR.* code""" - return _codec_strerror(err) - -cdef int code(codec *c, void *p, size_t len, dstr *d) except -1: - cdef int err - err = c.ops.code(c, p, len, d) - if err: - raise CodecError(err) - return 0 + """codec_strerror(ERR) -> STR: message for `CDCERR_*' code""" + return _codec_strerror(err) cdef class _BaseCodec: """Abstract superclass for codecs.""" cdef codec *c def __cinit__(me, *hunoz, **hukairz): me.c = NULL - def __init__(me, *hunoz, **hukairz): - raise TypeError, 'abstract class' def __dealloc__(me): if me.c is not NULL: me.c.ops.destroy(me.c) - cdef code(me, text, int finishp): - cdef void *p - cdef Py_ssize_t len - cdef dstr d + def __init__(me, *hunoz, **hukairz): + raise TypeError('abstract class') + cdef int _code(me, const void *p, Py_ssize_t sz, + dstr *d, bint finishp) except -1: cdef int err if me.c is NULL: - raise ValueError, 'Encoding finished' - DCREATE(&d) - try: - PyObject_AsReadBuffer(text, &p, &len) - code(me.c, p, len, &d) - if finishp: - code(me.c, NULL, 0, &d) - me.c.ops.destroy(me.c) - me.c = NULL - return PyString_FromStringAndSize(d.buf, d.len) - finally: - dstr_destroy(&d) + raise ValueError('Encoding finished') + err = me.c.ops.code(me.c, p, sz, d) + if err: + raise CodecError(err) + if finishp: + err = me.c.ops.code(me.c, p, sz, d) + me.c.ops.destroy(me.c) + me.c = NULL + if err: + raise CodecError(err) + return 0 + def done(me): """C.done() -> OUT: final output""" me.code('', True) cdef class _BaseEncoder (_BaseCodec): - def encode(me, text, finishp = False): + def encode(me, input, finishp = False): """C.encode(IN, [finishp = False]) -> OUT: continue/finish encoding""" - return me.code(text, finishp) + cdef const void *p + cdef Py_ssize_t sz + cdef dstr d + DCREATE(&d) + try: + PyObject_AsReadBuffer(input, &p, &sz) + me._code(p, sz, &d, finishp) + return TEXT_FROMSTRLEN(d.buf, d.len) + finally: + dstr_destroy(&d) + def done(me): + """C.done() -> OUT: final output""" + cdef dstr d + DCREATE(&d) + try: + me._code(NULL, 0, &d, True) + return TEXT_FROMSTRLEN(d.buf, d.len) + finally: + dstr_destroy(&d) cdef class _BaseDecoder (_BaseCodec): - def decode(me, text, finishp = False): + def decode(me, input, finishp = False): """C.decode(IN, [finishp = False]) -> OUT: continue/finish decoding""" - return me.code(text, finishp) + cdef const char *p + cdef Py_ssize_t sz + cdef dstr d + DCREATE(&d) + try: + TEXT_PTRLEN(input, &p, &sz) + me._code(p, sz, &d, finishp) + return BIN_FROMSTRLEN(d.buf, d.len) + finally: + dstr_destroy(&d) + def done(me): + """C.done() -> OUT: final output""" + cdef dstr d + DCREATE(&d) + try: + me._code(NULL, 0, &d, True) + return BIN_FROMSTRLEN(d.buf, d.len) + finally: + dstr_destroy(&d) ###-------------------------------------------------------------------------- ### Base64. @@ -155,60 +167,60 @@ cdef extern from 'mLib/base64.h': cdef class Base64Encoder (_BaseEncoder): """ - Base64Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base64Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) Base64 encoder. """ - def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK): - me.c = base64_class.encoder(flags, indent, maxline) + def __init__(me, indent = '\n', maxline = 72, flags = 0): + me.c = base64_class.encoder(flags, TEXT_PTR(indent), maxline) cdef class Base64Decoder (_BaseDecoder): """ - Base64Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base64Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL]) Base64 decoder. """ - def __init__(me, flags = CDCF_IGNJUNK): + def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL): me.c = base64_class.decoder(flags) cdef class File64Encoder (_BaseEncoder): """ - File64Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + File64Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) Base64 encoder, using `%' instead of `/', so encoded strings are safe as filenames. """ - def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK): - me.c = file64_class.encoder(flags, indent, maxline) + def __init__(me, indent = '\n', maxline = 72, flags = 0): + me.c = file64_class.encoder(flags, TEXT_PTR(indent), maxline) cdef class File64Decoder (_BaseDecoder): """ - File64Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + File64Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL]) Base64 encoder, using `%' instead of `/', so encoded strings are safe as filenames. """ - def __init__(me, flags = CDCF_IGNJUNK): + def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL): me.c = file64_class.decoder(flags) cdef class Base64URLEncoder (_BaseEncoder): """ - Base64URLEncoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base64URLEncoder([indent = '\\n'], [maxline = 72], [flags = 0]) Base64 encoder, using `-' and `_' instead of `+' and `/', so encoded strings are safe as URL components. """ - def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK): - me.c = base64url_class.encoder(flags, indent, maxline) + def __init__(me, indent = '\n', maxline = 72, flags = 0): + me.c = base64url_class.encoder(flags, TEXT_PTR(indent), maxline) cdef class Base64URLDecoder (_BaseDecoder): """ - Base64URLDecoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base64URLDecoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL]) Base64 decoder, using `-' and `_' instead of `+' and `/', so encoded strings are safe as URL components. """ - def __init__(me, flags = CDCF_IGNJUNK): + def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL): me.c = base64url_class.decoder(flags) ###-------------------------------------------------------------------------- @@ -220,40 +232,40 @@ cdef extern from 'mLib/base32.h': cdef class Base32Encoder (_BaseEncoder): """ - Base32Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base32Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) Base32 encoder. """ - def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK): - me.c = base32_class.encoder(flags, indent, maxline) + def __init__(me, indent = '\n', maxline = 72, flags = 0): + me.c = base32_class.encoder(flags, TEXT_PTR(indent), maxline) cdef class Base32Decoder (_BaseDecoder): """ - Base32Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base32Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL | CDCF_IGNCASE]) Base32 decoder. """ - def __init__(me, flags = CDCF_IGNJUNK): + def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL | _CDCF_IGNCASE): me.c = base32_class.decoder(flags) cdef class Base32HexEncoder (_BaseEncoder): """ - Base32Encoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base32Encoder([indent = '\\n'], [maxline = 72], [flags = 0]) Base32 encoder, using digits and letters in ascending order, rather than avoiding digits which visually resemble letters. """ - def __init__(me, indent = '\n', maxline = 72, flags = CDCF_IGNJUNK): - me.c = base32hex_class.encoder(flags, indent, maxline) + def __init__(me, indent = '\n', maxline = 72, flags = 0): + me.c = base32hex_class.encoder(flags, TEXT_PTR(indent), maxline) cdef class Base32HexDecoder (_BaseDecoder): """ - Base32Decoder([indent = '\\n'], [maxline = 72], [flags = CDCF.IGNJUNK]) + Base32Decoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL | CDCF_IGNCASE]) Base32 decoder, using digits and letters in ascending order, rather than avoiding digits which visually resemble letters. """ - def __init__(me, flags = CDCF_IGNJUNK): + def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL | _CDCF_IGNCASE): me.c = base32hex_class.decoder(flags) ###-------------------------------------------------------------------------- @@ -264,23 +276,20 @@ cdef extern from 'mLib/hex.h': cdef class HexEncoder (_BaseEncoder): """ - HexEncoder([indent = '\\n'], [maxline = 72], - [flags = CDCF.IGNJUNK | CDCF.LOWERC]) + HexEncoder([indent = '\\n'], [maxline = 72], [flags = CDCF_LOWERC]) Hexadecimal encoder. """ - def __init__(me, indent = '\n', maxline = 72, - flags = CDCF_IGNJUNK | CDCF_LOWERC): - me.c = hex_class.encoder(flags, indent, maxline) + def __init__(me, indent = '\n', maxline = 72, flags = _CDCF_LOWERC): + me.c = hex_class.encoder(flags, TEXT_PTR(indent), maxline) cdef class HexDecoder (_BaseDecoder): """ - HexDecoder([indent = '\\n'], [maxline = 72], - [flags = CDCF.IGNJUNK | CDCF.LOWERC]) + HexDecoder([flags = CDCF_IGNSPC | CDCF_IGNNEWL | CDCF_IGNCASE]) Hexadecimal decoder. """ - def __init__(me, flags = CDCF_IGNJUNK | CDCF_LOWERC): + def __init__(me, flags = _CDCF_IGNSPC | _CDCF_IGNNEWL | _CDCF_IGNCASE): me.c = hex_class.decoder(flags) ###----- That's all, folks -------------------------------------------------- diff --git a/codec.pyx.in b/codec.pyx.in index 3da1b83..4f57935 100644 --- a/codec.pyx.in +++ b/codec.pyx.in @@ -40,59 +40,62 @@ cdef class %CLASS%Encode: Obsolete %CLASS% encoder. """ cdef %PREFIX%_ctx ctx - def __cinit__(me, *hunoz, **hukairz): + def __cinit__(me): _%PREFIX%_init(&me.ctx) me.ctx.indent = NULL + def __dealloc__(me): + if me.ctx.indent: + xfree(me.ctx.indent) def __init__(me, indent = '\n', maxline = 72): if me.ctx.indent: xfree(me.ctx.indent) - me.ctx.indent = xstrdup(indent) + me.ctx.indent = xstrdup(TEXT_PTR(indent)) me.ctx.maxline = maxline - def __dealloc__(me): + @property + def indent(me): + """E.indent -> INT: indent level for new lines""" + return me.ctx.indent + @indent.setter + def indent(me, indent): if me.ctx.indent: xfree(me.ctx.indent) - property indent: - """E.indent -> INT: indent level for new lines""" - def __get__(me): - return me.ctx.indent - def __set__(me, indent): - if me.ctx.indent: - xfree(me.ctx.indent) - me.ctx.indent = xstrdup(indent) - property maxline: + if indent is None: + me.ctx.indent = NULL + else: + me.ctx.indent = xstrdup(TEXT_PTR(indent)) + @property + def maxline(me): """E.maxline -> INT: maximum length of line, or 0 to prevent splitting""" - def __get__(me): - return me.ctx.maxline - def __set__(me, maxline): + return me.ctx.maxline + @maxline.setter + def maxline(me, maxline): me.ctx.maxline = maxline - def encode(me, text): + def encode(me, input): """E.encode(IN) -> OUT: continue encoding""" - cdef void *p - cdef Py_ssize_t len + cdef const void *p + cdef Py_ssize_t sz cdef dstr d DCREATE(&d) try: - PyObject_AsReadBuffer(text, &p, &len) - _%PREFIX%_encode(&me.ctx, p, len, &d) - rc = PyString_FromStringAndSize(d.buf, d.len) + PyObject_AsReadBuffer(input, &p, &sz) + _%PREFIX%_encode(&me.ctx, p, sz, &d) + return TEXT_FROMSTRLEN(d.buf, d.len) finally: dstr_destroy(&d) - return rc def done(me): """E.done() -> OUT: finish encoding, returning final output""" cdef dstr d DCREATE(&d) try: _%PREFIX%_encode(&me.ctx, NULL, 0, &d) - rc = PyString_FromStringAndSize(d.buf, d.len) + return TEXT_FROMSTRLEN(d.buf, d.len) finally: dstr_destroy(&d) - return rc -def %PREFIX%_encode(text, *arg, **kw): +def %PREFIX%_encode(input, *arg, **kw): """%PREFIX%_encode(IN, [ARGS...]) -> OUT: %CLASS%-encode the string IN""" e = %CLASS%Encode(*arg, **kw) - return e.encode(text) + e.done() + return e.encode(input) + e.done() cdef class %CLASS%Decode: """ @@ -101,36 +104,34 @@ cdef class %CLASS%Decode: Obsolete %CLASS% decoder. """ cdef %PREFIX%_ctx ctx - def __cinit__(me, *hunoz, **hukairz): + def __cinit__(me): _%PREFIX%_init(&me.ctx) me.ctx.indent = NULL - def decode(me, text): + def decode(me, input): """D.encode(IN) -> OUT: continue decoding""" - cdef void *p - cdef Py_ssize_t len + cdef const char *p + cdef Py_ssize_t sz cdef dstr d DCREATE(&d) try: - PyObject_AsReadBuffer(text, &p, &len) - _%PREFIX%_decode(&me.ctx, p, len, &d) - rc = PyString_FromStringAndSize(d.buf, d.len) + TEXT_PTRLEN(input, &p, &sz) + _%PREFIX%_decode(&me.ctx, p, sz, &d) + return BIN_FROMSTRLEN(d.buf, d.len) finally: dstr_destroy(&d) - return rc def done(me): """D.done() -> OUT: finish decoding, returning final output""" cdef dstr d DCREATE(&d) try: _%PREFIX%_decode(&me.ctx, NULL, 0, &d) - rc = PyString_FromStringAndSize(d.buf, d.len) + return BIN_FROMSTRLEN(d.buf, d.len) finally: dstr_destroy(&d) - return rc -def %PREFIX%_decode(text, *arg, **kw): +def %PREFIX%_decode(input, *arg, **kw): """%PREFIX%_decode(IN) -> OUT: %CLASS%-decode the string IN""" d = %CLASS%Decode(*arg, **kw) - return d.decode(text) + d.done() + return d.decode(input) + d.done() ###----- That's all, folks -------------------------------------------------- diff --git a/defs.pxi b/defs.pxi index 1ed4cb7..903a6d0 100644 --- a/defs.pxi +++ b/defs.pxi @@ -170,13 +170,46 @@ cdef extern from 'mLib/str.h': STRF_PREFIX char *str_qword(char **pp, unsigned f) size_t str_qsplit(char *p, char **v, size_t c, char **rest, unsigned f) - int str_matchx(char *p, char *s, unsigned f) + bint str_matchx(char *p, char *s, unsigned f) void str_sanitize(char *d, char *p, size_t sz) cdef extern from 'mLib/versioncmp.h': int _versioncmp "versioncmp" (char *va, char *vb) ###-------------------------------------------------------------------------- +### Binary encoding functions. + +cdef extern from 'mLib/codec.h': + ctypedef struct codec + ctypedef struct codec_class: + char *name + codec *(*encoder)(unsigned f, char *ind, unsigned max) + codec *(*decoder)(unsigned f) + ctypedef struct codec_ops: + codec_class *c + int (*code)(codec *c, const void *p, size_t, dstr *d) + void (*destroy)(codec *c) + ctypedef struct codec: + codec_ops *ops + enum: + _CDCF_LOWERC "CDCF_LOWERC" + _CDCF_IGNCASE "CDCF_IGNCASE" + _CDCF_NOEQPAD "CDCF_NOEQPAD" + _CDCF_IGNEQPAD "CDCF_IGNEQPAD" + _CDCF_IGNEQMID "CDCF_IGNEQMID" + _CDCF_IGNZPAD "CDCF_IGNZPAD" + _CDCF_IGNNEWL "CDCF_IGNNEWL" + _CDCF_IGNSPC "CDCF_IGNSPC" + _CDCF_IGNINVCH "CDCF_IGNINVCH" + _CDCF_IGNJUNK "CDCF_IGNJUNK" + enum: + _CDCERR_OK "CDCERR_OK" + _CDCERR_INVCH "CDCERR_INVCH" + _CDCERR_INVEQPAD "CDCERR_INVEQPAD" + _CDCERR_INVZPAD "CDCERR_INVZPAD" + char *_codec_strerror "codec_strerror" (int err) + +###-------------------------------------------------------------------------- ### Form-urlencoding functions. cdef extern from 'mLib/url.h': @@ -406,8 +439,8 @@ cdef extern from 'mLib/fwatch.h': pass void fwatch_init(fwatch *f, char *name) void fwatch_initfd(fwatch *f, int fd) - int fwatch_update(fwatch *f, char *name) - int fwatch_updatefd(fwatch *f, int fd) + bint fwatch_update(fwatch *f, char *name) + bint fwatch_updatefd(fwatch *f, int fd) ###-------------------------------------------------------------------------- ### File descriptor hacking. @@ -439,8 +472,13 @@ cdef extern from 'mLib/daemonize.h': cdef extern from 'grim.h': int PSIZEOF(void *x) + object BIN_FROMSTRLEN(const void *p, Py_ssize_t sz) + void *BIN_PTR(object bin) + void BIN_SETLEN(object bin, Py_ssize_t sz) + int TEXT_CHECK(object p) + char *TEXT_PTR(str s) void TEXT_PTRLEN(str s, const char **p, Py_ssize_t *sz) - object TEXT_FROMSTRLEN(const char *p, Py_ssize_t sz) - ctypedef void *cvp + str TEXT_FROMSTR(const char *p) + str TEXT_FROMSTRLEN(const char *p, Py_ssize_t sz) ###----- That's all, folks -------------------------------------------------- diff --git a/fdutils.pyx b/fdutils.pyx index 71135e6..124ef23 100644 --- a/fdutils.pyx +++ b/fdutils.pyx @@ -48,16 +48,16 @@ def fdsend(sock, file, buffer): fdsend(SOCK, FILE, BUFFER) -> RC: send FILE over Unix-domain socket SOCK, along with BUFFER """ - cdef void *p + cdef const void *p cdef Py_ssize_t len cdef int rc - PyObject_AsReadBuffer(buffer, &p, &len) + PyObject_AsReadBuffer(buffer, &p, &len) rc = fdpass_send(_getfd(sock), _getfd(file), p, len) if rc < 0: _oserror() return rc -def fdrecv(sock, unsigned size): +def fdrecv(sock, size_t size): """ fdrecv(SOCK, SIZE) -> FD, BUFFER receive file FD and BUFFER of length up to SIZE from Unix-domain SOCK @@ -67,13 +67,11 @@ def fdrecv(sock, unsigned size): cdef Py_ssize_t len cdef PyObject *obj cdef int fd - buf = PyString_FromStringAndSize(NULL, size) - p = PyString_AS_STRING(buf) - len = fdpass_recv(_getfd(sock), &fd, p, size) + buf = BIN_FROMSTRLEN(NULL, size) + len = fdpass_recv(_getfd(sock), &fd, BIN_PTR(buf), size) if len < 0: _oserror() - obj = buf - _PyString_Resize(&obj, len) - return fd, obj + BIN_SETLEN(buf, len) + return fd, buf ###----- That's all, folks -------------------------------------------------- diff --git a/fwatch.pyx b/fwatch.pyx index eedb872..086526b 100644 --- a/fwatch.pyx +++ b/fwatch.pyx @@ -30,24 +30,38 @@ cdef class FWatch: FILE may be a string, file descriptor, or an object with a `fileno' method. """ cdef fwatch fw - cdef public file - def __cinit__(me, file): - me._init(file) - def __init__(me, file): - me._init(file) - cdef _init(me, file): - if isinstance(file, str): - fwatch_init(&me.fw, file) + cdef fobj + cdef const char *fname + cdef int fd + def __cinit__(me): + me.fname = NULL + me.fd = -1 + cdef _setfile(me, object file): + if TEXT_CHECK(file): + me.fname = TEXT_PTR(file) + me.fd = -1 else: - fwatch_initfd(&me.fw, _getfd(file)) - me.file = file + me.fd = _getfd(file) + me.fname = NULL + me.fobj = file + def __init__(me, object file): + me._setfile(file) + if me.fname: + fwatch_init(&me.fw, me.fname) + else: + fwatch_initfd(&me.fw, me.fd) + @property + def file(me): + return me.fobj + @file.setter + def file(me, file): + me._setfile(file) def update(me): """FW.update() -> RC: nonzero if the file has changed state""" cdef int rc - if isinstance(me.file, str): - rc = fwatch_update(&me.fw, me.file) + if me.fname: + return fwatch_update(&me.fw, me.fname) else: - rc = fwatch_updatefd(&me.fw, _getfd(me.file)) - return rc + return fwatch_updatefd(&me.fw, me.fd) ###----- That's all, folks -------------------------------------------------- diff --git a/grim.h b/grim.h index 016e2ef..4495ee5 100644 --- a/grim.h +++ b/grim.h @@ -80,6 +80,7 @@ typedef const void *cvp; #define BIN_PTR(obj) PyBytes_AS_STRING(obj) #define BIN_LEN(obj) PyBytes_GET_SIZE(obj) #define BIN_FROMSTR(str) PyBytes_FromString(str) +#define BIN_FROMSTRLEN(str, len) PyBytes_FromStringAndSize(str, len) #define BIN_FORMAT PyBytes_FromFormat #define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0) @@ -131,6 +132,7 @@ typedef const void *cvp; #define BIN_PTR(obj) PyString_AS_STRING(obj) #define BIN_LEN(obj) PyString_GET_SIZE(obj) #define BIN_FROMSTR(str) PyString_FromString(str) +#define BIN_FROMSTRLEN(str, len) PyString_FromStringAndSize(str, len) #define BIN_FORMAT PyString_FromFormat #define BIN_SETLEN(obj, len) do Py_SIZE(obj) = (len); while (0) diff --git a/mLib.pyx b/mLib.pyx index bae8e30..e9e2d7e 100644 --- a/mLib.pyx +++ b/mLib.pyx @@ -51,22 +51,22 @@ include 'atom.pyx' include 'assoc.pyx' ## String utilities. -#include 'str.pyx' +include 'str.pyx' ## Encodings. -#include 'codec.pyx' -#include 'base64.pyx' -#include 'base32.pyx' -#include 'hex.pyx' -#include 'url.pyx' +include 'codec.pyx' +include 'base64.pyx' +include 'base32.pyx' +include 'hex.pyx' +include 'url.pyx' ## Error reporting. -#include 'report.pyx' +include 'report.pyx' ## File utilities. -#include 'fwatch.pyx' -#include 'fdutils.pyx' -#include 'mdup.pyx' +include 'fwatch.pyx' +include 'fdutils.pyx' +include 'mdup.pyx' ## Other useful stuff. #include 'stuff.pyx' diff --git a/mdup.pyx b/mdup.pyx index 7256211..12e727c 100644 --- a/mdup.pyx +++ b/mdup.pyx @@ -32,20 +32,23 @@ def mdup(v): with CUR reflecting the new file descriptors even on error. Returns the same LIST on success. """ - cdef mdup_fd *vv + cdef mdup_fd *vv = NULL cdef size_t n cdef int i cdef int rc n = len(v) vv = xmalloc(n * PSIZEOF(vv)) - for 0 <= i < n: - vv[i].cur, vv[i].want = v[i] - rc = _mdup(vv, n) - for 0 <= i < n: - v[i] = vv[i].cur, vv[i].want - if rc < 0: - _oserror() + try: + for 0 <= i < n: + vv[i].cur, vv[i].want = v[i] + rc = _mdup(vv, n) + for 0 <= i < n: + v[i] = vv[i].cur, vv[i].want + if rc < 0: + _oserror() + finally: + xfree(vv) return v ###----- That's all, folks -------------------------------------------------- diff --git a/report.pyx b/report.pyx index 9d19ed2..e42a86d 100644 --- a/report.pyx +++ b/report.pyx @@ -24,24 +24,23 @@ ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. quis = '' -cdef char *_progstring -_progstring = NULL +cdef char *_progstring = NULL -def ego(char *prog): +def ego(object prog): """ego(PROG): set program name""" global quis, _progstring if _progstring: xfree(_progstring) - _progstring = xstrdup(prog) + _progstring = xstrdup(TEXT_PTR(prog)) _ego(_progstring) - quis = _quis() + quis = TEXT_FROMSTR(_quis()) -def moan(char *msg): +def moan(object msg): """moan(MSG): report a warning""" - _moan('%s', msg) -def die(char *msg, rc = 126): + _moan('%s', TEXT_PTR(msg)) +def die(object msg, rc = 126): """die(MSG, [rc = 126]): report a fatal error and exit""" - _moan('%s', msg) - raise SystemExit, rc + _moan('%s', TEXT_PTR(msg)) + raise SystemExit(rc) ###----- That's all, folks -------------------------------------------------- diff --git a/setup.py b/setup.py index 1ec65dc..bc0c2d5 100755 --- a/setup.py +++ b/setup.py @@ -22,18 +22,20 @@ mLib = DC.Extension('mLib', [pyxc, ## The `cythonize' function generates the C sources immediately, so we have ## to generate its inputs even earlier. genfiles = [ -# MS.Derive('base64.pyx', 'codec.pyx.in', -# {'CLASS': 'Base64', 'PREFIX': 'base64'}), -# MS.Derive('base32.pyx', 'codec.pyx.in', -# {'CLASS': 'Base32', 'PREFIX': 'base32'}), -# MS.Derive('hex.pyx', 'codec.pyx.in', -# {'CLASS': 'Hex', 'PREFIX': 'hex'}) + MS.Derive('base64.pyx', 'codec.pyx.in', + {'CLASS': 'Base64', 'PREFIX': 'base64'}), + MS.Derive('base32.pyx', 'codec.pyx.in', + {'CLASS': 'Base32', 'PREFIX': 'base32'}), + MS.Derive('hex.pyx', 'codec.pyx.in', + {'CLASS': 'Hex', 'PREFIX': 'hex'}) ] for g in genfiles: g.gen() ## Generate the main C code. if OS.path.exists(pyxc): OS.rename(pyxc, "mLib.c") -CB.cythonize("mLib.pyx", compile_time_env = dict(PYVERSION = PYVERSION)) +CB.cythonize("mLib.pyx", + compile_time_env = dict(PYVERSION = PYVERSION), + compiler_directives = dict(c_string_encoding = 'utf8')) OS.rename("mLib.c", pyxc) MS.setup(name = 'mLib-python', diff --git a/str.pyx b/str.pyx index f1a2ee5..97dfa6a 100644 --- a/str.pyx +++ b/str.pyx @@ -23,83 +23,79 @@ ### along with mLib/Python; if not, write to the Free Software Foundation, ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -def word(char *p, quotep = False): +def word(object p, object quotep = False): """word(STR, [quotep = False]) -> WORD, REST""" - cdef unsigned f - cdef char *op - cdef char *pp + cdef unsigned f = 0 + cdef char *op = xstrdup(TEXT_PTR(p)) + cdef char *pp = op cdef char *q cdef object w cdef object r - f = 0 if quotep: - f = f | STRF_QUOTE - pp = op = xstrdup(p) + f |= STRF_QUOTE q = str_qword(&pp, f) if q is NULL: w = None else: - w = q + w = q if pp is NULL: r = '' else: - r = pp + r = pp xfree(op) return w, r -def split(char *p, int n = -1, quotep = False): +def split(object p, int n = -1, quotep = False): """split(STR, [n = -1], [quotep = False]) -> WORDS, REST""" - cdef unsigned f - cdef char *op - cdef char *pp + cdef unsigned f = 0 + cdef char *op = xstrdup(TEXT_PTR(p)) + cdef char *pp = op cdef char *q - cdef object l + cdef object l = [] cdef object r - f = 0 if quotep: - f = f | STRF_QUOTE - l = [] - op = pp = xstrdup(p) + f |= STRF_QUOTE while n != 0: q = str_qword(&pp, f) if q is NULL: break - l.append(q) + l.append(q) if n > 0: - n = n - 1 + n -= 1 if pp is NULL: r = '' else: - r = pp + r = pp xfree(op) return l, r -def match(char *p, char *s, prefixp = False): +def match(object p, object s, prefixp = False): """match(PAT, STR, [prefixp = False]) -> BOOL""" - cdef unsigned f + cdef unsigned f = 0 - f = 0 if prefixp: - f = f | STRF_PREFIX - return _tobool(str_matchx(p, s, f)) + f |= STRF_PREFIX + return str_matchx(TEXT_PTR(p), TEXT_PTR(s), f) -def sanitize(char *p, int n = -1): +def sanitize(object s, int n = -1): """sanitize(STR, [n = -1]) -> STR""" + cdef Py_ssize_t nn + cdef const char *ss = _text_strlen(s, &nn) cdef char *buf cdef object d if n < 0: - n = strlen(p) + n = nn buf = xmalloc(n + 1) - str_sanitize(buf, p, n + 1) - d = buf + str_sanitize(buf, ss, n + 1) + d = buf xfree(buf) return d -def versioncmp(char *va, char *vb): +def versioncmp(object va, object vb): """versioncmp(V0, V1) -> -1 | 0 | +1""" - return _versioncmp(va, vb) + return _versioncmp(TEXT_PTR(va), TEXT_PTR(vb)) ###----- That's all, folks -------------------------------------------------- diff --git a/test.py b/test.py index 33f781e..9cd09e2 100644 --- a/test.py +++ b/test.py @@ -1,6 +1,10 @@ #! /usr/bin/python ### -*- coding: utf-8 -*- +import contextlib as CTX +import fcntl as FC +import os as OS +import socket as S import sys as SYS import mLib as M @@ -19,10 +23,12 @@ def must_equal(x, y): print(";; test begins (Python %s)" % SYS.version) ## crc32 +print(";; test crc32...") must_equal(M.crc32(_bin("abc")), 0x352441c2) must_equal(M.CRC32().chunk(_bin("a")).chunk(_bin("bc")).done(), 0x352441c2) ## unihash +print(";; test unihash...") assert M.Unihash().key is None must_equal(M.Unihash.hash(_bin("abc")), 0xbf71f6a2) must_equal(M.Unihash().chunk(_bin("a")).chunk(_bin("bc")).done(), 0xbf71f6a2) @@ -35,6 +41,7 @@ M.setglobalkey(0x8498a262) must_equal(M.Unihash.hash(_bin("abc")), 0xecd1e2a2) ## atom +print(";; test atom...") foo = M.Atom("foo") bar = M.Atom("bär") assert foo != bar @@ -63,8 +70,176 @@ def test_mapping(mapcls, keya, keyz): must_equal(list(tab.keys()), [keya]) must_equal(list(tab.values()), [42]) must_equal(list(tab.items()), [(keya, 42)]) +print(";; test assoc...") test_mapping(M.AssocTable, foo, bar) +print(";; test sym...") test_mapping(M.SymTable, 'foo', 'bar') +## str +print(";; test str...") +must_equal(M.word(" foo bar baz"), ("foo", "bar baz")) +must_equal(M.word(" 'foo \\' bar' baz ", quotep = True), + ("foo ' bar", "baz ")) +must_equal(M.split(' one "two \\" three" four five six', 3, quotep = True), + (["one", 'two " three', "four"], "five six")) +assert M.match("foo*bar", "food is served at the bar") +must_equal(M.sanitize("some awful string!", 12), "some_awful_s") +assert M.versioncmp("1.2.5~pre99", "1.2.5") < 0 +assert M.versioncmp("1.2.5pre99", "1.2.5") > 0 + +## codec +ref = _bin("just >some ?string to encode") +def test_codecclass(enccls, deccls, encref): + enc = enccls("!", 8, M.CDCF_LOWERC) + e = enc.encode(ref[:10]) + e += enc.encode(ref[10:]) + e += enc.done() + must_equal(e, encref) + encref = encref.replace("!", "") + dec = deccls(M.CDCF_IGNCASE) + d = dec.decode(encref[:15]) + d += dec.decode(encref[15:]) + d += dec.done() + must_equal(d, ref) + try: deccls().decode("???") + except M.CodecError as e: + if e.err == M.CDCERR_INVCH: pass + else: raise + else: assert False +def test_oldcodec(enccls, deccls, encref): + enc = enccls() + enc.indent = "!" + enc.maxline = 8 + e = enc.encode(ref[:10]) + e += enc.encode(ref[10:]) + e += enc.done() + must_equal(e, encref) + encref = encref.replace("!", "") + dec = deccls() + d = dec.decode(encref[:15]) + d += dec.decode(encref[15:]) + d += dec.done() + must_equal(d, ref) + +print(";; test base64...") +test_codecclass(M.Base64Encoder, M.Base64Decoder, + "anVzdCA+!c29tZSA/!c3RyaW5n!IHRvIGVu!Y29kZQ==") +test_codecclass(M.File64Encoder, M.File64Decoder, + "anVzdCA+!c29tZSA%!c3RyaW5n!IHRvIGVu!Y29kZQ==") +test_codecclass(M.Base64URLEncoder, M.Base64URLDecoder, + "anVzdCA-!c29tZSA_!c3RyaW5n!IHRvIGVu!Y29kZQ==") +test_oldcodec(M.Base64Encode, M.Base64Decode, + "anVzdCA+!c29tZSA/!c3RyaW5n!IHRvIGVu!Y29kZQ==") + +print(";; test base32...") +test_codecclass(M.Base32Encoder, M.Base32Decoder, + "nj2xg5ba!hzzw63lf!ea7xg5ds!nfxgoidu!n4qgk3td!n5sgk===") +test_codecclass(M.Base32HexEncoder, M.Base32HexDecoder, + "d9qn6t10!7ppmurb5!40vn6t3i!d5n6e83k!dsg6arj3!dti6a===") +test_oldcodec(M.Base32Encode, M.Base32Decode, + "NJ2XG5BA!HZZW63LF!EA7XG5DS!NFXGOIDU!N4QGK3TD!N5SGK===") + +print(";; test hex...") +test_codecclass(M.HexEncoder, M.HexDecoder, + "6a757374!203e736f!6d65203f!73747269!6e672074!6f20656e!636f6465") +test_oldcodec(M.HexEncode, M.HexDecode, + "6a757374!203e736f!6d65203f!73747269!6e672074!6f20656e!636f6465") + +## url +print(";; test url...") +uenc = M.URLEncode() +uenc.encode("one", "some/string!") +uenc.strictp = True +uenc.encode("two", "some other/string!") +uenc.laxp = True +uenc.encode("three", "another!string") +r = uenc.result +must_equal(r, "one=some/string%21&two=some+other%2fstring%21&three=another!string") +must_equal(list(M.URLDecode(r)), + [("one", "some/string!"), + ("two", "some other/string!"), + ("three", "another!string")]) + +## report +@CTX.contextmanager +def stderr_test(want_out): + pin, pout = OS.pipe() + fin = OS.fdopen(pin, 'r') + olderr = OS.dup(2) + OS.dup2(pout, 2) + OS.close(pout) + try: + yield + OS.close(2) + out = fin.read() + finally: + fin.close() + OS.dup2(olderr, 2) + must_equal(out, want_out) +print(";; test report...") +M.ego("my/path/name") +must_equal(M.quis, "name") +with stderr_test("name: test moaning\n"): + M.moan("test moaning") +with stderr_test("name: test death\n"): + try: M.die("test death") + except SystemExit as e: assert e.code == 126 + else: assert False + +## fwatch +print(";; test fwatch...") +fd = OS.open(",test-stamp", OS.O_WRONLY | OS.O_CREAT, 0o666) +fw = M.FWatch(fd) +assert not fw.update() +OS.write(fd, _bin("stuff\n")) +assert fw.update() +assert fw.file is fd +fd2 = OS.open(",test-stamp.new", OS.O_WRONLY | OS.O_CREAT, 0o666) +OS.rename(",test-stamp.new", ",test-stamp") +assert not fw.update() +fw.file = ",test-stamp" +assert fw.update() +OS.close(fd) +OS.close(fd2) +OS.unlink(",test-stamp") + +## fdflags +print(";; test fdflags...") +pin, pout = OS.pipe() +ofl = FC.fcntl(pin, FC.F_GETFL) +ofd = FC.fcntl(pout, FC.F_GETFD) +fout = OS.fdopen(pout, 'wb') +M.fdflags(pin, fbic = OS.O_NONBLOCK, fxor = OS.O_NONBLOCK) +assert FC.fcntl(pin, FC.F_GETFL) == ofl | OS.O_NONBLOCK +M.fdflags(pin, fbic = OS.O_NONBLOCK, fxor = 0) +assert FC.fcntl(pin, FC.F_GETFL) == ofl&~OS.O_NONBLOCK +M.fdflags(fout, fdbic = FC.FD_CLOEXEC, fdxor = FC.FD_CLOEXEC) +assert FC.fcntl(pout, FC.F_GETFD) == ofd | FC.FD_CLOEXEC +M.fdflags(fout, fdbic = FC.FD_CLOEXEC, fdxor = 0) +assert FC.fcntl(pout, FC.F_GETFD) == ofd&~FC.FD_CLOEXEC +OS.close(pin) +fout.close() + +## fdpass +print(";; test fdpass...") +pin, pout = OS.pipe() +fin = OS.fdopen(pin, 'r') +OS.write(pout, _bin("Hello, world!")) +OS.close(pout) +sk0, sk1 = S.socketpair(S.AF_UNIX, S.SOCK_STREAM) +M.fdsend(sk0, fin, _bin("Have a pipe!")) +fin.close() +sk0.close() +fd, msg = M.fdrecv(sk1, 16) +sk1.close() +must_equal(msg, _bin("Have a pipe!")) +data = OS.read(fd, 16) +OS.close(fd) +must_equal(data, _bin("Hello, world!")) + +## mdup +## print(";; test mdup...") + + ## Done! print(";; test completed OK") diff --git a/url.pyx b/url.pyx index 72467f1..b620294 100644 --- a/url.pyx +++ b/url.pyx @@ -31,53 +31,56 @@ cdef class URLEncode: def __cinit__(me, *hunoz, **hukairz): url_initenc(&me.ctx) DCREATE(&me.d) + def __dealloc__(me): + dstr_destroy(&me.d) def __init__(me, strictp = False, laxp = False, semip = False): cdef unsigned f f = 0 if strictp: - f = f | URLF_STRICT + f |= URLF_STRICT if laxp: - f = f | URLF_LAX + f |= URLF_LAX if semip: - f = f | URLF_SEMI + f |= URLF_SEMI me.ctx.f = f - def encode(me, char *name, char *value): + def encode(me, object name, object value): """UE.encode(NAME, VALUE): encode a key/value pair""" - url_enc(&me.ctx, &me.d, name, value) + url_enc(&me.ctx, &me.d, TEXT_PTR(name), TEXT_PTR(value)) return me - property result: + @property + def result(me): """UE.result -> STR: the encoded string""" - def __get__(me): - return PyString_FromStringAndSize(me.d.buf, me.d.len) - property strictp: + return TEXT_FROMSTRLEN(me.d.buf, me.d.len) + @property + def strictp(me): """UE.strictp -> BOOL: strictly escape non-alphanumerics?""" - def __get__(me): - return _tobool(me.ctx.f & URLF_STRICT) - def __set__(me, val): - if val: - me.ctx.f = me.ctx.f | URLF_STRICT - else: - me.ctx.f = me.ctx.f & ~URLF_STRICT - property laxp: + return (me.ctx.f&URLF_STRICT) + @strictp.setter + def strictp(me, val): + if val: + me.ctx.f |= URLF_STRICT + else: + me.ctx.f &= ~URLF_STRICT + @property + def laxp(me): """UE.laxp -> BOOL: only escape obviously unsafe characters?""" - def __get__(me): - return _tobool(me.ctx.f & URLF_LAX) - def __set__(me, val): - if val: - me.ctx.f = me.ctx.f | URLF_LAX - else: - me.ctx.f = me.ctx.f & ~URLF_LAX - property semip: + return (me.ctx.f&URLF_LAX) + @laxp.setter + def laxp(me, val): + if val: + me.ctx.f |= URLF_LAX + else: + me.ctx.f &= ~URLF_LAX + @property + def semip(me): """UE.semip -> BOOL: separate key/value pairs with semicolons?""" - def __get__(me): - return _tobool(me.ctx.f & URLF_SEMI) - def __set__(me, val): - if val: - me.ctx.f = me.ctx.f | URLF_SEMI - else: - me.ctx.f = me.ctx.f & ~URLF_SEMI - def __del__(me): - dstr_destroy(&me.d) + return (me.ctx.f&URLF_SEMI) + @semip.setter + def semip(me, val): + if val: + me.ctx.f |= URLF_SEMI + else: + me.ctx.f &= ~URLF_SEMI cdef class URLDecode: """URLDecode(STR, [semip = False]): iterator over (KEY, VALUE) pairs""" @@ -85,15 +88,17 @@ cdef class URLDecode: cdef char *p def __cinit__(me, *hunoz, **hukairz): - me.p = xstrdup('') + me.p = NULL url_initdec(&me.ctx, me.p) - def __init__(me, char *string, semip = False): + def __dealloc__(me): + xfree(me.p) + def __init__(me, object string, semip = False): cdef unsigned f f = 0 if semip: - f = f | URLF_SEMI + f |= URLF_SEMI xfree(me.p) - me.p = xstrdup(string) + me.p = xstrdup(TEXT_PTR(string)) me.ctx.p = me.p me.ctx.f = f def __iter__(me): @@ -108,23 +113,22 @@ cdef class URLDecode: DCREATE(&v) anyp = url_dec(&me.ctx, &n, &v) if anyp: - nn = PyString_FromStringAndSize(n.buf, n.len) - vv = PyString_FromStringAndSize(v.buf, v.len) + nn = TEXT_FROMSTRLEN(n.buf, n.len) + vv = TEXT_FROMSTRLEN(v.buf, v.len) dstr_destroy(&n) dstr_destroy(&v) if not anyp: raise StopIteration return nn, vv - property semip: - """UD.semip -> BOOL: key/value pairs separated with semicolons?""" - def __get__(me): - return _tobool(me.ctx.f & URLF_SEMI) - def __set__(me, val): - if val: - me.ctx.f = me.ctx.f | URLF_SEMI - else: - me.ctx.f = me.ctx.f & ~URLF_SEMI - def __del__(me): - xfree(me.p) + @property + def semip(me): + """UE.semip -> BOOL: separate key/value pairs with semicolons?""" + return (me.ctx.f&URLF_SEMI) + @semip.setter + def semip(me, val): + if val: + me.ctx.f |= URLF_SEMI + else: + me.ctx.f &= ~URLF_SEMI ###----- That's all, folks -------------------------------------------------- diff --git a/utils.pyx b/utils.pyx index 17fa416..eab722f 100644 --- a/utils.pyx +++ b/utils.pyx @@ -23,16 +23,20 @@ ### along with mLib/Python; if not, write to the Free Software Foundation, ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#cdef object _oserror(): -# raise OSError, (errno, strerror(errno)) +cdef const char *_text_strlen(object s, Py_ssize_t *sz) except NULL: + cdef const char *p + TEXT_PTRLEN(s, &p, sz) + return p -#cdef int _getfd(object fdobj): -# try: -# fd = int(fdobj) -# except TypeError: -# ##PyErr_Clear() -# fd = fdobj.fileno() -# return fd +cdef object _oserror(): + raise OSError(errno, strerror(errno)) + +cdef int _getfd(object fdobj): + try: + fd = int(fdobj) + except TypeError: + fd = fdobj.fileno() + return fd #cdef object _checkcallable(object f, object what): # if f is not None and not callable(f): -- 2.11.0