Main.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. #
  2. # Cython Top Level
  3. #
  4. from __future__ import absolute_import
  5. import os
  6. import re
  7. import sys
  8. import io
  9. if sys.version_info[:2] < (2, 6) or (3, 0) <= sys.version_info[:2] < (3, 3):
  10. sys.stderr.write("Sorry, Cython requires Python 2.6+ or 3.3+, found %d.%d\n" % tuple(sys.version_info[:2]))
  11. sys.exit(1)
  12. try:
  13. from __builtin__ import basestring
  14. except ImportError:
  15. basestring = str
  16. # Do not import Parsing here, import it when needed, because Parsing imports
  17. # Nodes, which globally needs debug command line options initialized to set a
  18. # conditional metaclass. These options are processed by CmdLine called from
  19. # main() in this file.
  20. # import Parsing
  21. from . import Errors
  22. from .StringEncoding import EncodedString
  23. from .Scanning import PyrexScanner, FileSourceDescriptor
  24. from .Errors import PyrexError, CompileError, error, warning
  25. from .Symtab import ModuleScope
  26. from .. import Utils
  27. from . import Options
  28. from . import Version # legacy import needed by old PyTables versions
  29. version = Version.version # legacy attribute - use "Cython.__version__" instead
  30. module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")
  31. verbose = 0
  32. standard_include_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
  33. os.path.pardir, 'Includes'))
  34. class CompilationData(object):
  35. # Bundles the information that is passed from transform to transform.
  36. # (For now, this is only)
  37. # While Context contains every pxd ever loaded, path information etc.,
  38. # this only contains the data related to a single compilation pass
  39. #
  40. # pyx ModuleNode Main code tree of this compilation.
  41. # pxds {string : ModuleNode} Trees for the pxds used in the pyx.
  42. # codewriter CCodeWriter Where to output final code.
  43. # options CompilationOptions
  44. # result CompilationResult
  45. pass
  46. class Context(object):
  47. # This class encapsulates the context needed for compiling
  48. # one or more Cython implementation files along with their
  49. # associated and imported declaration files. It includes
  50. # the root of the module import namespace and the list
  51. # of directories to search for include files.
  52. #
  53. # modules {string : ModuleScope}
  54. # include_directories [string]
  55. # future_directives [object]
  56. # language_level int currently 2 or 3 for Python 2/3
  57. cython_scope = None
  58. language_level = None # warn when not set but default to Py2
  59. def __init__(self, include_directories, compiler_directives, cpp=False,
  60. language_level=None, options=None):
  61. # cython_scope is a hack, set to False by subclasses, in order to break
  62. # an infinite loop.
  63. # Better code organization would fix it.
  64. from . import Builtin, CythonScope
  65. self.modules = {"__builtin__" : Builtin.builtin_scope}
  66. self.cython_scope = CythonScope.create_cython_scope(self)
  67. self.modules["cython"] = self.cython_scope
  68. self.include_directories = include_directories
  69. self.future_directives = set()
  70. self.compiler_directives = compiler_directives
  71. self.cpp = cpp
  72. self.options = options
  73. self.pxds = {} # full name -> node tree
  74. self._interned = {} # (type(value), value, *key_args) -> interned_value
  75. if language_level is not None:
  76. self.set_language_level(language_level)
  77. self.gdb_debug_outputwriter = None
  78. def set_language_level(self, level):
  79. from .Future import print_function, unicode_literals, absolute_import, division
  80. future_directives = set()
  81. if level == '3str':
  82. level = 3
  83. else:
  84. level = int(level)
  85. if level >= 3:
  86. future_directives.add(unicode_literals)
  87. if level >= 3:
  88. future_directives.update([print_function, absolute_import, division])
  89. self.language_level = level
  90. self.future_directives = future_directives
  91. if level >= 3:
  92. self.modules['builtins'] = self.modules['__builtin__']
  93. def intern_ustring(self, value, encoding=None):
  94. key = (EncodedString, value, encoding)
  95. try:
  96. return self._interned[key]
  97. except KeyError:
  98. pass
  99. value = EncodedString(value)
  100. if encoding:
  101. value.encoding = encoding
  102. self._interned[key] = value
  103. return value
  104. def intern_value(self, value, *key):
  105. key = (type(value), value) + key
  106. try:
  107. return self._interned[key]
  108. except KeyError:
  109. pass
  110. self._interned[key] = value
  111. return value
  112. # pipeline creation functions can now be found in Pipeline.py
  113. def process_pxd(self, source_desc, scope, module_name):
  114. from . import Pipeline
  115. if isinstance(source_desc, FileSourceDescriptor) and source_desc._file_type == 'pyx':
  116. source = CompilationSource(source_desc, module_name, os.getcwd())
  117. result_sink = create_default_resultobj(source, self.options)
  118. pipeline = Pipeline.create_pyx_as_pxd_pipeline(self, result_sink)
  119. result = Pipeline.run_pipeline(pipeline, source)
  120. else:
  121. pipeline = Pipeline.create_pxd_pipeline(self, scope, module_name)
  122. result = Pipeline.run_pipeline(pipeline, source_desc)
  123. return result
  124. def nonfatal_error(self, exc):
  125. return Errors.report_error(exc)
  126. def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1,
  127. absolute_fallback=True):
  128. # Finds and returns the module scope corresponding to
  129. # the given relative or absolute module name. If this
  130. # is the first time the module has been requested, finds
  131. # the corresponding .pxd file and process it.
  132. # If relative_to is not None, it must be a module scope,
  133. # and the module will first be searched for relative to
  134. # that module, provided its name is not a dotted name.
  135. debug_find_module = 0
  136. if debug_find_module:
  137. print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
  138. module_name, relative_to, pos, need_pxd))
  139. scope = None
  140. pxd_pathname = None
  141. if relative_to:
  142. if module_name:
  143. # from .module import ...
  144. qualified_name = relative_to.qualify_name(module_name)
  145. else:
  146. # from . import ...
  147. qualified_name = relative_to.qualified_name
  148. scope = relative_to
  149. relative_to = None
  150. else:
  151. qualified_name = module_name
  152. if not module_name_pattern.match(qualified_name):
  153. raise CompileError(pos or (module_name, 0, 0),
  154. "'%s' is not a valid module name" % module_name)
  155. if relative_to:
  156. if debug_find_module:
  157. print("...trying relative import")
  158. scope = relative_to.lookup_submodule(module_name)
  159. if not scope:
  160. pxd_pathname = self.find_pxd_file(qualified_name, pos)
  161. if pxd_pathname:
  162. scope = relative_to.find_submodule(module_name)
  163. if not scope:
  164. if debug_find_module:
  165. print("...trying absolute import")
  166. if absolute_fallback:
  167. qualified_name = module_name
  168. scope = self
  169. for name in qualified_name.split("."):
  170. scope = scope.find_submodule(name)
  171. if debug_find_module:
  172. print("...scope = %s" % scope)
  173. if not scope.pxd_file_loaded:
  174. if debug_find_module:
  175. print("...pxd not loaded")
  176. if not pxd_pathname:
  177. if debug_find_module:
  178. print("...looking for pxd file")
  179. # Only look in sys.path if we are explicitly looking
  180. # for a .pxd file.
  181. pxd_pathname = self.find_pxd_file(qualified_name, pos, sys_path=need_pxd)
  182. if debug_find_module:
  183. print("......found %s" % pxd_pathname)
  184. if not pxd_pathname and need_pxd:
  185. # Set pxd_file_loaded such that we don't need to
  186. # look for the non-existing pxd file next time.
  187. scope.pxd_file_loaded = True
  188. package_pathname = self.search_include_directories(qualified_name, ".py", pos)
  189. if package_pathname and package_pathname.endswith('__init__.py'):
  190. pass
  191. else:
  192. error(pos, "'%s.pxd' not found" % qualified_name.replace('.', os.sep))
  193. if pxd_pathname:
  194. scope.pxd_file_loaded = True
  195. try:
  196. if debug_find_module:
  197. print("Context.find_module: Parsing %s" % pxd_pathname)
  198. rel_path = module_name.replace('.', os.sep) + os.path.splitext(pxd_pathname)[1]
  199. if not pxd_pathname.endswith(rel_path):
  200. rel_path = pxd_pathname # safety measure to prevent printing incorrect paths
  201. source_desc = FileSourceDescriptor(pxd_pathname, rel_path)
  202. err, result = self.process_pxd(source_desc, scope, qualified_name)
  203. if err:
  204. raise err
  205. (pxd_codenodes, pxd_scope) = result
  206. self.pxds[module_name] = (pxd_codenodes, pxd_scope)
  207. except CompileError:
  208. pass
  209. return scope
  210. def find_pxd_file(self, qualified_name, pos, sys_path=True):
  211. # Search include path (and sys.path if sys_path is True) for
  212. # the .pxd file corresponding to the given fully-qualified
  213. # module name.
  214. # Will find either a dotted filename or a file in a
  215. # package directory. If a source file position is given,
  216. # the directory containing the source file is searched first
  217. # for a dotted filename, and its containing package root
  218. # directory is searched first for a non-dotted filename.
  219. pxd = self.search_include_directories(qualified_name, ".pxd", pos, sys_path=sys_path)
  220. if pxd is None: # XXX Keep this until Includes/Deprecated is removed
  221. if (qualified_name.startswith('python') or
  222. qualified_name in ('stdlib', 'stdio', 'stl')):
  223. standard_include_path = os.path.abspath(os.path.normpath(
  224. os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
  225. deprecated_include_path = os.path.join(standard_include_path, 'Deprecated')
  226. self.include_directories.append(deprecated_include_path)
  227. try:
  228. pxd = self.search_include_directories(qualified_name, ".pxd", pos)
  229. finally:
  230. self.include_directories.pop()
  231. if pxd:
  232. name = qualified_name
  233. if name.startswith('python'):
  234. warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1)
  235. elif name in ('stdlib', 'stdio'):
  236. warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1)
  237. elif name in ('stl'):
  238. warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1)
  239. if pxd is None and Options.cimport_from_pyx:
  240. return self.find_pyx_file(qualified_name, pos)
  241. return pxd
  242. def find_pyx_file(self, qualified_name, pos):
  243. # Search include path for the .pyx file corresponding to the
  244. # given fully-qualified module name, as for find_pxd_file().
  245. return self.search_include_directories(qualified_name, ".pyx", pos)
  246. def find_include_file(self, filename, pos):
  247. # Search list of include directories for filename.
  248. # Reports an error and returns None if not found.
  249. path = self.search_include_directories(filename, "", pos,
  250. include=True)
  251. if not path:
  252. error(pos, "'%s' not found" % filename)
  253. return path
  254. def search_include_directories(self, qualified_name, suffix, pos,
  255. include=False, sys_path=False):
  256. include_dirs = self.include_directories
  257. if sys_path:
  258. include_dirs = include_dirs + sys.path
  259. # include_dirs must be hashable for caching in @cached_function
  260. include_dirs = tuple(include_dirs + [standard_include_path])
  261. return search_include_directories(include_dirs, qualified_name,
  262. suffix, pos, include)
  263. def find_root_package_dir(self, file_path):
  264. return Utils.find_root_package_dir(file_path)
  265. def check_package_dir(self, dir, package_names):
  266. return Utils.check_package_dir(dir, tuple(package_names))
  267. def c_file_out_of_date(self, source_path, output_path):
  268. if not os.path.exists(output_path):
  269. return 1
  270. c_time = Utils.modification_time(output_path)
  271. if Utils.file_newer_than(source_path, c_time):
  272. return 1
  273. pos = [source_path]
  274. pxd_path = Utils.replace_suffix(source_path, ".pxd")
  275. if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
  276. return 1
  277. for kind, name in self.read_dependency_file(source_path):
  278. if kind == "cimport":
  279. dep_path = self.find_pxd_file(name, pos)
  280. elif kind == "include":
  281. dep_path = self.search_include_directories(name, pos)
  282. else:
  283. continue
  284. if dep_path and Utils.file_newer_than(dep_path, c_time):
  285. return 1
  286. return 0
  287. def find_cimported_module_names(self, source_path):
  288. return [ name for kind, name in self.read_dependency_file(source_path)
  289. if kind == "cimport" ]
  290. def is_package_dir(self, dir_path):
  291. return Utils.is_package_dir(dir_path)
  292. def read_dependency_file(self, source_path):
  293. dep_path = Utils.replace_suffix(source_path, ".dep")
  294. if os.path.exists(dep_path):
  295. f = open(dep_path, "rU")
  296. chunks = [ line.strip().split(" ", 1)
  297. for line in f.readlines()
  298. if " " in line.strip() ]
  299. f.close()
  300. return chunks
  301. else:
  302. return ()
  303. def lookup_submodule(self, name):
  304. # Look up a top-level module. Returns None if not found.
  305. return self.modules.get(name, None)
  306. def find_submodule(self, name):
  307. # Find a top-level module, creating a new one if needed.
  308. scope = self.lookup_submodule(name)
  309. if not scope:
  310. scope = ModuleScope(name,
  311. parent_module = None, context = self)
  312. self.modules[name] = scope
  313. return scope
  314. def parse(self, source_desc, scope, pxd, full_module_name):
  315. if not isinstance(source_desc, FileSourceDescriptor):
  316. raise RuntimeError("Only file sources for code supported")
  317. source_filename = source_desc.filename
  318. scope.cpp = self.cpp
  319. # Parse the given source file and return a parse tree.
  320. num_errors = Errors.num_errors
  321. try:
  322. with Utils.open_source_file(source_filename) as f:
  323. from . import Parsing
  324. s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
  325. scope = scope, context = self)
  326. tree = Parsing.p_module(s, pxd, full_module_name)
  327. if self.options.formal_grammar:
  328. try:
  329. from ..Parser import ConcreteSyntaxTree
  330. except ImportError:
  331. raise RuntimeError(
  332. "Formal grammar can only be used with compiled Cython with an available pgen.")
  333. ConcreteSyntaxTree.p_module(source_filename)
  334. except UnicodeDecodeError as e:
  335. #import traceback
  336. #traceback.print_exc()
  337. raise self._report_decode_error(source_desc, e)
  338. if Errors.num_errors > num_errors:
  339. raise CompileError()
  340. return tree
  341. def _report_decode_error(self, source_desc, exc):
  342. msg = exc.args[-1]
  343. position = exc.args[2]
  344. encoding = exc.args[0]
  345. line = 1
  346. column = idx = 0
  347. with io.open(source_desc.filename, "r", encoding='iso8859-1', newline='') as f:
  348. for line, data in enumerate(f, 1):
  349. idx += len(data)
  350. if idx >= position:
  351. column = position - (idx - len(data)) + 1
  352. break
  353. return error((source_desc, line, column),
  354. "Decoding error, missing or incorrect coding=<encoding-name> "
  355. "at top of source (cannot decode with encoding %r: %s)" % (encoding, msg))
  356. def extract_module_name(self, path, options):
  357. # Find fully_qualified module name from the full pathname
  358. # of a source file.
  359. dir, filename = os.path.split(path)
  360. module_name, _ = os.path.splitext(filename)
  361. if "." in module_name:
  362. return module_name
  363. names = [module_name]
  364. while self.is_package_dir(dir):
  365. parent, package_name = os.path.split(dir)
  366. if parent == dir:
  367. break
  368. names.append(package_name)
  369. dir = parent
  370. names.reverse()
  371. return ".".join(names)
  372. def setup_errors(self, options, result):
  373. Errors.reset() # clear any remaining error state
  374. if options.use_listing_file:
  375. path = result.listing_file = Utils.replace_suffix(result.main_source_file, ".lis")
  376. else:
  377. path = None
  378. Errors.open_listing_file(path=path,
  379. echo_to_stderr=options.errors_to_stderr)
  380. def teardown_errors(self, err, options, result):
  381. source_desc = result.compilation_source.source_desc
  382. if not isinstance(source_desc, FileSourceDescriptor):
  383. raise RuntimeError("Only file sources for code supported")
  384. Errors.close_listing_file()
  385. result.num_errors = Errors.num_errors
  386. if result.num_errors > 0:
  387. err = True
  388. if err and result.c_file:
  389. try:
  390. Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
  391. except EnvironmentError:
  392. pass
  393. result.c_file = None
  394. def get_output_filename(source_filename, cwd, options):
  395. if options.cplus:
  396. c_suffix = ".cpp"
  397. else:
  398. c_suffix = ".c"
  399. suggested_file_name = Utils.replace_suffix(source_filename, c_suffix)
  400. if options.output_file:
  401. out_path = os.path.join(cwd, options.output_file)
  402. if os.path.isdir(out_path):
  403. return os.path.join(out_path, os.path.basename(suggested_file_name))
  404. else:
  405. return out_path
  406. else:
  407. return suggested_file_name
  408. def create_default_resultobj(compilation_source, options):
  409. result = CompilationResult()
  410. result.main_source_file = compilation_source.source_desc.filename
  411. result.compilation_source = compilation_source
  412. source_desc = compilation_source.source_desc
  413. result.c_file = get_output_filename(source_desc.filename,
  414. compilation_source.cwd, options)
  415. result.embedded_metadata = options.embedded_metadata
  416. return result
  417. def run_pipeline(source, options, full_module_name=None, context=None):
  418. from . import Pipeline
  419. source_ext = os.path.splitext(source)[1]
  420. options.configure_language_defaults(source_ext[1:]) # py/pyx
  421. if context is None:
  422. context = options.create_context()
  423. # Set up source object
  424. cwd = os.getcwd()
  425. abs_path = os.path.abspath(source)
  426. full_module_name = full_module_name or context.extract_module_name(source, options)
  427. Utils.raise_error_if_module_name_forbidden(full_module_name)
  428. if options.relative_path_in_code_position_comments:
  429. rel_path = full_module_name.replace('.', os.sep) + source_ext
  430. if not abs_path.endswith(rel_path):
  431. rel_path = source # safety measure to prevent printing incorrect paths
  432. else:
  433. rel_path = abs_path
  434. source_desc = FileSourceDescriptor(abs_path, rel_path)
  435. source = CompilationSource(source_desc, full_module_name, cwd)
  436. # Set up result object
  437. result = create_default_resultobj(source, options)
  438. if options.annotate is None:
  439. # By default, decide based on whether an html file already exists.
  440. html_filename = os.path.splitext(result.c_file)[0] + ".html"
  441. if os.path.exists(html_filename):
  442. with io.open(html_filename, "r", encoding="UTF-8") as html_file:
  443. if u'<!-- Generated by Cython' in html_file.read(100):
  444. options.annotate = True
  445. # Get pipeline
  446. if source_ext.lower() == '.py' or not source_ext:
  447. pipeline = Pipeline.create_py_pipeline(context, options, result)
  448. else:
  449. pipeline = Pipeline.create_pyx_pipeline(context, options, result)
  450. context.setup_errors(options, result)
  451. err, enddata = Pipeline.run_pipeline(pipeline, source)
  452. context.teardown_errors(err, options, result)
  453. return result
  454. # ------------------------------------------------------------------------
  455. #
  456. # Main Python entry points
  457. #
  458. # ------------------------------------------------------------------------
  459. class CompilationSource(object):
  460. """
  461. Contains the data necessary to start up a compilation pipeline for
  462. a single compilation unit.
  463. """
  464. def __init__(self, source_desc, full_module_name, cwd):
  465. self.source_desc = source_desc
  466. self.full_module_name = full_module_name
  467. self.cwd = cwd
  468. class CompilationOptions(object):
  469. r"""
  470. See default_options at the end of this module for a list of all possible
  471. options and CmdLine.usage and CmdLine.parse_command_line() for their
  472. meaning.
  473. """
  474. def __init__(self, defaults=None, **kw):
  475. self.include_path = []
  476. if defaults:
  477. if isinstance(defaults, CompilationOptions):
  478. defaults = defaults.__dict__
  479. else:
  480. defaults = default_options
  481. options = dict(defaults)
  482. options.update(kw)
  483. # let's assume 'default_options' contains a value for most known compiler options
  484. # and validate against them
  485. unknown_options = set(options) - set(default_options)
  486. # ignore valid options that are not in the defaults
  487. unknown_options.difference_update(['include_path'])
  488. if unknown_options:
  489. message = "got unknown compilation option%s, please remove: %s" % (
  490. 's' if len(unknown_options) > 1 else '',
  491. ', '.join(unknown_options))
  492. raise ValueError(message)
  493. directive_defaults = Options.get_directive_defaults()
  494. directives = dict(options['compiler_directives']) # copy mutable field
  495. # check for invalid directives
  496. unknown_directives = set(directives) - set(directive_defaults)
  497. if unknown_directives:
  498. message = "got unknown compiler directive%s: %s" % (
  499. 's' if len(unknown_directives) > 1 else '',
  500. ', '.join(unknown_directives))
  501. raise ValueError(message)
  502. options['compiler_directives'] = directives
  503. if directives.get('np_pythran', False) and not options['cplus']:
  504. import warnings
  505. warnings.warn("C++ mode forced when in Pythran mode!")
  506. options['cplus'] = True
  507. if 'language_level' in directives and 'language_level' not in kw:
  508. options['language_level'] = directives['language_level']
  509. elif not options.get('language_level'):
  510. options['language_level'] = directive_defaults.get('language_level')
  511. if 'formal_grammar' in directives and 'formal_grammar' not in kw:
  512. options['formal_grammar'] = directives['formal_grammar']
  513. if options['cache'] is True:
  514. options['cache'] = os.path.join(Utils.get_cython_cache_dir(), 'compiler')
  515. self.__dict__.update(options)
  516. def configure_language_defaults(self, source_extension):
  517. if source_extension == 'py':
  518. if self.compiler_directives.get('binding') is None:
  519. self.compiler_directives['binding'] = True
  520. def create_context(self):
  521. return Context(self.include_path, self.compiler_directives,
  522. self.cplus, self.language_level, options=self)
  523. def get_fingerprint(self):
  524. r"""
  525. Return a string that contains all the options that are relevant for cache invalidation.
  526. """
  527. # Collect only the data that can affect the generated file(s).
  528. data = {}
  529. for key, value in self.__dict__.items():
  530. if key in ['show_version', 'errors_to_stderr', 'verbose', 'quiet']:
  531. # verbosity flags have no influence on the compilation result
  532. continue
  533. elif key in ['output_file', 'output_dir']:
  534. # ignore the exact name of the output file
  535. continue
  536. elif key in ['timestamps']:
  537. # the cache cares about the content of files, not about the timestamps of sources
  538. continue
  539. elif key in ['cache']:
  540. # hopefully caching has no influence on the compilation result
  541. continue
  542. elif key in ['compiler_directives']:
  543. # directives passed on to the C compiler do not influence the generated C code
  544. continue
  545. elif key in ['include_path']:
  546. # this path changes which headers are tracked as dependencies,
  547. # it has no influence on the generated C code
  548. continue
  549. elif key in ['working_path']:
  550. # this path changes where modules and pxd files are found;
  551. # their content is part of the fingerprint anyway, their
  552. # absolute path does not matter
  553. continue
  554. elif key in ['create_extension']:
  555. # create_extension() has already mangled the options, e.g.,
  556. # embedded_metadata, when the fingerprint is computed so we
  557. # ignore it here.
  558. continue
  559. elif key in ['build_dir']:
  560. # the (temporary) directory where we collect dependencies
  561. # has no influence on the C output
  562. continue
  563. elif key in ['use_listing_file', 'generate_pxi', 'annotate', 'annotate_coverage_xml']:
  564. # all output files are contained in the cache so the types of
  565. # files generated must be part of the fingerprint
  566. data[key] = value
  567. elif key in ['formal_grammar', 'evaluate_tree_assertions']:
  568. # these bits can change whether compilation to C passes/fails
  569. data[key] = value
  570. elif key in ['embedded_metadata', 'emit_linenums', 'c_line_in_traceback', 'gdb_debug', 'relative_path_in_code_position_comments']:
  571. # the generated code contains additional bits when these are set
  572. data[key] = value
  573. elif key in ['cplus', 'language_level', 'compile_time_env', 'np_pythran']:
  574. # assorted bits that, e.g., influence the parser
  575. data[key] = value
  576. elif key == ['capi_reexport_cincludes']:
  577. if self.capi_reexport_cincludes:
  578. # our caching implementation does not yet include fingerprints of all the header files
  579. raise NotImplementedError('capi_reexport_cincludes is not compatible with Cython caching')
  580. elif key == ['common_utility_include_dir']:
  581. if self.common_utility_include_dir:
  582. raise NotImplementedError('common_utility_include_dir is not compatible with Cython caching yet')
  583. else:
  584. # any unexpected option should go into the fingerprint; it's better
  585. # to recompile than to return incorrect results from the cache.
  586. data[key] = value
  587. def to_fingerprint(item):
  588. r"""
  589. Recursively turn item into a string, turning dicts into lists with
  590. deterministic ordering.
  591. """
  592. if isinstance(item, dict):
  593. item = sorted([(repr(key), to_fingerprint(value)) for key, value in item.items()])
  594. return repr(item)
  595. return to_fingerprint(data)
  596. class CompilationResult(object):
  597. """
  598. Results from the Cython compiler:
  599. c_file string or None The generated C source file
  600. h_file string or None The generated C header file
  601. i_file string or None The generated .pxi file
  602. api_file string or None The generated C API .h file
  603. listing_file string or None File of error messages
  604. object_file string or None Result of compiling the C file
  605. extension_file string or None Result of linking the object file
  606. num_errors integer Number of compilation errors
  607. compilation_source CompilationSource
  608. """
  609. def __init__(self):
  610. self.c_file = None
  611. self.h_file = None
  612. self.i_file = None
  613. self.api_file = None
  614. self.listing_file = None
  615. self.object_file = None
  616. self.extension_file = None
  617. self.main_source_file = None
  618. class CompilationResultSet(dict):
  619. """
  620. Results from compiling multiple Pyrex source files. A mapping
  621. from source file paths to CompilationResult instances. Also
  622. has the following attributes:
  623. num_errors integer Total number of compilation errors
  624. """
  625. num_errors = 0
  626. def add(self, source, result):
  627. self[source] = result
  628. self.num_errors += result.num_errors
  629. def compile_single(source, options, full_module_name = None):
  630. """
  631. compile_single(source, options, full_module_name)
  632. Compile the given Pyrex implementation file and return a CompilationResult.
  633. Always compiles a single file; does not perform timestamp checking or
  634. recursion.
  635. """
  636. return run_pipeline(source, options, full_module_name)
  637. def compile_multiple(sources, options):
  638. """
  639. compile_multiple(sources, options)
  640. Compiles the given sequence of Pyrex implementation files and returns
  641. a CompilationResultSet. Performs timestamp checking and/or recursion
  642. if these are specified in the options.
  643. """
  644. # run_pipeline creates the context
  645. # context = options.create_context()
  646. sources = [os.path.abspath(source) for source in sources]
  647. processed = set()
  648. results = CompilationResultSet()
  649. timestamps = options.timestamps
  650. verbose = options.verbose
  651. context = None
  652. cwd = os.getcwd()
  653. for source in sources:
  654. if source not in processed:
  655. if context is None:
  656. context = options.create_context()
  657. output_filename = get_output_filename(source, cwd, options)
  658. out_of_date = context.c_file_out_of_date(source, output_filename)
  659. if (not timestamps) or out_of_date:
  660. if verbose:
  661. sys.stderr.write("Compiling %s\n" % source)
  662. result = run_pipeline(source, options, context=context)
  663. results.add(source, result)
  664. # Compiling multiple sources in one context doesn't quite
  665. # work properly yet.
  666. context = None
  667. processed.add(source)
  668. return results
  669. def compile(source, options = None, full_module_name = None, **kwds):
  670. """
  671. compile(source [, options], [, <option> = <value>]...)
  672. Compile one or more Pyrex implementation files, with optional timestamp
  673. checking and recursing on dependencies. The source argument may be a string
  674. or a sequence of strings. If it is a string and no recursion or timestamp
  675. checking is requested, a CompilationResult is returned, otherwise a
  676. CompilationResultSet is returned.
  677. """
  678. options = CompilationOptions(defaults = options, **kwds)
  679. if isinstance(source, basestring) and not options.timestamps:
  680. return compile_single(source, options, full_module_name)
  681. else:
  682. return compile_multiple(source, options)
  683. @Utils.cached_function
  684. def search_include_directories(dirs, qualified_name, suffix, pos, include=False):
  685. """
  686. Search the list of include directories for the given file name.
  687. If a source file position is given, first searches the directory
  688. containing that file. Returns None if not found, but does not
  689. report an error.
  690. The 'include' option will disable package dereferencing.
  691. """
  692. if pos:
  693. file_desc = pos[0]
  694. if not isinstance(file_desc, FileSourceDescriptor):
  695. raise RuntimeError("Only file sources for code supported")
  696. if include:
  697. dirs = (os.path.dirname(file_desc.filename),) + dirs
  698. else:
  699. dirs = (Utils.find_root_package_dir(file_desc.filename),) + dirs
  700. dotted_filename = qualified_name
  701. if suffix:
  702. dotted_filename += suffix
  703. if not include:
  704. names = qualified_name.split('.')
  705. package_names = tuple(names[:-1])
  706. module_name = names[-1]
  707. module_filename = module_name + suffix
  708. package_filename = "__init__" + suffix
  709. for dirname in dirs:
  710. path = os.path.join(dirname, dotted_filename)
  711. if os.path.exists(path):
  712. return path
  713. if not include:
  714. package_dir = Utils.check_package_dir(dirname, package_names)
  715. if package_dir is not None:
  716. path = os.path.join(package_dir, module_filename)
  717. if os.path.exists(path):
  718. return path
  719. path = os.path.join(package_dir, module_name,
  720. package_filename)
  721. if os.path.exists(path):
  722. return path
  723. return None
  724. # ------------------------------------------------------------------------
  725. #
  726. # Main command-line entry point
  727. #
  728. # ------------------------------------------------------------------------
  729. def setuptools_main():
  730. return main(command_line = 1)
  731. def main(command_line = 0):
  732. args = sys.argv[1:]
  733. any_failures = 0
  734. if command_line:
  735. from .CmdLine import parse_command_line
  736. options, sources = parse_command_line(args)
  737. else:
  738. options = CompilationOptions(default_options)
  739. sources = args
  740. if options.show_version:
  741. sys.stderr.write("Cython version %s\n" % version)
  742. if options.working_path!="":
  743. os.chdir(options.working_path)
  744. try:
  745. result = compile(sources, options)
  746. if result.num_errors > 0:
  747. any_failures = 1
  748. except (EnvironmentError, PyrexError) as e:
  749. sys.stderr.write(str(e) + '\n')
  750. any_failures = 1
  751. if any_failures:
  752. sys.exit(1)
  753. # ------------------------------------------------------------------------
  754. #
  755. # Set the default options depending on the platform
  756. #
  757. # ------------------------------------------------------------------------
  758. default_options = dict(
  759. show_version = 0,
  760. use_listing_file = 0,
  761. errors_to_stderr = 1,
  762. cplus = 0,
  763. output_file = None,
  764. annotate = None,
  765. annotate_coverage_xml = None,
  766. generate_pxi = 0,
  767. capi_reexport_cincludes = 0,
  768. working_path = "",
  769. timestamps = None,
  770. verbose = 0,
  771. quiet = 0,
  772. compiler_directives = {},
  773. embedded_metadata = {},
  774. evaluate_tree_assertions = False,
  775. emit_linenums = False,
  776. relative_path_in_code_position_comments = True,
  777. c_line_in_traceback = True,
  778. language_level = None, # warn but default to 2
  779. formal_grammar = False,
  780. gdb_debug = False,
  781. compile_time_env = None,
  782. common_utility_include_dir = None,
  783. output_dir=None,
  784. build_dir=None,
  785. cache=None,
  786. create_extension=None,
  787. np_pythran=False
  788. )