Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/misc/cython.py
4056 views
1
"""
2
Cython -- C-Extensions for Python
3
4
AUTHORS:
5
6
- William Stein (2006-01-18): initial version
7
- William Stein (2007-07-28): update from sagex to cython
8
- Martin Albrecht & William Stein (2011-08): cfile & cargs
9
"""
10
11
#*****************************************************************************
12
# Copyright (C) 2006 William Stein <[email protected]>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
19
import os, sys, platform
20
21
from misc import SPYX_TMP, SAGE_ROOT, SAGE_LOCAL
22
from sage.misc.misc import UNAME
23
24
def cblas():
25
"""
26
Return the name of the cblas library on this system. If the environment
27
variable :envvar:`$SAGE_CBLAS` is set, just return its value. If not,
28
return ``'cblas'`` if :file:`/usr/lib/libcblas.so` or
29
:file:`/usr/lib/libcblas.dylib` exists, return ``'blas'`` if
30
:file:`/usr/lib/libblas.dll.a` exists, and return ``'gslcblas'`` otherwise.
31
32
EXAMPLES::
33
34
sage: sage.misc.cython.cblas() # random -- depends on OS, etc.
35
'cblas'
36
"""
37
if os.environ.has_key('SAGE_CBLAS'):
38
return os.environ['SAGE_CBLAS']
39
elif os.path.exists('/usr/lib/libcblas.dylib') or \
40
os.path.exists('/usr/lib/libcblas.so'):
41
return 'cblas'
42
elif os.path.exists('/usr/lib/libblas.dll.a'): # untested.
43
return 'blas'
44
else:
45
# This is very slow (?), but *guaranteed* to be available.
46
return 'gslcblas'
47
48
# In case of ATLAS we need to link against cblas as well as atlas
49
# In the other cases we just return the same library name as cblas()
50
# which is fine for the linker
51
#
52
# We should be using the Accelerate FrameWork on OS X, but that requires
53
# some magic due to distutils having ridden on the short bus :)
54
def atlas():
55
"""
56
Returns the name of the ATLAS library to use. On Darwin or Cygwin, this is
57
``'blas'``, and otherwise it is ``'atlas'``.
58
59
EXAMPLES::
60
61
sage: sage.misc.cython.atlas() # random -- depends on OS
62
'atlas'
63
"""
64
if UNAME == "Darwin" or "CYGWIN" in UNAME:
65
return 'blas'
66
else:
67
return 'atlas'
68
69
include_dirs = ['%sinclude/csage/'%SAGE_LOCAL,
70
'%sinclude/'%SAGE_LOCAL, \
71
'%sinclude/python%s/'%(SAGE_LOCAL, platform.python_version().rsplit('.', 1)[0]), \
72
'%slib/python%s/site-packages/numpy/core/include'%(SAGE_LOCAL, platform.python_version().rsplit('.', 1)[0]), \
73
'%s/devel/sage/sage/ext/'%SAGE_ROOT, \
74
'%s/devel/sage/'%SAGE_ROOT, \
75
'%s/devel/sage/sage/gsl/'%SAGE_ROOT]
76
77
78
standard_libs = ['mpfr', 'gmp', 'gmpxx', 'stdc++', 'pari', 'm', \
79
'jc', 'gsl', cblas(), atlas(), 'ntl', 'csage']
80
81
offset = 0
82
83
def parse_keywords(kwd, s):
84
r"""
85
Given a keyword ``kwd`` and a string ``s``, return a list of all arguments
86
on the same line as that keyword in ``s``, as well as a new copy of ``s``
87
in which each occurrence of ``kwd`` is in a comment. If a comment already
88
occurs on the line containing ``kwd``, no words after the ``#`` are added
89
to the list.
90
91
EXAMPLES::
92
93
sage: sage.misc.cython.parse_keywords('clib', " clib foo bar baz\n #cinclude bar\n")
94
(['foo', 'bar', 'baz'], ' #clib foo bar baz\n #cinclude bar\n')
95
96
sage: sage.misc.cython.parse_keywords('clib', "# qux clib foo bar baz\n #cinclude bar\n")
97
(['foo', 'bar', 'baz'], '# qux clib foo bar baz\n #cinclude bar\n')
98
sage: sage.misc.cython.parse_keywords('clib', "# clib foo bar # baz\n #cinclude bar\n")
99
(['foo', 'bar'], '# clib foo bar # baz\n #cinclude bar\n')
100
"""
101
j = 0
102
v = []
103
while True:
104
# see if kwd occurs
105
i = s[j:].find(kwd)
106
if i == -1: break
107
j = i + j
108
109
# add a hash, if necessary
110
last_hash = s[:j].rfind('#')
111
last_newline = s[:j].rfind('\n')
112
if last_hash > last_newline:
113
j += len(kwd)
114
else:
115
s = s[:j] + '#' + s[j:]
116
j += len(kwd) + 1
117
118
# find all other words on this line
119
k = s[j:].find('\n')
120
if k == -1:
121
k = len(s)
122
123
# add them to our list, until we find a comment
124
for X in s[j:j+k].split():
125
if X[0] == '#': # skip rest of line
126
break
127
v.append(X)
128
129
return v, s
130
131
def environ_parse(s):
132
r"""
133
Given a string s, find each substring of the form ``'\$ABC'``. If the
134
environment variable :envvar:`$ABC` is set, replace ``'\$ABC'`` with its
135
value and move on to the next such substring. If it is not set, stop
136
parsing there.
137
138
EXAMPLES::
139
140
sage: from sage.misc.cython import environ_parse
141
sage: environ_parse('$SAGE_ROOT') == SAGE_ROOT
142
True
143
sage: environ_parse('$THIS_IS_NOT_DEFINED_ANYWHERE')
144
'$THIS_IS_NOT_DEFINED_ANYWHERE'
145
sage: os.environ['DEFINE_THIS'] = 'hello'
146
sage: environ_parse('$DEFINE_THIS/$THIS_IS_NOT_DEFINED_ANYWHERE/$DEFINE_THIS')
147
'hello/$THIS_IS_NOT_DEFINED_ANYWHERE/$DEFINE_THIS'
148
"""
149
i = s.find('$')
150
if i == -1:
151
return s
152
j = s[i:].find('/')
153
if j == -1:
154
j = len(s)
155
else:
156
j = i + j
157
name = s[i+1:j]
158
if os.environ.has_key(name):
159
s = s[:i] + os.environ[name] + s[j:]
160
else:
161
return s
162
return environ_parse(s)
163
164
def pyx_preparse(s):
165
r"""
166
Preparse a pyx file:
167
168
* include ``cdefs.pxi``, ``interrupt.pxi``, ``stdsage.pxi``
169
* parse ``clang`` pragma (c or c++)
170
* parse ``clib`` pragma (additional libraries to link in)
171
* parse ``cinclude`` (additional include directories)
172
* parse ``cfile`` (additional files to be included)
173
* parse ``cargs`` (additional parameters passed to the compiler)
174
175
The pragmas:
176
177
- ``clang`` - may be either ``'c'`` or ``'c++'`` indicating whether a C or
178
C++ compiler should be used
179
180
- ``clib`` - additional libraries to be linked in, the space separated list
181
is split and passed to distutils.
182
183
- ``cinclude`` - additional directories to search for header files. The
184
space separated list is split and passed to distutils.
185
186
- ``cfile`` - additional C or C++ files to be compiled. Also,
187
:envvar:`$SAGE_ROOT` is expanded, but other environment variables are
188
not.
189
190
- ``cargs`` - additional parameters passed to the compiler
191
192
OUTPUT: preamble, libs, includes, language, files, args
193
194
EXAMPLES::
195
196
sage: from sage.misc.cython import pyx_preparse
197
sage: pyx_preparse("")
198
('\ninclude "interrupt.pxi" # ctrl-c interrupt block support\ninclude "stdsage.pxi" # ctrl-c interrupt block support\n\ninclude "cdefs.pxi"\n',
199
['mpfr',
200
'gmp',
201
'gmpxx',
202
'stdc++',
203
'pari',
204
'm',
205
'jc',
206
'gsl',
207
'...blas',
208
...,
209
'ntl',
210
'csage'],
211
['.../local/include/csage/',
212
'.../local/include/',
213
'.../local/include/python2.7/',
214
'.../local/lib/python2.7/site-packages/numpy/core/include',
215
'.../devel/sage/sage/ext/',
216
'.../devel/sage/',
217
'.../devel/sage/sage/gsl/'],
218
'c',
219
[], ['-w', '-O2'])
220
sage: s, libs, inc, lang, f, args = pyx_preparse("# clang c++\n #clib foo\n # cinclude bar\n")
221
sage: lang
222
'c++'
223
224
sage: libs
225
['foo', 'mpfr',
226
'gmp', 'gmpxx',
227
'stdc++',
228
'pari',
229
'm',
230
'jc',
231
'gsl', '...blas', ...,
232
'ntl',
233
'csage']
234
sage: libs[1:] == sage.misc.cython.standard_libs
235
True
236
237
sage: inc
238
['bar',
239
'.../local/include/csage/',
240
'.../local/include/',
241
'.../local/include/python2.7/',
242
'.../local/lib/python2.7/site-packages/numpy/core/include',
243
'.../devel/sage/sage/ext/',
244
'.../devel/sage/',
245
'.../devel/sage/sage/gsl/']
246
247
sage: s, libs, inc, lang, f, args = pyx_preparse("# cargs -O3 -ggdb\n")
248
sage: args
249
['-w', '-O2', '-O3', '-ggdb']
250
251
TESTS::
252
253
sage: module = sage.misc.cython.import_test("trac11680") # long time (7s on sage.math, 2012)
254
sage: R.<x> = QQ[]
255
sage: module.evaluate_at_power_of_gen(x^3 + x - 7, 5) # long time
256
x^15 + x^5 - 7
257
"""
258
lang, s = parse_keywords('clang', s)
259
if lang:
260
lang = lang[0].lower() # this allows both C++ and c++
261
else:
262
lang = "c"
263
264
v, s = parse_keywords('clib', s)
265
libs = v + standard_libs
266
267
additional_source_files, s = parse_keywords('cfile', s)
268
269
v, s = parse_keywords('cinclude', s)
270
inc = [environ_parse(x.replace('"','').replace("'","")) for x in v] + include_dirs
271
s = """\ninclude "cdefs.pxi"\n""" + s
272
if lang != "c++": # has issues with init_csage()
273
s = """\ninclude "interrupt.pxi" # ctrl-c interrupt block support\ninclude "stdsage.pxi" # ctrl-c interrupt block support\n""" + s
274
args, s = parse_keywords('cargs', s)
275
args = ['-w','-O2'] + args
276
277
return s, libs, inc, lang, additional_source_files, args
278
279
################################################################
280
# If the user attaches a .spyx file and changes it, we have
281
# to reload an .so.
282
#
283
# PROBLEM: Python does not allow one to reload an .so extension module.
284
# Solution, we create a different .so file and load that one,
285
# overwriting the definitions of everything in the original .so file.
286
#
287
# HOW: By using a sequence_number for each .spyx file; we keep
288
# these sequence numbers in a dict.
289
#
290
################################################################
291
292
sequence_number = {}
293
294
def cython(filename, verbose=False, compile_message=False,
295
use_cache=False, create_local_c_file=False, annotate=True, sage_namespace=True,
296
create_local_so_file=False):
297
"""
298
Compile a Cython file. This converts a Cython file to a C (or C++ file),
299
and then compiles that. The .c file and the .so file are
300
created in a temporary directory.
301
302
INPUTS:
303
304
- ``filename`` - the name of the file to be compiled. Should end with
305
'pyx'.
306
307
- ``verbose`` (bool, default False) - if True, print debugging
308
information.
309
310
- ``compile_message`` (bool, default False) - if True, print
311
``'Compiling <filename>...'``
312
313
- ``use_cache`` (bool, default False) - if True, check the
314
temporary build directory to see if there is already a
315
corresponding .so file. If so, and if the .so file is newer than the
316
Cython file, don't recompile, just reuse the .so file.
317
318
- ``create_local_c_file`` (bool, default False) - if True, save a
319
copy of the .c file in the current directory.
320
321
- ``annotate`` (bool, default True) - if True, create an html file which
322
annotates the conversion from .pyx to .c. By default this is only created
323
in the temporary directory, but if ``create_local_c_file`` is also True,
324
then save a copy of the .html file in the current directory.
325
326
- ``sage_namespace`` (bool, default True) - if True, import
327
``sage.all``.
328
329
- ``create_local_so_file`` (bool, default False) - if True, save a
330
copy of the compiled .so file in the current directory.
331
332
TESTS:
333
334
Before :trac:`12975`, it would have beeen needed to write ``#clang c++``,
335
but upper case ``C++`` has resulted in an error::
336
337
sage: from sage.all import SAGE_ROOT
338
sage: code = [
339
... "#clang C++",
340
... "#cinclude %s/local/include/singular %s/local/include/factory"%(SAGE_ROOT,SAGE_ROOT),
341
... "#clib m readline singular givaro ntl gmpxx gmp",
342
... "from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular",
343
... "from sage.libs.singular.polynomial cimport singular_polynomial_pow",
344
... "def test(MPolynomial_libsingular p):",
345
... " singular_polynomial_pow(&p._poly, p._poly, 2, p._parent_ring)"]
346
sage: cython(os.linesep.join(code))
347
348
The function ``test`` now manipulates internal C data of polynomials,
349
squaring them::
350
351
sage: P.<x,y>=QQ[]
352
sage: test(x)
353
sage: x
354
x^2
355
356
"""
357
if not filename.endswith('pyx'):
358
print "File (=%s) should have extension .pyx"%filename
359
360
# base is the name of the .so module that we create. If we are
361
# creating a local shared object file, we use a more natural
362
# naming convention. If we are not creating a local shared object
363
# file, the main constraint is that it is unique and determined by
364
# the file that we're running Cython on, so that in some cases we
365
# can cache the result (e.g., recompiling the same pyx file during
366
# the same session).
367
if create_local_so_file:
368
base, ext = os.path.splitext(os.path.basename(filename))
369
base = sanitize(base)
370
else:
371
base = sanitize(os.path.abspath(filename))
372
373
# This is the *temporary* directory where we build the pyx file.
374
# This is deleted when sage exits, which means pyx files must be
375
# rebuilt every time Sage is restarted at present.
376
build_dir = '%s/%s'%(SPYX_TMP, base)
377
378
if os.path.exists(build_dir):
379
# There is already a module here. Maybe we do not have to rebuild?
380
# Find the name.
381
if use_cache:
382
prev_so = [F for F in os.listdir(build_dir) if F[-3:] == '.so']
383
if len(prev_so) > 0:
384
prev_so = prev_so[0] # should have length 1 because of deletes below
385
if os.path.getmtime(filename) <= os.path.getmtime('%s/%s'%(build_dir, prev_so)):
386
# We do not have to rebuild.
387
return prev_so[:-3], build_dir
388
else:
389
os.makedirs(build_dir)
390
for F in os.listdir(build_dir):
391
G = '%s/%s'%(build_dir,F)
392
try:
393
if not os.path.isdir(G):
394
os.unlink(G)
395
except OSError:
396
pass
397
398
# Get the absolute path to the directory that contains the pyx file.
399
# We will use this only to make some convenient symbolic links.
400
abs_base = os.path.split(os.path.abspath(filename))[0]
401
402
# bad things happen if the current directory is devel/sage-*
403
if not os.path.exists("%s/sage" % abs_base) and not os.path.exists("%s/c_lib" % abs_base):
404
cmd = 'cd "%s"; ln -sf "%s"/* .'%(build_dir, abs_base)
405
os.system(cmd)
406
if os.path.exists("%s/setup.py" % build_dir):
407
os.unlink("%s/setup.py" % build_dir)
408
409
if compile_message:
410
print "Compiling %s..."%filename
411
412
F = open(filename).read()
413
414
F, libs, includes, language, additional_source_files, extra_args = pyx_preparse(F)
415
416
# add the working directory to the includes so custom headers etc. work
417
includes.append(os.path.split(os.path.splitext(filename)[0])[0])
418
419
if language == 'c++':
420
extension = "cpp"
421
else:
422
extension = "c"
423
424
if create_local_so_file:
425
name = base
426
else:
427
global sequence_number
428
if not sequence_number.has_key(base):
429
sequence_number[base] = 0
430
name = '%s_%s'%(base, sequence_number[base])
431
432
# increment the sequence number so will use a different one next time.
433
sequence_number[base] += 1
434
435
file_list = []
436
for fname in additional_source_files:
437
fname = fname.replace("$SAGE_ROOT",SAGE_ROOT)
438
if fname.startswith(os.path.sep):
439
file_list.append("'"+fname+"'")
440
else:
441
file_list.append("'"+os.path.abspath(os.curdir)+"/"+fname+"'")
442
additional_source_files = ",".join(file_list)
443
444
pyx = '%s/%s.pyx'%(build_dir, name)
445
open(pyx,'w').write(F)
446
setup="""
447
# Build using 'python setup.py'
448
import distutils.sysconfig, os, sys
449
from distutils.core import setup, Extension
450
451
if not os.environ.has_key('SAGE_ROOT'):
452
print " ERROR: The environment variable SAGE_ROOT must be defined."
453
sys.exit(1)
454
else:
455
SAGE_ROOT = os.environ['SAGE_ROOT']
456
SAGE_LOCAL = SAGE_ROOT + '/local/'
457
458
extra_link_args = ['-L' + SAGE_LOCAL + '/lib']
459
extra_compile_args = %s
460
461
ext_modules = [Extension('%s', sources=['%s.%s', %s],
462
libraries=%s,
463
library_dirs=[SAGE_LOCAL + '/lib/'],
464
extra_compile_args = extra_compile_args,
465
extra_link_args = extra_link_args,
466
language = '%s' )]
467
468
setup(ext_modules = ext_modules,
469
include_dirs = %s)
470
"""%(extra_args, name, name, extension, additional_source_files, libs, language, includes)
471
open('%s/setup.py'%build_dir,'w').write(setup)
472
473
cython_include = ' '.join(["-I '%s'"%x for x in includes if len(x.strip()) > 0 ])
474
475
options = ['-p']
476
if annotate:
477
options.append('-a')
478
if sage_namespace:
479
options.append('--pre-import sage.all')
480
481
cmd = "cd '%s' && cython %s %s '%s.pyx' 1>log 2>err " % (build_dir, ' '.join(options), cython_include, name)
482
483
if create_local_c_file:
484
target_c = '%s/_%s.c'%(os.path.abspath(os.curdir), base)
485
if language == 'c++':
486
target_c = target_c + "pp"
487
cmd += " && cp '%s.c' '%s'"%(name, target_c)
488
if annotate:
489
target_html = '%s/_%s.html'%(os.path.abspath(os.curdir), base)
490
cmd += " && cp '%s.html' '%s'"%(name, target_html)
491
492
if verbose:
493
print cmd
494
if os.system(cmd):
495
log = open('%s/log'%build_dir).read()
496
err = subtract_from_line_numbers(open('%s/err'%build_dir).read(), offset)
497
raise RuntimeError, "Error converting %s to C:\n%s\n%s"%(filename, log, err)
498
499
if language=='c++':
500
os.system("cd '%s' && mv '%s.c' '%s.cpp'"%(build_dir,name,name))
501
502
## if make_c_file_nice and os.path.exists(target_c):
503
## R = open(target_c).read()
504
## R = "/* THIS IS A PARSED TO MAKE READABLE VERSION OF THE C FILE. */" + R
505
506
## # 1. Get rid of the annoying __pyx_'s before variable names.
507
## # R = R.replace('__pyx_v_','').replace('__pyx','')
508
## # 2. Replace the line number references by the actual code from the file,
509
## # since it is very painful to go back and forth, and the philosophy
510
## # of Sage is that everything that can be very easy *is*.
511
512
## pyx_file = os.path.abspath('%s/%s.pyx'%(build_dir,name))
513
## S = '/* "%s":'%pyx_file
514
## n = len(S)
515
## last_i = -1
516
## X = F.split('\n')
517
## stars = '*'*80
518
## while True:
519
## i = R.find(S)
520
## if i == -1 or i == last_i: break
521
## last_i = i
522
## j = R[i:].find('\n')
523
## if j == -1: break
524
## line_number = int(R[i+n: i+j])
525
## try:
526
## line = X[line_number-1]
527
## except IndexError:
528
## line = '(missing code)'
529
## R = R[:i+2] + '%s\n\n Line %s: %s\n\n%s'%(stars, line_number, line, stars) + R[i+j:]
530
531
## open(target_c,'w').write(R)
532
533
534
cmd = 'cd %s && python setup.py build 1>log 2>err'%build_dir
535
if verbose: print cmd
536
if os.system(cmd):
537
log = open('%s/log'%build_dir).read()
538
err = open('%s/err'%build_dir).read()
539
raise RuntimeError, "Error compiling %s:\n%s\n%s"%(filename, log, err)
540
541
# Move from lib directory.
542
cmd = 'mv %s/build/lib.*/* %s'%(build_dir, build_dir)
543
if verbose: print cmd
544
if os.system(cmd):
545
raise RuntimeError, "Error copying extension module for %s"%filename
546
547
if create_local_so_file:
548
# Copy from lib directory into local directory
549
cmd = 'cp %s/%s.so %s'%(build_dir, name, os.path.abspath(os.curdir))
550
if os.system(cmd):
551
raise RuntimeError, "Error making local copy of shared object library for %s"%filename
552
553
return name, build_dir
554
555
556
557
def subtract_from_line_numbers(s, n):
558
r"""
559
Given a string ``s`` and an integer ``n``, for any line of ``s`` which has
560
the form ``'text:NUM:text'`` subtract ``n`` from NUM and return
561
``'text:(NUM-n):text'``. Return other lines of ``s`` without change.
562
563
EXAMPLES::
564
565
sage: from sage.misc.cython import subtract_from_line_numbers
566
sage: subtract_from_line_numbers('hello:1234:hello', 3)
567
'hello:1231:hello\n'
568
sage: subtract_from_line_numbers('text:123\nhello:1234:', 3)
569
'text:123\nhello:1231:\n'
570
"""
571
ans = []
572
for X in s.split('\n'):
573
i = X.find(':')
574
j = i+1 + X[i+1:].find(':')
575
try:
576
ans.append('%s:%s:%s\n'%(X[:i], int(X[i+1:j]) - n, X[j+1:]))
577
except ValueError:
578
ans.append(X)
579
return '\n'.join(ans)
580
581
582
################################################################
583
# COMPILE
584
################################################################
585
def cython_lambda(vars, expr,
586
verbose=False,
587
compile_message=False,
588
use_cache=False):
589
"""
590
Create a compiled function which evaluates ``expr`` assuming machine values
591
for ``vars``.
592
593
.. warning::
594
595
This implementation is not well tested.
596
597
INPUT:
598
599
- ``vars`` - list of pairs (variable name, c-data type), where the variable
600
names and data types are strings, OR a string such as ``'double x, int y,
601
int z'``
602
603
- ``expr`` - an expression involving the vars and constants; you can access
604
objects defined in the current module scope ``globals()`` using
605
``sage.object_name``.
606
607
EXAMPLES:
608
609
We create a Lambda function in pure Python (using the r to make sure the 3.2
610
is viewed as a Python float)::
611
612
sage: f = lambda x,y: x*x + y*y + x + y + 17r*x + 3.2r
613
614
We make the same Lambda function, but in a compiled form. ::
615
616
sage: g = cython_lambda('double x, double y', 'x*x + y*y + x + y + 17*x + 3.2') # optional -- gcc
617
sage: g(2,3) # optional -- gcc
618
55.200000000000003
619
sage: g(0,0) # optional -- gcc
620
3.2000000000000002
621
622
We access a global function and variable. ::
623
624
sage: a = 25
625
sage: f = cython_lambda('double x', 'sage.math.sin(x) + sage.a') # optional -- gcc
626
sage: f(10) # optional -- gcc
627
24.455978889110629
628
sage: a = 50
629
sage: f(10) # optional -- gcc
630
49.455978889110632
631
"""
632
if isinstance(vars, str):
633
v = vars
634
else:
635
v = ', '.join(['%s %s'%(typ,var) for typ, var in vars])
636
637
s = """
638
class _s:
639
def __getattr__(self, x):
640
return globals()[x]
641
642
sage = _s()
643
644
def f(%s):
645
return %s
646
"""%(v, expr)
647
if verbose:
648
print s
649
import sage.misc.misc
650
tmpfile = sage.misc.misc.tmp_filename() + ".spyx"
651
open(tmpfile,'w').write(s)
652
653
import sage.server.support
654
d = {}
655
sage.server.support.cython_import_all(tmpfile, d,
656
verbose=verbose, compile_message=compile_message,
657
use_cache=use_cache,
658
create_local_c_file=False)
659
return d['f']
660
661
def cython_create_local_so(filename):
662
r"""
663
Compile filename and make it available as a loadable shared object file.
664
665
INPUT:
666
667
- ``filename`` - string: a Cython (formerly SageX) (.spyx) file
668
669
OUTPUT: None
670
671
EFFECT: A compiled, python "importable" loadable shared object file is created.
672
673
.. note::
674
675
Shared object files are *not* reloadable. The intent is for
676
imports in other scripts. A possible development cycle might
677
go thus:
678
679
- Attach a .spyx file
680
- Interactively test and edit it to your satisfaction
681
- Use ``cython_create_local_so`` to create the shared object file
682
- Import the .so file in other scripts
683
684
EXAMPLES::
685
686
sage: curdir = os.path.abspath(os.curdir)
687
sage: dir = tmp_dir(); os.chdir(dir)
688
sage: f = file('hello.spyx', 'w')
689
sage: s = "def hello():\n print 'hello'\n"
690
sage: f.write(s)
691
sage: f.close()
692
sage: cython_create_local_so('hello.spyx') # optional -- gcc
693
Compiling hello.spyx...
694
sage: sys.path.append('.') # optional -- gcc
695
sage: import hello # optional -- gcc
696
sage: hello.hello() # optional -- gcc
697
hello
698
sage: os.chdir(curdir)
699
700
AUTHORS:
701
702
- David Fu (2008-04-09): initial version
703
"""
704
cython(filename, compile_message=True, use_cache=False, create_local_so_file=True)
705
706
def sanitize(f):
707
"""
708
Given a filename ``f``, replace it by a filename that is a valid Python
709
module name.
710
711
This means that the characters are all alphanumeric or ``_``'s and doesn't
712
begin with a numeral.
713
714
EXAMPLES::
715
716
sage: from sage.misc.cython import sanitize
717
sage: sanitize('abc')
718
'abc'
719
sage: sanitize('abc/def')
720
'abc_def'
721
sage: sanitize('123/def-hij/file.py')
722
'_123_def_hij_file_py'
723
"""
724
s = ''
725
if f[0].isdigit():
726
s += '_'
727
for a in f:
728
if a.isalnum():
729
s += a
730
else:
731
s += '_'
732
return s
733
734
735
def compile_and_load(code):
736
r"""
737
INPUT:
738
739
- ``code`` -- string containing code that could be in a .pyx file
740
that is attached or put in a %cython block in the notebook.
741
742
OUTPUT: a module, which results from compiling the given code and
743
importing it
744
745
EXAMPLES::
746
747
sage: module = sage.misc.cython.compile_and_load("def f(int n):\n return n*n")
748
sage: module.f(10)
749
100
750
"""
751
from sage.misc.misc import tmp_filename
752
file = tmp_filename() + ".pyx"
753
open(file,'w').write(code)
754
from sage.server.support import cython_import
755
return cython_import(file, create_local_c_file=False)
756
757
758
TESTS = {
759
'trac11680':"""
760
#cargs -std=c99 -O3 -ggdb
761
#cinclude $SAGE_ROOT/devel/sage/sage/libs/flint $SAGE_LOCAL/include/FLINT
762
#clib flint
763
#cfile $SAGE_ROOT/devel/sage/sage/libs/flint/fmpq_poly.c
764
765
from sage.rings.rational cimport Rational
766
from sage.rings.polynomial.polynomial_rational_flint cimport Polynomial_rational_flint
767
from sage.libs.flint.fmpq_poly cimport (fmpq_poly_get_coeff_mpq, fmpq_poly_set_coeff_mpq,
768
fmpq_poly_length)
769
770
def evaluate_at_power_of_gen(Polynomial_rational_flint f, unsigned long n):
771
assert n >= 1
772
cdef Polynomial_rational_flint res = f._new()
773
cdef unsigned long k
774
cdef Rational z = Rational(0)
775
for k in range(fmpq_poly_length(f.__poly)):
776
fmpq_poly_get_coeff_mpq(z.value, f.__poly, k)
777
fmpq_poly_set_coeff_mpq(res.__poly, n*k, z.value)
778
return res
779
""",
780
781
'trac11680b':"""
782
def f(int a, int b, int c):
783
return a+b+c
784
"""
785
}
786
787
def import_test(name):
788
"""
789
This is used by the testing infrastructure to test building
790
Cython programs.
791
792
INPUT:
793
794
- ``name`` -- string; name of a key to the TESTS dictionary above
795
796
OUTPUT: a module, which results from compiling the given code and importing it
797
798
EXAMPLES::
799
800
sage: module = sage.misc.cython.import_test("trac11680b")
801
sage: module.f(2,3,4)
802
9
803
"""
804
return compile_and_load(TESTS[name])
805
806
807