Spaces:
Build error
Build error
# | |
# Cython Top Level | |
# | |
from __future__ import absolute_import | |
import os | |
import re | |
import sys | |
import io | |
if sys.version_info[:2] < (2, 6) or (3, 0) <= sys.version_info[:2] < (3, 3): | |
sys.stderr.write("Sorry, Cython requires Python 2.6+ or 3.3+, found %d.%d\n" % tuple(sys.version_info[:2])) | |
sys.exit(1) | |
try: | |
from __builtin__ import basestring | |
except ImportError: | |
basestring = str | |
# Do not import Parsing here, import it when needed, because Parsing imports | |
# Nodes, which globally needs debug command line options initialized to set a | |
# conditional metaclass. These options are processed by CmdLine called from | |
# main() in this file. | |
# import Parsing | |
from . import Errors | |
from .StringEncoding import EncodedString | |
from .Scanning import PyrexScanner, FileSourceDescriptor | |
from .Errors import PyrexError, CompileError, error, warning | |
from .Symtab import ModuleScope | |
from .. import Utils | |
from . import Options | |
from . import Version # legacy import needed by old PyTables versions | |
version = Version.version # legacy attribute - use "Cython.__version__" instead | |
module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$") | |
verbose = 0 | |
standard_include_path = os.path.abspath(os.path.join(os.path.dirname(__file__), | |
os.path.pardir, 'Includes')) | |
class CompilationData(object): | |
# Bundles the information that is passed from transform to transform. | |
# (For now, this is only) | |
# While Context contains every pxd ever loaded, path information etc., | |
# this only contains the data related to a single compilation pass | |
# | |
# pyx ModuleNode Main code tree of this compilation. | |
# pxds {string : ModuleNode} Trees for the pxds used in the pyx. | |
# codewriter CCodeWriter Where to output final code. | |
# options CompilationOptions | |
# result CompilationResult | |
pass | |
class Context(object): | |
# This class encapsulates the context needed for compiling | |
# one or more Cython implementation files along with their | |
# associated and imported declaration files. It includes | |
# the root of the module import namespace and the list | |
# of directories to search for include files. | |
# | |
# modules {string : ModuleScope} | |
# include_directories [string] | |
# future_directives [object] | |
# language_level int currently 2 or 3 for Python 2/3 | |
cython_scope = None | |
language_level = None # warn when not set but default to Py2 | |
def __init__(self, include_directories, compiler_directives, cpp=False, | |
language_level=None, options=None): | |
# cython_scope is a hack, set to False by subclasses, in order to break | |
# an infinite loop. | |
# Better code organization would fix it. | |
from . import Builtin, CythonScope | |
self.modules = {"__builtin__" : Builtin.builtin_scope} | |
self.cython_scope = CythonScope.create_cython_scope(self) | |
self.modules["cython"] = self.cython_scope | |
self.include_directories = include_directories | |
self.future_directives = set() | |
self.compiler_directives = compiler_directives | |
self.cpp = cpp | |
self.options = options | |
self.pxds = {} # full name -> node tree | |
self._interned = {} # (type(value), value, *key_args) -> interned_value | |
if language_level is not None: | |
self.set_language_level(language_level) | |
self.gdb_debug_outputwriter = None | |
def set_language_level(self, level): | |
from .Future import print_function, unicode_literals, absolute_import, division | |
future_directives = set() | |
if level == '3str': | |
level = 3 | |
else: | |
level = int(level) | |
if level >= 3: | |
future_directives.add(unicode_literals) | |
if level >= 3: | |
future_directives.update([print_function, absolute_import, division]) | |
self.language_level = level | |
self.future_directives = future_directives | |
if level >= 3: | |
self.modules['builtins'] = self.modules['__builtin__'] | |
def intern_ustring(self, value, encoding=None): | |
key = (EncodedString, value, encoding) | |
try: | |
return self._interned[key] | |
except KeyError: | |
pass | |
value = EncodedString(value) | |
if encoding: | |
value.encoding = encoding | |
self._interned[key] = value | |
return value | |
def intern_value(self, value, *key): | |
key = (type(value), value) + key | |
try: | |
return self._interned[key] | |
except KeyError: | |
pass | |
self._interned[key] = value | |
return value | |
# pipeline creation functions can now be found in Pipeline.py | |
def process_pxd(self, source_desc, scope, module_name): | |
from . import Pipeline | |
if isinstance(source_desc, FileSourceDescriptor) and source_desc._file_type == 'pyx': | |
source = CompilationSource(source_desc, module_name, os.getcwd()) | |
result_sink = create_default_resultobj(source, self.options) | |
pipeline = Pipeline.create_pyx_as_pxd_pipeline(self, result_sink) | |
result = Pipeline.run_pipeline(pipeline, source) | |
else: | |
pipeline = Pipeline.create_pxd_pipeline(self, scope, module_name) | |
result = Pipeline.run_pipeline(pipeline, source_desc) | |
return result | |
def nonfatal_error(self, exc): | |
return Errors.report_error(exc) | |
def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, | |
absolute_fallback=True): | |
# Finds and returns the module scope corresponding to | |
# the given relative or absolute module name. If this | |
# is the first time the module has been requested, finds | |
# the corresponding .pxd file and process it. | |
# If relative_to is not None, it must be a module scope, | |
# and the module will first be searched for relative to | |
# that module, provided its name is not a dotted name. | |
debug_find_module = 0 | |
if debug_find_module: | |
print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % ( | |
module_name, relative_to, pos, need_pxd)) | |
scope = None | |
pxd_pathname = None | |
if relative_to: | |
if module_name: | |
# from .module import ... | |
qualified_name = relative_to.qualify_name(module_name) | |
else: | |
# from . import ... | |
qualified_name = relative_to.qualified_name | |
scope = relative_to | |
relative_to = None | |
else: | |
qualified_name = module_name | |
if not module_name_pattern.match(qualified_name): | |
raise CompileError(pos or (module_name, 0, 0), | |
"'%s' is not a valid module name" % module_name) | |
if relative_to: | |
if debug_find_module: | |
print("...trying relative import") | |
scope = relative_to.lookup_submodule(module_name) | |
if not scope: | |
pxd_pathname = self.find_pxd_file(qualified_name, pos) | |
if pxd_pathname: | |
scope = relative_to.find_submodule(module_name) | |
if not scope: | |
if debug_find_module: | |
print("...trying absolute import") | |
if absolute_fallback: | |
qualified_name = module_name | |
scope = self | |
for name in qualified_name.split("."): | |
scope = scope.find_submodule(name) | |
if debug_find_module: | |
print("...scope = %s" % scope) | |
if not scope.pxd_file_loaded: | |
if debug_find_module: | |
print("...pxd not loaded") | |
if not pxd_pathname: | |
if debug_find_module: | |
print("...looking for pxd file") | |
# Only look in sys.path if we are explicitly looking | |
# for a .pxd file. | |
pxd_pathname = self.find_pxd_file(qualified_name, pos, sys_path=need_pxd) | |
if debug_find_module: | |
print("......found %s" % pxd_pathname) | |
if not pxd_pathname and need_pxd: | |
# Set pxd_file_loaded such that we don't need to | |
# look for the non-existing pxd file next time. | |
scope.pxd_file_loaded = True | |
package_pathname = self.search_include_directories(qualified_name, ".py", pos) | |
if package_pathname and package_pathname.endswith('__init__.py'): | |
pass | |
else: | |
error(pos, "'%s.pxd' not found" % qualified_name.replace('.', os.sep)) | |
if pxd_pathname: | |
scope.pxd_file_loaded = True | |
try: | |
if debug_find_module: | |
print("Context.find_module: Parsing %s" % pxd_pathname) | |
rel_path = module_name.replace('.', os.sep) + os.path.splitext(pxd_pathname)[1] | |
if not pxd_pathname.endswith(rel_path): | |
rel_path = pxd_pathname # safety measure to prevent printing incorrect paths | |
source_desc = FileSourceDescriptor(pxd_pathname, rel_path) | |
err, result = self.process_pxd(source_desc, scope, qualified_name) | |
if err: | |
raise err | |
(pxd_codenodes, pxd_scope) = result | |
self.pxds[module_name] = (pxd_codenodes, pxd_scope) | |
except CompileError: | |
pass | |
return scope | |
def find_pxd_file(self, qualified_name, pos, sys_path=True): | |
# Search include path (and sys.path if sys_path is True) for | |
# the .pxd file corresponding to the given fully-qualified | |
# module name. | |
# Will find either a dotted filename or a file in a | |
# package directory. If a source file position is given, | |
# the directory containing the source file is searched first | |
# for a dotted filename, and its containing package root | |
# directory is searched first for a non-dotted filename. | |
pxd = self.search_include_directories(qualified_name, ".pxd", pos, sys_path=sys_path) | |
if pxd is None: # XXX Keep this until Includes/Deprecated is removed | |
if (qualified_name.startswith('python') or | |
qualified_name in ('stdlib', 'stdio', 'stl')): | |
standard_include_path = os.path.abspath(os.path.normpath( | |
os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes'))) | |
deprecated_include_path = os.path.join(standard_include_path, 'Deprecated') | |
self.include_directories.append(deprecated_include_path) | |
try: | |
pxd = self.search_include_directories(qualified_name, ".pxd", pos) | |
finally: | |
self.include_directories.pop() | |
if pxd: | |
name = qualified_name | |
if name.startswith('python'): | |
warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1) | |
elif name in ('stdlib', 'stdio'): | |
warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1) | |
elif name in ('stl'): | |
warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1) | |
if pxd is None and Options.cimport_from_pyx: | |
return self.find_pyx_file(qualified_name, pos) | |
return pxd | |
def find_pyx_file(self, qualified_name, pos): | |
# Search include path for the .pyx file corresponding to the | |
# given fully-qualified module name, as for find_pxd_file(). | |
return self.search_include_directories(qualified_name, ".pyx", pos) | |
def find_include_file(self, filename, pos): | |
# Search list of include directories for filename. | |
# Reports an error and returns None if not found. | |
path = self.search_include_directories(filename, "", pos, | |
include=True) | |
if not path: | |
error(pos, "'%s' not found" % filename) | |
return path | |
def search_include_directories(self, qualified_name, suffix, pos, | |
include=False, sys_path=False): | |
include_dirs = self.include_directories | |
if sys_path: | |
include_dirs = include_dirs + sys.path | |
# include_dirs must be hashable for caching in @cached_function | |
include_dirs = tuple(include_dirs + [standard_include_path]) | |
return search_include_directories(include_dirs, qualified_name, | |
suffix, pos, include) | |
def find_root_package_dir(self, file_path): | |
return Utils.find_root_package_dir(file_path) | |
def check_package_dir(self, dir, package_names): | |
return Utils.check_package_dir(dir, tuple(package_names)) | |
def c_file_out_of_date(self, source_path, output_path): | |
if not os.path.exists(output_path): | |
return 1 | |
c_time = Utils.modification_time(output_path) | |
if Utils.file_newer_than(source_path, c_time): | |
return 1 | |
pos = [source_path] | |
pxd_path = Utils.replace_suffix(source_path, ".pxd") | |
if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time): | |
return 1 | |
for kind, name in self.read_dependency_file(source_path): | |
if kind == "cimport": | |
dep_path = self.find_pxd_file(name, pos) | |
elif kind == "include": | |
dep_path = self.search_include_directories(name, pos) | |
else: | |
continue | |
if dep_path and Utils.file_newer_than(dep_path, c_time): | |
return 1 | |
return 0 | |
def find_cimported_module_names(self, source_path): | |
return [ name for kind, name in self.read_dependency_file(source_path) | |
if kind == "cimport" ] | |
def is_package_dir(self, dir_path): | |
return Utils.is_package_dir(dir_path) | |
def read_dependency_file(self, source_path): | |
dep_path = Utils.replace_suffix(source_path, ".dep") | |
if os.path.exists(dep_path): | |
f = open(dep_path, "rU") | |
chunks = [ line.strip().split(" ", 1) | |
for line in f.readlines() | |
if " " in line.strip() ] | |
f.close() | |
return chunks | |
else: | |
return () | |
def lookup_submodule(self, name): | |
# Look up a top-level module. Returns None if not found. | |
return self.modules.get(name, None) | |
def find_submodule(self, name): | |
# Find a top-level module, creating a new one if needed. | |
scope = self.lookup_submodule(name) | |
if not scope: | |
scope = ModuleScope(name, | |
parent_module = None, context = self) | |
self.modules[name] = scope | |
return scope | |
def parse(self, source_desc, scope, pxd, full_module_name): | |
if not isinstance(source_desc, FileSourceDescriptor): | |
raise RuntimeError("Only file sources for code supported") | |
source_filename = source_desc.filename | |
scope.cpp = self.cpp | |
# Parse the given source file and return a parse tree. | |
num_errors = Errors.num_errors | |
try: | |
with Utils.open_source_file(source_filename) as f: | |
from . import Parsing | |
s = PyrexScanner(f, source_desc, source_encoding = f.encoding, | |
scope = scope, context = self) | |
tree = Parsing.p_module(s, pxd, full_module_name) | |
if self.options.formal_grammar: | |
try: | |
from ..Parser import ConcreteSyntaxTree | |
except ImportError: | |
raise RuntimeError( | |
"Formal grammar can only be used with compiled Cython with an available pgen.") | |
ConcreteSyntaxTree.p_module(source_filename) | |
except UnicodeDecodeError as e: | |
#import traceback | |
#traceback.print_exc() | |
raise self._report_decode_error(source_desc, e) | |
if Errors.num_errors > num_errors: | |
raise CompileError() | |
return tree | |
def _report_decode_error(self, source_desc, exc): | |
msg = exc.args[-1] | |
position = exc.args[2] | |
encoding = exc.args[0] | |
line = 1 | |
column = idx = 0 | |
with io.open(source_desc.filename, "r", encoding='iso8859-1', newline='') as f: | |
for line, data in enumerate(f, 1): | |
idx += len(data) | |
if idx >= position: | |
column = position - (idx - len(data)) + 1 | |
break | |
return error((source_desc, line, column), | |
"Decoding error, missing or incorrect coding=<encoding-name> " | |
"at top of source (cannot decode with encoding %r: %s)" % (encoding, msg)) | |
def extract_module_name(self, path, options): | |
# Find fully_qualified module name from the full pathname | |
# of a source file. | |
dir, filename = os.path.split(path) | |
module_name, _ = os.path.splitext(filename) | |
if "." in module_name: | |
return module_name | |
names = [module_name] | |
while self.is_package_dir(dir): | |
parent, package_name = os.path.split(dir) | |
if parent == dir: | |
break | |
names.append(package_name) | |
dir = parent | |
names.reverse() | |
return ".".join(names) | |
def setup_errors(self, options, result): | |
Errors.reset() # clear any remaining error state | |
if options.use_listing_file: | |
path = result.listing_file = Utils.replace_suffix(result.main_source_file, ".lis") | |
else: | |
path = None | |
Errors.open_listing_file(path=path, | |
echo_to_stderr=options.errors_to_stderr) | |
def teardown_errors(self, err, options, result): | |
source_desc = result.compilation_source.source_desc | |
if not isinstance(source_desc, FileSourceDescriptor): | |
raise RuntimeError("Only file sources for code supported") | |
Errors.close_listing_file() | |
result.num_errors = Errors.num_errors | |
if result.num_errors > 0: | |
err = True | |
if err and result.c_file: | |
try: | |
Utils.castrate_file(result.c_file, os.stat(source_desc.filename)) | |
except EnvironmentError: | |
pass | |
result.c_file = None | |
def get_output_filename(source_filename, cwd, options): | |
if options.cplus: | |
c_suffix = ".cpp" | |
else: | |
c_suffix = ".c" | |
suggested_file_name = Utils.replace_suffix(source_filename, c_suffix) | |
if options.output_file: | |
out_path = os.path.join(cwd, options.output_file) | |
if os.path.isdir(out_path): | |
return os.path.join(out_path, os.path.basename(suggested_file_name)) | |
else: | |
return out_path | |
else: | |
return suggested_file_name | |
def create_default_resultobj(compilation_source, options): | |
result = CompilationResult() | |
result.main_source_file = compilation_source.source_desc.filename | |
result.compilation_source = compilation_source | |
source_desc = compilation_source.source_desc | |
result.c_file = get_output_filename(source_desc.filename, | |
compilation_source.cwd, options) | |
result.embedded_metadata = options.embedded_metadata | |
return result | |
def run_pipeline(source, options, full_module_name=None, context=None): | |
from . import Pipeline | |
source_ext = os.path.splitext(source)[1] | |
options.configure_language_defaults(source_ext[1:]) # py/pyx | |
if context is None: | |
context = options.create_context() | |
# Set up source object | |
cwd = os.getcwd() | |
abs_path = os.path.abspath(source) | |
full_module_name = full_module_name or context.extract_module_name(source, options) | |
Utils.raise_error_if_module_name_forbidden(full_module_name) | |
if options.relative_path_in_code_position_comments: | |
rel_path = full_module_name.replace('.', os.sep) + source_ext | |
if not abs_path.endswith(rel_path): | |
rel_path = source # safety measure to prevent printing incorrect paths | |
else: | |
rel_path = abs_path | |
source_desc = FileSourceDescriptor(abs_path, rel_path) | |
source = CompilationSource(source_desc, full_module_name, cwd) | |
# Set up result object | |
result = create_default_resultobj(source, options) | |
if options.annotate is None: | |
# By default, decide based on whether an html file already exists. | |
html_filename = os.path.splitext(result.c_file)[0] + ".html" | |
if os.path.exists(html_filename): | |
with io.open(html_filename, "r", encoding="UTF-8") as html_file: | |
if u'<!-- Generated by Cython' in html_file.read(100): | |
options.annotate = True | |
# Get pipeline | |
if source_ext.lower() == '.py' or not source_ext: | |
pipeline = Pipeline.create_py_pipeline(context, options, result) | |
else: | |
pipeline = Pipeline.create_pyx_pipeline(context, options, result) | |
context.setup_errors(options, result) | |
err, enddata = Pipeline.run_pipeline(pipeline, source) | |
context.teardown_errors(err, options, result) | |
return result | |
# ------------------------------------------------------------------------ | |
# | |
# Main Python entry points | |
# | |
# ------------------------------------------------------------------------ | |
class CompilationSource(object): | |
""" | |
Contains the data necessary to start up a compilation pipeline for | |
a single compilation unit. | |
""" | |
def __init__(self, source_desc, full_module_name, cwd): | |
self.source_desc = source_desc | |
self.full_module_name = full_module_name | |
self.cwd = cwd | |
class CompilationOptions(object): | |
r""" | |
See default_options at the end of this module for a list of all possible | |
options and CmdLine.usage and CmdLine.parse_command_line() for their | |
meaning. | |
""" | |
def __init__(self, defaults=None, **kw): | |
self.include_path = [] | |
if defaults: | |
if isinstance(defaults, CompilationOptions): | |
defaults = defaults.__dict__ | |
else: | |
defaults = default_options | |
options = dict(defaults) | |
options.update(kw) | |
# let's assume 'default_options' contains a value for most known compiler options | |
# and validate against them | |
unknown_options = set(options) - set(default_options) | |
# ignore valid options that are not in the defaults | |
unknown_options.difference_update(['include_path']) | |
if unknown_options: | |
message = "got unknown compilation option%s, please remove: %s" % ( | |
's' if len(unknown_options) > 1 else '', | |
', '.join(unknown_options)) | |
raise ValueError(message) | |
directive_defaults = Options.get_directive_defaults() | |
directives = dict(options['compiler_directives']) # copy mutable field | |
# check for invalid directives | |
unknown_directives = set(directives) - set(directive_defaults) | |
if unknown_directives: | |
message = "got unknown compiler directive%s: %s" % ( | |
's' if len(unknown_directives) > 1 else '', | |
', '.join(unknown_directives)) | |
raise ValueError(message) | |
options['compiler_directives'] = directives | |
if directives.get('np_pythran', False) and not options['cplus']: | |
import warnings | |
warnings.warn("C++ mode forced when in Pythran mode!") | |
options['cplus'] = True | |
if 'language_level' in directives and 'language_level' not in kw: | |
options['language_level'] = directives['language_level'] | |
elif not options.get('language_level'): | |
options['language_level'] = directive_defaults.get('language_level') | |
if 'formal_grammar' in directives and 'formal_grammar' not in kw: | |
options['formal_grammar'] = directives['formal_grammar'] | |
if options['cache'] is True: | |
options['cache'] = os.path.join(Utils.get_cython_cache_dir(), 'compiler') | |
self.__dict__.update(options) | |
def configure_language_defaults(self, source_extension): | |
if source_extension == 'py': | |
if self.compiler_directives.get('binding') is None: | |
self.compiler_directives['binding'] = True | |
def create_context(self): | |
return Context(self.include_path, self.compiler_directives, | |
self.cplus, self.language_level, options=self) | |
def get_fingerprint(self): | |
r""" | |
Return a string that contains all the options that are relevant for cache invalidation. | |
""" | |
# Collect only the data that can affect the generated file(s). | |
data = {} | |
for key, value in self.__dict__.items(): | |
if key in ['show_version', 'errors_to_stderr', 'verbose', 'quiet']: | |
# verbosity flags have no influence on the compilation result | |
continue | |
elif key in ['output_file', 'output_dir']: | |
# ignore the exact name of the output file | |
continue | |
elif key in ['timestamps']: | |
# the cache cares about the content of files, not about the timestamps of sources | |
continue | |
elif key in ['cache']: | |
# hopefully caching has no influence on the compilation result | |
continue | |
elif key in ['compiler_directives']: | |
# directives passed on to the C compiler do not influence the generated C code | |
continue | |
elif key in ['include_path']: | |
# this path changes which headers are tracked as dependencies, | |
# it has no influence on the generated C code | |
continue | |
elif key in ['working_path']: | |
# this path changes where modules and pxd files are found; | |
# their content is part of the fingerprint anyway, their | |
# absolute path does not matter | |
continue | |
elif key in ['create_extension']: | |
# create_extension() has already mangled the options, e.g., | |
# embedded_metadata, when the fingerprint is computed so we | |
# ignore it here. | |
continue | |
elif key in ['build_dir']: | |
# the (temporary) directory where we collect dependencies | |
# has no influence on the C output | |
continue | |
elif key in ['use_listing_file', 'generate_pxi', 'annotate', 'annotate_coverage_xml']: | |
# all output files are contained in the cache so the types of | |
# files generated must be part of the fingerprint | |
data[key] = value | |
elif key in ['formal_grammar', 'evaluate_tree_assertions']: | |
# these bits can change whether compilation to C passes/fails | |
data[key] = value | |
elif key in ['embedded_metadata', 'emit_linenums', 'c_line_in_traceback', 'gdb_debug', 'relative_path_in_code_position_comments']: | |
# the generated code contains additional bits when these are set | |
data[key] = value | |
elif key in ['cplus', 'language_level', 'compile_time_env', 'np_pythran']: | |
# assorted bits that, e.g., influence the parser | |
data[key] = value | |
elif key == ['capi_reexport_cincludes']: | |
if self.capi_reexport_cincludes: | |
# our caching implementation does not yet include fingerprints of all the header files | |
raise NotImplementedError('capi_reexport_cincludes is not compatible with Cython caching') | |
elif key == ['common_utility_include_dir']: | |
if self.common_utility_include_dir: | |
raise NotImplementedError('common_utility_include_dir is not compatible with Cython caching yet') | |
else: | |
# any unexpected option should go into the fingerprint; it's better | |
# to recompile than to return incorrect results from the cache. | |
data[key] = value | |
def to_fingerprint(item): | |
r""" | |
Recursively turn item into a string, turning dicts into lists with | |
deterministic ordering. | |
""" | |
if isinstance(item, dict): | |
item = sorted([(repr(key), to_fingerprint(value)) for key, value in item.items()]) | |
return repr(item) | |
return to_fingerprint(data) | |
class CompilationResult(object): | |
""" | |
Results from the Cython compiler: | |
c_file string or None The generated C source file | |
h_file string or None The generated C header file | |
i_file string or None The generated .pxi file | |
api_file string or None The generated C API .h file | |
listing_file string or None File of error messages | |
object_file string or None Result of compiling the C file | |
extension_file string or None Result of linking the object file | |
num_errors integer Number of compilation errors | |
compilation_source CompilationSource | |
""" | |
def __init__(self): | |
self.c_file = None | |
self.h_file = None | |
self.i_file = None | |
self.api_file = None | |
self.listing_file = None | |
self.object_file = None | |
self.extension_file = None | |
self.main_source_file = None | |
class CompilationResultSet(dict): | |
""" | |
Results from compiling multiple Pyrex source files. A mapping | |
from source file paths to CompilationResult instances. Also | |
has the following attributes: | |
num_errors integer Total number of compilation errors | |
""" | |
num_errors = 0 | |
def add(self, source, result): | |
self[source] = result | |
self.num_errors += result.num_errors | |
def compile_single(source, options, full_module_name = None): | |
""" | |
compile_single(source, options, full_module_name) | |
Compile the given Pyrex implementation file and return a CompilationResult. | |
Always compiles a single file; does not perform timestamp checking or | |
recursion. | |
""" | |
return run_pipeline(source, options, full_module_name) | |
def compile_multiple(sources, options): | |
""" | |
compile_multiple(sources, options) | |
Compiles the given sequence of Pyrex implementation files and returns | |
a CompilationResultSet. Performs timestamp checking and/or recursion | |
if these are specified in the options. | |
""" | |
# run_pipeline creates the context | |
# context = options.create_context() | |
sources = [os.path.abspath(source) for source in sources] | |
processed = set() | |
results = CompilationResultSet() | |
timestamps = options.timestamps | |
verbose = options.verbose | |
context = None | |
cwd = os.getcwd() | |
for source in sources: | |
if source not in processed: | |
if context is None: | |
context = options.create_context() | |
output_filename = get_output_filename(source, cwd, options) | |
out_of_date = context.c_file_out_of_date(source, output_filename) | |
if (not timestamps) or out_of_date: | |
if verbose: | |
sys.stderr.write("Compiling %s\n" % source) | |
result = run_pipeline(source, options, context=context) | |
results.add(source, result) | |
# Compiling multiple sources in one context doesn't quite | |
# work properly yet. | |
context = None | |
processed.add(source) | |
return results | |
def compile(source, options = None, full_module_name = None, **kwds): | |
""" | |
compile(source [, options], [, <option> = <value>]...) | |
Compile one or more Pyrex implementation files, with optional timestamp | |
checking and recursing on dependencies. The source argument may be a string | |
or a sequence of strings. If it is a string and no recursion or timestamp | |
checking is requested, a CompilationResult is returned, otherwise a | |
CompilationResultSet is returned. | |
""" | |
options = CompilationOptions(defaults = options, **kwds) | |
if isinstance(source, basestring) and not options.timestamps: | |
return compile_single(source, options, full_module_name) | |
else: | |
return compile_multiple(source, options) | |
def search_include_directories(dirs, qualified_name, suffix, pos, include=False): | |
""" | |
Search the list of include directories for the given file name. | |
If a source file position is given, first searches the directory | |
containing that file. Returns None if not found, but does not | |
report an error. | |
The 'include' option will disable package dereferencing. | |
""" | |
if pos: | |
file_desc = pos[0] | |
if not isinstance(file_desc, FileSourceDescriptor): | |
raise RuntimeError("Only file sources for code supported") | |
if include: | |
dirs = (os.path.dirname(file_desc.filename),) + dirs | |
else: | |
dirs = (Utils.find_root_package_dir(file_desc.filename),) + dirs | |
dotted_filename = qualified_name | |
if suffix: | |
dotted_filename += suffix | |
if not include: | |
names = qualified_name.split('.') | |
package_names = tuple(names[:-1]) | |
module_name = names[-1] | |
module_filename = module_name + suffix | |
package_filename = "__init__" + suffix | |
for dirname in dirs: | |
path = os.path.join(dirname, dotted_filename) | |
if os.path.exists(path): | |
return path | |
if not include: | |
package_dir = Utils.check_package_dir(dirname, package_names) | |
if package_dir is not None: | |
path = os.path.join(package_dir, module_filename) | |
if os.path.exists(path): | |
return path | |
path = os.path.join(package_dir, module_name, | |
package_filename) | |
if os.path.exists(path): | |
return path | |
return None | |
# ------------------------------------------------------------------------ | |
# | |
# Main command-line entry point | |
# | |
# ------------------------------------------------------------------------ | |
def setuptools_main(): | |
return main(command_line = 1) | |
def main(command_line = 0): | |
args = sys.argv[1:] | |
any_failures = 0 | |
if command_line: | |
from .CmdLine import parse_command_line | |
options, sources = parse_command_line(args) | |
else: | |
options = CompilationOptions(default_options) | |
sources = args | |
if options.show_version: | |
sys.stderr.write("Cython version %s\n" % version) | |
if options.working_path!="": | |
os.chdir(options.working_path) | |
try: | |
result = compile(sources, options) | |
if result.num_errors > 0: | |
any_failures = 1 | |
except (EnvironmentError, PyrexError) as e: | |
sys.stderr.write(str(e) + '\n') | |
any_failures = 1 | |
if any_failures: | |
sys.exit(1) | |
# ------------------------------------------------------------------------ | |
# | |
# Set the default options depending on the platform | |
# | |
# ------------------------------------------------------------------------ | |
default_options = dict( | |
show_version = 0, | |
use_listing_file = 0, | |
errors_to_stderr = 1, | |
cplus = 0, | |
output_file = None, | |
annotate = None, | |
annotate_coverage_xml = None, | |
generate_pxi = 0, | |
capi_reexport_cincludes = 0, | |
working_path = "", | |
timestamps = None, | |
verbose = 0, | |
quiet = 0, | |
compiler_directives = {}, | |
embedded_metadata = {}, | |
evaluate_tree_assertions = False, | |
emit_linenums = False, | |
relative_path_in_code_position_comments = True, | |
c_line_in_traceback = True, | |
language_level = None, # warn but default to 2 | |
formal_grammar = False, | |
gdb_debug = False, | |
compile_time_env = None, | |
common_utility_include_dir = None, | |
output_dir=None, | |
build_dir=None, | |
cache=None, | |
create_extension=None, | |
np_pythran=False | |
) | |