Commit fe9bba05 authored by Vitja Makarov's avatar Vitja Makarov

Merge remote branch 'upstream/master'

parents eb6e9b56 33aca7ce
...@@ -33,3 +33,8 @@ ef9d2c680684d0df7d81f529cda29e9e1741f575 cython-0.10.1 ...@@ -33,3 +33,8 @@ ef9d2c680684d0df7d81f529cda29e9e1741f575 cython-0.10.1
59c67af0674bd93c5fd8958e08c76a9dab9aae37 sage-cythonizes 59c67af0674bd93c5fd8958e08c76a9dab9aae37 sage-cythonizes
0000000000000000000000000000000000000000 sage-cythonizes 0000000000000000000000000000000000000000 sage-cythonizes
478f57be445d18fe294db849d7ad317fe7d7658f 0.14.alpha0 478f57be445d18fe294db849d7ad317fe7d7658f 0.14.alpha0
31b531a6c45b2c34ae5a1af8a2c09f152adea60d 0.14.beta1
7fa84cb6d3d75eb3d015aeeb60bf8b642171fe93 0.14.beta2
7fa84cb6d3d75eb3d015aeeb60bf8b642171fe93 0.14.beta2
8412b39fbc3eb709a543e2f1e95c0c8881ea9ed4 0.14.beta2
a6b9f0a6d02d23fc3d3a9d0587867faa3afb2fcd 0.14.rc0
from glob import glob from glob import glob
import re, os, sys import re, os, sys
from cython import set
try:
set
except NameError:
# Python 2.3
from sets import Set as set
from distutils.extension import Extension from distutils.extension import Extension
...@@ -56,8 +52,8 @@ distutils_settings = { ...@@ -56,8 +52,8 @@ distutils_settings = {
'runtime_library_dirs': transitive_list, 'runtime_library_dirs': transitive_list,
'include_dirs': transitive_list, 'include_dirs': transitive_list,
'extra_objects': list, 'extra_objects': list,
'extra_compile_args': list, 'extra_compile_args': transitive_list,
'extra_link_args': list, 'extra_link_args': transitive_list,
'export_symbols': list, 'export_symbols': list,
'depends': transitive_list, 'depends': transitive_list,
'language': transitive_str, 'language': transitive_str,
...@@ -97,7 +93,7 @@ class DistutilsInfo(object): ...@@ -97,7 +93,7 @@ class DistutilsInfo(object):
elif exn is not None: elif exn is not None:
for key in distutils_settings: for key in distutils_settings:
if key in ('name', 'sources'): if key in ('name', 'sources'):
pass continue
value = getattr(exn, key, None) value = getattr(exn, key, None)
if value: if value:
self.values[key] = value self.values[key] = value
...@@ -168,7 +164,7 @@ def strip_string_literals(code, prefix='__Pyx_L'): ...@@ -168,7 +164,7 @@ def strip_string_literals(code, prefix='__Pyx_L'):
# Try to close the quote. # Try to close the quote.
elif in_quote: elif in_quote:
if code[q-1] == '\\': if code[q-1] == '\\' and not raw:
k = 2 k = 2
while q >= k and code[q-k] == '\\': while q >= k and code[q-k] == '\\':
k += 1 k += 1
...@@ -177,7 +173,7 @@ def strip_string_literals(code, prefix='__Pyx_L'): ...@@ -177,7 +173,7 @@ def strip_string_literals(code, prefix='__Pyx_L'):
continue continue
if code[q:q+len(in_quote)] == in_quote: if code[q:q+len(in_quote)] == in_quote:
counter += 1 counter += 1
label = "%s%s" % (prefix, counter) label = "%s%s_" % (prefix, counter)
literals[label] = code[start+len(in_quote):q] literals[label] = code[start+len(in_quote):q]
new_code.append("%s%s%s" % (in_quote, label, in_quote)) new_code.append("%s%s%s" % (in_quote, label, in_quote))
q += len(in_quote) q += len(in_quote)
...@@ -193,7 +189,7 @@ def strip_string_literals(code, prefix='__Pyx_L'): ...@@ -193,7 +189,7 @@ def strip_string_literals(code, prefix='__Pyx_L'):
end = None end = None
new_code.append(code[start:hash_mark+1]) new_code.append(code[start:hash_mark+1])
counter += 1 counter += 1
label = "%s%s" % (prefix, counter) label = "%s%s_" % (prefix, counter)
literals[label] = code[hash_mark+1:end] literals[label] = code[hash_mark+1:end]
new_code.append(label) new_code.append(label)
if end is None: if end is None:
...@@ -208,11 +204,11 @@ def strip_string_literals(code, prefix='__Pyx_L'): ...@@ -208,11 +204,11 @@ def strip_string_literals(code, prefix='__Pyx_L'):
in_quote = code[q]*3 in_quote = code[q]*3
else: else:
in_quote = code[q] in_quote = code[q]
end = q end = marker = q
while end>0 and code[end-1] in 'rRbBuU': while marker > 0 and code[marker-1] in 'rRbBuU':
if code[end-1] in 'rR': if code[marker-1] in 'rR':
raw = True raw = True
end -= 1 marker -= 1
new_code.append(code[start:end]) new_code.append(code[start:end])
start = q start = q
q += len(in_quote) q += len(in_quote)
...@@ -314,8 +310,8 @@ class DependencyTree(object): ...@@ -314,8 +310,8 @@ class DependencyTree(object):
self_pxd = [] self_pxd = []
a = self.cimports(filename) a = self.cimports(filename)
b = filter(None, [self.find_pxd(m, filename) for m in self.cimports(filename)]) b = filter(None, [self.find_pxd(m, filename) for m in self.cimports(filename)])
if len(a) != len(b): if len(a) - int('cython' in a) != len(b):
print(filename) print("missing cimport", filename)
print("\n\t".join(a)) print("\n\t".join(a))
print("\n\t".join(b)) print("\n\t".join(b))
return tuple(self_pxd + filter(None, [self.find_pxd(m, filename) for m in self.cimports(filename)])) return tuple(self_pxd + filter(None, [self.find_pxd(m, filename) for m in self.cimports(filename)]))
...@@ -390,9 +386,14 @@ def create_dependency_tree(ctx=None): ...@@ -390,9 +386,14 @@ def create_dependency_tree(ctx=None):
return _dep_tree return _dep_tree
# This may be useful for advanced users? # This may be useful for advanced users?
def create_extension_list(patterns, ctx=None, aliases=None): def create_extension_list(patterns, exclude=[], ctx=None, aliases=None):
seen = set() seen = set()
deps = create_dependency_tree(ctx) deps = create_dependency_tree(ctx)
to_exclude = set()
if not isinstance(exclude, list):
exclude = [exclude]
for pattern in exclude:
to_exclude.update(glob(pattern))
if not isinstance(patterns, list): if not isinstance(patterns, list):
patterns = [patterns] patterns = [patterns]
module_list = [] module_list = []
...@@ -416,27 +417,39 @@ def create_extension_list(patterns, ctx=None, aliases=None): ...@@ -416,27 +417,39 @@ def create_extension_list(patterns, ctx=None, aliases=None):
else: else:
raise TypeError(pattern) raise TypeError(pattern)
for file in glob(filepattern): for file in glob(filepattern):
if file in to_exclude:
continue
pkg = deps.package(file) pkg = deps.package(file)
if '*' in name: if '*' in name:
module_name = deps.fully_qualifeid_name(file) module_name = deps.fully_qualifeid_name(file)
else: else:
module_name = name module_name = name
if module_name not in seen: if module_name not in seen:
kwds = deps.distutils_info(file, aliases, base).values
if base is not None:
for key, value in base.values.items():
if key not in kwds:
kwds[key] = value
module_list.append(exn_type( module_list.append(exn_type(
name=module_name, name=module_name,
sources=[file], sources=[file],
**deps.distutils_info(file, aliases, base).values)) **kwds))
m = module_list[-1]
seen.add(name) seen.add(name)
return module_list return module_list
# This is the user-exposed entry point. # This is the user-exposed entry point.
def cythonize(module_list, nthreads=0, aliases=None, **options): def cythonize(module_list, exclude=[], nthreads=0, aliases=None, quiet=False, **options):
if 'include_path' not in options: if 'include_path' not in options:
options['include_path'] = ['.'] options['include_path'] = ['.']
c_options = CompilationOptions(**options) c_options = CompilationOptions(**options)
cpp_options = CompilationOptions(**options); cpp_options.cplus = True cpp_options = CompilationOptions(**options); cpp_options.cplus = True
ctx = c_options.create_context() ctx = c_options.create_context()
module_list = create_extension_list(module_list, ctx=ctx, aliases=aliases) module_list = create_extension_list(
module_list,
exclude=exclude,
ctx=ctx,
aliases=aliases)
deps = create_dependency_tree(ctx) deps = create_dependency_tree(ctx)
to_compile = [] to_compile = []
for m in module_list: for m in module_list:
...@@ -463,7 +476,11 @@ def cythonize(module_list, nthreads=0, aliases=None, **options): ...@@ -463,7 +476,11 @@ def cythonize(module_list, nthreads=0, aliases=None, **options):
dep_timestamp, dep = deps.newest_dependency(source) dep_timestamp, dep = deps.newest_dependency(source)
priority = 2 - (dep in deps.immediate_dependencies(source)) priority = 2 - (dep in deps.immediate_dependencies(source))
if c_timestamp < dep_timestamp: if c_timestamp < dep_timestamp:
print("Compiling %s because it depends on %s" % (source, dep)) if not quiet:
if source == dep:
print("Compiling %s because it changed." % source)
else:
print("Compiling %s because it depends on %s." % (source, dep))
to_compile.append((priority, source, c_file, options)) to_compile.append((priority, source, c_file, options))
new_sources.append(c_file) new_sources.append(c_file)
else: else:
......
#no doctest
print "Warning: Using prototype cython.inline code..."
import tempfile import tempfile
import sys, os, re, inspect import sys, os, re, inspect
from cython import set
try: try:
import hashlib import hashlib
...@@ -51,7 +49,14 @@ def unbound_symbols(code, context=None): ...@@ -51,7 +49,14 @@ def unbound_symbols(code, context=None):
unbound.append(name) unbound.append(name)
return unbound return unbound
def get_type(arg, context=None): def unsafe_type(arg, context=None):
py_type = type(arg)
if py_type is int:
return 'long'
else:
return safe_type(arg, context)
def safe_type(arg, context=None):
py_type = type(arg) py_type = type(arg)
if py_type in [list, tuple, dict, str]: if py_type in [list, tuple, dict, str]:
return py_type.__name__ return py_type.__name__
...@@ -61,8 +66,6 @@ def get_type(arg, context=None): ...@@ -61,8 +66,6 @@ def get_type(arg, context=None):
return 'double' return 'double'
elif py_type is bool: elif py_type is bool:
return 'bint' return 'bint'
elif py_type is int:
return 'long'
elif 'numpy' in sys.modules and isinstance(arg, sys.modules['numpy'].ndarray): elif 'numpy' in sys.modules and isinstance(arg, sys.modules['numpy'].ndarray):
return 'numpy.ndarray[numpy.%s_t, ndim=%s]' % (arg.dtype.name, arg.ndim) return 'numpy.ndarray[numpy.%s_t, ndim=%s]' % (arg.dtype.name, arg.ndim)
else: else:
...@@ -77,15 +80,19 @@ def get_type(arg, context=None): ...@@ -77,15 +80,19 @@ def get_type(arg, context=None):
return 'object' return 'object'
def cython_inline(code, def cython_inline(code,
types='aggressive', get_type=unsafe_type,
lib_dir=os.path.expanduser('~/.cython/inline'), lib_dir=os.path.expanduser('~/.cython/inline'),
cython_include_dirs=['.'], cython_include_dirs=['.'],
force=False,
quiet=False,
locals=None, locals=None,
globals=None, globals=None,
**kwds): **kwds):
if get_type is None:
get_type = lambda x: 'object'
code, literals = strip_string_literals(code) code, literals = strip_string_literals(code)
code = strip_common_indent(code) code = strip_common_indent(code)
ctx = Context(include_dirs, default_options) ctx = Context(cython_include_dirs, default_options)
if locals is None: if locals is None:
locals = inspect.currentframe().f_back.f_back.f_locals locals = inspect.currentframe().f_back.f_back.f_locals
if globals is None: if globals is None:
...@@ -99,10 +106,11 @@ def cython_inline(code, ...@@ -99,10 +106,11 @@ def cython_inline(code,
elif symbol in globals: elif symbol in globals:
kwds[symbol] = globals[symbol] kwds[symbol] = globals[symbol]
else: else:
print "Couldn't find ", symbol print("Couldn't find ", symbol)
except AssertionError: except AssertionError:
# Parsing from strings not fully supported (e.g. cimports). if not quiet:
print "Could not parse code as a string (to extract unbound symbols)." # Parsing from strings not fully supported (e.g. cimports).
print("Could not parse code as a string (to extract unbound symbols).")
arg_names = kwds.keys() arg_names = kwds.keys()
arg_names.sort() arg_names.sort()
arg_sigs = tuple([(get_type(kwds[arg], ctx), arg) for arg in arg_names]) arg_sigs = tuple([(get_type(kwds[arg], ctx), arg) for arg in arg_names])
...@@ -113,8 +121,12 @@ def cython_inline(code, ...@@ -113,8 +121,12 @@ def cython_inline(code,
os.makedirs(lib_dir) os.makedirs(lib_dir)
if lib_dir not in sys.path: if lib_dir not in sys.path:
sys.path.append(lib_dir) sys.path.append(lib_dir)
__import__(module_name) if force:
raise ImportError
else:
__import__(module_name)
except ImportError: except ImportError:
cflags = []
c_include_dirs = [] c_include_dirs = []
cimports = [] cimports = []
qualified = re.compile(r'([.\w]+)[.]') qualified = re.compile(r'([.\w]+)[.]')
...@@ -126,6 +138,7 @@ def cython_inline(code, ...@@ -126,6 +138,7 @@ def cython_inline(code,
if m.groups()[0] == 'numpy': if m.groups()[0] == 'numpy':
import numpy import numpy
c_include_dirs.append(numpy.get_include()) c_include_dirs.append(numpy.get_include())
cflags.append('-Wno-unused')
module_body, func_body = extract_func_code(code) module_body, func_body = extract_func_code(code)
params = ', '.join(['%s %s' % a for a in arg_sigs]) params = ', '.join(['%s %s' % a for a in arg_sigs])
module_code = """ module_code = """
...@@ -141,10 +154,11 @@ def __invoke(%(params)s): ...@@ -141,10 +154,11 @@ def __invoke(%(params)s):
extension = Extension( extension = Extension(
name = module_name, name = module_name,
sources = [pyx_file], sources = [pyx_file],
include_dirs = c_include_dirs) include_dirs = c_include_dirs,
extra_compile_args = cflags)
build_extension = build_ext(Distribution()) build_extension = build_ext(Distribution())
build_extension.finalize_options() build_extension.finalize_options()
build_extension.extensions = cythonize([extension], ctx=ctx) build_extension.extensions = cythonize([extension], ctx=ctx, quiet=quiet)
build_extension.build_temp = os.path.dirname(pyx_file) build_extension.build_temp = os.path.dirname(pyx_file)
build_extension.build_lib = lib_dir build_extension.build_lib = lib_dir
build_extension.run() build_extension.run()
......
from Cython.Shadow import inline
from Cython.Build.Inline import safe_type
from Cython.TestUtils import CythonTest
try:
import numpy
has_numpy = True
except:
has_numpy = False
test_kwds = dict(force=True, quiet=True)
global_value = 100
class TestStripLiterals(CythonTest):
def test_simple(self):
self.assertEquals(inline("return 1+2", **test_kwds), 3)
def test_types(self):
self.assertEquals(inline("""
cimport cython
return cython.typeof(a), cython.typeof(b)
""", a=1.0, b=[], **test_kwds), ('double', 'list object'))
def test_locals(self):
a = 1
b = 2
self.assertEquals(inline("return a+b", **test_kwds), 3)
def test_globals(self):
self.assertEquals(inline("return global_value + 1", **test_kwds), global_value + 1)
if has_numpy:
def test_numpy(self):
import numpy
a = numpy.ndarray((10, 20))
a[0,0] = 10
self.assertEquals(safe_type(a), 'numpy.ndarray[numpy.float64_t, ndim=2]')
self.assertEquals(inline("return a[0,0]", a=a, **test_kwds), 10.0)
from Cython.Build.Dependencies import strip_string_literals
from Cython.TestUtils import CythonTest
class TestStripLiterals(CythonTest):
def t(self, before, expected):
actual, literals = strip_string_literals(before, prefix="_L")
self.assertEquals(expected, actual)
for key, value in literals.items():
actual = actual.replace(key, value)
self.assertEquals(before, actual)
def test_empty(self):
self.t("", "")
def test_single_quote(self):
self.t("'x'", "'_L1_'")
def test_double_quote(self):
self.t('"x"', '"_L1_"')
def test_nested_quotes(self):
self.t(""" '"' "'" """, """ '_L1_' "_L2_" """)
def test_triple_quote(self):
self.t(" '''a\n''' ", " '''_L1_''' ")
def test_backslash(self):
self.t(r"'a\'b'", "'_L1_'")
self.t(r"'a\\'", "'_L1_'")
self.t(r"'a\\\'b'", "'_L1_'")
def test_unicode(self):
self.t("u'abc'", "u'_L1_'")
def test_raw(self):
self.t(r"r'abc\'", "r'_L1_'")
def test_raw_unicode(self):
self.t(r"ru'abc\'", "ru'_L1_'")
def test_comment(self):
self.t("abc # foo", "abc #_L1_")
def test_comment_and_quote(self):
self.t("abc # 'x'", "abc #_L1_")
self.t("'abc#'", "'_L1_'")
def test_include(self):
self.t("include 'a.pxi' # something here",
"include '_L1_' #_L2_")
def test_extern(self):
self.t("cdef extern from 'a.h': # comment",
"cdef extern from '_L1_': #_L2_")
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# Cython - Command Line Parsing # Cython - Command Line Parsing
# #
import os
import sys import sys
import Options import Options
...@@ -27,6 +28,7 @@ Options: ...@@ -27,6 +28,7 @@ Options:
Level indicates aggressiveness, default 0 releases nothing. Level indicates aggressiveness, default 0 releases nothing.
-w, --working <directory> Sets the working directory for Cython (the directory modules -w, --working <directory> Sets the working directory for Cython (the directory modules
are searched from) are searched from)
--gdb Output debug information for cygdb
-D, --no-docstrings Remove docstrings. -D, --no-docstrings Remove docstrings.
-a, --annotate Produce a colorized HTML version of the source. -a, --annotate Produce a colorized HTML version of the source.
...@@ -114,6 +116,9 @@ def parse_command_line(args): ...@@ -114,6 +116,9 @@ def parse_command_line(args):
Options.convert_range = True Options.convert_range = True
elif option == "--line-directives": elif option == "--line-directives":
options.emit_linenums = True options.emit_linenums = True
elif option == "--gdb":
options.gdb_debug = True
options.output_dir = os.curdir
elif option == '-2': elif option == '-2':
options.language_level = 2 options.language_level = 2
elif option == '-3': elif option == '-3':
......
...@@ -917,6 +917,14 @@ class CCodeWriter(object): ...@@ -917,6 +917,14 @@ class CCodeWriter(object):
return self.buffer.getvalue() return self.buffer.getvalue()
def write(self, s): def write(self, s):
# also put invalid markers (lineno 0), to indicate that those lines
# have no Cython source code correspondence
if self.marker is None:
cython_lineno = self.last_marker_line
else:
cython_lineno = self.marker[0]
self.buffer.markers.extend([cython_lineno] * s.count('\n'))
self.buffer.write(s) self.buffer.write(s)
def insertion_point(self): def insertion_point(self):
...@@ -1001,6 +1009,7 @@ class CCodeWriter(object): ...@@ -1001,6 +1009,7 @@ class CCodeWriter(object):
self.emit_marker() self.emit_marker()
if self.emit_linenums and self.last_marker_line != 0: if self.emit_linenums and self.last_marker_line != 0:
self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc)) self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
if code: if code:
if safe: if safe:
self.put_safe(code) self.put_safe(code)
......
...@@ -97,6 +97,10 @@ class CompilerCrash(CompileError): ...@@ -97,6 +97,10 @@ class CompilerCrash(CompileError):
message += u'%s: %s' % (cause.__class__.__name__, cause) message += u'%s: %s' % (cause.__class__.__name__, cause)
CompileError.__init__(self, pos, message) CompileError.__init__(self, pos, message)
class NoElementTreeInstalledException(PyrexError):
"""raised when the user enabled options.gdb_debug but no ElementTree
implementation was found
"""
listing_file = None listing_file = None
num_errors = 0 num_errors = 0
......
...@@ -4187,12 +4187,13 @@ class ScopedExprNode(ExprNode): ...@@ -4187,12 +4187,13 @@ class ScopedExprNode(ExprNode):
code.put_var_declaration(entry) code.put_var_declaration(entry)
if entry.type.is_pyobject and entry.used: if entry.type.is_pyobject and entry.used:
py_entries.append(entry) py_entries.append(entry)
code.put_init_var_to_py_none(entry)
if not py_entries: if not py_entries:
# no local Python references => no cleanup required # no local Python references => no cleanup required
generate_inner_evaluation_code(code) generate_inner_evaluation_code(code)
code.putln('} /* exit inner scope */') code.putln('} /* exit inner scope */')
return return
for entry in py_entries:
code.put_init_var_to_py_none(entry)
# must free all local Python references at each exit point # must free all local Python references at each exit point
old_loop_labels = tuple(code.new_loop_labels()) old_loop_labels = tuple(code.new_loop_labels())
......
...@@ -13,7 +13,9 @@ except NameError: ...@@ -13,7 +13,9 @@ except NameError:
# Python 2.3 # Python 2.3
from sets import Set as set from sets import Set as set
import itertools
from time import time from time import time
import Code import Code
import Errors import Errors
import Parsing import Parsing
...@@ -84,6 +86,8 @@ class Context(object): ...@@ -84,6 +86,8 @@ class Context(object):
self.include_directories = include_directories + [standard_include_path] self.include_directories = include_directories + [standard_include_path]
self.set_language_level(language_level) self.set_language_level(language_level)
self.gdb_debug_outputwriter = None
def set_language_level(self, level): def set_language_level(self, level):
self.language_level = level self.language_level = level
...@@ -180,13 +184,22 @@ class Context(object): ...@@ -180,13 +184,22 @@ class Context(object):
from Cython.TestUtils import TreeAssertVisitor from Cython.TestUtils import TreeAssertVisitor
test_support.append(TreeAssertVisitor()) test_support.append(TreeAssertVisitor())
return ([ if options.gdb_debug:
create_parse(self), from Cython.Debugger import DebugWriter
] + self.create_pipeline(pxd=False, py=py) + test_support + [ from ParseTreeTransforms import DebugTransform
inject_pxd_code, self.gdb_debug_outputwriter = DebugWriter.CythonDebugWriter(
abort_on_errors, options.output_dir)
generate_pyx_code, debug_transform = [DebugTransform(self, options, result)]
]) else:
debug_transform = []
return list(itertools.chain(
[create_parse(self)],
self.create_pipeline(pxd=False, py=py),
test_support,
[inject_pxd_code, abort_on_errors],
debug_transform,
[generate_pyx_code]))
def create_pxd_pipeline(self, scope, module_name): def create_pxd_pipeline(self, scope, module_name):
def parse_pxd(source_desc): def parse_pxd(source_desc):
...@@ -706,6 +719,7 @@ def compile_multiple(sources, options): ...@@ -706,6 +719,7 @@ def compile_multiple(sources, options):
a CompilationResultSet. Performs timestamp checking and/or recursion a CompilationResultSet. Performs timestamp checking and/or recursion
if these are specified in the options. if these are specified in the options.
""" """
context = options.create_context()
sources = [os.path.abspath(source) for source in sources] sources = [os.path.abspath(source) for source in sources]
processed = set() processed = set()
results = CompilationResultSet() results = CompilationResultSet()
...@@ -809,4 +823,5 @@ default_options = dict( ...@@ -809,4 +823,5 @@ default_options = dict(
evaluate_tree_assertions = False, evaluate_tree_assertions = False,
emit_linenums = False, emit_linenums = False,
language_level = 2, language_level = 2,
gdb_debug = False,
) )
...@@ -297,12 +297,34 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -297,12 +297,34 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
f = open_new_file(result.c_file) f = open_new_file(result.c_file)
rootwriter.copyto(f) rootwriter.copyto(f)
if options.gdb_debug:
self._serialize_lineno_map(env, rootwriter)
f.close() f.close()
result.c_file_generated = 1 result.c_file_generated = 1
if Options.annotate or options.annotate: if Options.annotate or options.annotate:
self.annotate(rootwriter) self.annotate(rootwriter)
rootwriter.save_annotation(result.main_source_file, result.c_file) rootwriter.save_annotation(result.main_source_file, result.c_file)
def _serialize_lineno_map(self, env, ccodewriter):
tb = env.context.gdb_debug_outputwriter
markers = ccodewriter.buffer.allmarkers()
d = {}
for c_lineno, cython_lineno in enumerate(markers):
if cython_lineno > 0:
d.setdefault(cython_lineno, []).append(c_lineno + 1)
tb.start('LineNumberMapping')
for cython_lineno, c_linenos in sorted(d.iteritems()):
attrs = {
'c_linenos': ' '.join(map(str, c_linenos)),
'cython_lineno': str(cython_lineno),
}
tb.start('LineNumber', attrs)
tb.end('LineNumber')
tb.end('LineNumberMapping')
tb.serialize()
def find_referenced_modules(self, env, module_list, modules_seen): def find_referenced_modules(self, env, module_list, modules_seen):
if env not in modules_seen: if env not in modules_seen:
modules_seen[env] = 1 modules_seen[env] = 1
......
...@@ -1728,3 +1728,136 @@ class TransformBuiltinMethods(EnvTransform): ...@@ -1728,3 +1728,136 @@ class TransformBuiltinMethods(EnvTransform):
self.visitchildren(node) self.visitchildren(node)
return node return node
class DebugTransform(CythonTransform):
"""
Create debug information and all functions' visibility to extern in order
to enable debugging.
"""
def __init__(self, context, options, result):
super(DebugTransform, self).__init__(context)
self.visited = cython.set()
# our treebuilder and debug output writer
# (see Cython.Debugger.debug_output.CythonDebugWriter)
self.tb = self.context.gdb_debug_outputwriter
#self.c_output_file = options.output_file
self.c_output_file = result.c_file
# tells visit_NameNode whether it should register step-into functions
self.register_stepinto = False
def visit_ModuleNode(self, node):
self.tb.module_name = node.full_module_name
attrs = dict(
module_name=node.full_module_name,
filename=node.pos[0].filename,
c_filename=self.c_output_file)
self.tb.start('Module', attrs)
# serialize functions
self.tb.start('Functions')
self.visitchildren(node)
self.tb.end('Functions')
# 2.3 compatibility. Serialize global variables
self.tb.start('Globals')
entries = {}
for k, v in node.scope.entries.iteritems():
if (v.qualified_name not in self.visited and not
v.name.startswith('__pyx_') and not
v.type.is_cfunction and not
v.type.is_extension_type):
entries[k]= v
self.serialize_local_variables(entries)
self.tb.end('Globals')
# self.tb.end('Module') # end Module after the line number mapping in
# Cython.Compiler.ModuleNode.ModuleNode._serialize_lineno_map
return node
def visit_FuncDefNode(self, node):
self.visited.add(node.local_scope.qualified_name)
# node.entry.visibility = 'extern'
if node.py_func is None:
pf_cname = ''
else:
pf_cname = node.py_func.entry.func_cname
attrs = dict(
name=node.entry.name,
cname=node.entry.func_cname,
pf_cname=pf_cname,
qualified_name=node.local_scope.qualified_name,
lineno=str(node.pos[1]))
self.tb.start('Function', attrs=attrs)
self.tb.start('Locals')
self.serialize_local_variables(node.local_scope.entries)
self.tb.end('Locals')
self.tb.start('Arguments')
for arg in node.local_scope.arg_entries:
self.tb.start(arg.name)
self.tb.end(arg.name)
self.tb.end('Arguments')
self.tb.start('StepIntoFunctions')
self.register_stepinto = True
self.visitchildren(node)
self.register_stepinto = False
self.tb.end('StepIntoFunctions')
self.tb.end('Function')
return node
def visit_NameNode(self, node):
if (self.register_stepinto and
node.type.is_cfunction and
getattr(node, 'is_called', False) and
node.entry.func_cname is not None):
# don't check node.entry.in_cinclude, as 'cdef extern: ...'
# declared functions are not 'in_cinclude'.
# This means we will list called 'cdef' functions as
# "step into functions", but this is not an issue as they will be
# recognized as Cython functions anyway.
attrs = dict(name=node.entry.func_cname)
self.tb.start('StepIntoFunction', attrs=attrs)
self.tb.end('StepIntoFunction')
self.visitchildren(node)
return node
def serialize_local_variables(self, entries):
for entry in entries.values():
if entry.type.is_pyobject:
vartype = 'PythonObject'
else:
vartype = 'CObject'
cname = entry.cname
# if entry.type.is_extension_type:
# cname = entry.type.typeptr_cname
if not entry.pos:
# this happens for variables that are not in the user's code,
# e.g. for the global __builtins__, __doc__, etc. We can just
# set the lineno to 0 for those.
lineno = '0'
else:
lineno = str(entry.pos[1])
attrs = dict(
name=entry.name,
cname=cname,
qualified_name=entry.qualified_name,
type=vartype,
lineno=lineno)
self.tb.start('LocalVar', attrs)
self.tb.end('LocalVar')
import os
from Cython.Compiler import CmdLine
from Cython.TestUtils import TransformTest from Cython.TestUtils import TransformTest
from Cython.Compiler.ParseTreeTransforms import * from Cython.Compiler.ParseTreeTransforms import *
from Cython.Compiler.Nodes import * from Cython.Compiler.Nodes import *
class TestNormalizeTree(TransformTest): class TestNormalizeTree(TransformTest):
def test_parserbehaviour_is_what_we_coded_for(self): def test_parserbehaviour_is_what_we_coded_for(self):
t = self.fragment(u"if x: y").root t = self.fragment(u"if x: y").root
...@@ -140,6 +144,81 @@ class TestWithTransform(object): # (TransformTest): # Disabled! ...@@ -140,6 +144,81 @@ class TestWithTransform(object): # (TransformTest): # Disabled!
""", t) """, t)
# TODO: Re-enable once they're more robust.
if sys.version_info[:2] >= (2, 5) and False:
from Cython.Debugger import DebugWriter
from Cython.Debugger.Tests.TestLibCython import DebuggerTestCase
else:
# skip test, don't let it inherit unittest.TestCase
DebuggerTestCase = object
class TestDebugTransform(DebuggerTestCase):
def elem_hasattrs(self, elem, attrs):
# we shall supporteth python 2.3 !
return all([attr in elem.attrib for attr in attrs])
def test_debug_info(self):
try:
assert os.path.exists(self.debug_dest)
t = DebugWriter.etree.parse(self.debug_dest)
# the xpath of the standard ElementTree is primitive, don't use
# anything fancy
L = list(t.find('/Module/Globals'))
# assertTrue is retarded, use the normal assert statement
assert L
xml_globals = dict(
[(e.attrib['name'], e.attrib['type']) for e in L])
self.assertEqual(len(L), len(xml_globals))
L = list(t.find('/Module/Functions'))
assert L
xml_funcs = dict([(e.attrib['qualified_name'], e) for e in L])
self.assertEqual(len(L), len(xml_funcs))
# test globals
self.assertEqual('CObject', xml_globals.get('c_var'))
self.assertEqual('PythonObject', xml_globals.get('python_var'))
# test functions
funcnames = 'codefile.spam', 'codefile.ham', 'codefile.eggs'
required_xml_attrs = 'name', 'cname', 'qualified_name'
assert all([f in xml_funcs for f in funcnames])
spam, ham, eggs = [xml_funcs[funcname] for funcname in funcnames]
self.assertEqual(spam.attrib['name'], 'spam')
self.assertNotEqual('spam', spam.attrib['cname'])
assert self.elem_hasattrs(spam, required_xml_attrs)
# test locals of functions
spam_locals = list(spam.find('Locals'))
assert spam_locals
spam_locals.sort(key=lambda e: e.attrib['name'])
names = [e.attrib['name'] for e in spam_locals]
self.assertEqual(list('abcd'), names)
assert self.elem_hasattrs(spam_locals[0], required_xml_attrs)
# test arguments of functions
spam_arguments = list(spam.find('Arguments'))
assert spam_arguments
self.assertEqual(1, len(list(spam_arguments)))
# test step-into functions
step_into = spam.find('StepIntoFunctions')
spam_stepinto = [x.attrib['name'] for x in step_into]
assert spam_stepinto
self.assertEqual(2, len(spam_stepinto))
assert 'puts' in spam_stepinto
assert 'some_c_function' in spam_stepinto
except:
print open(self.debug_dest).read()
raise
if __name__ == "__main__": if __name__ == "__main__":
import unittest import unittest
unittest.main() unittest.main()
#!/usr/bin/env python
"""
The Cython debugger
The current directory should contain a directory named 'cython_debug', or a
path to the cython project directory should be given (the parent directory of
cython_debug).
Additional gdb args can be provided only if a path to the project directory is
given.
"""
import os
import sys
import glob
import tempfile
import textwrap
import subprocess
usage = "Usage: cygdb [PATH [GDB_ARGUMENTS]]"
def make_command_file(path_to_debug_info, prefix_code='', no_import=False):
if not no_import:
pattern = os.path.join(path_to_debug_info,
'cython_debug',
'cython_debug_info_*')
debug_files = glob.glob(pattern)
if not debug_files:
sys.exit('%s.\nNo debug files were found in %s. Aborting.' % (
usage, os.path.abspath(path_to_debug_info)))
fd, tempfilename = tempfile.mkstemp()
f = os.fdopen(fd, 'w')
f.write(prefix_code)
f.write('set breakpoint pending on\n')
f.write("set print pretty on\n")
f.write('python from Cython.Debugger import libcython, libpython\n')
if no_import:
# don't do this, this overrides file command in .gdbinit
# f.write("file %s\n" % sys.executable)
pass
else:
path = os.path.join(path_to_debug_info, "cython_debug", "interpreter")
interpreter = open(path).read()
f.write("file %s\n" % interpreter)
f.write('\n'.join('cy import %s\n' % fn for fn in debug_files))
f.write(textwrap.dedent('''\
python
import sys
try:
gdb.lookup_type('PyModuleObject')
except RuntimeError:
sys.stderr.write(
'Python was not compiled with debug symbols (or it was '
'stripped). Some functionality may not work (properly).\\n')
end
'''))
f.close()
return tempfilename
def main(path_to_debug_info=None, gdb_argv=None, no_import=False):
"""
Start the Cython debugger. This tells gdb to import the Cython and Python
extensions (libcython.py and libpython.py) and it enables gdb's pending
breakpoints.
path_to_debug_info is the path to the Cython build directory
gdb_argv is the list of options to gdb
no_import tells cygdb whether it should import debug information
"""
if path_to_debug_info is None:
if len(sys.argv) > 1:
path_to_debug_info = sys.argv[1]
else:
path_to_debug_info = os.curdir
if gdb_argv is None:
gdb_argv = sys.argv[2:]
if path_to_debug_info == '--':
no_import = True
tempfilename = make_command_file(path_to_debug_info, no_import=no_import)
p = subprocess.Popen(['gdb', '-command', tempfilename] + gdb_argv)
while True:
try:
p.wait()
except KeyboardInterrupt:
pass
else:
break
os.remove(tempfilename)
from __future__ import with_statement
import os
import sys
import errno
try:
from lxml import etree
have_lxml = True
except ImportError:
have_lxml = False
try:
# Python 2.5
from xml.etree import cElementTree as etree
except ImportError:
try:
# Python 2.5
from xml.etree import ElementTree as etree
except ImportError:
try:
# normal cElementTree install
import cElementTree as etree
except ImportError:
try:
# normal ElementTree install
import elementtree.ElementTree as etree
except ImportError:
etree = None
from Cython.Compiler import Errors
class CythonDebugWriter(object):
"""
Class to output debugging information for cygdb
It writes debug information to cython_debug/cython_debug_info_<modulename>
in the build directory.
"""
def __init__(self, output_dir):
if etree is None:
raise Errors.NoElementTreeInstalledException()
self.output_dir = os.path.join(output_dir, 'cython_debug')
self.tb = etree.TreeBuilder()
# set by Cython.Compiler.ParseTreeTransforms.DebugTransform
self.module_name = None
self.start('cython_debug', attrs=dict(version='1.0'))
def start(self, name, attrs=None):
self.tb.start(name, attrs or {})
def end(self, name):
self.tb.end(name)
def serialize(self):
self.tb.end('Module')
self.tb.end('cython_debug')
xml_root_element = self.tb.close()
try:
os.makedirs(self.output_dir)
except OSError, e:
if e.errno != errno.EEXIST:
raise
et = etree.ElementTree(xml_root_element)
kw = {}
if have_lxml:
kw['pretty_print'] = True
fn = "cython_debug_info_" + self.module_name
et.write(os.path.join(self.output_dir, fn), encoding="UTF-8", **kw)
interpreter_path = os.path.join(self.output_dir, 'interpreter')
with open(interpreter_path, 'w') as f:
f.write(sys.executable)
from __future__ import with_statement
import os
import re
import sys
import uuid
import shutil
import warnings
import textwrap
import unittest
import tempfile
import subprocess
import distutils.core
from distutils import sysconfig
from distutils import ccompiler
import runtests
import Cython.Distutils.extension
from Cython.Debugger import Cygdb as cygdb
root = os.path.dirname(os.path.abspath(__file__))
codefile = os.path.join(root, 'codefile')
cfuncs_file = os.path.join(root, 'cfuncs.c')
with open(codefile) as f:
source_to_lineno = dict((line.strip(), i + 1) for i, line in enumerate(f))
class DebuggerTestCase(unittest.TestCase):
def setUp(self):
"""
Run gdb and have cygdb import the debug information from the code
defined in TestParseTreeTransforms's setUp method
"""
self.tempdir = tempfile.mkdtemp()
self.destfile = os.path.join(self.tempdir, 'codefile.pyx')
self.debug_dest = os.path.join(self.tempdir,
'cython_debug',
'cython_debug_info_codefile')
self.cfuncs_destfile = os.path.join(self.tempdir, 'cfuncs')
self.cwd = os.getcwd()
os.chdir(self.tempdir)
shutil.copy(codefile, self.destfile)
shutil.copy(cfuncs_file, self.cfuncs_destfile + '.c')
compiler = ccompiler.new_compiler()
compiler.compile(['cfuncs.c'], debug=True, extra_postargs=['-fPIC'])
opts = dict(
test_directory=self.tempdir,
module='codefile',
)
cython_compile_testcase = runtests.CythonCompileTestCase(
workdir=self.tempdir,
# we clean up everything (not only compiled files)
cleanup_workdir=False,
**opts
)
cython_compile_testcase.run_cython(
targetdir=self.tempdir,
incdir=None,
annotate=False,
extra_compile_options={
'gdb_debug':True,
'output_dir':self.tempdir,
},
**opts
)
cython_compile_testcase.run_distutils(
incdir=None,
workdir=self.tempdir,
extra_extension_args={'extra_objects':['cfuncs.o']},
**opts
)
# ext = Cython.Distutils.extension.Extension(
# 'codefile',
# ['codefile.pyx'],
# pyrex_gdb=True,
# extra_objects=['cfuncs.o'])
#
# distutils.core.setup(
# script_args=['build_ext', '--inplace'],
# ext_modules=[ext],
# cmdclass=dict(build_ext=Cython.Distutils.build_ext)
# )
def tearDown(self):
os.chdir(self.cwd)
shutil.rmtree(self.tempdir)
class GdbDebuggerTestCase(DebuggerTestCase):
def setUp(self):
super(GdbDebuggerTestCase, self).setUp()
prefix_code = textwrap.dedent('''\
python
import os
import sys
import traceback
def excepthook(type, value, tb):
traceback.print_exception(type, value, tb)
os._exit(1)
sys.excepthook = excepthook
# Have tracebacks end up on sys.stderr (gdb replaces sys.stderr
# with an object that calls gdb.write())
sys.stderr = sys.__stderr__
end
''')
code = textwrap.dedent('''\
python
from Cython.Debugger.Tests import test_libcython_in_gdb
test_libcython_in_gdb.main(version=%r)
end
''' % (sys.version_info[:2],))
self.gdb_command_file = cygdb.make_command_file(self.tempdir,
prefix_code)
with open(self.gdb_command_file, 'a') as f:
f.write(code)
args = ['gdb', '-batch', '-x', self.gdb_command_file, '-n', '--args',
sys.executable, '-c', 'import codefile']
paths = []
path = os.environ.get('PYTHONPATH')
if path:
paths.append(path)
paths.append(os.path.dirname(os.path.dirname(
os.path.abspath(Cython.__file__))))
env = dict(os.environ, PYTHONPATH=os.pathsep.join(paths))
try:
p = subprocess.Popen(['gdb', '-v'], stdout=subprocess.PIPE)
have_gdb = True
except OSError:
# gdb was not installed
have_gdb = False
else:
gdb_version = p.stdout.read().decode('ascii')
p.wait()
p.stdout.close()
if have_gdb:
# Based on Lib/test/test_gdb.py
regex = "^GNU gdb [^\d]*(\d+)\.(\d+)"
gdb_version_number = re.search(regex, gdb_version).groups()
# Be Python 3 compatible
if not have_gdb or list(map(int, gdb_version_number)) < [7, 2]:
self.p = None
warnings.warn('Skipping gdb tests, need gdb >= 7.2')
else:
self.p = subprocess.Popen(
args,
stdout=open(os.devnull, 'w'),
stderr=subprocess.PIPE,
env=env)
def tearDown(self):
super(GdbDebuggerTestCase, self).tearDown()
if self.p:
self.p.stderr.close()
self.p.wait()
os.remove(self.gdb_command_file)
class TestAll(GdbDebuggerTestCase):
def test_all(self):
if self.p is None:
return
out, err = self.p.communicate()
border = '*' * 30
start = '%s v INSIDE GDB v %s' % (border, border)
end = '%s ^ INSIDE GDB ^ %s' % (border, border)
errmsg = '\n%s\n%s%s' % (start, err.decode('UTF-8'), end)
self.assertEquals(0, self.p.wait(), errmsg)
sys.stderr.write(err)
if __name__ == '__main__':
unittest.main()
void
some_c_function(void)
{
int a, b, c;
a = 1;
b = 2;
}
cdef extern from "stdio.h":
int puts(char *s)
cdef extern:
void some_c_function()
import os
cdef int c_var = 12
python_var = 13
def spam(a=0):
cdef:
int b, c
b = c = d = 0
b = 1
c = 2
int(10)
puts("spam")
os.path.join("foo", "bar")
some_c_function()
cdef ham():
pass
cpdef eggs():
pass
cdef class SomeClass(object):
def spam(self):
pass
spam()
print "bye!"
This diff is collapsed.
# -*- coding: UTF-8 -*-
"""
Test libpython.py. This is already partly tested by test_libcython_in_gdb and
Lib/test/test_gdb.py in the Python source. These tests are run in gdb and
called from test_libcython_in_gdb.main()
"""
import os
import sys
import gdb
from Cython.Debugger import libcython
from Cython.Debugger import libpython
import test_libcython_in_gdb
from test_libcython_in_gdb import _debug, inferior_python_version
class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase):
"""
Test whether types of Python objects are correctly inferred and that
the right libpython.PySomeTypeObjectPtr classes are instantiated.
Also test whether values are appropriately formatted (don't be too
laborious as Lib/test/test_gdb.py already covers this extensively).
Don't take care of decreffing newly allocated objects as a new
interpreter is started for every test anyway.
"""
def setUp(self):
super(TestPrettyPrinters, self).setUp()
self.break_and_run('b = c = d = 0')
def get_pyobject(self, code):
value = gdb.parse_and_eval(code)
assert libpython.pointervalue(value) != 0
return value
def pyobject_fromcode(self, code, gdbvar=None):
if gdbvar is not None:
d = {'varname':gdbvar, 'code':code}
gdb.execute('set $%(varname)s = %(code)s' % d)
code = '$' + gdbvar
return libpython.PyObjectPtr.from_pyobject_ptr(self.get_pyobject(code))
def get_repr(self, pyobject):
return pyobject.get_truncated_repr(libpython.MAX_OUTPUT_LEN)
def alloc_bytestring(self, string, gdbvar=None):
if inferior_python_version < (3, 0):
funcname = 'PyString_FromString'
else:
funcname = 'PyBytes_FromString'
assert '"' not in string
# ensure double quotes
code = '(PyObject *) %s("%s")' % (funcname, string)
return self.pyobject_fromcode(code, gdbvar=gdbvar)
def alloc_unicodestring(self, string, gdbvar=None):
self.alloc_bytestring(string.encode('UTF-8'), gdbvar='_temp')
postfix = libpython.get_inferior_unicode_postfix()
funcname = 'PyUnicode%s_FromEncodedObject' % (postfix,)
return self.pyobject_fromcode(
'(PyObject *) %s($_temp, "UTF-8", "strict")' % funcname,
gdbvar=gdbvar)
def test_bytestring(self):
bytestring = self.alloc_bytestring("spam")
if inferior_python_version < (3, 0):
bytestring_class = libpython.PyStringObjectPtr
expected = repr("spam")
else:
bytestring_class = libpython.PyBytesObjectPtr
expected = "b'spam'"
self.assertEqual(type(bytestring), bytestring_class)
self.assertEqual(self.get_repr(bytestring), expected)
def test_unicode(self):
unicode_string = self.alloc_unicodestring(u"spam ἄλφα")
expected = "'spam ἄλφα'"
if inferior_python_version < (3, 0):
expected = 'u' + expected
self.assertEqual(type(unicode_string), libpython.PyUnicodeObjectPtr)
self.assertEqual(self.get_repr(unicode_string), expected)
def test_int(self):
if inferior_python_version < (3, 0):
intval = self.pyobject_fromcode('PyInt_FromLong(100)')
self.assertEqual(type(intval), libpython.PyIntObjectPtr)
self.assertEqual(self.get_repr(intval), '100')
def test_long(self):
longval = self.pyobject_fromcode('PyLong_FromLong(200)',
gdbvar='longval')
assert gdb.parse_and_eval('$longval->ob_type == &PyLong_Type')
self.assertEqual(type(longval), libpython.PyLongObjectPtr)
self.assertEqual(self.get_repr(longval), '200')
def test_frame_type(self):
frame = self.pyobject_fromcode('PyEval_GetFrame()')
self.assertEqual(type(frame), libpython.PyFrameObjectPtr)
This diff is collapsed.
This diff is collapsed.
...@@ -17,11 +17,55 @@ from distutils.dep_util import newer, newer_group ...@@ -17,11 +17,55 @@ from distutils.dep_util import newer, newer_group
from distutils import log from distutils import log
from distutils.dir_util import mkpath from distutils.dir_util import mkpath
from distutils.command import build_ext as _build_ext from distutils.command import build_ext as _build_ext
from distutils import sysconfig
extension_name_re = _build_ext.extension_name_re extension_name_re = _build_ext.extension_name_re
show_compilers = _build_ext.show_compilers show_compilers = _build_ext.show_compilers
class Optimization(object):
def __init__(self):
self.flags = (
'OPT',
'CFLAGS',
'CPPFLAGS',
'EXTRA_CFLAGS',
'BASECFLAGS',
'PY_CFLAGS',
)
self.state = sysconfig.get_config_vars(*self.flags)
self.config_vars = sysconfig.get_config_vars()
def disable_optimization(self):
"disable optimization for the C or C++ compiler"
badoptions = ('-O1', '-O2', '-O3')
for flag, option in zip(self.flags, self.state):
if option is not None:
L = [opt for opt in option.split() if opt not in badoptions]
self.config_vars[flag] = ' '.join(L)
def restore_state(self):
"restore the original state"
for flag, option in zip(self.flags, self.state):
if option is not None:
self.config_vars[flag] = option
optimization = Optimization()
try:
any
except NameError:
def any(it):
for x in it:
if x:
return True
return False
class build_ext(_build_ext.build_ext): class build_ext(_build_ext.build_ext):
description = "build C/C++ and Cython extensions (compile/link to build directory)" description = "build C/C++ and Cython extensions (compile/link to build directory)"
...@@ -47,10 +91,13 @@ class build_ext(_build_ext.build_ext): ...@@ -47,10 +91,13 @@ class build_ext(_build_ext.build_ext):
"generate .pxi file for public declarations"), "generate .pxi file for public declarations"),
('pyrex-directives=', None, ('pyrex-directives=', None,
"compiler directive overrides"), "compiler directive overrides"),
('pyrex-gdb', None,
"generate debug information for cygdb"),
]) ])
boolean_options.extend([ boolean_options.extend([
'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives', 'pyrex-c-in-temp' 'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives',
'pyrex-c-in-temp', 'pyrex-gdb',
]) ])
def initialize_options(self): def initialize_options(self):
...@@ -62,6 +109,7 @@ class build_ext(_build_ext.build_ext): ...@@ -62,6 +109,7 @@ class build_ext(_build_ext.build_ext):
self.pyrex_directives = None self.pyrex_directives = None
self.pyrex_c_in_temp = 0 self.pyrex_c_in_temp = 0
self.pyrex_gen_pxi = 0 self.pyrex_gen_pxi = 0
self.pyrex_gdb = False
def finalize_options (self): def finalize_options (self):
_build_ext.build_ext.finalize_options(self) _build_ext.build_ext.finalize_options(self)
...@@ -73,10 +121,22 @@ class build_ext(_build_ext.build_ext): ...@@ -73,10 +121,22 @@ class build_ext(_build_ext.build_ext):
if self.pyrex_directives is None: if self.pyrex_directives is None:
self.pyrex_directives = {} self.pyrex_directives = {}
# finalize_options () # finalize_options ()
def run(self):
# We have one shot at this before build_ext initializes the compiler.
# If --pyrex-gdb is in effect as a command line option or as option
# of any Extension module, disable optimization for the C or C++
# compiler.
if (self.pyrex_gdb or any([getattr(ext, 'pyrex_gdb', False)
for ext in self.extensions])):
optimization.disable_optimization()
_build_ext.build_ext.run(self)
def build_extensions(self): def build_extensions(self):
# First, sanity-check the 'extensions' list # First, sanity-check the 'extensions' list
self.check_extensions_list(self.extensions) self.check_extensions_list(self.extensions)
for ext in self.extensions: for ext in self.extensions:
ext.sources = self.cython_sources(ext.sources, ext) ext.sources = self.cython_sources(ext.sources, ext)
self.build_extension(ext) self.build_extension(ext)
...@@ -128,7 +188,7 @@ class build_ext(_build_ext.build_ext): ...@@ -128,7 +188,7 @@ class build_ext(_build_ext.build_ext):
cplus = self.pyrex_cplus or getattr(extension, 'pyrex_cplus', 0) or \ cplus = self.pyrex_cplus or getattr(extension, 'pyrex_cplus', 0) or \
(extension.language and extension.language.lower() == 'c++') (extension.language and extension.language.lower() == 'c++')
pyrex_gen_pxi = self.pyrex_gen_pxi or getattr(extension, 'pyrex_gen_pxi', 0) pyrex_gen_pxi = self.pyrex_gen_pxi or getattr(extension, 'pyrex_gen_pxi', 0)
pyrex_gdb = self.pyrex_gdb or getattr(extension, 'pyrex_gdb', False)
# Set up the include_path for the Cython compiler: # Set up the include_path for the Cython compiler:
# 1. Start with the command line option. # 1. Start with the command line option.
# 2. Add in any (unique) paths from the extension # 2. Add in any (unique) paths from the extension
...@@ -201,6 +261,10 @@ class build_ext(_build_ext.build_ext): ...@@ -201,6 +261,10 @@ class build_ext(_build_ext.build_ext):
if rebuild: if rebuild:
log.info("cythoning %s to %s", source, target) log.info("cythoning %s to %s", source, target)
self.mkpath(os.path.dirname(target)) self.mkpath(os.path.dirname(target))
if self.inplace:
output_dir = os.curdir
else:
output_dir = self.build_lib
options = CompilationOptions(pyrex_default_options, options = CompilationOptions(pyrex_default_options,
use_listing_file = create_listing, use_listing_file = create_listing,
include_path = includes, include_path = includes,
...@@ -208,7 +272,9 @@ class build_ext(_build_ext.build_ext): ...@@ -208,7 +272,9 @@ class build_ext(_build_ext.build_ext):
output_file = target, output_file = target,
cplus = cplus, cplus = cplus,
emit_linenums = line_directives, emit_linenums = line_directives,
generate_pxi = pyrex_gen_pxi) generate_pxi = pyrex_gen_pxi,
output_dir = output_dir,
gdb_debug = pyrex_gdb)
result = cython_compile(source, options=options, result = cython_compile(source, options=options,
full_module_name=module_name) full_module_name=module_name)
else: else:
......
...@@ -31,6 +31,8 @@ class Extension(_Extension.Extension): ...@@ -31,6 +31,8 @@ class Extension(_Extension.Extension):
put generated C files in temp directory. put generated C files in temp directory.
pyrex_gen_pxi : boolean pyrex_gen_pxi : boolean
generate .pxi file for public declarations generate .pxi file for public declarations
pyrex_gdb : boolean
generate Cython debug information for this extension for cygdb
""" """
# When adding arguments to this constructor, be sure to update # When adding arguments to this constructor, be sure to update
...@@ -56,6 +58,7 @@ class Extension(_Extension.Extension): ...@@ -56,6 +58,7 @@ class Extension(_Extension.Extension):
pyrex_cplus = 0, pyrex_cplus = 0,
pyrex_c_in_temp = 0, pyrex_c_in_temp = 0,
pyrex_gen_pxi = 0, pyrex_gen_pxi = 0,
pyrex_gdb = False,
**kw): **kw):
_Extension.Extension.__init__(self, name, sources, _Extension.Extension.__init__(self, name, sources,
...@@ -81,6 +84,7 @@ class Extension(_Extension.Extension): ...@@ -81,6 +84,7 @@ class Extension(_Extension.Extension):
self.pyrex_cplus = pyrex_cplus self.pyrex_cplus = pyrex_cplus
self.pyrex_c_in_temp = pyrex_c_in_temp self.pyrex_c_in_temp = pyrex_c_in_temp
self.pyrex_gen_pxi = pyrex_gen_pxi self.pyrex_gen_pxi = pyrex_gen_pxi
self.pyrex_gdb = pyrex_gdb
# class Extension # class Extension
......
...@@ -188,6 +188,7 @@ except NameError: # Py3 ...@@ -188,6 +188,7 @@ except NameError: # Py3
py_float = float py_float = float
py_complex = complex py_complex = complex
try: try:
# Python 3 # Python 3
from builtins import set, frozenset from builtins import set, frozenset
......
...@@ -11,6 +11,7 @@ class StringIOTree(object): ...@@ -11,6 +11,7 @@ class StringIOTree(object):
stream = StringIO() stream = StringIO()
self.stream = stream self.stream = stream
self.write = stream.write self.write = stream.write
self.markers = []
def getvalue(self): def getvalue(self):
content = [x.getvalue() for x in self.prepended_children] content = [x.getvalue() for x in self.prepended_children]
...@@ -31,6 +32,8 @@ class StringIOTree(object): ...@@ -31,6 +32,8 @@ class StringIOTree(object):
# itself is empty -- this makes it ready for insertion # itself is empty -- this makes it ready for insertion
if self.stream.tell(): if self.stream.tell():
self.prepended_children.append(StringIOTree(self.stream)) self.prepended_children.append(StringIOTree(self.stream))
self.prepended_children[-1].markers = self.markers
self.markers = []
self.stream = StringIO() self.stream = StringIO()
self.write = self.stream.write self.write = self.stream.write
...@@ -59,6 +62,11 @@ class StringIOTree(object): ...@@ -59,6 +62,11 @@ class StringIOTree(object):
self.prepended_children.append(other) self.prepended_children.append(other)
return other return other
def allmarkers(self):
children = self.prepended_children
return [m for c in children for m in c.allmarkers()] + self.markers
__doc__ = r""" __doc__ = r"""
Implements a buffer with insertion points. When you know you need to Implements a buffer with insertion points. When you know you need to
"get back" to a place and write more later, simply call insertion_point() "get back" to a place and write more later, simply call insertion_point()
......
import unittest
from Cython import StringIOTree as stringtree
code = """
cdef int spam # line 1
cdef ham():
a = 1
b = 2
c = 3
d = 4
def eggs():
pass
cpdef bacon():
print spam
print 'scotch'
print 'tea?'
print 'or coffee?' # line 16
"""
linemap = dict(enumerate(code.splitlines()))
class TestStringIOTree(unittest.TestCase):
def setUp(self):
self.tree = stringtree.StringIOTree()
def test_markers(self):
assert not self.tree.allmarkers()
def test_insertion(self):
self.write_lines((1, 2, 3))
line_4_to_6_insertion_point = self.tree.insertion_point()
self.write_lines((7, 8))
line_9_to_13_insertion_point = self.tree.insertion_point()
self.write_lines((14, 15, 16))
line_4_insertion_point = line_4_to_6_insertion_point.insertion_point()
self.write_lines((5, 6), tree=line_4_to_6_insertion_point)
line_9_to_12_insertion_point = (
line_9_to_13_insertion_point.insertion_point())
self.write_line(13, tree=line_9_to_13_insertion_point)
self.write_line(4, tree=line_4_insertion_point)
self.write_line(9, tree=line_9_to_12_insertion_point)
line_10_insertion_point = line_9_to_12_insertion_point.insertion_point()
self.write_line(11, tree=line_9_to_12_insertion_point)
self.write_line(10, tree=line_10_insertion_point)
self.write_line(12, tree=line_9_to_12_insertion_point)
self.assertEqual(self.tree.allmarkers(), range(1, 17))
self.assertEqual(code.strip(), self.tree.getvalue().strip())
def write_lines(self, linenos, tree=None):
for lineno in linenos:
self.write_line(lineno, tree=tree)
def write_line(self, lineno, tree=None):
if tree is None:
tree = self.tree
tree.markers.append(lineno)
tree.write(linemap[lineno] + '\n')
__version__ = "0.14.alpha0" __version__ = "0.14.rc0"
# Void cython.* directives (for case insensitive operating systems). # Void cython.* directives (for case insensitive operating systems).
from Cython.Shadow import * from Cython.Shadow import *
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
double sinc(double x) { double sinc(double x) {
return x == 0 ? 1 : sin(x)/x; return x == 0 ? 1 : sin(x)/x;
} }
\ No newline at end of file
#
# This example demonstrates how to access the internals
# of a Numeric array object.
#
cdef extern from "Numeric/arrayobject.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 print_2d_array(ArrayType a):
print "Type:", chr(a.descr.type)
if chr(a.descr.type) <> "f":
raise TypeError("Float array required")
if a.nd <> 2:
raise ValueError("2 dimensional array required")
cdef int nrows, ncols
cdef float *elems, x
nrows = a.dimensions[0]
ncols = a.dimensions[1]
elems = <float *>a.data
hyphen = "-"
divider = ("+" + 10 * hyphen) * ncols + "+"
print divider
for row in range(nrows):
for col in range(ncols):
x = elems[row * ncols + col]
print "| %8f" % x,
print "|"
print divider
cimport numpy
import numpy
def sum_of_squares(numpy.ndarray[double, ndim=1] arr):
cdef long N = arr.shape[0]
cdef double ss = 0
for i in range(N):
ss += arr[i]**2
return ss
import glob # Run as:
# python setup.py build_ext --inplace
from distutils.core import setup from distutils.core import setup
from distutils.extension import Extension from distutils.extension import Extension
from Cython.Distutils import build_ext from Cython.Build import cythonize
ext_modules = cythonize("*.pyx", exclude="numpy_*.pyx")
# Only compile the following if numpy is installed.
try: try:
from numpy.distutils.misc_util import get_numpy_include_dirs from numpy.distutils.misc_util import get_numpy_include_dirs
numpy_include_dirs = get_numpy_include_dirs() numpy_demo = Extension("*",
except: ["numpy_*.pyx"],
numpy_include_dirs = [] include_dirs=get_numpy_include_dirs())
ext_modules.extend(cythonize(numpy_demo))
ext_modules=[ except ImportError:
Extension("primes", ["primes.pyx"]), pass
Extension("spam", ["spam.pyx"]),
]
for file in glob.glob("*.pyx"):
if file != "numeric_demo.pyx":
ext_modules.append(Extension(file[:-4], [file], include_dirs = numpy_include_dirs))
setup( setup(
name = 'Demos', name = 'Demos',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules, ext_modules = ext_modules,
) )
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
cdef class Spam: cdef class Spam:
cdef public int amount cdef public int amount
def __new__(self): def __cinit__(self):
self.amount = 0 self.amount = 0
def __dealloc__(self): def __dealloc__(self):
......
#include "Python.h" static PyObject *__Pyx_UnpackItem(PyObject *, int); static int __Pyx_EndUnpack(PyObject *, int); static int __Pyx_PrintItem(PyObject *); static int __Pyx_PrintNewline(void); static void __Pyx_ReRaise(void); static void __Pyx_RaiseWithTraceback(PyObject *, PyObject *, PyObject *); static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); static PyObject *__Pyx_GetExcValue(void); static PyObject *__Pyx_GetName(PyObject *dict, char *name); static PyObject *__pyx_m; static PyObject *__pyx_d; static PyObject *__pyx_b; PyObject *__pyx_f_primes(PyObject *__pyx_self, PyObject *__pyx_args); /*proto*/ PyObject *__pyx_f_primes(PyObject *__pyx_self, PyObject *__pyx_args) { int __pyx_v_kmax; int __pyx_v_n; int __pyx_v_k; int __pyx_v_i; int (__pyx_v_p[1000]); PyObject *__pyx_v_result; PyObject *__pyx_r; PyObject *__pyx_1 = 0; int __pyx_2; int __pyx_3; int __pyx_4; PyObject *__pyx_5 = 0; PyObject *__pyx_6 = 0; if (!PyArg_ParseTuple(__pyx_args, "i", &__pyx_v_kmax)) return 0; __pyx_v_result = Py_None; Py_INCREF(__pyx_v_result); /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":2 */ /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":3 */ /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":4 */ __pyx_1 = PyList_New(0); if (!__pyx_1) goto __pyx_L1; Py_DECREF(__pyx_v_result); __pyx_v_result = __pyx_1; __pyx_1 = 0; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":5 */ __pyx_2 = (__pyx_v_kmax > 1000); if (__pyx_2) { /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":6 */ __pyx_v_kmax = 1000; goto __pyx_L2; } __pyx_L2:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":7 */ __pyx_v_k = 0; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":8 */ __pyx_v_n = 2; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":9 */ while (1) { __pyx_L3:; __pyx_2 = (__pyx_v_k < __pyx_v_kmax); if (!__pyx_2) break; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":10 */ __pyx_v_i = 0; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":11 */ while (1) { __pyx_L5:; if (__pyx_3 = (__pyx_v_i < __pyx_v_k)) { __pyx_3 = ((__pyx_v_n % (__pyx_v_p[__pyx_v_i])) != 0); } if (!__pyx_3) break; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":12 */ __pyx_v_i = (__pyx_v_i + 1); } __pyx_L6:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":13 */ __pyx_4 = (__pyx_v_i == __pyx_v_k); if (__pyx_4) { /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":14 */ (__pyx_v_p[__pyx_v_k]) = __pyx_v_n; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":15 */ __pyx_v_k = (__pyx_v_k + 1); /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":16 */ __pyx_1 = PyObject_GetAttrString(__pyx_v_result, "append"); if (!__pyx_1) goto __pyx_L1; __pyx_5 = PyInt_FromLong(__pyx_v_n); if (!__pyx_5) goto __pyx_L1; __pyx_6 = PyTuple_New(1); if (!__pyx_6) goto __pyx_L1; PyTuple_SET_ITEM(__pyx_6, 0, __pyx_5); __pyx_5 = 0; __pyx_5 = PyObject_CallObject(__pyx_1, __pyx_6); if (!__pyx_5) goto __pyx_L1; Py_DECREF(__pyx_6); __pyx_6 = 0; Py_DECREF(__pyx_5); __pyx_5 = 0; goto __pyx_L7; } __pyx_L7:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":17 */ __pyx_v_n = (__pyx_v_n + 1); } __pyx_L4:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":18 */ Py_INCREF(__pyx_v_result); __pyx_r = __pyx_v_result; goto __pyx_L0; __pyx_r = Py_None; Py_INCREF(__pyx_r); goto __pyx_L0; __pyx_L1:; Py_XDECREF(__pyx_1); Py_XDECREF(__pyx_5); Py_XDECREF(__pyx_6); __pyx_r = 0; __pyx_L0:; Py_DECREF(__pyx_v_result); return __pyx_r; } static struct PyMethodDef __pyx_methods[] = { {"primes", (PyCFunction)__pyx_f_primes, METH_VARARGS, 0}, {0, 0, 0, 0} }; void initprimes(void); /*proto*/ void initprimes(void) { __pyx_m = Py_InitModule4("primes", __pyx_methods, 0, 0, PYTHON_API_VERSION); __pyx_d = PyModule_GetDict(__pyx_m); __pyx_b = PyImport_AddModule("__builtin__"); PyDict_SetItemString(__pyx_d, "__builtins__", __pyx_b); }/* Runtime support code */ #include "Python.h" static PyObject *__Pyx_UnpackItem(PyObject *, int); static int __Pyx_EndUnpack(PyObject *, int); static int __Pyx_PrintItem(PyObject *); static int __Pyx_PrintNewline(void); static void __Pyx_ReRaise(void); static void __Pyx_RaiseWithTraceback(PyObject *, PyObject *, PyObject *); static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); static PyObject *__Pyx_GetExcValue(void); static PyObject *__Pyx_GetName(PyObject *dict, char *name); static PyObject *__pyx_m; static PyObject *__pyx_d; static PyObject *__pyx_b; PyObject *__pyx_f_primes(PyObject *__pyx_self, PyObject *__pyx_args); /*proto*/ PyObject *__pyx_f_primes(PyObject *__pyx_self, PyObject *__pyx_args) { int __pyx_v_kmax; int __pyx_v_n; int __pyx_v_k; int __pyx_v_i; int (__pyx_v_p[1000]); PyObject *__pyx_v_result; PyObject *__pyx_r; PyObject *__pyx_1 = 0; int __pyx_2; int __pyx_3; int __pyx_4; PyObject *__pyx_5 = 0; PyObject *__pyx_6 = 0; if (!PyArg_ParseTuple(__pyx_args, "i", &__pyx_v_kmax)) return 0; __pyx_v_result = Py_None; Py_INCREF(__pyx_v_result); /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":2 */ /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":3 */ /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":4 */ __pyx_1 = PyList_New(0); if (!__pyx_1) goto __pyx_L1; Py_DECREF(__pyx_v_result); __pyx_v_result = __pyx_1; __pyx_1 = 0; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":5 */ __pyx_2 = (__pyx_v_kmax > 1000); if (__pyx_2) { /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":6 */ __pyx_v_kmax = 1000; goto __pyx_L2; } __pyx_L2:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":7 */ __pyx_v_k = 0; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":8 */ __pyx_v_n = 2; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":9 */ while (1) { __pyx_L3:; __pyx_2 = (__pyx_v_k < __pyx_v_kmax); if (!__pyx_2) break; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":10 */ __pyx_v_i = 0; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":11 */ while (1) { __pyx_L5:; if (__pyx_3 = (__pyx_v_i < __pyx_v_k)) { __pyx_3 = ((__pyx_v_n % (__pyx_v_p[__pyx_v_i])) != 0); } if (!__pyx_3) break; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":12 */ __pyx_v_i = (__pyx_v_i + 1); } __pyx_L6:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":13 */ __pyx_4 = (__pyx_v_i == __pyx_v_k); if (__pyx_4) { /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":14 */ (__pyx_v_p[__pyx_v_k]) = __pyx_v_n; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":15 */ __pyx_v_k = (__pyx_v_k + 1); /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":16 */ __pyx_1 = PyObject_GetAttrString(__pyx_v_result, "append"); if (!__pyx_1) goto __pyx_L1; __pyx_5 = PyInt_FromLong(__pyx_v_n); if (!__pyx_5) goto __pyx_L1; __pyx_6 = PyTuple_New(1); if (!__pyx_6) goto __pyx_L1; PyTuple_SET_ITEM(__pyx_6, 0, __pyx_5); __pyx_5 = 0; __pyx_5 = PyObject_CallObject(__pyx_1, __pyx_6); if (!__pyx_5) goto __pyx_L1; Py_DECREF(__pyx_6); __pyx_6 = 0; Py_DECREF(__pyx_5); __pyx_5 = 0; goto __pyx_L7; } __pyx_L7:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":17 */ __pyx_v_n = (__pyx_v_n + 1); } __pyx_L4:; /* "ProjectsA:Python:Pyrex:Demos:primes.pyx":18 */ Py_INCREF(__pyx_v_result); __pyx_r = __pyx_v_result; goto __pyx_L0; __pyx_r = Py_None; Py_INCREF(__pyx_r); goto __pyx_L0; __pyx_L1:; Py_XDECREF(__pyx_1); Py_XDECREF(__pyx_5); Py_XDECREF(__pyx_6); __pyx_r = 0; __pyx_L0:; Py_DECREF(__pyx_v_result); return __pyx_r; } static struct PyMethodDef __pyx_methods[] = { {"primes", (PyCFunction)__pyx_f_primes, METH_VARARGS, 0}, {0, 0, 0, 0} }; void initprimes(void); /*proto*/ void initprimes(void) { __pyx_m = Py_InitModule4("primes", __pyx_methods, 0, 0, PYTHON_API_VERSION); __pyx_d = PyModule_GetDict(__pyx_m); __pyx_b = PyImport_AddModule("__builtin__"); PyDict_SetItemString(__pyx_d, "__builtins__", __pyx_b); }/* Runtime support code */
\ No newline at end of file
......
...@@ -5,6 +5,7 @@ include setup.py ...@@ -5,6 +5,7 @@ include setup.py
include setupegg.py include setupegg.py
include bin/* include bin/*
include cython.py include cython.py
include cygdb.py
recursive-include Cython *.pyx *.pxd recursive-include Cython *.pyx *.pxd
include Doc/* include Doc/*
...@@ -24,5 +25,8 @@ include runtests.py ...@@ -24,5 +25,8 @@ include runtests.py
include Cython/Mac/Makefile include Cython/Mac/Makefile
include Cython/Mac/_Filemodule_patched.c include Cython/Mac/_Filemodule_patched.c
include Cython/Debugger/Tests/cfuncs.c
include Cython/Debugger/Tests/codefile
recursive-include pyximport *.py recursive-include pyximport *.py
include pyximport/PKG-INFO pyximport/README include pyximport/PKG-INFO pyximport/README
#!/usr/bin/env python
import sys
from Cython.Debugger import Cygdb as cygdb
if __name__ == '__main__':
cygdb.main()
#!/usr/bin/env python
import sys
from Cython.Debugger import Cygdb as cygdb
if __name__ == '__main__':
cygdb.main()
...@@ -344,15 +344,27 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -344,15 +344,27 @@ class CythonCompileTestCase(unittest.TestCase):
else: else:
return geterrors() return geterrors()
def run_cython(self, test_directory, module, targetdir, incdir, annotate): def run_cython(self, test_directory, module, targetdir, incdir, annotate,
extra_compile_options=None):
include_dirs = INCLUDE_DIRS[:] include_dirs = INCLUDE_DIRS[:]
if incdir: if incdir:
include_dirs.append(incdir) include_dirs.append(incdir)
source = self.find_module_source_file( source = self.find_module_source_file(
os.path.join(test_directory, module + '.pyx')) os.path.join(test_directory, module + '.pyx'))
target = os.path.join(targetdir, self.build_target_filename(module)) target = os.path.join(targetdir, self.build_target_filename(module))
if extra_compile_options is None:
extra_compile_options = {}
try:
CompilationOptions
except NameError:
from Cython.Compiler.Main import CompilationOptions
from Cython.Compiler.Main import compile as cython_compile
from Cython.Compiler.Main import default_options
options = CompilationOptions( options = CompilationOptions(
pyrex_default_options, default_options,
include_path = include_dirs, include_path = include_dirs,
output_file = target, output_file = target,
annotate = annotate, annotate = annotate,
...@@ -361,11 +373,13 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -361,11 +373,13 @@ class CythonCompileTestCase(unittest.TestCase):
language_level = self.language_level, language_level = self.language_level,
generate_pxi = False, generate_pxi = False,
evaluate_tree_assertions = True, evaluate_tree_assertions = True,
**extra_compile_options
) )
cython_compile(source, options=options, cython_compile(source, options=options,
full_module_name=module) full_module_name=module)
def run_distutils(self, test_directory, module, workdir, incdir): def run_distutils(self, test_directory, module, workdir, incdir,
extra_extension_args=None):
cwd = os.getcwd() cwd = os.getcwd()
os.chdir(workdir) os.chdir(workdir)
try: try:
...@@ -379,11 +393,16 @@ class CythonCompileTestCase(unittest.TestCase): ...@@ -379,11 +393,16 @@ class CythonCompileTestCase(unittest.TestCase):
if match(module): if match(module):
ext_include_dirs += get_additional_include_dirs() ext_include_dirs += get_additional_include_dirs()
self.copy_related_files(test_directory, workdir, module) self.copy_related_files(test_directory, workdir, module)
if extra_extension_args is None:
extra_extension_args = {}
extension = Extension( extension = Extension(
module, module,
sources = self.find_source_files(workdir, module), sources = self.find_source_files(workdir, module),
include_dirs = ext_include_dirs, include_dirs = ext_include_dirs,
extra_compile_args = CFLAGS, extra_compile_args = CFLAGS,
**extra_extension_args
) )
if self.language == 'cpp': if self.language == 'cpp':
extension.language = 'c++' extension.language = 'c++'
...@@ -626,6 +645,13 @@ class CythonUnitTestCase(CythonCompileTestCase): ...@@ -626,6 +645,13 @@ class CythonUnitTestCase(CythonCompileTestCase):
except Exception: except Exception:
pass pass
try:
import gdb
include_debugger = sys.version_info[:2] > (2, 5)
except:
include_debugger = False
def collect_unittests(path, module_prefix, suite, selectors): def collect_unittests(path, module_prefix, 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")
...@@ -634,8 +660,12 @@ def collect_unittests(path, module_prefix, suite, selectors): ...@@ -634,8 +660,12 @@ def collect_unittests(path, module_prefix, suite, selectors):
return dirname == "Tests" return dirname == "Tests"
loader = unittest.TestLoader() loader = unittest.TestLoader()
skipped_dirs = [] if include_debugger:
skipped_dirs = []
else:
cython_dir = os.path.dirname(os.path.abspath(__file__))
skipped_dirs = [os.path.join(cython_dir, 'Cython', 'Debugger')]
for dirpath, dirnames, filenames in os.walk(path): for dirpath, dirnames, filenames in os.walk(path):
if dirpath != path and "__init__.py" not in filenames: if dirpath != path and "__init__.py" not in filenames:
...@@ -660,34 +690,44 @@ def collect_unittests(path, module_prefix, suite, selectors): ...@@ -660,34 +690,44 @@ def collect_unittests(path, module_prefix, suite, selectors):
module = getattr(module, x) module = getattr(module, x)
suite.addTests([loader.loadTestsFromModule(module)]) suite.addTests([loader.loadTestsFromModule(module)])
def collect_doctests(path, module_prefix, suite, selectors): def collect_doctests(path, module_prefix, suite, selectors):
def package_matches(dirname): def package_matches(dirname):
if dirname == 'Debugger' and not include_debugger:
return False
return dirname not in ("Mac", "Distutils", "Plex") return dirname not in ("Mac", "Distutils", "Plex")
def file_matches(filename): def file_matches(filename):
return (filename.endswith(".py") and not ('~' in filename filename, ext = os.path.splitext(filename)
or '#' in filename or filename.startswith('.'))) blacklist = ['libcython', 'libpython', 'test_libcython_in_gdb',
'TestLibCython']
return (ext == '.py' and not
'~' in filename and not
'#' in filename and not
filename.startswith('.') and not
filename in blacklist)
import doctest, types import doctest, types
for dirpath, dirnames, filenames in os.walk(path): for dirpath, dirnames, filenames in os.walk(path):
parentname = os.path.split(dirpath)[-1] for dir in list(dirnames):
if package_matches(parentname): if not package_matches(dir):
for f in filenames: dirnames.remove(dir)
if file_matches(f): for f in filenames:
if not f.endswith('.py'): continue if file_matches(f):
filepath = os.path.join(dirpath, f) if not f.endswith('.py'): continue
if os.path.getsize(filepath) == 0: continue filepath = os.path.join(dirpath, f)
if 'no doctest' in open(filepath).next(): continue if os.path.getsize(filepath) == 0: continue
filepath = filepath[:-len(".py")] filepath = filepath[:-len(".py")]
modulename = module_prefix + filepath[len(path)+1:].replace(os.path.sep, '.') modulename = module_prefix + filepath[len(path)+1:].replace(os.path.sep, '.')
if not [ 1 for match in selectors if match(modulename) ]: if not [ 1 for match in selectors if match(modulename) ]:
continue continue
module = __import__(modulename) module = __import__(modulename)
for x in modulename.split('.')[1:]: for x in modulename.split('.')[1:]:
module = getattr(module, x) module = getattr(module, x)
if hasattr(module, "__doc__") or hasattr(module, "__test__"): if hasattr(module, "__doc__") or hasattr(module, "__test__"):
try: try:
suite.addTest(doctest.DocTestSuite(module)) suite.addTest(doctest.DocTestSuite(module))
except ValueError: # no tests except ValueError: # no tests
pass pass
class EndToEndTest(unittest.TestCase): class EndToEndTest(unittest.TestCase):
......
...@@ -70,6 +70,9 @@ else: ...@@ -70,6 +70,9 @@ else:
# specific to setup # specific to setup
setuptools_extra_args = {} setuptools_extra_args = {}
# tells whether to include cygdb (the script and the Cython.Debugger package
include_debugger = sys.version_info[:2] > (2, 5)
if 'setuptools' in sys.modules: if 'setuptools' in sys.modules:
setuptools_extra_args['zip_safe'] = False setuptools_extra_args['zip_safe'] = False
setuptools_extra_args['entry_points'] = { setuptools_extra_args['entry_points'] = {
...@@ -81,8 +84,12 @@ if 'setuptools' in sys.modules: ...@@ -81,8 +84,12 @@ if 'setuptools' in sys.modules:
else: else:
if os.name == "posix": if os.name == "posix":
scripts = ["bin/cython"] scripts = ["bin/cython"]
if include_debugger:
scripts.append('bin/cygdb')
else: else:
scripts = ["cython.py"] scripts = ["cython.py"]
if include_debugger:
scripts.append('cygdb.py')
def compile_cython_modules(profile=False, compile_more=False, cython_with_refnanny=False): def compile_cython_modules(profile=False, compile_more=False, cython_with_refnanny=False):
source_root = os.path.abspath(os.path.dirname(__file__)) source_root = os.path.abspath(os.path.dirname(__file__))
...@@ -249,6 +256,22 @@ setup_args.update(setuptools_extra_args) ...@@ -249,6 +256,22 @@ setup_args.update(setuptools_extra_args)
from Cython import __version__ as version from Cython import __version__ as version
packages = [
'Cython',
'Cython.Build',
'Cython.Compiler',
'Cython.Runtime',
'Cython.Distutils',
'Cython.Plex',
'Cython.Tests',
'Cython.Build.Tests',
'Cython.Compiler.Tests',
]
if include_debugger:
packages.append('Cython.Debugger')
packages.append('Cython.Debugger.Tests')
setup( setup(
name = 'Cython', name = 'Cython',
version = version, version = version,
...@@ -289,17 +312,7 @@ setup( ...@@ -289,17 +312,7 @@ setup(
], ],
scripts = scripts, scripts = scripts,
packages=[ packages=packages,
'Cython',
'Cython.Build',
'Cython.Compiler',
'Cython.Runtime',
'Cython.Distutils',
'Cython.Plex',
'Cython.Tests',
'Cython.Compiler.Tests',
],
# pyximport # pyximport
py_modules = ["pyximport/__init__", py_modules = ["pyximport/__init__",
......
...@@ -5,4 +5,4 @@ cdef void foo(): ...@@ -5,4 +5,4 @@ cdef void foo():
i = j = c i = j = c
a = b = k a = b = k
(a, b), c = (d, e), f = (x, y), z (a, b), c = (d, e), f = (x, y), z
# a, b = p, q = x, y # a, b = p, q = x, y
\ No newline at end of file
...@@ -3,4 +3,4 @@ cdef extern class Spam.Eggs.Ham: ...@@ -3,4 +3,4 @@ cdef extern class Spam.Eggs.Ham:
cdef Ham ham cdef Ham ham
ham = None ham = None
\ No newline at end of file
...@@ -23,4 +23,4 @@ def f(a, b, c): ...@@ -23,4 +23,4 @@ def f(a, b, c):
i = 1 i = 1
\ No newline at end of file
...@@ -2,4 +2,4 @@ def f(): ...@@ -2,4 +2,4 @@ def f():
from spam import eggs from spam import eggs
from spam.morespam import bacon, eggs, ham from spam.morespam import bacon, eggs, ham
from spam import eggs as ova from spam import eggs as ova
\ No newline at end of file
...@@ -8,4 +8,4 @@ def z(a, b, c): ...@@ -8,4 +8,4 @@ def z(a, b, c):
f(x = 42, **b) f(x = 42, **b)
f(a, *b) f(a, *b)
f(a, x = 42, *b, **c) f(a, x = 42, *b, **c)
\ No newline at end of file
...@@ -3,4 +3,4 @@ def f(): ...@@ -3,4 +3,4 @@ def f():
import spam.eggs import spam.eggs
import spam, eggs, ham import spam, eggs, ham
import spam as tasty import spam as tasty
\ No newline at end of file
...@@ -14,4 +14,4 @@ a[f(2)] += 4 ...@@ -14,4 +14,4 @@ a[f(2)] += 4
print x[1] print x[1]
print a[1] print a[1]
x[<object>f(1)] = 15 x[<object>f(1)] = 15
\ No newline at end of file
...@@ -16,4 +16,4 @@ def f(a, b, c, x): ...@@ -16,4 +16,4 @@ def f(a, b, c, x):
finally: finally:
i = 42 i = 42
\ No newline at end of file
from d cimport d from d cimport d
\ No newline at end of file
...@@ -64,4 +64,4 @@ cdef class EqualsEvens: ...@@ -64,4 +64,4 @@ cdef class EqualsEvens:
elif op == Py_NE: elif op == Py_NE:
return other % 2 == 1 return other % 2 == 1
else: else:
return False return False
\ No newline at end of file
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