secret
Last active

wrap Person.h in Cython

  • Download Gist
.gitignore
1 2
*.pyxbldc
/.do_buil*
Makefile
Makefile
1 2 3 4 5 6 7 8 9
all:
 
Makefile:
@
 
%: FORCE
+./do $@
 
.PHONY: FORCE
Person.c
C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/* http://stackoverflow.com/q/7619785 */
#include "Person.h"
#include "Person_private.h"
 
#include <stdlib.h>
#include <string.h>
#include <assert.h>
 
Person_ptr Person_create(const char* name)
{
Person_ptr self = PERSON(malloc(sizeof(Person)));
PERSON_CHECK_INSTANCE(self);
person_init(self, name);
return self;
}
 
void Person_destroy(Person_ptr self)
{
PERSON_CHECK_INSTANCE(self);
if (NULL != self->name) free(self->name);
}
 
/* Do not free not change the returned string! */
const char* Person_get_name(const Person_ptr self)
{
PERSON_CHECK_INSTANCE(self);
return self->name;
}
 
int Person_get_age(const Person_ptr self)
{
PERSON_CHECK_INSTANCE(self);
return self->age;
}
 
void Person_birthday(Person_ptr self)
{
PERSON_CHECK_INSTANCE(self);
++self->age;
}
/* private/protected methods */
void person_init(Person_ptr self, const char* name)
{
self->name = malloc(sizeof(char) * (strlen(name) + 1));
assert(NULL != self->name);
strcpy(self->name, name);
self->age = 0;
}
Person.h
C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/* http://stackoverflow.com/q/7619785 */
#ifndef __PERSON_H__
#define __PERSON_H__
 
#include <assert.h>
 
typedef struct Person_TAG* Person_ptr;
 
#define PERSON(x) \
((Person_ptr) (x))
 
#define PERSON_CHECK_INSTANCE(x) \
assert(PERSON(x) != PERSON(NULL))
 
/* public interface */
Person_ptr Person_create(const char* name);
void Person_destroy(Person_ptr self);
 
const char* Person_get_name(const Person_ptr self);
int Person_get_age(const Person_ptr self);
void Person_birthday(Person_ptr self);
 
# endif /* __PERSON_H__ */
Person_private.h
C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* http://stackoverflow.com/q/7619785 */
#ifndef __PERSON_PRIVATE_H__
#define __PERSON_PRIVATE_H__
 
#include "Person.h"
 
typedef struct Person_TAG {
char* name;
int age;
} Person;
 
 
void person_init(Person_ptr self, const char* name);
 
# endif /* __PERSON_PRIVATE_H__ */
all.do
1
redo test
clean.do
1 2
exec >&2
git clean -x -d -f
cperson.pxd
Cython
1 2 3 4 5 6 7 8 9 10 11 12 13
 
cdef extern from "Person.h" nogil:
cdef struct Person_TAG:
pass
ctypedef Person_TAG* Person_ptr
 
Person_ptr Person_create(char* name)
void Person_destroy(Person_ptr self)
 
char* Person_get_name(Person_ptr self)
int Person_get_age(Person_ptr self)
void Person_birthday(Person_ptr self)
do
Shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
#!/bin/sh
#
# A minimal alternative to djb redo that doesn't support incremental builds.
# For the full version, visit http://github.com/apenwarr/redo
#
# The author disclaims copyright to this source file and hereby places it in
# the public domain. (2010 12 14)
#
 
# By default, no output coloring.
green=""
bold=""
plain=""
 
if [ -n "$TERM" -a "$TERM" != "dumb" ] && tty <&2 >/dev/null 2>&1; then
green="$(printf '\033[32m')"
bold="$(printf '\033[1m')"
plain="$(printf '\033[m')"
fi
 
_dirsplit()
{
base=${1##*/}
dir=${1%$base}
}
 
dirname()
(
_dirsplit "$1"
dir=${dir%/}
echo "${dir:-.}"
)
 
_dirsplit "$0"
export REDO=$(cd "${dir:-.}" && echo "$PWD/$base")
 
DO_TOP=
if [ -z "$DO_BUILT" ]; then
DO_TOP=1
[ -n "$*" ] || set all # only toplevel redo has a default target
export DO_BUILT=$PWD/.do_built
: >>"$DO_BUILT"
echo "Removing previously built files..." >&2
sort -u "$DO_BUILT" | tee "$DO_BUILT.new" |
while read f; do printf "%s\0%s.did\0" "$f" "$f"; done |
xargs -0 rm -f 2>/dev/null
mv "$DO_BUILT.new" "$DO_BUILT"
DO_PATH=$DO_BUILT.dir
export PATH=$DO_PATH:$PATH
rm -rf "$DO_PATH"
mkdir "$DO_PATH"
for d in redo redo-ifchange; do
ln -s "$REDO" "$DO_PATH/$d";
done
[ -e /bin/true ] && TRUE=/bin/true || TRUE=/usr/bin/true
for d in redo-ifcreate redo-stamp redo-always; do
ln -s $TRUE "$DO_PATH/$d";
done
fi
 
 
_find_dofile_pwd()
{
dofile=default.$1.do
while :; do
dofile=default.${dofile#default.*.}
[ -e "$dofile" -o "$dofile" = default.do ] && break
done
ext=${dofile#default}
ext=${ext%.do}
base=${1%$ext}
}
 
 
_find_dofile()
{
local prefix=
while :; do
_find_dofile_pwd "$1"
[ -e "$dofile" ] && break
[ "$PWD" = "/" ] && break
target=${PWD##*/}/$target
tmp=${PWD##*/}/$tmp
prefix=${PWD##*/}/$prefix
cd ..
done
base=$prefix$base
}
 
 
_run_dofile()
{
export DO_DEPTH="$DO_DEPTH "
export REDO_TARGET=$PWD/$target
local line1
set -e
read line1 <"$PWD/$dofile"
cmd=${line1#"#!/"}
if [ "$cmd" != "$line1" ]; then
/$cmd "$PWD/$dofile" "$@" >"$tmp.tmp2"
else
:; . "$PWD/$dofile" >"$tmp.tmp2"
fi
}
 
 
_do()
{
local dir=$1 target=$2 tmp=$3
if [ ! -e "$target" ] || [ -d "$target" -a ! -e "$target.did" ]; then
printf '%sdo %s%s%s%s\n' \
"$green" "$DO_DEPTH" "$bold" "$dir$target" "$plain" >&2
echo "$PWD/$target" >>"$DO_BUILT"
dofile=$target.do
base=$target
ext=
[ -e "$target.do" ] || _find_dofile "$target"
if [ ! -e "$dofile" ]; then
echo "do: $target: no .do file" >&2
return 1
fi
[ ! -e "$DO_BUILT" ] || [ ! -d "$(dirname "$target")" ] ||
: >>"$target.did"
( _run_dofile "$base" "$ext" "$tmp.tmp" )
rv=$?
if [ $rv != 0 ]; then
printf "do: %s%s\n" "$DO_DEPTH" \
"$dir$target: got exit code $rv" >&2
rm -f "$tmp.tmp" "$tmp.tmp2"
return $rv
fi
mv "$tmp.tmp" "$target" 2>/dev/null ||
! test -s "$tmp.tmp2" ||
mv "$tmp.tmp2" "$target" 2>/dev/null
rm -f "$tmp.tmp2"
else
echo "do $DO_DEPTH$target exists." >&2
fi
}
 
 
# Make corrections for directories that don't actually exist yet.
_dir_shovel()
{
local dir base
xdir=$1 xbase=$2 xbasetmp=$2
while [ ! -d "$xdir" -a -n "$xdir" ]; do
_dirsplit "${xdir%/}"
xbasetmp=${base}__$xbase
xdir=$dir xbase=$base/$xbase
echo "xbasetmp='$xbasetmp'" >&2
done
}
 
 
redo()
{
for i in "$@"; do
_dirsplit "$i"
_dir_shovel "$dir" "$base"
dir=$xdir base=$xbase basetmp=$xbasetmp
( cd "$dir" && _do "$dir" "$base" "$basetmp" ) || return 1
done
}
 
 
set -e
redo "$@"
 
if [ -n "$DO_TOP" ]; then
echo "Removing stamp files..." >&2
[ ! -e "$DO_BUILT" ] ||
while read f; do printf "%s.did\0" "$f"; done <"$DO_BUILT" |
xargs -0 rm -f 2>/dev/null
fi
person.pyx
Cython
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
from cperson cimport (Person_ptr, Person_create, Person_destroy,
Person_get_name, Person_get_age, Person_birthday)
 
cdef extern from *:
ctypedef char* const_char_p "const char*"
 
import operator
try:
from collections import namedtuple
NT = namedtuple('Person', 'name age')
except ImportError:
NT = lambda *args: args
 
_NOT_SET = object()
 
cdef class Person:
cdef Person_ptr thisptr
 
def __cinit__(self, unicode name not None, *unused_args, **unused_kwargs):
cdef bytes pyname = toutf8(name)
self.thisptr = Person_create(pyname)
if self.thisptr is NULL:
raise MemoryError
 
def __init__(self, name, age=_NOT_SET):
if age is not _NOT_SET: self.age = age
 
def __dealloc__(self):
if self.thisptr is not NULL:
Person_destroy(self.thisptr)
 
def __str__(self):
return u'Person(name="%s", age=%d)' % (self.name, self.age)
 
def __repr__(self):
return u'person.'+unicode(self).encode('unicode-escape').decode('ascii')
 
def __richcmp__(x, y, int op):
f = None
if op == 2: # ==
f = operator.eq
elif op == 0: # <
f = operator.lt
elif op == 1: # <=
f = operator.le
if f is not None:
return f(x.astuple(), y.astuple())
return NotImplemented
 
def astuple(self):
return NT(self.name, self.age)
 
property name:
def __get__(self):
assert self.thisptr is not NULL
return fromutf8(Person_get_name(self.thisptr))
 
property age:
def __get__(self):
assert self.thisptr is not NULL
return Person_get_age(self.thisptr)
def __set__(self, int newage):
if newage < self.age:
raise ValueError("can't set smaller age")
 
cdef Py_ssize_t i
for i in range(newage - self.age):
self.birthday()
 
def birthday(self):
assert self.thisptr is not NULL
Person_birthday(self.thisptr)
 
 
cdef bytes toutf8(unicode s):
return s.encode('utf-8')
cdef unicode fromutf8(const_char_p s):
assert s is not NULL
cdef bytes pys = s
return pys.decode('utf-8')
person.pyxbld
1 2 3 4 5 6 7 8 9
import os
from distutils.extension import Extension
 
dirname = os.path.dirname(__file__)
 
def make_ext(modname, pyxfilename):
return Extension(name=modname,
sources=[pyxfilename, "Person.c"],
include_dirs=[dirname])
test.do
1 2 3 4 5
exec >&2
DEPS=test_person.py
touch person.pyxbld
python $(which py.test) $DEPS &&
python3 $(which py.test) $DEPS
test_person.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try: from future_builtins import ascii
except ImportError: ascii = ascii # py3k
 
try: unicode = unicode
except NameError: unicode = str # py3k
 
try: xrange = xrange
except NameError: xrange = range # py3k
 
import sys
import pyximport; pyximport.install()
 
U_PREFIX = 'u' if sys.version_info < (3,) else ''
 
 
from person import Person
 
def U(bytes_or_unicode, source_encoding='utf-8'):
if not isinstance(bytes_or_unicode, unicode):
return bytes_or_unicode.decode(source_encoding)
return bytes_or_unicode
 
def assert_raises(exception_type, func, *args, **kwargs):
try: func(*args,**kwargs)
except exception_type: pass
else: assert 0, (func.__name__, args, kwargs)
 
def test_1sane():
p = Person(U('Gîl'))
assert p.name == U('Gîl') and p.age == 0
for _ in xrange(10): p.birthday()
assert p.age == 10
assert p == Person(name=U("Gîl"), age=10)
 
assert unicode(p) == U('Person(name="Gîl", age=10)')
rp = r'person.Person(name="G\xeel", age=10)'
assert repr(p) == rp
assert ascii(p) == rp
 
def test_none():
for f in [Person, Person.__init__, Person.birthday, Person.__str__]:
assert_raises(TypeError, f, None)
assert_raises(TypeError, Person)
 
def test_age():
p = Person(U('Brúnor'))
assert p.age == 0
p.age = 1000
assert p.age == 1000
assert_raises(ValueError, setattr, p, 'age', 50)
assert_raises(TypeError, setattr, p, 'age', None)
assert_raises(TypeError, Person, U('Nethon'), None)
assert_raises(TypeError, Person, U("Cílil"), '1')
assert_raises(ValueError, Person, U('Círwen'), -1)
 
def test_astuple():
p = Person(U("Calemirtôr"), 11)
 
try:
from collections import namedtuple
assert (ascii(p.astuple()) ==
"Person(name=" + U_PREFIX + r"'Calemirt\xf4r', age=11)")
except ImportError: pass
assert p.astuple() == (U("Calemirtôr"), 11)
 
def test_new_attr():
p = Person(U('dae'))
try: p.dae = None
except AttributeError: pass
else: assert 0
 
def test_richcmp():
name = U('Aglareth')
x = Person(name)
y = Person(U('Duron'))
assert x != y
x.birthday()
z = Person(name)
assert z < x
assert not (z == x)
assert x > z
assert z <= x
assert x != z
assert x >= z
 
def run_tests():
import inspect
import test_person
for name, f in inspect.getmembers(test_person, inspect.isfunction):
if name.startswith('test_'):
f()
 
if __name__=="__main__":
run_tests()

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.