-
-
Save zed/4e72cc3ac15408df452e to your computer and use it in GitHub Desktop.
wrap Person.h in Cython
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.pyxbldc | |
/.do_buil* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
redo test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exec >&2 | |
git clean -x -d -f |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: | |
Makefile: | |
@ | |
%: FORCE | |
+./do $@ | |
.PHONY: FORCE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exec >&2 | |
DEPS=test_person.py | |
touch person.pyxbld | |
python $(which py.test) $DEPS && | |
python3 $(which py.test) $DEPS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment