Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

""" 

Load Python, Sage, Cython, Fortran and Magma files in Sage 

""" 

 

#***************************************************************************** 

# Copyright (C) 2006 William Stein <wstein@gmail.com> 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 2 of the License, or 

# (at your option) any later version. 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

 

 

import os 

import base64 

 

from sage.cpython.string import str_to_bytes, bytes_to_str, FS_ENCODING 

 

def is_loadable_filename(filename): 

""" 

Returns whether a file can be loaded into Sage. This checks only 

whether its name ends in one of the supported extensions ``.py``, 

``.pyx``, ``.sage``, ``.spyx``, ``.f``, ``.f90`` and ``.m``. 

Note: :func:`load` assumes the latter signifies a Magma file. 

 

INPUT: 

 

- ``filename`` - a string 

 

OUTPUT: 

 

- a boolean 

 

EXAMPLES:: 

 

sage: sage.repl.load.is_loadable_filename('foo.bar') 

False 

sage: sage.repl.load.is_loadable_filename('foo.c') 

False 

sage: sage.repl.load.is_loadable_filename('foo.sage') 

True 

sage: sage.repl.load.is_loadable_filename('FOO.F90') 

True 

sage: sage.repl.load.is_loadable_filename('foo.m') 

True 

""" 

ext = os.path.splitext(filename)[1].lower() 

return ext in ('.py', '.pyx', '.sage', '.spyx', '.f', '.f90', '.m') 

 

 

def load_cython(name): 

""" 

Helper function to load a Cython file. 

 

INPUT: 

 

- ``name`` -- filename of the Cython file 

 

OUTPUT: 

 

- A string with Python code to import the names from the compiled 

module. 

""" 

from sage.misc.cython import cython 

mod, dir = cython(name, compile_message=True, use_cache=True) 

import sys 

sys.path.append(dir) 

return 'from {} import *'.format(mod) 

 

 

def load(filename, globals, attach=False): 

r""" 

Executes a file in the scope given by ``globals``. If the name 

starts with ``http://``, it is treated as a URL and downloaded. 

 

.. NOTE:: 

 

For Cython files, the situation is more complicated -- 

the module is first compiled to a temporary module ``t`` and 

executed via:: 

 

from t import * 

 

INPUT: 

 

- ``filename`` -- a string denoting a filename or URL. 

 

- ``globals`` -- a string:object dictionary; the context in which 

to execute the file contents. 

 

- ``attach`` -- a boolean (default: False); whether to add the 

file to the list of attached files. 

 

EXAMPLES: 

 

Note that ``.py`` files are *not* preparsed:: 

 

sage: t = tmp_filename(ext='.py') 

sage: with open(t, 'w') as f: 

....: _ = f.write("print(('hi', 2^3)); z = -2^7") 

sage: z = 1 

sage: sage.repl.load.load(t, globals()) 

('hi', 1) 

sage: z 

-7 

 

A ``.sage`` file *is* preparsed:: 

 

sage: t = tmp_filename(ext='.sage') 

sage: with open(t, 'w') as f: 

....: _ = f.write("print(('hi', 2^3)); z = -2^7") 

sage: z = 1 

sage: sage.repl.load.load(t, globals()) 

('hi', 8) 

sage: z 

-128 

 

Cython files are *not* preparsed:: 

 

sage: t = tmp_filename(ext='.pyx') 

sage: with open(t, 'w') as f: 

....: _ = f.write("print(('hi', 2^3)); z = -2^7") 

sage: z = 1 

sage: sage.repl.load.load(t, globals()) 

Compiling ... 

('hi', 1) 

sage: z 

-7 

 

If the file isn't a Cython, Python, or a Sage file, a ValueError 

is raised:: 

 

sage: sage.repl.load.load(tmp_filename(ext=".foo"), globals()) 

Traceback (most recent call last): 

... 

ValueError: unknown file extension '.foo' for load or attach (supported extensions: .py, .pyx, .sage, .spyx, .f, .f90, .m) 

 

We load a file given at a remote URL:: 

 

sage: sage.repl.load.load('http://wstein.org/loadtest.py', globals()) # optional - internet 

hi from the net 

5 

 

We can load files using secure http (https):: 

 

sage: sage.repl.load.load('https://github.com/jasongrout/minimum_rank/raw/minimum_rank_1_0_0/minrank.py', globals()) # optional - internet 

 

We attach a file:: 

 

sage: t = tmp_filename(ext='.py') 

sage: with open(t, 'w') as f: 

....: _ = f.write("print('hello world')") 

sage: sage.repl.load.load(t, globals(), attach=True) 

hello world 

sage: t in attached_files() 

True 

 

You can't attach remote URLs (yet):: 

 

sage: sage.repl.load.load('http://wstein.org/loadtest.py', globals(), attach=True) # optional - internet 

Traceback (most recent call last): 

... 

NotImplementedError: you can't attach a URL 

 

The default search path for loading and attaching files is the 

current working directory, i.e., ``'.'``. But you can modify the 

path with :func:`load_attach_path`:: 

 

sage: sage.repl.attach.reset(); reset_load_attach_path() 

sage: load_attach_path() 

['.'] 

sage: t_dir = tmp_dir() 

sage: fname = 'test.py' 

sage: fullpath = os.path.join(t_dir, fname) 

sage: with open(fullpath, 'w') as f: 

....: _ = f.write("print(37 * 3)") 

sage: load_attach_path(t_dir) 

sage: attach(fname) 

111 

sage: sage.repl.attach.reset(); reset_load_attach_path() # clean up 

 

or by setting the environment variable ``SAGE_LOAD_ATTACH_PATH`` 

to a colon-separated list before starting Sage:: 

 

$ export SAGE_LOAD_ATTACH_PATH="/path/to/my/library:/path/to/utils" 

$ sage 

sage: load_attach_path() # not tested 

['.', '/path/to/my/library', '/path/to/utils'] 

 

TESTS: 

 

Make sure that load handles filenames with spaces in the name or path:: 

 

sage: t = tmp_filename(ext=' b.sage') 

sage: with open(t, 'w') as f: 

....: _ = f.write("print(2)") 

sage: sage.repl.load.load(t, globals()) 

2 

 

Non-existing files with spaces give correct messages:: 

 

sage: sage.repl.load.load("this file should not exist", globals()) 

Traceback (most recent call last): 

... 

IOError: did not find file 'this file should not exist' to load or attach 

""" 

if attach: 

from sage.repl.attach import add_attached_file 

 

if isinstance(filename, bytes): 

# For Python 3 in particular, convert bytes filenames to str since the 

# rest of this functions operate on filename as a str 

filename = bytes_to_str(filename, FS_ENCODING, 'surrogateescape') 

 

filename = os.path.expanduser(filename) 

 

if filename.lower().startswith(('http://', 'https://')): 

if attach: 

# But see https://en.wikipedia.org/wiki/HTTP_ETag for how 

# we will do this. 

# http://www.diveintopython.net/http_web_services/etags.html 

raise NotImplementedError("you can't attach a URL") 

from sage.misc.remote_file import get_remote_file 

filename = get_remote_file(filename, verbose=False) 

 

from sage.repl.attach import load_attach_path 

for path in load_attach_path(): 

fpath = os.path.join(path, filename) 

fpath = os.path.expanduser(fpath) 

if os.path.isfile(fpath): 

break 

else: 

raise IOError('did not find file %r to load or attach' % filename) 

 

ext = os.path.splitext(fpath)[1].lower() 

if ext == '.py': 

if attach: 

add_attached_file(fpath) 

with open(fpath) as f: 

code = compile(f.read(), fpath, 'exec') 

exec(code, globals) 

elif ext == '.sage': 

from sage.repl.attach import load_attach_mode 

from sage.repl.preparse import preparse_file_named, preparse_file 

load_debug_mode, attach_debug_mode = load_attach_mode() 

if (attach and attach_debug_mode) or ((not attach) and load_debug_mode): 

# Preparse to a file to enable tracebacks with 

# code snippets. Use preparse_file_named to make 

# the file name appear in the traceback as well. 

# See Trac 11812. 

if attach: 

add_attached_file(fpath) 

with open(preparse_file_named(fpath)) as f: 

code = compile(f.read(), preparse_file_named(fpath), 'exec') 

exec(code, globals) 

else: 

# Preparse in memory only for speed. 

if attach: 

add_attached_file(fpath) 

with open(fpath) as f: 

exec(preparse_file(f.read()) + "\n", globals) 

elif ext == '.spyx' or ext == '.pyx': 

if attach: 

add_attached_file(fpath) 

exec(load_cython(fpath), globals) 

elif ext == '.f' or ext == '.f90': 

from sage.misc.inline_fortran import fortran 

with open(fpath) as f: 

fortran(f.read(), globals) 

elif ext == '.m': 

# Assume magma for now, though maybe .m is used by maple and 

# mathematica too, and we should really analyze the file 

# further. 

s = globals['magma'].load(fpath) 

i = s.find('\n'); s = s[i+1:] 

print(s) 

else: 

raise ValueError('unknown file extension %r for load or attach (supported extensions: .py, .pyx, .sage, .spyx, .f, .f90, .m)' % ext) 

 

 

def load_wrap(filename, attach=False): 

""" 

Encodes a load or attach command as valid Python code. 

 

INPUT: 

 

- ``filename`` - a string; the argument to the load or attach 

command 

 

- ``attach`` - a boolean (default: False); whether to attach 

``filename``, instead of loading it 

 

OUTPUT: 

 

- a string 

 

EXAMPLES:: 

 

sage: sage.repl.load.load_wrap('foo.py', True) 

'sage.repl.load.load(sage.repl.load.base64.b64decode("Zm9vLnB5"),globals(),True)' 

sage: sage.repl.load.load_wrap('foo.sage') 

'sage.repl.load.load(sage.repl.load.base64.b64decode("Zm9vLnNhZ2U="),globals(),False)' 

sage: m = sage.repl.load.base64.b64decode("Zm9vLnNhZ2U=") 

sage: m == b'foo.sage' 

True 

""" 

 

# Note: On Python 3 b64encode only accepts bytes, and returns bytes (yet 

# b64decode does accept str, but always returns bytes) 

b64 = base64.b64encode(str_to_bytes(filename, FS_ENCODING, 

"surrogateescape")) 

return 'sage.repl.load.load(sage.repl.load.base64.b64decode("{}"),globals(),{})'.format( 

bytes_to_str(b64, 'ascii'), attach)