Commit da5c4e10 authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

merge

parents 84bf6840 91ffc474
...@@ -274,6 +274,16 @@ class CodeWriter(TreeVisitor): ...@@ -274,6 +274,16 @@ class CodeWriter(TreeVisitor):
self.visit(node.body) self.visit(node.body)
self.dedent() self.dedent()
def visit_ReturnStatNode(self, node):
self.startline("return ")
self.visit(node.value)
self.endline()
def visit_DecoratorNode(self, node):
self.startline("@")
self.visit(node.decorator)
self.endline()
def visit_ReraiseStatNode(self, node): def visit_ReraiseStatNode(self, node):
self.line("raise") self.line("raise")
......
...@@ -64,12 +64,16 @@ class CCodeWriter: ...@@ -64,12 +64,16 @@ class CCodeWriter:
dl = code.count("{") - code.count("}") dl = code.count("{") - code.count("}")
if dl < 0: if dl < 0:
self.level += dl self.level += dl
elif dl == 0 and code.startswith('}'):
self.level -= 1
if self.bol: if self.bol:
self.indent() self.indent()
self._write(code) self._write(code)
self.bol = 0 self.bol = 0
if dl > 0: if dl > 0:
self.level += dl self.level += dl
elif dl == 0 and code.startswith('}'):
self.level += 1
def increase_indent(self): def increase_indent(self):
self.level = self.level + 1 self.level = self.level + 1
......
...@@ -1145,16 +1145,22 @@ class IteratorNode(ExprNode): ...@@ -1145,16 +1145,22 @@ class IteratorNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"if (PyList_CheckExact(%s)) { %s = 0; %s = %s; Py_INCREF(%s); }" % ( "if (PyList_CheckExact(%s) || PyTuple_CheckExact(%s)) {" % (
self.sequence.py_result(), self.sequence.py_result(),
self.sequence.py_result()))
code.putln(
"%s = 0; %s = %s; Py_INCREF(%s);" % (
self.counter.result_code, self.counter.result_code,
self.result_code, self.result_code,
self.sequence.py_result(), self.sequence.py_result(),
self.result_code)) self.result_code))
code.putln("else { %s = PyObject_GetIter(%s); %s }" % ( code.putln("} else {")
code.putln("%s = -1; %s = PyObject_GetIter(%s); %s" % (
self.counter.result_code,
self.result_code, self.result_code,
self.sequence.py_result(), self.sequence.py_result(),
code.error_goto_if_null(self.result_code, self.pos))) code.error_goto_if_null(self.result_code, self.pos)))
code.putln("}")
class NextNode(AtomicExprNode): class NextNode(AtomicExprNode):
...@@ -1173,15 +1179,19 @@ class NextNode(AtomicExprNode): ...@@ -1173,15 +1179,19 @@ class NextNode(AtomicExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
code.putln( code.putln(
"if (PyList_CheckExact(%s)) { if (%s >= PyList_GET_SIZE(%s)) break; %s = PyList_GET_ITEM(%s, %s++); Py_INCREF(%s); }" % ( "if (likely(%s != -1)) {" % self.iterator.counter.result_code)
self.iterator.py_result(), code.putln(
"if (%s >= PySequence_Fast_GET_SIZE(%s)) break;" % (
self.iterator.counter.result_code, self.iterator.counter.result_code,
self.iterator.py_result(), self.iterator.py_result()))
code.putln(
"%s = PySequence_Fast_GET_ITEM(%s, %s); Py_INCREF(%s); %s++;" % (
self.result_code, self.result_code,
self.iterator.py_result(), self.iterator.py_result(),
self.iterator.counter.result_code, self.iterator.counter.result_code,
self.result_code)) self.result_code,
code.putln("else {") self.iterator.counter.result_code))
code.putln("} else {")
code.putln( code.putln(
"%s = PyIter_Next(%s);" % ( "%s = PyIter_Next(%s);" % (
self.result_code, self.result_code,
...@@ -4019,6 +4029,59 @@ class CloneNode(CoercionNode): ...@@ -4019,6 +4029,59 @@ class CloneNode(CoercionNode):
def release_temp(self, env): def release_temp(self, env):
pass pass
class PersistentNode(ExprNode):
# A PersistentNode is like a CloneNode except it handles the temporary
# allocation itself by keeping track of the number of times it has been
# used.
subexprs = ["arg"]
temp_counter = 0
generate_counter = 0
result_code = None
def __init__(self, arg, uses):
self.pos = arg.pos
self.arg = arg
self.uses = uses
def analyse_types(self, env):
self.arg.analyse_types(env)
self.type = self.arg.type
self.result_ctype = self.arg.result_ctype
self.is_temp = 1
def calculate_result_code(self):
return self.arg.result_code
def generate_evaluation_code(self, code):
if self.generate_counter == 0:
self.arg.generate_evaluation_code(code)
code.putln("%s = %s;" % (
self.result_code, self.arg.result_as(self.ctype())))
if self.type.is_pyobject:
code.put_incref(self.result_code, self.ctype())
self.arg.generate_disposal_code(code)
self.generate_counter += 1
def generate_disposal_code(self, code):
if self.generate_counter == self.uses:
if self.type.is_pyobject:
code.put_decref_clear(self.result_code, self.ctype())
def allocate_temps(self, env, result=None):
if self.temp_counter == 0:
self.arg.allocate_temps(env)
if result is None:
self.result_code = env.allocate_temp(self.type)
else:
self.result_code = result
self.arg.release_temp(env)
self.temp_counter += 1
def release_temp(self, env):
if self.temp_counter == self.uses:
env.release_temp(self.result_code)
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
# #
# Runtime support code # Runtime support code
......
...@@ -65,6 +65,7 @@ def make_lexicon(): ...@@ -65,6 +65,7 @@ def make_lexicon():
escapeseq = Str("\\") + (two_oct | three_oct | two_hex | escapeseq = Str("\\") + (two_oct | three_oct | two_hex |
Str('u') + four_hex | Str('x') + two_hex | AnyChar) Str('u') + four_hex | Str('x') + two_hex | AnyChar)
deco = Str("@")
bra = Any("([{") bra = Any("([{")
ket = Any(")]}") ket = Any(")]}")
punct = Any(":,;+-*/|&<>=.%`~^?") punct = Any(":,;+-*/|&<>=.%`~^?")
...@@ -82,6 +83,7 @@ def make_lexicon(): ...@@ -82,6 +83,7 @@ def make_lexicon():
(longconst, 'LONG'), (longconst, 'LONG'),
(fltconst, 'FLOAT'), (fltconst, 'FLOAT'),
(imagconst, 'IMAG'), (imagconst, 'IMAG'),
(deco, 'DECORATOR'),
(punct | diphthong, TEXT), (punct | diphthong, TEXT),
(bra, Method('open_bracket_action')), (bra, Method('open_bracket_action')),
......
...@@ -356,7 +356,8 @@ def create_generate_code(context, options, result): ...@@ -356,7 +356,8 @@ def create_generate_code(context, options, result):
def create_default_pipeline(context, options, result): def create_default_pipeline(context, options, result):
from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse
from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform
from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from Optimize import FlattenInListTransform, SwitchTransform
from Buffer import BufferTransform from Buffer import BufferTransform
from ModuleNode import check_c_classes from ModuleNode import check_c_classes
...@@ -364,11 +365,14 @@ def create_default_pipeline(context, options, result): ...@@ -364,11 +365,14 @@ def create_default_pipeline(context, options, result):
create_parse(context), create_parse(context),
NormalizeTree(context), NormalizeTree(context),
PostParse(context), PostParse(context),
FlattenInListTransform(),
WithTransform(context), WithTransform(context),
DecoratorTransform(context),
AnalyseDeclarationsTransform(context), AnalyseDeclarationsTransform(context),
check_c_classes, check_c_classes,
AnalyseExpressionsTransform(context), AnalyseExpressionsTransform(context),
BufferTransform(context), BufferTransform(context),
SwitchTransform(),
# CreateClosureClasses(context), # CreateClosureClasses(context),
create_generate_code(context, options, result) create_generate_code(context, options, result)
] ]
......
...@@ -2010,6 +2010,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) { ...@@ -2010,6 +2010,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) {
types.append((ndarrtype.typeptr_cname, "numpy_getbuffer", "numpy_releasebuffer")) types.append((ndarrtype.typeptr_cname, "numpy_getbuffer", "numpy_releasebuffer"))
except KeyError: except KeyError:
pass pass
code.putln("#if PY_VERSION_HEX < 0x02060000")
code.putln("static int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) {") code.putln("static int PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) {")
if len(types) > 0: if len(types) > 0:
clause = "if" clause = "if"
...@@ -2030,6 +2031,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) { ...@@ -2030,6 +2031,7 @@ static void numpy_releasebuffer(PyObject *obj, Py_buffer *view) {
clause = "else if" clause = "else if"
code.putln("}") code.putln("}")
code.putln("") code.putln("")
code.putln("#endif")
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
......
...@@ -1235,13 +1235,19 @@ class PyArgDeclNode(Node): ...@@ -1235,13 +1235,19 @@ class PyArgDeclNode(Node):
# entry Symtab.Entry # entry Symtab.Entry
child_attrs = [] child_attrs = []
pass
class DecoratorNode(Node):
# A decorator
#
# decorator NameNode or CallNode
child_attrs = ['decorator']
class DefNode(FuncDefNode): class DefNode(FuncDefNode):
# A Python function definition. # A Python function definition.
# #
# name string the Python name of the function # name string the Python name of the function
# decorators [DecoratorNode] list of decorators
# args [CArgDeclNode] formal arguments # args [CArgDeclNode] formal arguments
# star_arg PyArgDeclNode or None * argument # star_arg PyArgDeclNode or None * argument
# starstar_arg PyArgDeclNode or None ** argument # starstar_arg PyArgDeclNode or None ** argument
...@@ -1253,13 +1259,14 @@ class DefNode(FuncDefNode): ...@@ -1253,13 +1259,14 @@ class DefNode(FuncDefNode):
# #
# assmt AssignmentNode Function construction/assignment # assmt AssignmentNode Function construction/assignment
child_attrs = ["args", "star_arg", "starstar_arg", "body"] child_attrs = ["args", "star_arg", "starstar_arg", "body", "decorators"]
assmt = None assmt = None
num_kwonly_args = 0 num_kwonly_args = 0
num_required_kw_args = 0 num_required_kw_args = 0
reqd_kw_flags_cname = "0" reqd_kw_flags_cname = "0"
is_wrapper = 0 is_wrapper = 0
decorators = None
def __init__(self, pos, **kwds): def __init__(self, pos, **kwds):
FuncDefNode.__init__(self, pos, **kwds) FuncDefNode.__init__(self, pos, **kwds)
......
import Nodes import Nodes
import ExprNodes import ExprNodes
import PyrexTypes
import Visitor import Visitor
def unwrap_node(node):
while isinstance(node, ExprNodes.PersistentNode):
node = node.arg
return node
def is_common_value(a, b): def is_common_value(a, b):
a = unwrap_node(a)
b = unwrap_node(b)
if isinstance(a, ExprNodes.NameNode) and isinstance(b, ExprNodes.NameNode): if isinstance(a, ExprNodes.NameNode) and isinstance(b, ExprNodes.NameNode):
return a.name == b.name return a.name == b.name
if isinstance(a, ExprNodes.AttributeNode) and isinstance(b, ExprNodes.AttributeNode): if isinstance(a, ExprNodes.AttributeNode) and isinstance(b, ExprNodes.AttributeNode):
...@@ -11,13 +18,20 @@ def is_common_value(a, b): ...@@ -11,13 +18,20 @@ def is_common_value(a, b):
return False return False
class SwitchTransformVisitor(Visitor.VisitorTransform): class SwitchTransform(Visitor.VisitorTransform):
"""
This transformation tries to turn long if statements into C switch statements.
The requirement is that every clause be an (or of) var == value, where the var
is common among all clauses and both var and value are not Python objects.
"""
def extract_conditions(self, cond): def extract_conditions(self, cond):
if isinstance(cond, ExprNodes.CoerceToTempNode): if isinstance(cond, ExprNodes.CoerceToTempNode):
cond = cond.arg cond = cond.arg
if isinstance(cond, ExprNodes.TypecastNode):
cond = cond.operand
if (isinstance(cond, ExprNodes.PrimaryCmpNode) if (isinstance(cond, ExprNodes.PrimaryCmpNode)
and cond.cascade is None and cond.cascade is None
and cond.operator == '==' and cond.operator == '=='
...@@ -40,14 +54,8 @@ class SwitchTransformVisitor(Visitor.VisitorTransform): ...@@ -40,14 +54,8 @@ class SwitchTransformVisitor(Visitor.VisitorTransform):
return t1, c1+c2 return t1, c1+c2
return None, None return None, None
def is_common_value(self, a, b):
if isinstance(a, ExprNodes.NameNode) and isinstance(b, ExprNodes.NameNode):
return a.name == b.name
if isinstance(a, ExprNodes.AttributeNode) and isinstance(b, ExprNodes.AttributeNode):
return not a.is_py_attr and is_common_value(a.obj, b.obj)
return False
def visit_IfStatNode(self, node): def visit_IfStatNode(self, node):
self.visitchildren(node)
if len(node.if_clauses) < 3: if len(node.if_clauses) < 3:
return node return node
common_var = None common_var = None
...@@ -56,7 +64,7 @@ class SwitchTransformVisitor(Visitor.VisitorTransform): ...@@ -56,7 +64,7 @@ class SwitchTransformVisitor(Visitor.VisitorTransform):
var, conditions = self.extract_conditions(if_clause.condition) var, conditions = self.extract_conditions(if_clause.condition)
if var is None: if var is None:
return node return node
elif common_var is not None and not self.is_common_value(var, common_var): elif common_var is not None and not is_common_value(var, common_var):
return node return node
else: else:
common_var = var common_var = var
...@@ -68,7 +76,59 @@ class SwitchTransformVisitor(Visitor.VisitorTransform): ...@@ -68,7 +76,59 @@ class SwitchTransformVisitor(Visitor.VisitorTransform):
cases = cases, cases = cases,
else_clause = node.else_clause) else_clause = node.else_clause)
def visit_Node(self, node): def visit_Node(self, node):
self.visitchildren(node) self.visitchildren(node)
return node return node
class FlattenInListTransform(Visitor.VisitorTransform):
"""
This transformation flattens "x in [val1, ..., valn]" into a sequential list
of comparisons.
"""
def visit_PrimaryCmpNode(self, node):
self.visitchildren(node)
if node.cascade is not None:
return node
elif node.operator == 'in':
conjunction = 'or'
eq_or_neq = '=='
elif node.operator == 'not_in':
conjunction = 'and'
eq_or_neq = '!='
else:
return node
if isinstance(node.operand2, ExprNodes.TupleNode) or isinstance(node.operand2, ExprNodes.ListNode):
args = node.operand2.args
if len(args) == 0:
return ExprNodes.BoolNode(pos = node.pos, value = node.operator == 'not_in')
else:
lhs = ExprNodes.PersistentNode(node.operand1, len(args))
conds = []
for arg in args:
cond = ExprNodes.PrimaryCmpNode(
pos = node.pos,
operand1 = lhs,
operator = eq_or_neq,
operand2 = arg,
cascade = None)
conds.append(ExprNodes.TypecastNode(
pos = node.pos,
operand = cond,
type = PyrexTypes.c_bint_type))
def concat(left, right):
return ExprNodes.BoolBinopNode(
pos = node.pos,
operator = conjunction,
operand1 = left,
operand2 = right)
return reduce(concat, conds)
else:
return node
def visit_Node(self, node):
self.visitchildren(node)
return node
...@@ -217,6 +217,26 @@ class WithTransform(CythonTransform): ...@@ -217,6 +217,26 @@ class WithTransform(CythonTransform):
return result.stats return result.stats
class DecoratorTransform(CythonTransform):
def visit_DefNode(self, func_node):
if not func_node.decorators:
return func_node
decorator_result = NameNode(func_node.pos, name = func_node.name)
for decorator in func_node.decorators[::-1]:
decorator_result = SimpleCallNode(
decorator.pos,
function = decorator.decorator,
args = [decorator_result])
func_name_node = NameNode(func_node.pos, name = func_node.name)
reassignment = SingleAssignmentNode(
func_node.pos,
lhs = func_name_node,
rhs = decorator_result)
return [func_node, reassignment]
class AnalyseDeclarationsTransform(CythonTransform): class AnalyseDeclarationsTransform(CythonTransform):
def __call__(self, root): def __call__(self, root):
......
...@@ -975,10 +975,13 @@ def p_from_import_statement(s, first_statement = 0): ...@@ -975,10 +975,13 @@ def p_from_import_statement(s, first_statement = 0):
s.error("from __future__ imports must occur at the beginning of the file") s.error("from __future__ imports must occur at the beginning of the file")
else: else:
for (name_pos, name, as_name, kind) in imported_names: for (name_pos, name, as_name, kind) in imported_names:
if name == "braces":
s.error("not a chance", name_pos)
break
try: try:
directive = getattr(Future, name) directive = getattr(Future, name)
except AttributeError: except AttributeError:
s.error("future feature %s is not defined" % name) s.error("future feature %s is not defined" % name, name_pos)
break break
s.context.future_directives.add(directive) s.context.future_directives.add(directive)
return Nodes.PassStatNode(pos) return Nodes.PassStatNode(pos)
...@@ -1369,6 +1372,14 @@ def p_statement(s, ctx, first_statement = 0): ...@@ -1369,6 +1372,14 @@ def p_statement(s, ctx, first_statement = 0):
return p_DEF_statement(s) return p_DEF_statement(s)
elif s.sy == 'IF': elif s.sy == 'IF':
return p_IF_statement(s, ctx) return p_IF_statement(s, ctx)
elif s.sy == 'DECORATOR':
if ctx.level not in ('module', 'class', 'c_class', 'property'):
s.error('decorator not allowed here')
s.level = ctx.level
decorators = p_decorators(s)
if s.sy != 'def':
s.error("Decorators can only be followed by functions ")
return p_def_statement(s, decorators)
else: else:
overridable = 0 overridable = 0
if s.sy == 'cdef': if s.sy == 'cdef':
...@@ -1602,9 +1613,9 @@ def p_c_simple_base_type(s, self_flag, nonempty): ...@@ -1602,9 +1613,9 @@ def p_c_simple_base_type(s, self_flag, nonempty):
# Treat trailing [] on type as buffer access # Treat trailing [] on type as buffer access
if s.sy == '[': if 0: # s.sy == '[':
if is_basic: if is_basic:
p.error("Basic C types do not support buffer access") s.error("Basic C types do not support buffer access")
return p_buffer_access(s, type_node) return p_buffer_access(s, type_node)
else: else:
return type_node return type_node
...@@ -2100,7 +2111,21 @@ def p_ctypedef_statement(s, ctx): ...@@ -2100,7 +2111,21 @@ def p_ctypedef_statement(s, ctx):
declarator = declarator, visibility = visibility, declarator = declarator, visibility = visibility,
in_pxd = ctx.level == 'module_pxd') in_pxd = ctx.level == 'module_pxd')
def p_def_statement(s): def p_decorators(s):
decorators = []
while s.sy == 'DECORATOR':
pos = s.position()
s.next()
decorator = ExprNodes.NameNode(
pos, name = Utils.EncodedString(
p_dotted_name(s, as_allowed=0)[2] ))
if s.sy == '(':
decorator = p_call(s, decorator)
decorators.append(Nodes.DecoratorNode(pos, decorator=decorator))
s.expect_newline("Expected a newline after decorator")
return decorators
def p_def_statement(s, decorators=None):
# s.sy == 'def' # s.sy == 'def'
pos = s.position() pos = s.position()
s.next() s.next()
...@@ -2129,7 +2154,7 @@ def p_def_statement(s): ...@@ -2129,7 +2154,7 @@ def p_def_statement(s):
doc, body = p_suite(s, Ctx(level = 'function'), with_doc = 1) doc, body = p_suite(s, Ctx(level = 'function'), with_doc = 1)
return Nodes.DefNode(pos, name = name, args = args, return Nodes.DefNode(pos, name = name, args = args,
star_arg = star_arg, starstar_arg = starstar_arg, star_arg = star_arg, starstar_arg = starstar_arg,
doc = doc, body = body) doc = doc, body = body, decorators = decorators)
def p_py_arg_decl(s): def p_py_arg_decl(s):
pos = s.position() pos = s.position()
......
import unittest
from Cython.TestUtils import TransformTest
from Cython.Compiler.ParseTreeTransforms import DecoratorTransform
class TestDecorator(TransformTest):
def test_decorator(self):
t = self.run_pipeline([DecoratorTransform(None)], u"""
def decorator(fun):
return fun
@decorator
def decorated():
pass
""")
self.assertCode(u"""
def decorator(fun):
return fun
def decorated():
pass
decorated = decorator(decorated)
""", t)
if __name__ == '__main__':
unittest.main()
...@@ -7,6 +7,7 @@ from Cython.Distutils import build_ext ...@@ -7,6 +7,7 @@ from Cython.Distutils import build_ext
ext_modules=[ ext_modules=[
Extension("primes", ["primes.pyx"]), Extension("primes", ["primes.pyx"]),
Extension("spam", ["spam.pyx"]), Extension("spam", ["spam.pyx"]),
# Extension("optargs", ["optargs.pyx"], language = "c++"),
] ]
for file in glob.glob("*.pyx"): for file in glob.glob("*.pyx"):
......
SLIDES=$(subst .txt,.html,$(wildcard *.txt)) SLIDES=$(subst .txt,.html,$(wildcard *.txt))
SOURCES=$(subst .py,.c,$(subst .pyx,.c,$(wildcard */*.py */*.pyx)))
slides: $(SLIDES) slides: $(SLIDES) $(SOURCES)
%.html: %.txt %.html: %.txt
rst2s5 --current-slide --language=en $< $@ rst2s5 --current-slide --language=en $< $@
%.c: %.py
cython --annotate $<
%.c: %.pyx
cython --annotate $<
clean: clean:
rm -f *~ $(SLIDES) rm -f *~ $(SLIDES) $(SOURCES) $(subst .c,.html,$(SOURCES))
==============================================
The Cython Compiler for C-Extensions in Python
==============================================
Dr. Stefan Behnel
-----------------
.. class:: center
http://www.cython.org/
cython-dev@codespeak.net
.. footer:: Dr. Stefan Behnel, EuroPython 2008, Vilnius/Lietuva
.. include:: <s5defs.txt>
About myself
============
* Passionate Python developer since 2002
* after Basic, Logo, Pascal, Prolog, Scheme, Java, ...
* CS studies in Germany, Ireland, France
* PhD in distributed systems in 2007
* Language design for self-organising systems
* Darmstadt University of Technologies, Germany
* Current occupations:
* Employed by Senacor Technologies AG, Germany
* IT transformations, SOA design, Java-Development, ...
* »lxml« OpenSource XML toolkit for Python
* http://codespeak.net/lxml/
* Cython
What is Cython?
===============
Cython is
* an Open-Source project
* http://cython.org
* a Python compiler (almost)
* an enhanced, optimising fork of Pyrex
* an extended Python language for
* writing fast Python extension modules
* interfacing Python with C libraries
A little bit of history
=======================
* April 2002: release of Pyrex 0.1 by Greg Ewing
* Greg considers Pyrex a language in design phase
* Pyrex became a key language for many projects
* over the years, many people patched their Pyrex
* not all patches were answered by Greg (not in time)
* minor forks and enhanced branches followed
* March 2006: my fork of Pyrex for »lxml« XML toolkit
* November 2006: »SageX« fork of Pyrex
* by Robert Bradshaw, William Stein (Univ. Seattle, USA)
* context: »Sage«, a free mathematics software package
* 28th July 2007: official Cython launch
* integration of lxml's Pyrex fork into SageX
* the rest is in http://hg.cython.org/cython-devel/
Major Cython Developers
=======================
* Robert Bradshaw and Stefan Behnel
* lead developers
* Greg Ewing
* main developer and maintainer of Pyrex
* we happily share code and discuss ideas
* Dag Sverre Seljebotn
* Google Summer-of-Code developer
* NumPy integration, many ideas and enhancements
* many, *many* others - see
* http://cython.org/
* the mailing list archives of Cython and Pyrex
How to use Cython
=================
* you write Python code
* Cython translates it into C code
* your C compiler builds a shared library for CPython
* you import your module
* Cython has support for
* compile-time includes/imports
* with dependency tracking
* distutils
* *optionally* compile Python code from setup.py!
Example: compiling Python code
==============================
.. sourcecode:: bash
$ cat worker.py
.. sourcecode:: python
class HardWorker(object):
u"Almost Sisyphus"
def __init__(self, task):
self.task = task
def work_hard(self):
for i in range(100):
self.task()
.. sourcecode:: bash
$ cython worker.py
* translates to 842 line `.c file <ep2008/worker.c>`_ (Cython 0.9.8)
* lots of portability #define's
* tons of helpful C comments with Python code snippets
* a lot of code that you don't want to write yourself
Portable Code
=============
* Cython compiler generates C code that compiles
* with all major compilers (C and C++)
* on all major platforms
* in Python 2.3 to 3.0 beta1
* Cython language syntax follows Python 2.6
* optional Python 3 syntax support is on TODO list
* get involved to get it quicker!
\... the fastest way to port Python 2 code to Py3 ;-)
Python 2 feature support
========================
* most of Python 2 syntax is supported
* top-level classes and functions
* exceptions
* object operations, arithmetic, ...
* plus some Py3/2.6 features:
* keyword-only arguments
* unicode literals via ``__future__`` import
* in recent developer branch:
* ``with`` statement
* closures (i.e. local classes and functions) are close!
Speed
=====
Cython generates very efficient C code
* according to PyBench:
* conditions and loops run 2-8x faster than in Py2.5
* most benchmarks run 30%-80% faster
* overall more than 30% faster for plain Python code
* optional type declarations
* let Cython generate plain C instead of C-API calls
* make code several times faster than the above
Type declarations
=================
* »cdef« keyword declares
* local variables with C types
* functions with C signatures
* classes as builtin extension types
* Example::
def stupid_lower_case(char* s):
cdef Py_ssize_t size, i
size = len(s)
for i in range(size):
if s[i] >= 'A' and s[i] <= 'Z':
s[i] += 'a' - 'A'
return s
Why is this stupid?
===================
Ask Cython!
.. sourcecode:: bash
$ cat stupidlowercase.py
::
def stupid_lower_case(char* s):
cdef Py_ssize_t size, i
size = len(s)
for i in range(size):
if s[i] >= 'A' and s[i] <= 'Z':
s[i] += 'a' - 'A'
return s
.. sourcecode:: bash
$ cython --annotate stupidlowercase.py
=> `stupidlowercase.html <ep2008/stupidlowercase.html>`_
Calling C functions
===================
* Example::
cdef extern from "Python.h":
# copied from the Python C-API docs:
object PyUnicode_DecodeASCII(
char* s, Py_ssize_t size, char* errors)
cdef extern from "string.h":
int strlen(char *s)
cdef slightly_better_lower_case(char* s):
cdef Py_ssize_t i, size = strlen(s)
for i in range(size):
if s[i] >= 'A' and s[i] <= 'Z':
s[i] += 'a' - 'A'
return PyUnicode_DecodeASCII(s, size, NULL)
Features in work
================
* Dynamic classes and functions with closures
.. sourcecode:: python
def factory(a,b):
def closure_function(c):
return a+b+c
return closure_function
* Native support for new ``buffer`` protocol
* part of NumPy integration by Dag Seljebotn
.. sourcecode:: python
def inplace_negative_grayscale_image(
ndarray[unsigned char, 2] image):
cdef int i, j
for i in range(image.shape[0]):
for j in range(image.shape[1]):
arr[i, j] = 255 - arr[i, j]
Huge pile of great ideas
========================
* Cython Enhancement Proposals (CEPs)
* http://wiki.cython.org/enhancements
* native pickle support for extension classes
* meta-programming facilities
* type inference strategies
* compile-time assertions for optimisations
* object-like C-array handling
* improved C++ integration
* ...
Conclusion
==========
* Cython is a tool for
* efficiently translating Python code to C
* easily interfacing to external C libraries
* Use it to
* speed up existing Python modules
* concentrate on optimisations, not rewrites!
* write C extensions for CPython
* don't change the language just to get fast code!
* wrap C libraries *in Python*
* concentrate on the mapping, not the glue!
... but Cython is also
======================
* a great project
* a very open playground for great ideas!
Cython
======
**Cython**
**C-Extensions in Python**
\... use it, and join the project!
http://cython.org/
def stupid_lower_case(char* s):
cdef Py_ssize_t size, i
size = len(s)
for i in range(size):
if s[i] >= 'A' and s[i] <= 'Z':
s[i] += 'a' - 'A'
return s
class HardWorker(object):
u"Almost Sisyphus"
def __init__(self, task):
self.task = task
def work_hard(self):
for i in range(100):
self.task()
/* Following are the presentation styles -- edit away! */ /* Following are the presentation styles -- edit away! */
/* body {background: #FFF url(cython.png) 1em 1em no-repeat; color: #000; font-size: 2em;} */ /* body {background: #FFF url(cython.png) 1em 1em no-repeat; color: #000; font-size: 2em;} */
:link, :visited {text-decoration: none; color: #646464;} :link, :visited {text-decoration: none; color: #545454;}
#controls :active {color: #646464 !important;} #controls :active {color: #545454 !important;}
#controls :focus {outline: 1px dotted #646464;} #controls :focus {outline: 1px dotted #545454;}
h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;}
ul, pre {margin: 0; line-height: 1em;} ul, pre {margin: 0; line-height: 1em;}
html, body {margin: 0; padding: 0;} html, body {margin: 0; padding: 0;}
...@@ -19,6 +19,8 @@ blockquote b i {font-style: italic;} ...@@ -19,6 +19,8 @@ blockquote b i {font-style: italic;}
kbd {font-weight: bold; font-size: 1em;} kbd {font-weight: bold; font-size: 1em;}
sup {font-size: smaller; line-height: 1px;} sup {font-size: smaller; line-height: 1px;}
.slide {padding-top: 3em; }
.slide code {padding: 2px 0.25em; font-weight: bold; color: #533;} .slide code {padding: 2px 0.25em; font-weight: bold; color: #533;}
.slide code.bad, code del {color: red;} .slide code.bad, code del {color: red;}
.slide code.old {color: silver;} .slide code.old {color: silver;}
...@@ -32,19 +34,19 @@ sup {font-size: smaller; line-height: 1px;} ...@@ -32,19 +34,19 @@ sup {font-size: smaller; line-height: 1px;}
.slide ul ul li {margin: .4em; font-size: 85%; list-style: square;} .slide ul ul li {margin: .4em; font-size: 85%; list-style: square;}
.slide img.leader {display: block; margin: 0 auto;} .slide img.leader {display: block; margin: 0 auto;}
div#header, div#footer {background: #f0f0f0; color: #646464; div#header, div#footer {background: #f0f0f0; color: #545454;
font-family: Verdana, Helvetica, sans-serif; padding: 0;} font-family: Verdana, Helvetica, sans-serif; padding: 0;}
div#header {background: #f0f0f0 url(cython-logo64.png) 1ex 0.6ex no-repeat; div#header {background: #f0f0f0 url(cython-logo64.png) 1ex 0.2ex no-repeat;
line-height: 1px; border-bottom: solid #646464 4px;} line-height: 1px; border-bottom: solid #545454 4px; height: 2.4em;}
div#footer {font-size: 0.5em; font-weight: bold; padding: 0.6em 0; div#footer {font-size: 0.5em; font-weight: bold; padding: 0.6em 0;
border-top: solid #646464 3px;} border-top: solid #545454 3px;}
#footer h1, #footer h2 {display: block; padding: 0 1em;} #footer h1, #footer h2 {display: block; padding: 0 1.5ex;}
#footer h2 {font-style: italic;} #footer h2 {font-style: italic;}
#footer a {color: #646464;} #footer a {color: #545454;}
div.long {font-size: 0.75em;} div.long {font-size: 0.75em;}
.slide h1 {position: absolute; top: 0.4em; left: 87px; z-index: 1; .slide h1 {position: absolute; top: 0.2em; left: 87px; z-index: 1;
margin: 0; padding: 0.2em 0 0 25px; white-space: nowrap; margin: 0; padding: 0.3ex 0 0 25px; white-space: nowrap;
font: bold 150%/1em Helvetica, sans-serif; /* text-transform: capitalize; */ font: bold 150%/1em Helvetica, sans-serif; /* text-transform: capitalize; */
color: #646464; background: #f0f0f0;} color: #646464; background: #f0f0f0;}
.slide h3 {font-size: 130%;} .slide h3 {font-size: 130%;}
...@@ -58,12 +60,12 @@ html>body div#controls {position: fixed; padding: 0 0 1em 0; ...@@ -58,12 +60,12 @@ html>body div#controls {position: fixed; padding: 0 0 1em 0;
div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; div#controls form {position: absolute; bottom: 0; right: 0; width: 100%;
margin: 0; padding: 0;} margin: 0; padding: 0;}
#controls #navLinks a {padding: 0; margin: 0 0.5em; #controls #navLinks a {padding: 0; margin: 0 0.5em;
background: #f0f0f0; border: none; color: #646464; background: #f0f0f0; border: none; color: #545454;
cursor: pointer;} cursor: pointer;}
#controls #navList {height: 1em;} #controls #navList {height: 1em;}
#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; background: #f0f0f0; color: black;} #controls #navList #jumplist {position: absolute; bottom: 0; right: 0; background: #f0f0f0; color: black;}
#currentSlide {text-align: center; font-size: 0.5em; color: #646464; left: 90%; bottom: 2px;} #currentSlide {text-align: center; font-size: 0.5em; color: #545454; left: 90%; bottom: 2px;}
#slide0 {padding-top: 3em; font-size: 90%;} #slide0 {padding-top: 3em; font-size: 90%;}
#slide0 h1 {position: static; margin: 1em 0 0; padding: 0; #slide0 h1 {position: static; margin: 1em 0 0; padding: 0;
...@@ -80,7 +82,7 @@ ul.urls {list-style: none; display: inline; margin: 0;} ...@@ -80,7 +82,7 @@ ul.urls {list-style: none; display: inline; margin: 0;}
html>body .external {border-bottom: none;} html>body .external {border-bottom: none;}
/* .external:after {content: " \274F"; font-size: smaller; color: #7B7;} */ /* .external:after {content: " \274F"; font-size: smaller; color: #7B7;} */
/* .incremental, .incremental *, .incremental *:after {color: #646464; visibility: visible;} */ /* .incremental, .incremental *, .incremental *:after {color: #545454; visibility: visible;} */
.incremental, .incremental *, .incremental *:after {visibility: hidden;} .incremental, .incremental *, .incremental *:after {visibility: hidden;}
img.incremental {visibility: hidden;} img.incremental {visibility: hidden;}
.slide .current {color: #B02;} .slide .current {color: #B02;}
......
/* The following rule is necessary to have all slides appear in print! DO NOT REMOVE IT! */.slide, ul {page-break-inside: avoid; visibility: visible !important;}h1 {page-break-after: avoid;} body {font-size: 12pt; background: white;}* {color: black;} #slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;}#slide0 h3 {margin: 0; padding: 0;}#slide0 h4 {margin: 0 0 0.5em; padding: 0;}#slide0 {margin-bottom: 3em;} h1 {border-top: 2pt solid gray; border-bottom: 1px dotted silver;}.extra {background: transparent !important;}div.extra, pre.extra, .example {font-size: 10pt; color: #333;}ul.extra a {font-weight: bold;}p.example {display: none;} #header {display: none;}#footer h1 {margin: 0; border-bottom: 1px solid; color: gray; font-style: italic;}#footer h2, #controls {display: none;} /* The following rule keeps the layout stuff out of print. Remove at your own risk! */.layout, .layout * {display: none !important;} /* The following rule is necessary to have all slides appear in print! DO NOT REMOVE IT! */
\ No newline at end of file .slide, ul {page-break-inside: avoid; visibility: visible !important;}
h1 {page-break-after: avoid;}
body {font-size: 12pt; background: white;}
* {color: black;}
#slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;}
#slide0 h3 {margin: 0; padding: 0;}
#slide0 h4 {margin: 0 0 0.5em; padding: 0;}
#slide0 {margin-bottom: 3em;}
h1 {border-top: 2pt solid gray; border-bottom: 1px dotted silver;}
.extra {background: transparent !important;}
div.extra, pre.extra, .example {font-size: 10pt; color: #333;}
ul.extra a {font-weight: bold;}
p.example {display: none;}
#header {display: none;}
#footer h1 {margin: 0; border-bottom: 1px solid; color: gray; font-style: italic;}
#footer h2, #controls {display: none;}
/* The following rule keeps the layout stuff out of print. Remove at your own risk! */
.layout, .layout * {display: none !important;}
...@@ -11,11 +11,11 @@ testclean: ...@@ -11,11 +11,11 @@ testclean:
rm -fr BUILD rm -fr BUILD
test: testclean test: testclean
${PYTHON} runtests.py ${PYTHON} runtests.py -vv
test3: testclean test3: testclean
${PYTHON} runtests.py --no-cleanup ${PYTHON} runtests.py --no-cleanup
python3.0 runtests.py --no-cython python3.0 runtests.py -vv --no-cython
s5: s5:
$(MAKE) -C Doc/s5 slides $(MAKE) -C Doc/s5 slides
...@@ -9,8 +9,8 @@ from distutils.core import Extension ...@@ -9,8 +9,8 @@ from distutils.core import Extension
from distutils.command.build_ext import build_ext from distutils.command.build_ext import build_ext
distutils_distro = Distribution() distutils_distro = Distribution()
TEST_DIRS = ['compile', 'errors', 'run'] TEST_DIRS = ['compile', 'errors', 'run', 'pyregr']
TEST_RUN_DIRS = ['run'] TEST_RUN_DIRS = ['run', 'pyregr']
INCLUDE_DIRS = [ d for d in os.getenv('INCLUDE', '').split(os.pathsep) if d ] INCLUDE_DIRS = [ d for d in os.getenv('INCLUDE', '').split(os.pathsep) if d ]
CFLAGS = os.getenv('CFLAGS', '').split() CFLAGS = os.getenv('CFLAGS', '').split()
...@@ -78,15 +78,21 @@ class TestBuilder(object): ...@@ -78,15 +78,21 @@ class TestBuilder(object):
filenames = os.listdir(path) filenames = os.listdir(path)
filenames.sort() filenames.sort()
for filename in filenames: for filename in filenames:
if not filename.endswith(".pyx"): if not (filename.endswith(".pyx") or filename.endswith(".py")):
continue continue
module = filename[:-4] if context == 'pyregr' and not filename.startswith('test_'):
continue
module = os.path.splitext(filename)[0]
fqmodule = "%s.%s" % (context, module) fqmodule = "%s.%s" % (context, module)
if not [ 1 for match in self.selectors if not [ 1 for match in self.selectors
if match(fqmodule) ]: if match(fqmodule) ]:
continue continue
if context in TEST_RUN_DIRS: if context in TEST_RUN_DIRS:
test = CythonRunTestCase( if module.startswith("test_"):
build_test = CythonUnitTestCase
else:
build_test = CythonRunTestCase
test = build_test(
path, workdir, module, path, workdir, module,
annotate=self.annotate, annotate=self.annotate,
cleanup_workdir=self.cleanup_workdir) cleanup_workdir=self.cleanup_workdir)
...@@ -133,11 +139,21 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -133,11 +139,21 @@ class CythonCompileTestCase(unittest.TestCase):
os.makedirs(self.workdir) os.makedirs(self.workdir)
def runTest(self): def runTest(self):
self.runCompileTest()
def runCompileTest(self):
self.compile(self.directory, self.module, self.workdir, self.compile(self.directory, self.module, self.workdir,
self.directory, self.expect_errors, self.annotate) self.directory, self.expect_errors, self.annotate)
def find_module_source_file(self, source_file):
if not os.path.exists(source_file):
source_file = source_file[:-1]
return source_file
def split_source_and_output(self, directory, module, workdir): def split_source_and_output(self, directory, module, workdir):
source_and_output = open(os.path.join(directory, module + '.pyx'), 'rU') source_file = os.path.join(directory, module) + '.pyx'
source_and_output = open(
self.find_module_source_file(source_file), 'rU')
out = open(os.path.join(workdir, module + '.pyx'), 'w') out = open(os.path.join(workdir, module + '.pyx'), 'w')
for line in source_and_output: for line in source_and_output:
last_line = line last_line = line
...@@ -157,7 +173,8 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -157,7 +173,8 @@ class CythonCompileTestCase(unittest.TestCase):
include_dirs = INCLUDE_DIRS[:] include_dirs = INCLUDE_DIRS[:]
if incdir: if incdir:
include_dirs.append(incdir) include_dirs.append(incdir)
source = os.path.join(directory, module + '.pyx') source = self.find_module_source_file(
os.path.join(directory, module + '.pyx'))
target = os.path.join(targetdir, module + '.c') target = os.path.join(targetdir, module + '.c')
options = CompilationOptions( options = CompilationOptions(
pyrex_default_options, pyrex_default_options,
...@@ -228,7 +245,7 @@ class CythonRunTestCase(CythonCompileTestCase): ...@@ -228,7 +245,7 @@ class CythonRunTestCase(CythonCompileTestCase):
result = self.defaultTestResult() result = self.defaultTestResult()
result.startTest(self) result.startTest(self)
try: try:
self.runTest() self.runCompileTest()
doctest.DocTestSuite(self.module).run(result) doctest.DocTestSuite(self.module).run(result)
except Exception: except Exception:
result.addError(self, sys.exc_info()) result.addError(self, sys.exc_info())
...@@ -238,6 +255,25 @@ class CythonRunTestCase(CythonCompileTestCase): ...@@ -238,6 +255,25 @@ class CythonRunTestCase(CythonCompileTestCase):
except Exception: except Exception:
pass pass
class CythonUnitTestCase(CythonCompileTestCase):
def shortDescription(self):
return "compiling and running unit tests in " + self.module
def run(self, result=None):
if result is None:
result = self.defaultTestResult()
result.startTest(self)
try:
self.runCompileTest()
unittest.defaultTestLoader.loadTestsFromName(self.module).run(result)
except Exception:
result.addError(self, sys.exc_info())
result.stopTest(self)
try:
self.tearDown()
except Exception:
pass
def collect_unittests(path, suite, selectors): def collect_unittests(path, suite, selectors):
def file_matches(filename): def file_matches(filename):
return filename.startswith("Test") and filename.endswith(".py") return filename.startswith("Test") and filename.endswith(".py")
......
def f(a, b):
assert a, a+b
cdef class Parrot:
cdef describe(self):
print "This is a parrot."
cdef action(self):
print "Polly wants a cracker!"
cdef extern from "foo.h":
ctypedef long long big_t
cdef void spam(big_t b)
spam(grail)
cdef int f() except -1:
cdef type t
cdef object x
t = buffer
t = enumerate
t = file
t = float
t = int
t = long
t = open
t = property
t = str
t = tuple
t = xrange
x = True
x = False
x = Ellipsis
x = Exception
x = StopIteration
x = StandardError
x = ArithmeticError
x = LookupError
x = AssertionError
x = AssertionError
x = EOFError
x = FloatingPointError
x = EnvironmentError
x = IOError
x = OSError
x = ImportError
x = IndexError
x = KeyError
x = KeyboardInterrupt
x = MemoryError
x = NameError
x = OverflowError
x = RuntimeError
x = NotImplementedError
x = SyntaxError
x = IndentationError
x = TabError
x = ReferenceError
x = SystemError
x = SystemExit
x = TypeError
x = UnboundLocalError
x = UnicodeError
x = UnicodeEncodeError
x = UnicodeDecodeError
x = UnicodeTranslateError
x = ValueError
x = ZeroDivisionError
x = MemoryErrorInst
x = Warning
x = UserWarning
x = DeprecationWarning
x = PendingDeprecationWarning
x = SyntaxWarning
#x = OverflowWarning # Does not seem to exist in 2.5
x = RuntimeWarning
x = FutureWarning
typecheck(x, Exception)
try:
pass
except ValueError:
pass
cdef int f() except -1:
cdef dict d
cdef object x, z
cdef int i
z = dict
d = dict(x)
d = dict(*x)
d.clear()
z = d.copy()
z = d.items()
z = d.keys()
z = d.values()
d.merge(x, i)
d.update(x)
d.merge_pairs(x, i)
cdef int f() except -1:
cdef list l
cdef object x, y, z
z = list
l = list(x)
l = list(*y)
z = l.insert
l.insert(17, 42)
l.append(88)
l.sort()
l.reverse()
z = l.as_tuple()
cdef int f() except -1:
cdef slice s
cdef object z
cdef int i
z = slice
s = slice(1, 2, 3)
z = slice.indices()
i = s.start
i = s.stop
i = s.step
cdef int f() except -1:
cdef type t1, t2
cdef object x
cdef int b
b = typecheck(x, t1)
b = issubtype(t1, t2)
cdef void foo():
cdef int i, j, k
i = j = k
a = b = c
i = j = c
a = b = k
(a, b), c = (d, e), f = (x, y), z
# a, b = p, q = x, y
\ No newline at end of file
cdef extern from "cdefemptysue.h":
cdef struct spam:
pass
ctypedef union eggs:
pass
cdef enum ham:
pass
cdef extern spam s
cdef extern eggs e
cdef extern ham h
cdef extern from "cheese.h":
ctypedef int camembert
struct roquefort:
int x
char *swiss
void cheddar()
class external.runny [object runny_obj]:
cdef int a
def __init__(self):
pass
cdef runny r
r = x
r.a = 42
cdef extern from "cheese.h":
pass
cdef int f():
pass
cdef char *g(int k, float z):
pass
cimport spam
cimport pkg.eggs
cdef spam.Spam yummy
cdef pkg.eggs.Eggs fried
spam.eat(yummy)
spam.tons = 3.14
ova = pkg.eggs
fried = pkg.eggs.Eggs()
from spam cimport Spam
from pkg.eggs cimport Eggs as ova
cdef extern Spam yummy
cdef ova fried
fried = None
from package.inpackage cimport Spam
cdef Spam s2
from cexportfunc cimport f, g
cdef extern from "ctypedefextern.h":
ctypedef int some_int
ctypedef some_int *some_ptr
cdef void spam():
cdef some_int i
cdef some_ptr p
p[0] = i
cdef class Spam:
answer = 42
cdef object f(object x) nogil:
pass
cdef void g(int x) nogil:
cdef object z
z = None
cdef void h(int x) nogil:
p()
cdef object p() nogil:
pass
cdef void r() nogil:
q()
cdef void (*fp)()
cdef void (*fq)() nogil
cdef extern void u()
cdef object m():
global fp, fq
cdef object x, y, obj
cdef int i, j, k
global fred
q()
with nogil:
r()
q()
i = 42
obj = None
17L
7j
asdf
`"Hello"`
import fred
from fred import obj
for x in obj:
pass
obj[i]
obj[i:j]
obj[i:j:k]
obj.fred
(x, y)
[x, y]
{x: y}
obj and x
t(obj)
f(42)
x + obj
-obj
x = y = obj
x, y = y, x
obj[i] = x
obj.fred = x
print obj
del fred
return obj
raise obj
if obj:
pass
while obj:
pass
for x <= obj <= y:
pass
try:
pass
except:
pass
try:
pass
finally:
pass
fq = u
fq = fp
cdef void q():
pass
cdef class C:
pass
cdef void t(C c) nogil:
pass
cdef extern from "foo.h":
int fred()
cdef extern from "externsue.h":
enum Eggs:
runny, firm, hard
struct Spam:
int i
union Soviet:
char c
cdef extern Eggs e
cdef extern Spam s
cdef extern Soviet u
cdef void tomato():
global e
e = runny
e = firm
e = hard
cdef class Widget:
pass
cdef class Container:
pass
cdef Widget w
cdef Container c
w.parent = c
cdef class Spam:
cdef public object eggs
def __getattr__(self, name):
print "Spam getattr:", name
cdef int f() except -1:
g = getattr3
cdef public int grail
cdef public spam(int servings):
pass
cdef public class sandwich [object sandwich, type sandwich_Type]:
cdef int tomato
cdef float lettuce
include "i_public.pxi"
cdef struct S:
int q
cdef int f() except -1:
cdef int i, j, k
cdef float x, y, z
cdef object a, b, c, d, e
cdef int m[3]
cdef S s
global g
i += j + k
x += y + z
x += i
a += b + c
g += a
m[i] += j
a[i] += b + c
a[b + c] += d
(a + b)[c] += d
a[i : j] += b
(a + b)[i : j] += c
a.b += c + d
(a + b).c += d
s.q += i
cdef int f() except -1:
cdef object a, b
cdef char *p
a += b
a -= b
a *= b
a /= b
a %= b
a **= b
a <<= b
a >>= b
a &= b
a ^= b
a |= b
p += 42
p -= 42
p += a
cdef int f() except -1:
cdef object x, y, z
cdef int i
cdef unsigned int ui
z = x[y]
z = x[i]
x[y] = z
x[i] = z
z = x[ui]
x[ui] = z
cdef api float f(float x):
return 0.5 * x * x
cdef int f(int x):
return x * x
cdef int g(int x):
return 5 * x
def f(a, *p, **n):
pass
cdef int f() except -1:
cdef int i, x, y
for x < i < y:
pass
cdef int f() except -1:
cdef int i, x, y
for i from x < i < y:
pass
cimport spam, eggs
cdef extern spam.Spam yummy
cdef eggs.Eggs fried
fried = None
from spam cimport Spam
from eggs cimport Eggs
cdef extern Spam yummy
cdef Eggs fried
fried = None
def f():
cdef list l
l = list()
l.append("second")
l.insert(0, "first")
return l
cdef extern from "l_capi_api.h":
float f(float)
int import_l_capi() except -1
def test():
print f(3.1415)
import_l_capi()
cimport l_cfuncexport
from l_cfuncexport cimport g
print l_cfuncexport.f(42)
print g(42)
class Spam:
"""Spam, glorious spam!"""
cdef int tomato() except -1:
print "Entering tomato"
raise Exception("Eject! Eject! Eject!")
print "Leaving tomato"
cdef void sandwich():
print "Entering sandwich"
tomato()
print "Leaving sandwich"
def snack():
print "Entering snack"
tomato()
print "Leaving snack"
def lunch():
print "Entering lunch"
sandwich()
print "Leaving lunch"
cdef class SpamDish:
cdef int spam
cdef void describe(self):
print "This dish contains", self.spam, "tons of spam."
cdef class FancySpamDish(SpamDish):
cdef int lettuce
cdef void describe(self):
print "This dish contains", self.spam, "tons of spam",
print "and", self.lettuce, "milligrams of lettuce."
cdef void describe_dish(SpamDish d):
d.describe()
def test():
cdef SpamDish s
cdef FancySpamDish ss
s = SpamDish()
s.spam = 42
ss = FancySpamDish()
ss.spam = 88
ss.lettuce = 5
describe_dish(s)
describe_dish(ss)
from b_extimpinherit cimport Parrot
cdef class Norwegian(Parrot):
cdef action(self):
print "This parrot is resting."
cdef plumage(self):
print "Lovely plumage!"
def main():
cdef Parrot p
cdef Norwegian n
p = Parrot()
n = Norwegian()
print "Parrot:"
p.describe()
p.action()
print "Norwegian:"
n.describe()
n.action()
n.plumage()
cdef class Parrot:
cdef object plumage
def __init__(self):
self.plumage = "yellow"
def describe(self):
print "This bird has lovely", self.plumage, "plumage."
cdef class Norwegian(Parrot):
def __init__(self):
self.plumage = "blue"
cdef class Spam:
cdef public int tons
cdef readonly float tastiness
cdef int temperature
def __init__(self, tons, tastiness, temperature):
self.tons = tons
self.tastiness = tastiness
self.temperature = temperature
def get_temperature(self):
return self.temperature
cdef extern from "numeric.h":
struct PyArray_Descr:
int type_num, elsize
char type
ctypedef class Numeric.ArrayType [object PyArrayObject]:
cdef char *data
cdef int nd
cdef int *dimensions, *strides
cdef object base
cdef PyArray_Descr *descr
cdef int flags
def ogle(ArrayType a):
print "No. of dimensions:", a.nd
print " Dim Value"
for i in range(a.nd):
print "%5d %5d" % (i, a.dimensions[i])
print "flags:", a.flags
print "Type no.", a.descr.type_num
print "Element size:", a.descr.elsize
cdef class CheeseShop:
cdef object cheeses
def __cinit__(self):
self.cheeses = []
property cheese:
"A senseless waste of a property."
def __get__(self):
return "We don't have: %s" % self.cheeses
def __set__(self, value):
self.cheeses.append(value)
def __del__(self):
del self.cheeses[:]
cdef class Animal:
cdef object __weakref__
cdef public object name
def test(obj, attr, dflt):
return getattr3(obj, attr, dflt)
import spam
print "Imported spam"
print dir(spam)
import sys
print "Imported sys"
print sys
cdef class Parrot:
cdef void describe(self):
print "This parrot is resting."
cdef class Norwegian(Parrot):
cdef void describe(self):
Parrot.describe(self)
print "Lovely plumage!"
cdef Parrot p1, p2
p1 = Parrot()
p2 = Norwegian()
p1.describe()
p2.describe()
def pd(d):
l = []
i = d.items()
i.sort()
for kv in i:
l.append("%r: %r" % kv)
return "{%s}" % ", ".join(l)
def c(a, b, c):
print "a =", a, "b =", b, "c =", c
def d(a, b, *, c = 88):
print "a =", a, "b =", b, "c =", c
def e(a, b, c = 88, **kwds):
print "a =", a, "b =", b, "c =", c, "kwds =", pd(kwds)
def f(a, b, *, c, d = 42):
print "a =", a, "b =", b, "c =", c, "d =", d
def g(a, b, *, c, d = 42, e = 17, f, **kwds):
print "a =", a, "b =", b, "c =", c, "d =", d, "e =", e, "f =", f, "kwds =", pd(kwds)
def h(a, b, *args, c, d = 42, e = 17, f, **kwds):
print "a =", a, "b =", b, "args =", args, "c =", c, "d =", d, "e =", e, "f =", f, "kwds =", pd(kwds)
class Inquisition(object):
"""Something that nobody expects."""
def __repr__(self):
return "Surprise!"
def f():
print "Spam!"
f()
def foo():
raise Exception
cdef int spam() except -1:
raise Exception("Spam error")
cdef int grail() except -1:
spam()
def tomato():
grail()
seq = [1, [2, 3]]
def f():
a, (b, c) = [1, [2, 3]]
print a
print b
print c
def g():
a, b, c = seq
def h():
a, = seq
def f():
return 42
cdef int g():
cdef object x
return x
DEF nan = float('nan')
DEF inf = float('inf')
DEF minf = -float('inf')
cdef int f() except -1:
cdef float x, y, z
x = nan
y = inf
z = minf
import sys
from Pyrex.Compiler.Main import main
sys.argv[1:] = "-I spam -Ieggs --include-dir ham".split()
main(command_line = 1)
cdef class Spam:
pass
def probe():
pass
cdef class Spam:
pass
cdef int f() except -1:
cdef type t
t = Spam
cdef class C:
cdef int i
cdef int f() except -1:
cdef object x
cdef void *p
cdef int i
x = <object>p
p = <void *>x
x = (<object>p).foo
i = (<C>p).i
(<C>p).i = i
...@@ -2,6 +2,7 @@ def f(): ...@@ -2,6 +2,7 @@ def f():
cdef int int1, int3 cdef int int1, int3
cdef int *ptr1, *ptr2, *ptr3 cdef int *ptr1, *ptr2, *ptr3
ptr1 = ptr2 + ptr3 # error ptr1 = ptr2 + ptr3 # error
_ERRORS = u""" _ERRORS = u"""
/Local/Projects/D/Pyrex/Source/Tests/Errors2/e_addop.pyx:4:13: Invalid operand types for '+' (int *; int *) 4:13: Invalid operand types for '+' (int *; int *)
""" """
from __future__ import braces
_ERRORS = """
1:23: not a chance
"""
__doc__ = """
>>> test()
2
"""
def test():
cdef int x[2][2]
x[0][0] = 1
x[0][1] = 2
x[1][0] = 3
x[1][1] = 4
return f(x)[1]
cdef int* f(int x[2][2]):
return x[0]
__doc__ = u"""
>>> f(1,2)
4
>>> f.HERE
1
>>> g(1,2)
5
>>> g.HERE
5
>>> h(1,2)
6
>>> h.HERE
1
"""
class wrap:
def __init__(self, func):
self.func = func
self.HERE = 1
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
def decorate(func):
try:
func.HERE += 1
except AttributeError:
func = wrap(func)
return func
def decorate2(a,b):
return decorate
@decorate
def f(a,b):
return a+b+1
@decorate
@decorate
@decorate
@decorate
@decorate
def g(a,b):
return a+b+2
@decorate2(1,2)
def h(a,b):
return a+b+3
__doc__ = u"""
>>> test_in('ABC')
1
>>> test_in('abc')
2
>>> test_in('X')
3
>>> test_in('XYZ')
4
>>> test_in('ABCXYZ')
5
>>> test_in('')
5
>>> test_not_in('abc')
1
>>> test_not_in('CDE')
2
>>> test_not_in('CDEF')
3
>>> test_not_in('BCD')
4
"""
def test_in(s):
if s in ('ABC', 'BCD'):
return 1
elif s.upper() in ('ABC', 'BCD'):
return 2
elif len(s) in (1,2):
return 3
elif len(s) in (3,4):
return 4
else:
return 5
def test_not_in(s):
if s not in ('ABC', 'BCD', 'CDE', 'CDEF'):
return 1
elif s.upper() not in ('ABC', 'BCD', 'CDEF'):
return 2
elif len(s) not in [3]:
return 3
elif len(s) not in [1,2]:
return 4
else:
return 5
__doc__ = u""" __doc__ = u"""
>>> go() >>> go_py()
Spam!
Spam!
Spam!
Spam!
Spam!
>>> go_c()
Spam! Spam!
Spam! Spam!
Spam! Spam!
...@@ -7,7 +14,11 @@ __doc__ = u""" ...@@ -7,7 +14,11 @@ __doc__ = u"""
Spam! Spam!
""" """
def go(): def go_py():
for i in range(5): for i in range(5):
print u"Spam!" print u"Spam!"
def go_c():
cdef int i
for i in range(5):
print u"Spam!"
__doc__ = u"""
>>> test()
5
"""
def test():
a = b = c = 5
return a
__doc__ = u"""
>>> switch_simple_py(1)
1
>>> switch_simple_py(2)
2
>>> switch_simple_py(3)
3
>>> switch_simple_py(4)
8
>>> switch_simple_py(5)
0
>>> switch_py(1)
1
>>> switch_py(2)
2
>>> switch_py(3)
3
>>> switch_py(4)
4
>>> switch_py(5)
4
>>> switch_py(6)
0
>>> switch_py(8)
4
>>> switch_py(10)
7
>>> switch_py(12)
8
>>> switch_py(13)
0
>>> switch_simple_c(1)
1
>>> switch_simple_c(2)
2
>>> switch_simple_c(3)
3
>>> switch_simple_c(4)
8
>>> switch_simple_c(5)
0
>>> switch_c(1)
1
>>> switch_c(2)
2
>>> switch_c(3)
3
>>> switch_c(4)
4
>>> switch_c(5)
4
>>> switch_c(6)
0
>>> switch_c(8)
4
>>> switch_c(10)
7
>>> switch_c(12)
8
>>> switch_c(13)
0
"""
def switch_simple_py(x):
if x == 1:
return 1
elif 2 == x:
return 2
elif x in [3]:
return 3
elif x in (4,):
return 8
else:
return 0
return -1
def switch_py(x):
if x == 1:
return 1
elif 2 == x:
return 2
elif x in [3]:
return 3
elif x in [4,5,7,8]:
return 4
elif x in (10,11): # doesn't work: (7,8,10,11)
return 7
elif x in (12,):
return 8
else:
return 0
return -1
def switch_simple_c(int x):
if x == 1:
return 1
elif 2 == x:
return 2
elif x in [3]:
return 3
elif x in (4,):
return 8
else:
return 0
return -1
def switch_c(int x):
if x == 1:
return 1
elif 2 == x:
return 2
elif x in [3]:
return 3
elif x in [4,5,7,8]:
return 4
elif x in (10,11): # doesn't work: (7,8,10,11)
return 7
elif x in (12,):
return 8
else:
return 0
return -1
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment