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,621 @@
1
+ # sage_setup: distribution = sagemath-environment
2
+ """
3
+ Recognizing package directories
4
+ """
5
+ # ****************************************************************************
6
+ # Copyright (C) 2020-2022 Matthias Koeppe
7
+ #
8
+ # This program is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ # https://www.gnu.org/licenses/
13
+ # ****************************************************************************
14
+
15
+ import os
16
+ import glob
17
+ import re
18
+ import sys
19
+
20
+ from collections import defaultdict
21
+ from contextlib import contextmanager
22
+
23
+
24
+ class SourceDistributionFilter:
25
+ r"""
26
+ A :class:`collections.abc.Container` for source files in distributions.
27
+
28
+ INPUT:
29
+
30
+ - ``include_distributions`` -- (default: ``None``) if not ``None``,
31
+ should be a sequence or set of strings: include files whose
32
+ ``distribution`` (from a ``# sage_setup:`` ``distribution = PACKAGE``
33
+ directive in the source file) is an element of ``distributions``.
34
+
35
+ - ``exclude_distributions`` -- (default: ``None``) if not ``None``,
36
+ should be a sequence or set of strings: exclude files whose
37
+ ``distribution`` (from a ``# sage_setup:`` ``distribution = PACKAGE``
38
+ directive in the module source file) is in ``exclude_distributions``.
39
+
40
+ EXAMPLES::
41
+
42
+ sage: from sage.misc.package_dir import SourceDistributionFilter
43
+ sage: F = SourceDistributionFilter()
44
+ sage: sage.misc.package_dir.__file__ in F
45
+ True
46
+ sage: F = SourceDistributionFilter(include_distributions=['sagemath-environment'])
47
+ sage: sage.misc.package_dir.__file__ in F
48
+ True
49
+ sage: F = SourceDistributionFilter(exclude_distributions=['sagemath-environment'])
50
+ sage: sage.misc.package_dir.__file__ in F
51
+ False
52
+ """
53
+ def __init__(self, include_distributions=None, exclude_distributions=None):
54
+ r"""
55
+ TESTS:
56
+
57
+ ``exclude_distributions=None`` is normalized to the empty tuple::
58
+
59
+ sage: from sage.misc.package_dir import SourceDistributionFilter
60
+ sage: F = SourceDistributionFilter()
61
+ sage: F._exclude_distributions
62
+ ()
63
+ """
64
+ self._include_distributions = include_distributions
65
+ if exclude_distributions is None:
66
+ exclude_distributions = ()
67
+ self._exclude_distributions = exclude_distributions
68
+
69
+ def __contains__(self, filename):
70
+ r"""
71
+ TESTS:
72
+
73
+ No file access is used when neither ``include_distributions`` nor
74
+ ``exclude_distributions`` is given::
75
+
76
+ sage: from sage.misc.package_dir import SourceDistributionFilter
77
+ sage: F = SourceDistributionFilter()
78
+ sage: '/doesnotexist' in F
79
+ True
80
+
81
+ ``exclude_distributions`` can also be an empty container::
82
+
83
+ sage: F = SourceDistributionFilter(exclude_distributions=())
84
+ sage: '/doesnotexist' in F
85
+ True
86
+ """
87
+ if self._include_distributions is None and not self._exclude_distributions:
88
+ return True
89
+ distribution = read_distribution(filename)
90
+ if self._include_distributions is not None:
91
+ if distribution not in self._include_distributions:
92
+ return False
93
+ return distribution not in self._exclude_distributions
94
+
95
+
96
+ distribution_directive = re.compile(r"(\s*#?\s*)(sage_setup:\s*distribution\s*=\s*([-_A-Za-z0-9]*))")
97
+
98
+
99
+ def read_distribution(src_file):
100
+ r"""
101
+ Parse ``src_file`` for a ``# sage_setup:`` ``distribution = PKG`` directive.
102
+
103
+ INPUT:
104
+
105
+ - ``src_file`` -- file name of a Python or Cython source file
106
+
107
+ OUTPUT:
108
+
109
+ A string, the name of the distribution package (``PKG``), or the empty
110
+ string if no directive was found.
111
+
112
+ EXAMPLES::
113
+
114
+ sage: # needs SAGE_SRC
115
+ sage: from sage.env import SAGE_SRC
116
+ sage: from sage.misc.package_dir import read_distribution
117
+ sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'tdlib.pyx'))
118
+ 'sagemath-tdlib'
119
+ sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'modular_decomposition.py'))
120
+ ''
121
+ """
122
+ with open(src_file, encoding='utf-8', errors='ignore') as fh:
123
+ for line in fh:
124
+ # Adapted from Cython's Build/Dependencies.py
125
+ line = line.lstrip()
126
+ if not line:
127
+ continue
128
+ if line.startswith('#') or line.startswith(';'):
129
+ line = line[1:].lstrip()
130
+ elif line.startswith('/*') or line.startswith('//') or line.startswith(';;'):
131
+ line = line[2:].lstrip()
132
+ else:
133
+ break
134
+ kind = "sage_setup:"
135
+ if line.startswith(kind):
136
+ key, _, value = (s.strip() for s in line[len(kind):].partition('='))
137
+ if key == "distribution":
138
+ return value
139
+ return ''
140
+
141
+
142
+ def update_distribution(src_file, distribution, *, verbose=False):
143
+ r"""
144
+ Add or update a ``# sage_setup:`` ``distribution = PKG`` directive in ``src_file``.
145
+
146
+ For a Python or Cython file, if a ``distribution`` directive
147
+ is not already present, it is added.
148
+
149
+ For any other file, if a ``distribution`` directive is not already
150
+ present, no action is taken.
151
+
152
+ INPUT:
153
+
154
+ - ``src_file`` -- file name of a source file
155
+
156
+ EXAMPLES::
157
+
158
+ sage: from sage.misc.package_dir import read_distribution, update_distribution
159
+ sage: import tempfile
160
+ sage: def test(filename, file_contents):
161
+ ....: with tempfile.TemporaryDirectory() as d:
162
+ ....: fname = os.path.join(d, filename)
163
+ ....: with open(fname, 'w') as f:
164
+ ....: f.write(file_contents)
165
+ ....: with open(fname, 'r') as f:
166
+ ....: print(f.read() + "====")
167
+ ....: update_distribution(fname, 'sagemath-categories')
168
+ ....: with open(fname, 'r') as f:
169
+ ....: print(f.read() + "====")
170
+ ....: update_distribution(fname, '')
171
+ ....: with open(fname, 'r') as f:
172
+ ....: print(f.read(), end="")
173
+ sage: test('module.py', '# Python file\n')
174
+ # Python file
175
+ ====
176
+ # sage_setup: distribution...= sagemath-categories
177
+ # Python file
178
+ ====
179
+ # sage_setup: distribution...=
180
+ # Python file
181
+ sage: test('file.cpp', '// sage_setup: ' 'distribution=sagemath-modules\n'
182
+ ....: '// C++ file with existing directive\n')
183
+ // sage_setup: distribution...=sagemath-modules
184
+ // C++ file with existing directive
185
+ ====
186
+ // sage_setup: distribution...= sagemath-categories
187
+ // C++ file with existing directive
188
+ ====
189
+ // sage_setup: distribution...=
190
+ // C++ file with existing directive
191
+ sage: test('file.cpp', '// C++ file without existing directive\n')
192
+ // C++ file without existing directive
193
+ ====
194
+ // C++ file without existing directive
195
+ ====
196
+ // C++ file without existing directive
197
+ """
198
+ if not distribution:
199
+ distribution = ''
200
+ directive = 'sage_setup: ' f'distribution = {distribution}'.rstrip()
201
+ try:
202
+ with open(src_file) as f:
203
+ src_lines = f.read().splitlines()
204
+ except UnicodeDecodeError:
205
+ # Silently skip binary files
206
+ return
207
+ any_found = False
208
+ any_change = False
209
+ for i, line in enumerate(src_lines):
210
+ if m := distribution_directive.search(line):
211
+ old_distribution = m.group(3)
212
+ if any_found:
213
+ # Found a second distribution directive; remove it.
214
+ if not (line := distribution_directive.sub(r'', line)):
215
+ line = None
216
+ else:
217
+ line = distribution_directive.sub(fr'\1{directive}', line)
218
+ if line != src_lines[i]:
219
+ src_lines[i] = line
220
+ any_change = True
221
+ if verbose:
222
+ print(f"{src_file}: changed 'sage_setup: " f"distribution' "
223
+ f"from {old_distribution!r} to {distribution!r}")
224
+ any_found = True
225
+ if not any_found:
226
+ if any(src_file.endswith(ext)
227
+ for ext in [".pxd", ".pxi", ".py", ".pyx", ".sage"]):
228
+ src_lines.insert(0, f'# {directive}')
229
+ any_change = True
230
+ if verbose:
231
+ print(f"{src_file}: added 'sage_setup: "
232
+ f"distribution = {distribution}' directive")
233
+ if not any_change:
234
+ return
235
+ with open(src_file, 'w') as f:
236
+ for line in src_lines:
237
+ if line is not None:
238
+ f.write(line + '\n')
239
+
240
+
241
+ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None):
242
+ r"""
243
+ Return whether ``path`` is a directory that contains a Python package.
244
+
245
+ Ordinary Python packages are recognized by the presence of ``__init__.py``.
246
+
247
+ Implicit namespace packages (PEP 420) are only recognized if they
248
+ follow the conventions of the Sage library, i.e., the directory contains
249
+ a file ``all.py`` or a file matching the pattern ``all__*.py``
250
+ such as ``all__sagemath_categories.py``.
251
+
252
+ INPUT:
253
+
254
+ - ``path`` -- a directory name
255
+
256
+ - ``distribution_filter`` -- (default: ``None``)
257
+ only consider ``all*.py`` files whose distribution (from a
258
+ ``# sage_setup:`` ``distribution = PACKAGE`` directive in the source file)
259
+ is an element of ``distribution_filter``.
260
+
261
+ EXAMPLES:
262
+
263
+ :mod:`sage.cpython` is an ordinary package::
264
+
265
+ sage: from sage.misc.package_dir import is_package_or_sage_namespace_package_dir
266
+ sage: directory = sage.cpython.__path__[0]; directory
267
+ '.../sage/cpython'
268
+ sage: is_package_or_sage_namespace_package_dir(directory)
269
+ True
270
+
271
+ :mod:`sage.libs.mpfr` only has an ``__init__.pxd`` file, but we consider
272
+ it a package directory for consistency with Cython::
273
+
274
+ sage: directory = os.path.join(sage.libs.__path__[0], 'mpfr'); directory
275
+ '.../sage/libs/mpfr'
276
+ sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml)
277
+ True
278
+
279
+ :mod:`sage` is designated to become an implicit namespace package::
280
+
281
+ sage: directory = sage.__path__[0]; directory
282
+ '.../sage'
283
+ sage: is_package_or_sage_namespace_package_dir(directory) # known bug (seen in build.yml)
284
+ True
285
+
286
+ Not a package::
287
+
288
+ sage: directory = os.path.join(sage.symbolic.__path__[0], 'ginac'); directory # needs sage.symbolic
289
+ '.../sage/symbolic/ginac'
290
+ sage: is_package_or_sage_namespace_package_dir(directory) # needs sage.symbolic
291
+ False
292
+ """
293
+ if os.path.exists(os.path.join(path, '__init__.py')): # ordinary package
294
+ return True
295
+ if os.path.exists(os.path.join(path, '__init__.pxd')): # for consistency with Cython
296
+ return True
297
+ fname = os.path.join(path, 'all.py')
298
+ if os.path.exists(fname):
299
+ if distribution_filter is None or fname in distribution_filter: # complete namespace package
300
+ return True
301
+ for fname in glob.iglob(os.path.join(path, 'all__*.py')):
302
+ if distribution_filter is None or fname in distribution_filter: # partial namespace package
303
+ return True
304
+ return False
305
+
306
+
307
+ @contextmanager
308
+ def cython_namespace_package_support():
309
+ r"""
310
+ Activate namespace package support in Cython 0.x.
311
+
312
+ See https://github.com/cython/cython/issues/2918#issuecomment-991799049
313
+ """
314
+ import Cython.Build.Dependencies
315
+ import Cython.Build.Cythonize
316
+ import Cython.Utils
317
+ orig_is_package_dir = Cython.Utils.is_package_dir
318
+ Cython.Utils.is_package_dir = Cython.Build.Cythonize.is_package_dir = Cython.Build.Dependencies.is_package_dir = Cython.Utils.cached_function(is_package_or_sage_namespace_package_dir)
319
+ try:
320
+ yield
321
+ finally:
322
+ Cython.Utils.is_package_dir = Cython.Build.Cythonize.is_package_dir = Cython.Build.Dependencies.is_package_dir = orig_is_package_dir
323
+
324
+
325
+ def walk_packages(path=None, prefix='', onerror=None):
326
+ r"""
327
+ Yield :class:`pkgutil.ModuleInfo` for all modules recursively on ``path``.
328
+
329
+ This version of the standard library function :func:`pkgutil.walk_packages`
330
+ addresses https://github.com/python/cpython/issues/73444 by handling
331
+ the implicit namespace packages in the package layout used by Sage;
332
+ see :func:`is_package_or_sage_namespace_package_dir`.
333
+
334
+ INPUT:
335
+
336
+ - ``path`` -- list of paths to look for modules in or
337
+ ``None`` (all accessible modules)
338
+
339
+ - ``prefix`` -- string to output on the front of every module name
340
+ on output
341
+
342
+ - ``onerror`` -- a function which gets called with one argument (the
343
+ name of the package which was being imported) if any exception
344
+ occurs while trying to import a package. If ``None``, ignore
345
+ :exc:`ImportError` but propagate all other exceptions.
346
+
347
+ EXAMPLES::
348
+
349
+ sage: sorted(sage.misc.package_dir.walk_packages(sage.misc.__path__)) # a namespace package
350
+ [..., ModuleInfo(module_finder=FileFinder('.../sage/misc'), name='package_dir', ispkg=False), ...]
351
+ """
352
+ # Adapted from https://github.com/python/cpython/blob/3.11/Lib/pkgutil.py
353
+
354
+ def iter_modules(path=None, prefix=''):
355
+ """
356
+ Yield :class:`ModuleInfo` for all submodules on ``path``.
357
+ """
358
+ from pkgutil import get_importer, iter_importers, ModuleInfo
359
+
360
+ if path is None:
361
+ importers = iter_importers()
362
+ elif isinstance(path, str):
363
+ raise ValueError("path must be None or list of paths to look for modules in")
364
+ else:
365
+ importers = map(get_importer, path)
366
+
367
+ yielded = {}
368
+ for i in importers:
369
+ for name, ispkg in iter_importer_modules(i, prefix):
370
+ if name not in yielded:
371
+ yielded[name] = 1
372
+ yield ModuleInfo(i, name, ispkg)
373
+
374
+ def iter_importer_modules(importer, prefix=''):
375
+ r"""
376
+ Yield :class:`ModuleInfo` for all modules of ``importer``.
377
+ """
378
+ from importlib.machinery import FileFinder
379
+
380
+ if isinstance(importer, FileFinder):
381
+ if importer.path is None or not os.path.isdir(importer.path):
382
+ return
383
+
384
+ yielded = {}
385
+ import inspect
386
+ try:
387
+ filenames = os.listdir(importer.path)
388
+ except OSError:
389
+ # ignore unreadable directories like import does
390
+ filenames = []
391
+ filenames.sort() # handle packages before same-named modules
392
+
393
+ for fn in filenames:
394
+ modname = inspect.getmodulename(fn)
395
+ if modname and (modname in ['__init__', 'all']
396
+ or modname.startswith('all__')
397
+ or modname in yielded):
398
+ continue
399
+
400
+ path = os.path.join(importer.path, fn)
401
+ ispkg = False
402
+
403
+ if not modname and os.path.isdir(path) and '.' not in fn:
404
+ modname = fn
405
+ if not (ispkg := is_package_or_sage_namespace_package_dir(path)):
406
+ continue
407
+
408
+ if modname and '.' not in modname:
409
+ yielded[modname] = 1
410
+ yield prefix + modname, ispkg
411
+
412
+ elif not hasattr(importer, 'iter_modules'):
413
+ yield from []
414
+
415
+ else:
416
+ yield from importer.iter_modules(prefix)
417
+
418
+ def seen(p, m={}):
419
+ if p in m:
420
+ return True
421
+ m[p] = True
422
+
423
+ for info in iter_modules(path, prefix):
424
+ yield info
425
+
426
+ if info.ispkg:
427
+ try:
428
+ __import__(info.name)
429
+ except ImportError:
430
+ if onerror is not None:
431
+ onerror(info.name)
432
+ except Exception:
433
+ if onerror is not None:
434
+ onerror(info.name)
435
+ else:
436
+ raise
437
+ else:
438
+ path = getattr(sys.modules[info.name], '__path__', None) or []
439
+
440
+ # don't traverse path items we've seen before
441
+ path = [p for p in path if not seen(p)]
442
+
443
+ yield from walk_packages(path, info.name + '.', onerror)
444
+
445
+
446
+ def _all_filename(distribution):
447
+ if not distribution:
448
+ return 'all.py'
449
+ return f"all__{distribution.replace('-', '_')}.py"
450
+
451
+
452
+ def _distribution_from_all_filename(filename):
453
+ if m := re.match('all(__(.*?))?[.]py', filename):
454
+ if distribution_per_all_filename := m.group(2):
455
+ return distribution_per_all_filename.replace('_', '-')
456
+ return ''
457
+ return False
458
+
459
+
460
+ if __name__ == '__main__':
461
+
462
+ from argparse import ArgumentParser
463
+
464
+ parser = ArgumentParser(prog="sage --fixdistributions",
465
+ description="Maintenance tool for distribution packages of the Sage library",
466
+ epilog="By default, '%(prog)s' shows the distribution of each file.")
467
+ parser.add_argument('--add', metavar='DISTRIBUTION', type=str, default=None,
468
+ help=("add a 'sage_setup: DISTRIBUTION' directive to FILES; "
469
+ "do not change files that already have a nonempty directive"))
470
+ parser.add_argument('--set', metavar='DISTRIBUTION', type=str, default=None,
471
+ help="add or update the 'sage_setup: DISTRIBUTION' directive in FILES")
472
+ parser.add_argument('--from-egg-info', action='store_true', default=False,
473
+ help="take FILES from pkgs/DISTRIBUTION/DISTRIBUTION.egg-info/SOURCES.txt")
474
+ parser.add_argument("filename", metavar='FILES', nargs='*', type=str,
475
+ help=("source files or directories (default: all files from SAGE_SRC, "
476
+ "unless --from-egg-info, --add, or --set are used)"))
477
+
478
+ args = parser.parse_args()
479
+
480
+ distribution = args.set or args.add or ''
481
+
482
+ if distribution == 'all':
483
+ distributions = ["sagemath-bliss",
484
+ "sagemath-coxeter3",
485
+ "sagemath-mcqd",
486
+ "sagemath-meataxe",
487
+ "sagemath-sirocco",
488
+ "sagemath-tdlib",
489
+ "sagemath-environment",
490
+ "sagemath-categories",
491
+ "sagemath-repl",
492
+ "sagemath-objects"]
493
+ else:
494
+ distributions = [distribution.replace('_', '-')]
495
+
496
+ if args.from_egg_info:
497
+ if not distribution:
498
+ print("Switch '--from-egg-info' must be used with either "
499
+ "'--add DISTRIBUTION' or '--set DISTRIBUTION'")
500
+ sys.exit(1)
501
+ elif not args.filename:
502
+ if distribution:
503
+ print("Switches '--add' and '--set' require the switch '--from-egg-info' "
504
+ "or one or more file or directory names")
505
+ sys.exit(1)
506
+ from sage.env import SAGE_SRC
507
+ if (not SAGE_SRC
508
+ or not os.path.exists(os.path.join(SAGE_SRC, 'sage'))
509
+ or not os.path.exists(os.path.join(SAGE_SRC, 'conftest_test.py'))):
510
+ print(f'{SAGE_SRC=} does not seem to contain a copy of the Sage source tree')
511
+ sys.exit(1)
512
+ args.filename = [os.path.join(SAGE_SRC, 'sage')]
513
+
514
+ ordinary_packages = set()
515
+ package_distributions_per_directives = defaultdict(set) # path -> set of strings (distributions)
516
+ package_distributions_per_all_files = defaultdict(set) # path -> set of strings (distributions)
517
+
518
+ def handle_file(root, file):
519
+ path = os.path.join(root, file)
520
+ if args.set is not None:
521
+ update_distribution(path, distribution, verbose=True)
522
+ file_distribution = distribution
523
+ elif args.add is not None:
524
+ if not (file_distribution := read_distribution(path)):
525
+ update_distribution(path, distribution, verbose=True)
526
+ file_distribution = distribution
527
+ else:
528
+ file_distribution = read_distribution(path)
529
+ print(f'{path}: file in distribution {file_distribution!r}')
530
+ package_distributions_per_directives[root].add(file_distribution)
531
+ if file.startswith('__init__.'):
532
+ ordinary_packages.add(root)
533
+ elif (distribution_per_all_filename := _distribution_from_all_filename(file)) is False:
534
+ # Not an all*.py file.
535
+ pass
536
+ elif not distribution_per_all_filename:
537
+ # An all.py file.
538
+ if file_distribution:
539
+ # The all.py is declared to belong to a named distribution, that's OK
540
+ package_distributions_per_all_files[root].add(file_distribution)
541
+ else:
542
+ pass
543
+ else:
544
+ # An all__*.py file
545
+ if distribution_per_all_filename != file_distribution:
546
+ print(f'{path}: file should go in distribution {distribution_per_all_filename!r}, not {file_distribution!r}')
547
+ package_distributions_per_all_files[root].add(distribution_per_all_filename)
548
+
549
+ for distribution in distributions:
550
+
551
+ paths = list(args.filename)
552
+
553
+ if args.from_egg_info:
554
+ from sage.env import SAGE_ROOT
555
+ if not distribution:
556
+ print("Switch '--from-egg-info' must be used with either "
557
+ "'--add DISTRIBUTION' or '--set DISTRIBUTION'")
558
+ sys.exit(1)
559
+ if not SAGE_ROOT:
560
+ print(f'{SAGE_ROOT=} does not seem to contain a copy of the Sage source root')
561
+ sys.exit(1)
562
+ distribution_dir = os.path.join(SAGE_ROOT, 'pkgs', distribution)
563
+ if not os.path.exists(distribution_dir):
564
+ print(f'{distribution_dir} does not exist')
565
+ sys.exit(1)
566
+ distribution_underscore = distribution.replace('-', '_')
567
+ try:
568
+ with open(os.path.join(distribution_dir,
569
+ f'{distribution_underscore}.egg-info', 'SOURCES.txt')) as f:
570
+ paths.extend(os.path.join(SAGE_ROOT, 'src', line.strip())
571
+ for line in f
572
+ if line.startswith('sage/'))
573
+ print(f"sage --fixdistributions: found egg-info of distribution {distribution!r}")
574
+ except FileNotFoundError:
575
+ if len(distributions) > 1:
576
+ print(f"sage --fixdistributions: distribution {distribution!r} does not have egg-info, skipping it; "
577
+ f"run 'make {distribution_underscore}-sdist' or 'make {distribution_underscore}' to create it")
578
+ continue
579
+ else:
580
+ print(f"sage --fixdistributions: distribution {distribution!r} does not have egg-info; "
581
+ f"run 'make {distribution_underscore}-sdist' or 'make {distribution_underscore}' to create it")
582
+ sys.exit(1)
583
+
584
+ for path in paths:
585
+ path = os.path.relpath(path)
586
+ if os.path.isdir(path):
587
+ if not is_package_or_sage_namespace_package_dir(path):
588
+ print(f'{path}: non-package directory')
589
+ else:
590
+ for root, dirs, files in os.walk(path):
591
+ for dir in sorted(dirs):
592
+ path = os.path.join(root, dir)
593
+ if any(dir.startswith(prefix) for prefix in ['.', 'build', 'dist', '__pycache__', '_vendor', '.tox']):
594
+ # Silently skip
595
+ dirs.remove(dir)
596
+ elif not is_package_or_sage_namespace_package_dir(path):
597
+ print(f'{path}: non-package directory')
598
+ dirs.remove(dir)
599
+ for file in sorted(files):
600
+ if any(file.endswith(ext) for ext in [".pyc", ".pyo", ".bak", ".so", "~"]):
601
+ continue
602
+ handle_file(root, file)
603
+ else:
604
+ handle_file(*os.path.split(path))
605
+
606
+ print(f"sage --fixdistributions: checking consistency")
607
+
608
+ for package in ordinary_packages:
609
+ if len(package_distributions_per_directives[package]) > 1:
610
+ print(f'{package}: ordinary packages (with __init__.py) cannot be split in several distributions ('
611
+ + ', '.join(f'{dist!r}'
612
+ for dist in sorted(package_distributions_per_directives[package])) + ')')
613
+
614
+ for package, distributions_per_directives in package_distributions_per_directives.items():
615
+ if package in ordinary_packages:
616
+ pass
617
+ elif ((missing_all_files := distributions_per_directives - package_distributions_per_all_files[package])
618
+ and not (missing_all_files == {''} and len(distributions_per_directives) < 2)):
619
+ s = '' if len(missing_all_files) == 1 else 's'
620
+ print(f'{package}: missing file{s} ' + ', '.join(_all_filename(distribution)
621
+ for distribution in missing_all_files))