Main.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  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. pxd_pathname = self.find_pxd_file(qualified_name, pos)
  180. if debug_find_module:
  181. print("......found %s" % pxd_pathname)
  182. if not pxd_pathname and need_pxd:
  183. # Set pxd_file_loaded such that we don't need to
  184. # look for the non-existing pxd file next time.
  185. scope.pxd_file_loaded = True
  186. package_pathname = self.search_include_directories(qualified_name, ".py", pos)
  187. if package_pathname and package_pathname.endswith('__init__.py'):
  188. pass
  189. else:
  190. error(pos, "'%s.pxd' not found" % qualified_name.replace('.', os.sep))
  191. if pxd_pathname:
  192. scope.pxd_file_loaded = True
  193. try:
  194. if debug_find_module:
  195. print("Context.find_module: Parsing %s" % pxd_pathname)
  196. rel_path = module_name.replace('.', os.sep) + os.path.splitext(pxd_pathname)[1]
  197. if not pxd_pathname.endswith(rel_path):
  198. rel_path = pxd_pathname # safety measure to prevent printing incorrect paths
  199. if Options.source_root:
  200. rel_path = os.path.relpath(pxd_pathname, Options.source_root)
  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=False):
  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 options.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. if Options.source_root:
  435. rel_path = os.path.relpath(abs_path, Options.source_root)
  436. source_desc = FileSourceDescriptor(abs_path, rel_path)
  437. source = CompilationSource(source_desc, full_module_name, cwd)
  438. # Set up result object
  439. result = create_default_resultobj(source, options)
  440. if options.annotate is None:
  441. # By default, decide based on whether an html file already exists.
  442. html_filename = os.path.splitext(result.c_file)[0] + ".html"
  443. if os.path.exists(html_filename):
  444. with io.open(html_filename, "r", encoding="UTF-8") as html_file:
  445. if u'<!-- Generated by Cython' in html_file.read(100):
  446. options.annotate = True
  447. # Get pipeline
  448. if source_ext.lower() == '.py' or not source_ext:
  449. pipeline = Pipeline.create_py_pipeline(context, options, result)
  450. else:
  451. pipeline = Pipeline.create_pyx_pipeline(context, options, result)
  452. context.setup_errors(options, result)
  453. err, enddata = Pipeline.run_pipeline(pipeline, source)
  454. context.teardown_errors(err, options, result)
  455. if err is None and options.depfile:
  456. from ..Build.Dependencies import create_dependency_tree
  457. dependencies = create_dependency_tree(context).all_dependencies(result.main_source_file)
  458. Utils.write_depfile(result.c_file, result.main_source_file, dependencies)
  459. return result
  460. # ------------------------------------------------------------------------
  461. #
  462. # Main Python entry points
  463. #
  464. # ------------------------------------------------------------------------
  465. class CompilationSource(object):
  466. """
  467. Contains the data necessary to start up a compilation pipeline for
  468. a single compilation unit.
  469. """
  470. def __init__(self, source_desc, full_module_name, cwd):
  471. self.source_desc = source_desc
  472. self.full_module_name = full_module_name
  473. self.cwd = cwd
  474. class CompilationOptions(object):
  475. r"""
  476. See default_options at the end of this module for a list of all possible
  477. options and CmdLine.usage and CmdLine.parse_command_line() for their
  478. meaning.
  479. """
  480. def __init__(self, defaults=None, **kw):
  481. self.include_path = []
  482. if defaults:
  483. if isinstance(defaults, CompilationOptions):
  484. defaults = defaults.__dict__
  485. else:
  486. defaults = default_options
  487. options = dict(defaults)
  488. options.update(kw)
  489. # let's assume 'default_options' contains a value for most known compiler options
  490. # and validate against them
  491. unknown_options = set(options) - set(default_options)
  492. # ignore valid options that are not in the defaults
  493. unknown_options.difference_update(['include_path'])
  494. if unknown_options:
  495. message = "got unknown compilation option%s, please remove: %s" % (
  496. 's' if len(unknown_options) > 1 else '',
  497. ', '.join(unknown_options))
  498. raise ValueError(message)
  499. directive_defaults = Options.get_directive_defaults()
  500. directives = dict(options['compiler_directives']) # copy mutable field
  501. # check for invalid directives
  502. unknown_directives = set(directives) - set(directive_defaults)
  503. if unknown_directives:
  504. message = "got unknown compiler directive%s: %s" % (
  505. 's' if len(unknown_directives) > 1 else '',
  506. ', '.join(unknown_directives))
  507. raise ValueError(message)
  508. options['compiler_directives'] = directives
  509. if directives.get('np_pythran', False) and not options['cplus']:
  510. import warnings
  511. warnings.warn("C++ mode forced when in Pythran mode!")
  512. options['cplus'] = True
  513. if 'language_level' in directives and 'language_level' not in kw:
  514. options['language_level'] = directives['language_level']
  515. elif not options.get('language_level'):
  516. options['language_level'] = directive_defaults.get('language_level')
  517. if 'formal_grammar' in directives and 'formal_grammar' not in kw:
  518. options['formal_grammar'] = directives['formal_grammar']
  519. if options['cache'] is True:
  520. options['cache'] = os.path.join(Utils.get_cython_cache_dir(), 'compiler')
  521. self.__dict__.update(options)
  522. def configure_language_defaults(self, source_extension):
  523. if source_extension == 'py':
  524. if self.compiler_directives.get('binding') is None:
  525. self.compiler_directives['binding'] = True
  526. def create_context(self):
  527. return Context(self.include_path, self.compiler_directives,
  528. self.cplus, self.language_level, options=self)
  529. def get_fingerprint(self):
  530. r"""
  531. Return a string that contains all the options that are relevant for cache invalidation.
  532. """
  533. # Collect only the data that can affect the generated file(s).
  534. data = {}
  535. for key, value in self.__dict__.items():
  536. if key in ['show_version', 'errors_to_stderr', 'verbose', 'quiet']:
  537. # verbosity flags have no influence on the compilation result
  538. continue
  539. elif key in ['output_file', 'output_dir']:
  540. # ignore the exact name of the output file
  541. continue
  542. elif key in ['timestamps']:
  543. # the cache cares about the content of files, not about the timestamps of sources
  544. continue
  545. elif key in ['cache']:
  546. # hopefully caching has no influence on the compilation result
  547. continue
  548. elif key in ['compiler_directives']:
  549. # directives passed on to the C compiler do not influence the generated C code
  550. continue
  551. elif key in ['include_path']:
  552. # this path changes which headers are tracked as dependencies,
  553. # it has no influence on the generated C code
  554. continue
  555. elif key in ['working_path']:
  556. # this path changes where modules and pxd files are found;
  557. # their content is part of the fingerprint anyway, their
  558. # absolute path does not matter
  559. continue
  560. elif key in ['create_extension']:
  561. # create_extension() has already mangled the options, e.g.,
  562. # embedded_metadata, when the fingerprint is computed so we
  563. # ignore it here.
  564. continue
  565. elif key in ['build_dir']:
  566. # the (temporary) directory where we collect dependencies
  567. # has no influence on the C output
  568. continue
  569. elif key in ['use_listing_file', 'generate_pxi', 'annotate', 'annotate_coverage_xml']:
  570. # all output files are contained in the cache so the types of
  571. # files generated must be part of the fingerprint
  572. data[key] = value
  573. elif key in ['formal_grammar', 'evaluate_tree_assertions']:
  574. # these bits can change whether compilation to C passes/fails
  575. data[key] = value
  576. elif key in ['embedded_metadata', 'emit_linenums', 'c_line_in_traceback', 'gdb_debug', 'relative_path_in_code_position_comments']:
  577. # the generated code contains additional bits when these are set
  578. data[key] = value
  579. elif key in ['cplus', 'language_level', 'compile_time_env', 'np_pythran']:
  580. # assorted bits that, e.g., influence the parser
  581. data[key] = value
  582. elif key == ['capi_reexport_cincludes']:
  583. if self.capi_reexport_cincludes:
  584. # our caching implementation does not yet include fingerprints of all the header files
  585. raise NotImplementedError('capi_reexport_cincludes is not compatible with Cython caching')
  586. elif key == ['common_utility_include_dir']:
  587. if self.common_utility_include_dir:
  588. raise NotImplementedError('common_utility_include_dir is not compatible with Cython caching yet')
  589. else:
  590. # any unexpected option should go into the fingerprint; it's better
  591. # to recompile than to return incorrect results from the cache.
  592. data[key] = value
  593. def to_fingerprint(item):
  594. r"""
  595. Recursively turn item into a string, turning dicts into lists with
  596. deterministic ordering.
  597. """
  598. if isinstance(item, dict):
  599. item = sorted([(repr(key), to_fingerprint(value)) for key, value in item.items()])
  600. return repr(item)
  601. return to_fingerprint(data)
  602. class CompilationResult(object):
  603. """
  604. Results from the Cython compiler:
  605. c_file string or None The generated C source file
  606. h_file string or None The generated C header file
  607. i_file string or None The generated .pxi file
  608. api_file string or None The generated C API .h file
  609. listing_file string or None File of error messages
  610. object_file string or None Result of compiling the C file
  611. extension_file string or None Result of linking the object file
  612. num_errors integer Number of compilation errors
  613. compilation_source CompilationSource
  614. """
  615. def __init__(self):
  616. self.c_file = None
  617. self.h_file = None
  618. self.i_file = None
  619. self.api_file = None
  620. self.listing_file = None
  621. self.object_file = None
  622. self.extension_file = None
  623. self.main_source_file = None
  624. class CompilationResultSet(dict):
  625. """
  626. Results from compiling multiple Pyrex source files. A mapping
  627. from source file paths to CompilationResult instances. Also
  628. has the following attributes:
  629. num_errors integer Total number of compilation errors
  630. """
  631. num_errors = 0
  632. def add(self, source, result):
  633. self[source] = result
  634. self.num_errors += result.num_errors
  635. def compile_single(source, options, full_module_name = None):
  636. """
  637. compile_single(source, options, full_module_name)
  638. Compile the given Pyrex implementation file and return a CompilationResult.
  639. Always compiles a single file; does not perform timestamp checking or
  640. recursion.
  641. """
  642. return run_pipeline(source, options, full_module_name)
  643. def compile_multiple(sources, options):
  644. """
  645. compile_multiple(sources, options)
  646. Compiles the given sequence of Pyrex implementation files and returns
  647. a CompilationResultSet. Performs timestamp checking and/or recursion
  648. if these are specified in the options.
  649. """
  650. if options.module_name and len(sources) > 1:
  651. raise RuntimeError('Full module name can only be set '
  652. 'for single source compilation')
  653. # run_pipeline creates the context
  654. # context = options.create_context()
  655. sources = [os.path.abspath(source) for source in sources]
  656. processed = set()
  657. results = CompilationResultSet()
  658. timestamps = options.timestamps
  659. verbose = options.verbose
  660. context = None
  661. cwd = os.getcwd()
  662. for source in sources:
  663. if source not in processed:
  664. if context is None:
  665. context = options.create_context()
  666. output_filename = get_output_filename(source, cwd, options)
  667. out_of_date = context.c_file_out_of_date(source, output_filename)
  668. if (not timestamps) or out_of_date:
  669. if verbose:
  670. sys.stderr.write("Compiling %s\n" % source)
  671. result = run_pipeline(source, options,
  672. full_module_name=options.module_name,
  673. context=context)
  674. results.add(source, result)
  675. # Compiling multiple sources in one context doesn't quite
  676. # work properly yet.
  677. context = None
  678. processed.add(source)
  679. return results
  680. def compile(source, options = None, full_module_name = None, **kwds):
  681. """
  682. compile(source [, options], [, <option> = <value>]...)
  683. Compile one or more Pyrex implementation files, with optional timestamp
  684. checking and recursing on dependencies. The source argument may be a string
  685. or a sequence of strings. If it is a string and no recursion or timestamp
  686. checking is requested, a CompilationResult is returned, otherwise a
  687. CompilationResultSet is returned.
  688. """
  689. options = CompilationOptions(defaults = options, **kwds)
  690. if isinstance(source, basestring) and not options.timestamps:
  691. return compile_single(source, options, full_module_name)
  692. else:
  693. return compile_multiple(source, options)
  694. @Utils.cached_function
  695. def search_include_directories(dirs, qualified_name, suffix, pos, include=False):
  696. """
  697. Search the list of include directories for the given file name.
  698. If a source file position is given, first searches the directory
  699. containing that file. Returns None if not found, but does not
  700. report an error.
  701. The 'include' option will disable package dereferencing.
  702. """
  703. if pos:
  704. file_desc = pos[0]
  705. if not isinstance(file_desc, FileSourceDescriptor):
  706. raise RuntimeError("Only file sources for code supported")
  707. if include:
  708. dirs = (os.path.dirname(file_desc.filename),) + dirs
  709. else:
  710. dirs = (Utils.find_root_package_dir(file_desc.filename),) + dirs
  711. dotted_filename = qualified_name
  712. if suffix:
  713. dotted_filename += suffix
  714. if not include:
  715. names = qualified_name.split('.')
  716. package_names = tuple(names[:-1])
  717. module_name = names[-1]
  718. module_filename = module_name + suffix
  719. package_filename = "__init__" + suffix
  720. for dirname in dirs:
  721. path = os.path.join(dirname, dotted_filename)
  722. if os.path.exists(path):
  723. return path
  724. # Arcadia-specific lookup: search for packages in include paths,
  725. # ignoring existence of __init__.py files as packages markers
  726. # (they are not required by Arcadia build system)
  727. if not include:
  728. package_dir = os.path.join(dirname, *package_names)
  729. path = os.path.join(package_dir, module_filename)
  730. if os.path.exists(path):
  731. return path
  732. path = os.path.join(dirname, package_dir, module_name,
  733. package_filename)
  734. if os.path.exists(path):
  735. return path
  736. return None
  737. # ------------------------------------------------------------------------
  738. #
  739. # Main command-line entry point
  740. #
  741. # ------------------------------------------------------------------------
  742. def setuptools_main():
  743. return main(command_line = 1)
  744. def main(command_line = 0):
  745. args = sys.argv[1:]
  746. any_failures = 0
  747. if command_line:
  748. from .CmdLine import parse_command_line
  749. options, sources = parse_command_line(args)
  750. else:
  751. options = CompilationOptions(default_options)
  752. sources = args
  753. if options.show_version:
  754. sys.stderr.write("Cython version %s\n" % version)
  755. if options.working_path!="":
  756. os.chdir(options.working_path)
  757. try:
  758. result = compile(sources, options)
  759. if result.num_errors > 0:
  760. any_failures = 1
  761. except (EnvironmentError, PyrexError) as e:
  762. sys.stderr.write(str(e) + '\n')
  763. any_failures = 1
  764. if any_failures:
  765. sys.exit(1)
  766. # ------------------------------------------------------------------------
  767. #
  768. # Set the default options depending on the platform
  769. #
  770. # ------------------------------------------------------------------------
  771. default_options = dict(
  772. show_version = 0,
  773. use_listing_file = 0,
  774. errors_to_stderr = 1,
  775. cplus = 0,
  776. output_file = None,
  777. depfile = None,
  778. annotate = None,
  779. annotate_coverage_xml = None,
  780. generate_pxi = 0,
  781. capi_reexport_cincludes = 0,
  782. working_path = "",
  783. timestamps = None,
  784. verbose = 0,
  785. quiet = 0,
  786. compiler_directives = {},
  787. embedded_metadata = {},
  788. evaluate_tree_assertions = False,
  789. emit_linenums = False,
  790. relative_path_in_code_position_comments = True,
  791. c_line_in_traceback = True,
  792. language_level = None, # warn but default to 2
  793. formal_grammar = False,
  794. gdb_debug = False,
  795. init_suffix = None,
  796. compile_time_env = None,
  797. common_utility_include_dir = None,
  798. output_dir=None,
  799. build_dir=None,
  800. cache=None,
  801. create_extension=None,
  802. module_name=None,
  803. np_pythran=False
  804. )