passagemath-environment 10.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. passagemath_environment-10.4.1.data/scripts/sage +1140 -0
  2. passagemath_environment-10.4.1.data/scripts/sage-env +667 -0
  3. passagemath_environment-10.4.1.data/scripts/sage-num-threads.py +105 -0
  4. passagemath_environment-10.4.1.data/scripts/sage-python +2 -0
  5. passagemath_environment-10.4.1.data/scripts/sage-venv-config +42 -0
  6. passagemath_environment-10.4.1.data/scripts/sage-version.sh +9 -0
  7. passagemath_environment-10.4.1.dist-info/METADATA +76 -0
  8. passagemath_environment-10.4.1.dist-info/RECORD +70 -0
  9. passagemath_environment-10.4.1.dist-info/WHEEL +5 -0
  10. passagemath_environment-10.4.1.dist-info/top_level.txt +1 -0
  11. sage/all__sagemath_environment.py +4 -0
  12. sage/env.py +496 -0
  13. sage/features/__init__.py +981 -0
  14. sage/features/all.py +126 -0
  15. sage/features/bliss.py +85 -0
  16. sage/features/cddlib.py +38 -0
  17. sage/features/coxeter3.py +45 -0
  18. sage/features/csdp.py +83 -0
  19. sage/features/cython.py +38 -0
  20. sage/features/databases.py +302 -0
  21. sage/features/dvipng.py +40 -0
  22. sage/features/ecm.py +42 -0
  23. sage/features/ffmpeg.py +119 -0
  24. sage/features/four_ti_2.py +55 -0
  25. sage/features/fricas.py +66 -0
  26. sage/features/gap.py +86 -0
  27. sage/features/gfan.py +38 -0
  28. sage/features/giac.py +30 -0
  29. sage/features/graph_generators.py +171 -0
  30. sage/features/graphviz.py +117 -0
  31. sage/features/igraph.py +44 -0
  32. sage/features/imagemagick.py +138 -0
  33. sage/features/interfaces.py +256 -0
  34. sage/features/internet.py +65 -0
  35. sage/features/jmol.py +44 -0
  36. sage/features/join_feature.py +146 -0
  37. sage/features/kenzo.py +77 -0
  38. sage/features/latex.py +300 -0
  39. sage/features/latte.py +85 -0
  40. sage/features/lrs.py +164 -0
  41. sage/features/mcqd.py +45 -0
  42. sage/features/meataxe.py +46 -0
  43. sage/features/mip_backends.py +114 -0
  44. sage/features/msolve.py +68 -0
  45. sage/features/nauty.py +70 -0
  46. sage/features/normaliz.py +43 -0
  47. sage/features/palp.py +65 -0
  48. sage/features/pandoc.py +42 -0
  49. sage/features/pdf2svg.py +41 -0
  50. sage/features/phitigra.py +42 -0
  51. sage/features/pkg_systems.py +195 -0
  52. sage/features/polymake.py +43 -0
  53. sage/features/poppler.py +58 -0
  54. sage/features/rubiks.py +180 -0
  55. sage/features/sagemath.py +1205 -0
  56. sage/features/sat.py +103 -0
  57. sage/features/singular.py +48 -0
  58. sage/features/sirocco.py +45 -0
  59. sage/features/sphinx.py +71 -0
  60. sage/features/standard.py +38 -0
  61. sage/features/symengine_py.py +44 -0
  62. sage/features/tdlib.py +38 -0
  63. sage/features/threejs.py +75 -0
  64. sage/features/topcom.py +67 -0
  65. sage/misc/all__sagemath_environment.py +2 -0
  66. sage/misc/package.py +570 -0
  67. sage/misc/package_dir.py +621 -0
  68. sage/misc/temporary_file.py +546 -0
  69. sage/misc/viewer.py +369 -0
  70. sage/version.py +5 -0
@@ -0,0 +1,546 @@
1
+ # sage_setup: distribution = sagemath-environment
2
+ """
3
+ Temporary file handling
4
+
5
+ AUTHORS:
6
+
7
+ - Volker Braun, Jeroen Demeyer (2012-10-18): move these functions here
8
+ from sage/misc/misc.py and make them secure, see :issue:`13579`.
9
+
10
+ - Jeroen Demeyer (2013-03-17): add :class:`atomic_write`,
11
+ see :issue:`14292`.
12
+
13
+ - Sebastian Oehms (2021-08-07): add :class:`atomic_dir`,
14
+ see :issue:`32344`
15
+ """
16
+ # ****************************************************************************
17
+ # Copyright (C) 2012 Volker Braun <vbraun@stp.dias.ie>
18
+ # Copyright (C) 2012 Jeroen Demeyer <jdemeyer@cage.ugent.be>
19
+ #
20
+ # Distributed under the terms of the GNU General Public License (GPL)
21
+ # as published by the Free Software Foundation; either version 2 of
22
+ # the License, or (at your option) any later version.
23
+ # https://www.gnu.org/licenses/
24
+ # ****************************************************************************
25
+
26
+ import atexit
27
+ import os
28
+ import tempfile
29
+ from typing import IO
30
+
31
+ # Until tmp_dir() and tmp_filename() are removed, we use this directory
32
+ # as the parent for all temporary files & directories created by them.
33
+ # This lets us clean up after those two functions when sage exits normally
34
+ # using an atexit hook
35
+ TMP_DIR_FILENAME_BASE = tempfile.TemporaryDirectory()
36
+ atexit.register(lambda: TMP_DIR_FILENAME_BASE.cleanup())
37
+
38
+
39
+ #################################################################
40
+ # temporary directory
41
+ #################################################################
42
+
43
+ def tmp_dir(name='dir_', ext='') -> str:
44
+ r"""
45
+ Create and return a temporary directory in
46
+ ``$HOME/.sage/temp/hostname/pid/``
47
+
48
+ The temporary directory is deleted automatically when Sage exits.
49
+
50
+ INPUT:
51
+
52
+ - ``name`` -- (default: ``'dir_'``) a prefix for the directory name
53
+
54
+ - ``ext`` -- (default: ``''``) a suffix for the directory name
55
+
56
+ OUTPUT:
57
+
58
+ The absolute path of the temporary directory created, with a
59
+ trailing slash (or whatever the path separator is on your OS).
60
+
61
+ EXAMPLES::
62
+
63
+ sage: d = tmp_dir('dir_testing_', '.extension')
64
+ sage: d # random output
65
+ '/home/username/.sage/temp/hostname/7961/dir_testing_XgRu4p.extension/'
66
+ sage: os.chdir(d)
67
+ sage: f = open('file_inside_d', 'w')
68
+
69
+ Temporary directories are unaccessible by other users::
70
+
71
+ sage: os.stat(d).st_mode & 0o077
72
+ 0
73
+ sage: f.close()
74
+ """
75
+ tmp = tempfile.mkdtemp(prefix=name,
76
+ suffix=ext,
77
+ dir=TMP_DIR_FILENAME_BASE.name)
78
+ name = os.path.abspath(tmp)
79
+ return name + os.sep
80
+
81
+
82
+ #################################################################
83
+ # temporary filename
84
+ #################################################################
85
+
86
+ def tmp_filename(name='tmp_', ext='') -> str:
87
+ r"""
88
+ Create and return a temporary file in
89
+ ``$HOME/.sage/temp/hostname/pid/``
90
+
91
+ The temporary file is deleted automatically when Sage exits.
92
+
93
+ .. warning::
94
+
95
+ If you need a particular file extension always use
96
+ ``tmp_filename(ext='.foo')``, this will ensure that the file
97
+ does not yet exist. If you were to use
98
+ ``tmp_filename()+".foo"``, then you might overwrite an
99
+ existing file!
100
+
101
+ INPUT:
102
+
103
+ - ``name`` -- (default: ``'tmp_'``) a prefix for the file name
104
+
105
+ - ``ext`` -- (default: ``''``) a suffix for the file name. If you
106
+ want a filename extension in the usual sense, this should start
107
+ with a dot.
108
+
109
+ OUTPUT: the absolute path of the temporary file created
110
+
111
+ EXAMPLES::
112
+
113
+ sage: fn = tmp_filename('just_for_testing_', '.extension')
114
+ sage: fn # random
115
+ '/home/username/.sage/temp/hostname/8044/just_for_testing_tVVHsn.extension'
116
+ sage: f = open(fn, 'w')
117
+
118
+ Temporary files are unaccessible by other users::
119
+
120
+ sage: os.stat(fn).st_mode & 0o077
121
+ 0
122
+ sage: f.close()
123
+ """
124
+ handle, tmp = tempfile.mkstemp(prefix=name,
125
+ suffix=ext,
126
+ dir=TMP_DIR_FILENAME_BASE.name)
127
+ os.close(handle)
128
+ name = os.path.abspath(tmp)
129
+ return name
130
+
131
+
132
+ #################################################################
133
+ # write to a temporary file and move it in place
134
+ #################################################################
135
+ class atomic_write:
136
+ """
137
+ Write to a given file using a temporary file and then rename it
138
+ to the target file. This renaming should be atomic on modern
139
+ operating systems. Therefore, this class can be used to avoid race
140
+ conditions when a file might be read while it is being written.
141
+ It also avoids having partially written files due to exceptions
142
+ or crashes.
143
+
144
+ This is to be used in a ``with`` statement, where a temporary file
145
+ is created when entering the ``with`` and is moved in place of the
146
+ target file when exiting the ``with`` (if no exceptions occurred).
147
+
148
+ INPUT:
149
+
150
+ - ``target_filename`` -- the name of the file to be written
151
+ Normally, the contents of this file will be overwritten
152
+
153
+ - ``append`` -- boolean (default: ``False``); if ``True`` and
154
+ ``target_filename`` is an existing file, then copy the current
155
+ contents of ``target_filename`` to the temporary file when
156
+ entering the ``with`` statement. Otherwise, the temporary file is
157
+ initially empty.
158
+
159
+ - ``mode`` -- (default: ``0o666``) mode bits for the file. The
160
+ temporary file is created with mode ``mode & ~umask`` and the
161
+ resulting file will also have these permissions (unless the
162
+ mode bits of the file were changed manually). (Not to be confused with
163
+ the file opening mode.)
164
+
165
+ - ``binary`` -- boolean (default: ``False``);
166
+ the underlying file is opened in binary mode. If ``False`` then it is
167
+ opened in text mode and an encoding with which to write the file may be
168
+ supplied.
169
+
170
+ - ``**kwargs`` -- additional keyword arguments passed to the underlying
171
+ `io.open` call
172
+
173
+ EXAMPLES::
174
+
175
+ sage: from sage.misc.temporary_file import atomic_write
176
+ sage: target_file = tmp_filename()
177
+ sage: with open(target_file, 'w') as f:
178
+ ....: _ = f.write("Old contents")
179
+ sage: with atomic_write(target_file) as f:
180
+ ....: _ = f.write("New contents")
181
+ ....: f.flush()
182
+ ....: with open(target_file, 'r') as f2:
183
+ ....: f2.read()
184
+ 'Old contents'
185
+ sage: with open(target_file, 'r') as f:
186
+ ....: f.read()
187
+ 'New contents'
188
+
189
+ The name of the temporary file can be accessed using ``f.name``.
190
+ It is not a problem to close and re-open the temporary file::
191
+
192
+ sage: from sage.misc.temporary_file import atomic_write
193
+ sage: target_file = tmp_filename()
194
+ sage: with open(target_file, 'w') as f:
195
+ ....: _ = f.write("Old contents")
196
+ sage: with atomic_write(target_file) as f:
197
+ ....: f.close()
198
+ ....: with open(f.name, 'w') as f2:
199
+ ....: _ = f2.write("Newer contents")
200
+ sage: with open(target_file, 'r') as f:
201
+ ....: f.read()
202
+ 'Newer contents'
203
+
204
+ If an exception occurs while writing the file, the target file is
205
+ not touched::
206
+
207
+ sage: with atomic_write(target_file) as f:
208
+ ....: _ = f.write("Newest contents")
209
+ ....: raise RuntimeError
210
+ Traceback (most recent call last):
211
+ ...
212
+ RuntimeError
213
+ sage: with open(target_file, 'r') as f:
214
+ ....: f.read()
215
+ 'Newer contents'
216
+
217
+ Some examples of using the ``append`` option. Note that the file
218
+ is never opened in "append" mode, it is possible to overwrite
219
+ existing data::
220
+
221
+ sage: target_file = tmp_filename()
222
+ sage: with atomic_write(target_file, append=True) as f:
223
+ ....: _ = f.write("Hello")
224
+ sage: with atomic_write(target_file, append=True) as f:
225
+ ....: _ = f.write(" World")
226
+ sage: with open(target_file, 'r') as f:
227
+ ....: f.read()
228
+ 'Hello World'
229
+ sage: with atomic_write(target_file, append=True) as f:
230
+ ....: _ = f.seek(0)
231
+ ....: _ = f.write("HELLO")
232
+ sage: with open(target_file, 'r') as f:
233
+ ....: f.read()
234
+ 'HELLO World'
235
+
236
+ If the target file is a symbolic link, the link is kept and the
237
+ target of the link is written to::
238
+
239
+ sage: link_to_target = os.path.join(tmp_dir(), "templink")
240
+ sage: os.symlink(target_file, link_to_target)
241
+ sage: with atomic_write(link_to_target) as f:
242
+ ....: _ = f.write("Newest contents")
243
+ sage: with open(target_file, 'r') as f:
244
+ ....: f.read()
245
+ 'Newest contents'
246
+
247
+ We check the permission bits of the new file. Note that the old
248
+ permissions do not matter::
249
+
250
+ sage: os.chmod(target_file, 0o600)
251
+ sage: _ = os.umask(0o022)
252
+ sage: with atomic_write(target_file) as f:
253
+ ....: pass
254
+ sage: '{:#o}'.format(os.stat(target_file).st_mode & 0o777)
255
+ '0o644'
256
+ sage: _ = os.umask(0o077)
257
+ sage: with atomic_write(target_file, mode=0o777) as f:
258
+ ....: pass
259
+ sage: '{:#o}'.format(os.stat(target_file).st_mode & 0o777)
260
+ '0o700'
261
+
262
+ Test writing twice to the same target file. The outermost ``with``
263
+ "wins"::
264
+
265
+ sage: with open(target_file, 'w') as f:
266
+ ....: _ = f.write('>>> ')
267
+ sage: with atomic_write(target_file, append=True) as f, \
268
+ ....: atomic_write(target_file, append=True) as g:
269
+ ....: _ = f.write("AAA"); f.close()
270
+ ....: _ = g.write("BBB"); g.close()
271
+ sage: with open(target_file, 'r') as f:
272
+ ....: f.read()
273
+ '>>> AAA'
274
+
275
+ Supplying an encoding means we're writing the file in "text mode" (in the
276
+ same sense as `io.open`) and so we must write unicode strings::
277
+
278
+ sage: target_file = tmp_filename()
279
+ sage: with atomic_write(target_file, binary=False,
280
+ ....: encoding='utf-8') as f:
281
+ ....: _ = f.write(u'Hélas')
282
+ sage: import io
283
+ sage: with io.open(target_file, encoding='utf-8') as f:
284
+ ....: print(f.read())
285
+ Hélas
286
+
287
+ Supplying an encoding in binary mode (or other arguments that don't
288
+ make sense to `io.open` in binary mode) is an error::
289
+
290
+ sage: writer = atomic_write(target_file, binary=True,
291
+ ....: encoding='utf-8')
292
+ sage: with writer as f:
293
+ ....: _ = f.write(u'Hello')
294
+ Traceback (most recent call last):
295
+ ...
296
+ ValueError: binary mode doesn't take an encoding argument
297
+ sage: os.path.exists(writer.tempname)
298
+ False
299
+ """
300
+ def __init__(self, target_filename, append=False, mode=0o666,
301
+ binary=False, **kwargs) -> None:
302
+ """
303
+ TESTS::
304
+
305
+ sage: from sage.misc.temporary_file import atomic_write
306
+ sage: link_to_target = os.path.join(tmp_dir(), "templink")
307
+ sage: os.symlink("/foobar", link_to_target)
308
+ sage: aw = atomic_write(link_to_target)
309
+ sage: print(aw.target)
310
+ /foobar
311
+ sage: print(aw.tmpdir)
312
+ /
313
+ """
314
+ self.target = os.path.realpath(target_filename)
315
+ self.tmpdir = os.path.dirname(self.target)
316
+ self.append = append
317
+ # Remove umask bits from mode
318
+ umask = os.umask(0)
319
+ os.umask(umask)
320
+ self.mode = mode & (~umask)
321
+
322
+ # 'text' mode is the default on Python 3
323
+ self.binary = binary
324
+ self.kwargs = kwargs
325
+
326
+ def __enter__(self) -> IO:
327
+ """
328
+ Create and return a temporary file in ``self.tmpdir`` (normally
329
+ the same directory as the target file).
330
+
331
+ If ``self.append``, then copy the current contents of
332
+ ``self.target`` to the temporary file.
333
+
334
+ OUTPUT: a file returned by :func:`tempfile.NamedTemporaryFile`
335
+
336
+ TESTS::
337
+
338
+ sage: from sage.misc.temporary_file import atomic_write
339
+ sage: aw = atomic_write(tmp_filename())
340
+ sage: with aw as f:
341
+ ....: os.path.dirname(aw.target) == os.path.dirname(f.name)
342
+ True
343
+ """
344
+
345
+ fd, name = tempfile.mkstemp(dir=self.tmpdir)
346
+ self.tempname = os.path.abspath(name)
347
+
348
+ rmode = 'r' + ('b' if self.binary else '')
349
+ wmode = 'w+' + ('b' if self.binary else '')
350
+
351
+ try:
352
+ self.tempfile = open(name, wmode, **self.kwargs)
353
+ except Exception:
354
+ # Some invalid arguments were passed to io.open
355
+ os.unlink(name)
356
+ raise
357
+ finally:
358
+ os.close(fd)
359
+
360
+ os.chmod(name, self.mode)
361
+ if self.append:
362
+ try:
363
+ with open(self.target, rmode, **self.kwargs) as f:
364
+ r = f.read()
365
+ except OSError:
366
+ pass
367
+ else:
368
+ self.tempfile.write(r)
369
+
370
+ return self.tempfile
371
+
372
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
373
+ """
374
+ If the ``with`` block was successful, move the temporary file
375
+ to the target file. Otherwise, delete the temporary file.
376
+
377
+ TESTS:
378
+
379
+ Check that the temporary file is deleted if there was an
380
+ exception::
381
+
382
+ sage: from sage.misc.temporary_file import atomic_write
383
+ sage: with atomic_write(tmp_filename()) as f:
384
+ ....: tempname = f.name
385
+ ....: raise RuntimeError
386
+ Traceback (most recent call last):
387
+ ...
388
+ RuntimeError
389
+ sage: os.path.exists(tempname)
390
+ False
391
+ """
392
+ # Flush the file contents to disk (to be safe even if the
393
+ # system crashes) and close the file.
394
+ if not self.tempfile.closed:
395
+ self.tempfile.flush()
396
+ os.fsync(self.tempfile.fileno())
397
+ self.tempfile.close()
398
+
399
+ if exc_type is None:
400
+ # Success: move temporary file to target file
401
+ try:
402
+ os.rename(self.tempname, self.target)
403
+ except OSError:
404
+ os.unlink(self.target)
405
+ os.rename(self.tempname, self.target)
406
+ else:
407
+ # Failure: delete temporary file
408
+ os.unlink(self.tempname)
409
+
410
+ #################################################################
411
+ # write to a temporary directory and move it in place
412
+ #################################################################
413
+
414
+
415
+ class atomic_dir:
416
+ """
417
+ Write to a given directory using a temporary directory and then rename it
418
+ to the target directory. This is for creating a directory whose contents
419
+ are determined uniquely by the directory name. If multiple threads or
420
+ processes attempt to create it in parallel, then it does not matter which
421
+ thread created it. Despite this assumption the contents of the directories
422
+ differ in the examples for demonstration purpose.
423
+
424
+ See also :class:`atomic_write`.
425
+
426
+ INPUT:
427
+
428
+ - ``target_directory`` -- the name of the directory to be written;
429
+ if it exists then the previous contents will be kept
430
+
431
+ EXAMPLES::
432
+
433
+ sage: from sage.misc.temporary_file import atomic_dir
434
+ sage: target_dir = tmp_dir()
435
+ sage: with atomic_dir(target_dir) as d:
436
+ ....: target_file = os.path.join(d.name, 'test')
437
+ ....: with open(target_file, 'w') as f:
438
+ ....: _ = f.write("First")
439
+ ....: f.flush()
440
+ ....: with atomic_dir(target_dir) as e:
441
+ ....: target_file2 = os.path.join(e.name, 'test')
442
+ ....: with open(target_file2, 'w') as g:
443
+ ....: _ = g.write("Second")
444
+ ....: g.flush()
445
+ ....: with open(target_file, 'r') as f:
446
+ ....: f.read()
447
+ 'First'
448
+ sage: with atomic_dir(target_dir) as d:
449
+ ....: target_file = os.path.join(d.name, 'test')
450
+ ....: with open(target_file, 'w') as f:
451
+ ....: _ = f.write("Third")
452
+ sage: target = os.path.join(target_dir, 'test')
453
+ sage: with open(target, 'r') as h:
454
+ ....: h.read()
455
+ 'Second'
456
+ """
457
+ def __init__(self, target_directory) -> None:
458
+ r"""
459
+ TESTS::
460
+
461
+ sage: from sage.misc.temporary_file import atomic_dir
462
+ sage: link_to_target = os.path.join(tmp_dir(), "templink")
463
+ sage: os.symlink("/foobar", link_to_target)
464
+ sage: aw = atomic_dir(link_to_target)
465
+ sage: print(aw.target)
466
+ /foobar
467
+ sage: print(aw.tmpdir)
468
+ /
469
+ """
470
+ self.target = os.path.realpath(target_directory)
471
+ self.tmpdir = os.path.dirname(self.target)
472
+
473
+ def __enter__(self):
474
+ r"""
475
+ Create and return a temporary directory in ``self.tmpdir`` (normally
476
+ the same directory as the target file).
477
+
478
+ OUTPUT: a directory returned by :func:`tempfile.TemporaryDirectory`
479
+
480
+ TESTS::
481
+
482
+ sage: from sage.misc.temporary_file import atomic_dir
483
+ sage: aw = atomic_dir(tmp_dir())
484
+ sage: with aw as d:
485
+ ....: os.path.dirname(aw.target) == os.path.dirname(d.name)
486
+ True
487
+ """
488
+ tdir = tempfile.TemporaryDirectory(dir=self.tmpdir)
489
+ self.tempname = os.path.abspath(tdir.name)
490
+ return tdir
491
+
492
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
493
+ """
494
+ If the ``with`` block was successful, move the temporary directory
495
+ to the target directory. Otherwise, delete the temporary directory.
496
+
497
+ TESTS:
498
+
499
+ Check that the temporary directory is deleted if there was an
500
+ exception::
501
+
502
+ sage: from sage.misc.temporary_file import atomic_dir
503
+ sage: with atomic_dir(tmp_dir()) as d:
504
+ ....: tempname = d.name
505
+ ....: raise RuntimeError
506
+ Traceback (most recent call last):
507
+ ...
508
+ RuntimeError
509
+ sage: os.path.exists(tempname)
510
+ False
511
+ """
512
+ import shutil
513
+ if exc_type is None:
514
+ # Success: move temporary file to target file
515
+ try:
516
+ os.rename(self.tempname, self.target)
517
+ except OSError:
518
+ # Race: Another thread or process must have created
519
+ # the directory
520
+ pass
521
+ else:
522
+ # Failure: delete temporary file
523
+ shutil.rmtree(self.tempname)
524
+
525
+
526
+ _spyx_tmp = None
527
+
528
+
529
+ def spyx_tmp() -> str:
530
+ r"""
531
+ The temporary directory used to store pyx files.
532
+
533
+ We cache the result of this function "by hand" so that the same
534
+ temporary directory will always be returned. A function is used to
535
+ delay creating a directory until (if) it is needed. The temporary
536
+ directory is removed when sage terminates by way of an atexit
537
+ hook.
538
+ """
539
+ global _spyx_tmp
540
+ if _spyx_tmp:
541
+ return _spyx_tmp
542
+
543
+ d = tempfile.TemporaryDirectory()
544
+ _spyx_tmp = os.path.join(d.name, 'spyx')
545
+ atexit.register(lambda: d.cleanup())
546
+ return _spyx_tmp