6 if sys.version_info[:2] < (2, 3):
7 sys.stderr.write("Sorry, Cython requires Python 2.3 or later\n")
14 from sets import Set as set
21 from Scanning import PyrexScanner, FileSourceDescriptor
22 from Errors import PyrexError, CompileError, InternalError, error, warning
23 from Symtab import BuiltinScope, ModuleScope
24 from Cython import Utils
25 from Cython.Utils import open_new_file, replace_suffix
29 module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")
34 # For quick debugging in pipelines
38 def abort_on_errors(node):
39 # Stop the pipeline if there are any errors.
40 if Errors.num_errors != 0:
41 raise InternalError, "abort"
44 class CompilationData(object):
45 # Bundles the information that is passed from transform to transform.
46 # (For now, this is only)
48 # While Context contains every pxd ever loaded, path information etc.,
49 # this only contains the data related to a single compilation pass
51 # pyx ModuleNode Main code tree of this compilation.
52 # pxds {string : ModuleNode} Trees for the pxds used in the pyx.
53 # codewriter CCodeWriter Where to output final code.
54 # options CompilationOptions
55 # result CompilationResult
58 class Context(object):
59 # This class encapsulates the context needed for compiling
60 # one or more Cython implementation files along with their
61 # associated and imported declaration files. It includes
62 # the root of the module import namespace and the list
63 # of directories to search for include files.
65 # modules {string : ModuleScope}
66 # include_directories [string]
67 # future_directives [object]
68 # language_level int currently 2 or 3 for Python 2/3
70 def __init__(self, include_directories, compiler_directives, cpp=False, language_level=2):
71 #self.modules = {"__builtin__" : BuiltinScope()}
72 import Builtin, CythonScope
73 self.modules = {"__builtin__" : Builtin.builtin_scope}
74 self.modules["cython"] = CythonScope.create_cython_scope(self)
75 self.include_directories = include_directories
76 self.future_directives = set()
77 self.compiler_directives = compiler_directives
80 self.pxds = {} # full name -> node tree
82 standard_include_path = os.path.abspath(os.path.normpath(
83 os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
84 self.include_directories = include_directories + [standard_include_path]
86 self.set_language_level(language_level)
88 def set_language_level(self, level):
89 self.language_level = level
91 from Future import print_function, unicode_literals
92 self.future_directives.add(print_function)
93 self.future_directives.add(unicode_literals)
95 def create_pipeline(self, pxd, py=False):
96 from Visitor import PrintTree
97 from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
98 from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform
99 from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
100 from ParseTreeTransforms import InterpretCompilerDirectives, TransformBuiltinMethods
101 from ParseTreeTransforms import ExpandInplaceOperators
102 from TypeInference import MarkAssignments, MarkOverflowingArithmetic
103 from ParseTreeTransforms import AlignFunctionDefinitions, GilCheck
104 from AnalysedTreeTransforms import AutoTestDictTransform
105 from AutoDocTransforms import EmbedSignature
106 from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
107 from Optimize import EarlyReplaceBuiltinCalls, OptimizeBuiltinCalls
108 from Optimize import ConstantFolding, FinalOptimizePhase
109 from Optimize import DropRefcountingTransform
110 from Buffer import IntroduceBufferAuxiliaryVars
111 from ModuleNode import check_c_declarations, check_c_declarations_pxd
114 _check_c_declarations = check_c_declarations_pxd
115 _specific_post_parse = PxdPostParse(self)
117 _check_c_declarations = check_c_declarations
118 _specific_post_parse = None
121 _align_function_definitions = AlignFunctionDefinitions(self)
123 _align_function_definitions = None
128 _specific_post_parse,
129 InterpretCompilerDirectives(self, self.compiler_directives),
130 _align_function_definitions,
131 MarkClosureVisitor(self),
133 FlattenInListTransform(),
135 DecoratorTransform(self),
136 AnalyseDeclarationsTransform(self),
137 CreateClosureClasses(self),
138 AutoTestDictTransform(self),
139 EmbedSignature(self),
140 EarlyReplaceBuiltinCalls(self), ## Necessary?
141 MarkAssignments(self),
142 MarkOverflowingArithmetic(self),
143 TransformBuiltinMethods(self), ## Necessary?
144 IntroduceBufferAuxiliaryVars(self),
145 _check_c_declarations,
146 AnalyseExpressionsTransform(self),
147 ExpandInplaceOperators(self),
148 OptimizeBuiltinCalls(self), ## Necessary?
149 IterationTransform(),
151 DropRefcountingTransform(),
152 FinalOptimizePhase(self),
156 def create_pyx_pipeline(self, options, result, py=False):
157 def generate_pyx_code(module_node):
158 module_node.process_implementation(options, result)
159 result.compilation_source = module_node.compilation_source
162 def inject_pxd_code(module_node):
163 from textwrap import dedent
164 stats = module_node.body.stats
165 for name, (statlistnode, scope) in self.pxds.iteritems():
166 # Copy over function nodes to the module
167 # (this seems strange -- I believe the right concept is to split
168 # ModuleNode into a ModuleNode and a CodeGenerator, and tell that
169 # CodeGenerator to generate code both from the pyx and pxd ModuleNodes.
170 stats.append(statlistnode)
171 # Until utility code is moved to code generation phase everywhere,
172 # we need to copy it over to the main scope
173 module_node.scope.utility_code_list.extend(scope.utility_code_list)
177 if options.evaluate_tree_assertions:
178 from Cython.TestUtils import TreeAssertVisitor
179 test_support.append(TreeAssertVisitor())
183 ] + self.create_pipeline(pxd=False, py=py) + test_support + [
189 def create_pxd_pipeline(self, scope, module_name):
190 def parse_pxd(source_desc):
191 tree = self.parse(source_desc, scope, pxd=True,
192 full_module_name=module_name)
197 from CodeGeneration import ExtractPxdCode
199 # The pxd pipeline ends up with a CCodeWriter containing the
200 # code of the pxd, as well as a pxd scope.
201 return [parse_pxd] + self.create_pipeline(pxd=True) + [
202 ExtractPxdCode(self),
205 def create_py_pipeline(self, options, result):
206 return self.create_pyx_pipeline(options, result, py=True)
209 def process_pxd(self, source_desc, scope, module_name):
210 pipeline = self.create_pxd_pipeline(scope, module_name)
211 result = self.run_pipeline(pipeline, source_desc)
214 def nonfatal_error(self, exc):
215 return Errors.report_error(exc)
217 def run_pipeline(self, pipeline, source):
221 for phase in pipeline:
222 if phase is not None:
223 if DebugFlags.debug_verbose_pipeline:
225 print "Entering pipeline phase %r" % phase
227 if DebugFlags.debug_verbose_pipeline:
228 print " %.3f seconds" % (time() - t)
229 except CompileError, err:
231 Errors.report_error(err)
233 except InternalError, err:
234 # Only raise if there was not an earlier error
235 if Errors.num_errors == 0:
240 def find_module(self, module_name,
241 relative_to = None, pos = None, need_pxd = 1):
242 # Finds and returns the module scope corresponding to
243 # the given relative or absolute module name. If this
244 # is the first time the module has been requested, finds
245 # the corresponding .pxd file and process it.
246 # If relative_to is not None, it must be a module scope,
247 # and the module will first be searched for relative to
248 # that module, provided its name is not a dotted name.
249 debug_find_module = 0
250 if debug_find_module:
251 print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
252 module_name, relative_to, pos, need_pxd))
256 if not module_name_pattern.match(module_name):
258 pos = (module_name, 0, 0)
259 raise CompileError(pos,
260 "'%s' is not a valid module name" % module_name)
261 if "." not in module_name and relative_to:
262 if debug_find_module:
263 print("...trying relative import")
264 scope = relative_to.lookup_submodule(module_name)
266 qualified_name = relative_to.qualify_name(module_name)
267 pxd_pathname = self.find_pxd_file(qualified_name, pos)
269 scope = relative_to.find_submodule(module_name)
271 if debug_find_module:
272 print("...trying absolute import")
274 for name in module_name.split("."):
275 scope = scope.find_submodule(name)
276 if debug_find_module:
277 print("...scope =", scope)
278 if not scope.pxd_file_loaded:
279 if debug_find_module:
280 print("...pxd not loaded")
281 scope.pxd_file_loaded = 1
283 if debug_find_module:
284 print("...looking for pxd file")
285 pxd_pathname = self.find_pxd_file(module_name, pos)
286 if debug_find_module:
287 print("......found ", pxd_pathname)
288 if not pxd_pathname and need_pxd:
289 package_pathname = self.search_include_directories(module_name, ".py", pos)
290 if package_pathname and package_pathname.endswith('__init__.py'):
293 error(pos, "'%s.pxd' not found" % module_name)
296 if debug_find_module:
297 print("Context.find_module: Parsing %s" % pxd_pathname)
298 source_desc = FileSourceDescriptor(pxd_pathname)
299 err, result = self.process_pxd(source_desc, scope, module_name)
302 (pxd_codenodes, pxd_scope) = result
303 self.pxds[module_name] = (pxd_codenodes, pxd_scope)
308 def find_pxd_file(self, qualified_name, pos):
309 # Search include path for the .pxd file corresponding to the
310 # given fully-qualified module name.
311 # Will find either a dotted filename or a file in a
312 # package directory. If a source file position is given,
313 # the directory containing the source file is searched first
314 # for a dotted filename, and its containing package root
315 # directory is searched first for a non-dotted filename.
316 pxd = self.search_include_directories(qualified_name, ".pxd", pos)
317 if pxd is None: # XXX Keep this until Includes/Deprecated is removed
318 if (qualified_name.startswith('python') or
319 qualified_name in ('stdlib', 'stdio', 'stl')):
320 standard_include_path = os.path.abspath(os.path.normpath(
321 os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
322 deprecated_include_path = os.path.join(standard_include_path, 'Deprecated')
323 self.include_directories.append(deprecated_include_path)
325 pxd = self.search_include_directories(qualified_name, ".pxd", pos)
327 self.include_directories.pop()
329 name = qualified_name
330 if name.startswith('python'):
331 warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1)
332 elif name in ('stdlib', 'stdio'):
333 warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1)
334 elif name in ('stl'):
335 warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1)
338 def find_pyx_file(self, qualified_name, pos):
339 # Search include path for the .pyx file corresponding to the
340 # given fully-qualified module name, as for find_pxd_file().
341 return self.search_include_directories(qualified_name, ".pyx", pos)
343 def find_include_file(self, filename, pos):
344 # Search list of include directories for filename.
345 # Reports an error and returns None if not found.
346 path = self.search_include_directories(filename, "", pos,
349 error(pos, "'%s' not found" % filename)
352 def search_include_directories(self, qualified_name, suffix, pos,
354 # Search the list of include directories for the given
355 # file name. If a source file position is given, first
356 # searches the directory containing that file. Returns
357 # None if not found, but does not report an error.
358 # The 'include' option will disable package dereferencing.
359 dirs = self.include_directories
362 if not isinstance(file_desc, FileSourceDescriptor):
363 raise RuntimeError("Only file sources for code supported")
365 dirs = [os.path.dirname(file_desc.filename)] + dirs
367 dirs = [self.find_root_package_dir(file_desc.filename)] + dirs
369 dotted_filename = qualified_name
371 dotted_filename += suffix
373 names = qualified_name.split('.')
374 package_names = names[:-1]
375 module_name = names[-1]
376 module_filename = module_name + suffix
377 package_filename = "__init__" + suffix
380 path = os.path.join(dir, dotted_filename)
381 if Utils.path_exists(path):
384 package_dir = self.check_package_dir(dir, package_names)
385 if package_dir is not None:
386 path = os.path.join(package_dir, module_filename)
387 if Utils.path_exists(path):
389 path = os.path.join(dir, package_dir, module_name,
391 if Utils.path_exists(path):
395 def find_root_package_dir(self, file_path):
396 dir = os.path.dirname(file_path)
397 while self.is_package_dir(dir):
398 parent = os.path.dirname(dir)
404 def check_package_dir(self, dir, package_names):
405 for dirname in package_names:
406 dir = os.path.join(dir, dirname)
407 if not self.is_package_dir(dir):
411 def c_file_out_of_date(self, source_path):
412 c_path = Utils.replace_suffix(source_path, ".c")
413 if not os.path.exists(c_path):
415 c_time = Utils.modification_time(c_path)
416 if Utils.file_newer_than(source_path, c_time):
419 pxd_path = Utils.replace_suffix(source_path, ".pxd")
420 if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
422 for kind, name in self.read_dependency_file(source_path):
423 if kind == "cimport":
424 dep_path = self.find_pxd_file(name, pos)
425 elif kind == "include":
426 dep_path = self.search_include_directories(name, pos)
429 if dep_path and Utils.file_newer_than(dep_path, c_time):
433 def find_cimported_module_names(self, source_path):
434 return [ name for kind, name in self.read_dependency_file(source_path)
435 if kind == "cimport" ]
437 def is_package_dir(self, dir_path):
438 # Return true if the given directory is a package directory.
439 for filename in ("__init__.py",
442 path = os.path.join(dir_path, filename)
443 if Utils.path_exists(path):
446 def read_dependency_file(self, source_path):
447 dep_path = Utils.replace_suffix(source_path, ".dep")
448 if os.path.exists(dep_path):
449 f = open(dep_path, "rU")
450 chunks = [ line.strip().split(" ", 1)
451 for line in f.readlines()
452 if " " in line.strip() ]
458 def lookup_submodule(self, name):
459 # Look up a top-level module. Returns None if not found.
460 return self.modules.get(name, None)
462 def find_submodule(self, name):
463 # Find a top-level module, creating a new one if needed.
464 scope = self.lookup_submodule(name)
466 scope = ModuleScope(name,
467 parent_module = None, context = self)
468 self.modules[name] = scope
471 def parse(self, source_desc, scope, pxd, full_module_name):
472 if not isinstance(source_desc, FileSourceDescriptor):
473 raise RuntimeError("Only file sources for code supported")
474 source_filename = source_desc.filename
476 # Parse the given source file and return a parse tree.
478 f = Utils.open_source_file(source_filename, "rU")
480 s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
481 scope = scope, context = self)
482 tree = Parsing.p_module(s, pxd, full_module_name)
485 except UnicodeDecodeError, msg:
487 #traceback.print_exc()
488 error((source_desc, 0, 0), "Decoding error, missing or incorrect coding=<encoding-name> at top of source (%s)" % msg)
489 if Errors.num_errors > 0:
493 def extract_module_name(self, path, options):
494 # Find fully_qualified module name from the full pathname
496 dir, filename = os.path.split(path)
497 module_name, _ = os.path.splitext(filename)
498 if "." in module_name:
500 if module_name == "__init__":
501 dir, module_name = os.path.split(dir)
502 names = [module_name]
503 while self.is_package_dir(dir):
504 parent, package_name = os.path.split(dir)
507 names.append(package_name)
510 return ".".join(names)
512 def setup_errors(self, options, result):
513 if options.use_listing_file:
514 result.listing_file = Utils.replace_suffix(source, ".lis")
515 path = result.listing_file
518 Errors.open_listing_file(path=path,
519 echo_to_stderr=options.errors_to_stderr)
521 def teardown_errors(self, err, options, result):
522 source_desc = result.compilation_source.source_desc
523 if not isinstance(source_desc, FileSourceDescriptor):
524 raise RuntimeError("Only file sources for code supported")
525 Errors.close_listing_file()
526 result.num_errors = Errors.num_errors
527 if result.num_errors > 0:
529 if err and result.c_file:
531 Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
532 except EnvironmentError:
536 def create_parse(context):
538 source_desc = compsrc.source_desc
539 full_module_name = compsrc.full_module_name
540 initial_pos = (source_desc, 1, 0)
541 scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0)
542 tree = context.parse(source_desc, scope, pxd = 0, full_module_name = full_module_name)
543 tree.compilation_source = compsrc
549 def create_default_resultobj(compilation_source, options):
550 result = CompilationResult()
551 result.main_source_file = compilation_source.source_desc.filename
552 result.compilation_source = compilation_source
553 source_desc = compilation_source.source_desc
554 if options.output_file:
555 result.c_file = os.path.join(compilation_source.cwd, options.output_file)
561 result.c_file = Utils.replace_suffix(source_desc.filename, c_suffix)
564 def run_pipeline(source, options, full_module_name = None):
566 context = Context(options.include_path, options.compiler_directives,
567 options.cplus, options.language_level)
569 # Set up source object
571 source_desc = FileSourceDescriptor(os.path.join(cwd, source))
572 full_module_name = full_module_name or context.extract_module_name(source, options)
573 source = CompilationSource(source_desc, full_module_name, cwd)
575 # Set up result object
576 result = create_default_resultobj(source, options)
579 if source_desc.filename.endswith(".py"):
580 pipeline = context.create_py_pipeline(options, result)
582 pipeline = context.create_pyx_pipeline(options, result)
584 context.setup_errors(options, result)
585 err, enddata = context.run_pipeline(pipeline, source)
586 context.teardown_errors(err, options, result)
590 #------------------------------------------------------------------------
592 # Main Python entry points
594 #------------------------------------------------------------------------
596 class CompilationSource(object):
598 Contains the data necesarry to start up a compilation pipeline for
599 a single compilation unit.
601 def __init__(self, source_desc, full_module_name, cwd):
602 self.source_desc = source_desc
603 self.full_module_name = full_module_name
606 class CompilationOptions(object):
608 Options to the Cython compiler:
610 show_version boolean Display version number
611 use_listing_file boolean Generate a .lis file
612 errors_to_stderr boolean Echo errors to stderr when using .lis
613 include_path [string] Directories to search for include files
614 output_file string Name of generated .c file
615 generate_pxi boolean Generate .pxi file for public declarations
616 recursive boolean Recursively find and compile dependencies
617 timestamps boolean Only compile changed source files. If None,
618 defaults to true when recursive is true.
619 verbose boolean Always print source names being compiled
620 quiet boolean Don't print source names in recursive mode
621 compiler_directives dict Overrides for pragma options (see Options.py)
622 evaluate_tree_assertions boolean Test support: evaluate parse tree assertions
623 language_level integer The Python language level: 2 or 3
625 cplus boolean Compile as c++ code
628 def __init__(self, defaults = None, **kw):
629 self.include_path = []
631 if isinstance(defaults, CompilationOptions):
632 defaults = defaults.__dict__
634 defaults = default_options
635 self.__dict__.update(defaults)
636 self.__dict__.update(kw)
639 class CompilationResult(object):
641 Results from the Cython compiler:
643 c_file string or None The generated C source file
644 h_file string or None The generated C header file
645 i_file string or None The generated .pxi file
646 api_file string or None The generated C API .h file
647 listing_file string or None File of error messages
648 object_file string or None Result of compiling the C file
649 extension_file string or None Result of linking the object file
650 num_errors integer Number of compilation errors
651 compilation_source CompilationSource
659 self.listing_file = None
660 self.object_file = None
661 self.extension_file = None
662 self.main_source_file = None
665 class CompilationResultSet(dict):
667 Results from compiling multiple Pyrex source files. A mapping
668 from source file paths to CompilationResult instances. Also
669 has the following attributes:
671 num_errors integer Total number of compilation errors
676 def add(self, source, result):
677 self[source] = result
678 self.num_errors += result.num_errors
681 def compile_single(source, options, full_module_name = None):
683 compile_single(source, options, full_module_name)
685 Compile the given Pyrex implementation file and return a CompilationResult.
686 Always compiles a single file; does not perform timestamp checking or
689 return run_pipeline(source, options, full_module_name)
692 def compile_multiple(sources, options):
694 compile_multiple(sources, options)
696 Compiles the given sequence of Pyrex implementation files and returns
697 a CompilationResultSet. Performs timestamp checking and/or recursion
698 if these are specified in the options.
700 sources = [os.path.abspath(source) for source in sources]
702 results = CompilationResultSet()
703 recursive = options.recursive
704 timestamps = options.timestamps
705 if timestamps is None:
706 timestamps = recursive
707 verbose = options.verbose or ((recursive or timestamps) and not options.quiet)
708 for source in sources:
709 if source not in processed:
710 # Compiling multiple sources in one context doesn't quite
712 if not timestamps or context.c_file_out_of_date(source):
714 sys.stderr.write("Compiling %s\n" % source)
716 result = run_pipeline(source, options)
717 results.add(source, result)
718 processed.add(source)
720 for module_name in context.find_cimported_module_names(source):
721 path = context.find_pyx_file(module_name, [source])
726 "Cannot find .pyx file for cimported module '%s'\n" % module_name)
729 def compile(source, options = None, full_module_name = None, **kwds):
731 compile(source [, options], [, <option> = <value>]...)
733 Compile one or more Pyrex implementation files, with optional timestamp
734 checking and recursing on dependecies. The source argument may be a string
735 or a sequence of strings If it is a string and no recursion or timestamp
736 checking is requested, a CompilationResult is returned, otherwise a
737 CompilationResultSet is returned.
739 options = CompilationOptions(defaults = options, **kwds)
740 if isinstance(source, basestring) and not options.timestamps \
741 and not options.recursive:
742 return compile_single(source, options, full_module_name)
744 return compile_multiple(source, options)
746 #------------------------------------------------------------------------
748 # Main command-line entry point
750 #------------------------------------------------------------------------
751 def setuptools_main():
752 return main(command_line = 1)
754 def main(command_line = 0):
758 from CmdLine import parse_command_line
759 options, sources = parse_command_line(args)
761 options = CompilationOptions(default_options)
764 if options.show_version:
765 sys.stderr.write("Cython version %s\n" % Version.version)
766 if options.working_path!="":
767 os.chdir(options.working_path)
769 result = compile(sources, options)
770 if result.num_errors > 0:
772 except (EnvironmentError, PyrexError), e:
773 sys.stderr.write(str(e) + '\n')
780 #------------------------------------------------------------------------
782 # Set the default options depending on the platform
784 #------------------------------------------------------------------------
786 default_options = dict(
788 use_listing_file = 0,
789 errors_to_stderr = 1,
799 compiler_directives = {},
800 evaluate_tree_assertions = False,
801 emit_linenums = False,