-
-
Notifications
You must be signed in to change notification settings - Fork 404
/
all.py
126 lines (100 loc) · 4.14 KB
/
all.py
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
# sage_setup: distribution = sagemath-environment
r"""
Enumeration of all defined features
"""
# *****************************************************************************
# Copyright (C) 2021-2023 Matthias Koeppe
#
# Distributed under the terms of the GNU General Public License (GPL)
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# https://www.gnu.org/licenses/
# *****************************************************************************
import itertools
def all_features():
r"""
Return an iterable of all features.
EXAMPLES::
sage: from sage.features.all import all_features
sage: sorted(all_features(), key=lambda f: f.name) # random
[...Feature('sage.combinat')...]
"""
import pkgutil
import importlib
import sage.features
# Following https://packaging.python.org/guides/creating-and-discovering-plugins/#using-namespace-packages
for finder, name, ispkg in pkgutil.iter_modules(sage.features.__path__, sage.features.__name__ + "."):
module = importlib.import_module(name)
try:
af = module.all_features
except AttributeError:
pass
else:
if af != all_features:
yield from af()
def module_feature(module_name):
r"""
Find a top-level :class:`Feature` that provides the Python module of the given ``module_name``.
Only features known to :func:`all_features` are considered.
INPUT:
- ``module_name`` -- string
OUTPUT: a :class:`Feature` or ``None``.
EXAMPLES::
sage: from sage.features.all import module_feature
sage: module_feature('sage.combinat.tableau') # needs sage.combinat
Feature('sage.combinat')
sage: module_feature('sage.combinat.posets.poset') # needs sage.graphs
Feature('sage.graphs')
sage: module_feature('sage.schemes.toric.variety') # needs sage.geometry.polyhedron
Feature('sage.geometry.polyhedron')
sage: module_feature('scipy') # needs scipy
Feature('scipy')
sage: print(module_feature('sage.structure.element'))
None
sage: print(module_feature('sage.does_not_exist'))
None
"""
longest_prefix = ''
longest_prefix_feature = None
for feature in all_features():
for joined in itertools.chain([feature], feature.joined_features()):
if joined.name == module_name:
return feature
if (joined.name + '.').startswith(longest_prefix):
if (module_name + '.').startswith(joined.name + '.'):
longest_prefix = feature.name + '.'
longest_prefix_feature = feature
return longest_prefix_feature
def name_feature(name, toplevel=None):
r"""
Find a top-level :class:`Feature` that provides the top-level ``name``.
Only features known to :func:`all_features` are considered.
INPUT:
- ``name`` -- string
- ``toplevel`` -- a module or other namespace
OUTPUT: a :class:`Feature` or ``None``.
EXAMPLES::
sage: from sage.features.all import name_feature
sage: name_feature('QuadraticField') # needs sage.rings.number_field
Feature('sage.rings.number_field')
sage: name_feature('line') # needs sage.plot
Feature('sage.plot')
sage: print(name_feature('ZZ'))
None
sage: print(name_feature('does_not_exist'))
None
"""
if toplevel is None:
try:
import sage.all as toplevel
except ImportError:
return None
try:
obj = getattr(toplevel, name)
except AttributeError:
return None
from sage.misc.dev_tools import find_object_modules
for module, names in find_object_modules(obj).items():
if name in names and (feature := module_feature(module)):
return feature
return None