-
-
Notifications
You must be signed in to change notification settings - Fork 404
/
all.py
125 lines (99 loc) · 4.09 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
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